1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "ui/views/corewm/tooltip_win.h"
6
7#include <winuser.h>
8
9#include "base/debug/stack_trace.h"
10#include "base/i18n/rtl.h"
11#include "base/logging.h"
12#include "ui/base/l10n/l10n_util_win.h"
13#include "ui/gfx/rect.h"
14#include "ui/gfx/screen.h"
15#include "ui/gfx/win/dpi.h"
16#include "ui/views/corewm/cursor_height_provider_win.h"
17
18namespace views {
19namespace corewm {
20
21TooltipWin::TooltipWin(HWND parent)
22    : parent_hwnd_(parent),
23      tooltip_hwnd_(NULL),
24      showing_(false) {
25  memset(&toolinfo_, 0, sizeof(toolinfo_));
26  toolinfo_.cbSize = sizeof(toolinfo_);
27  toolinfo_.uFlags = TTF_IDISHWND | TTF_TRACK | TTF_ABSOLUTE;
28  toolinfo_.uId = reinterpret_cast<UINT_PTR>(parent_hwnd_);
29  toolinfo_.hwnd = parent_hwnd_;
30  toolinfo_.lpszText = NULL;
31  toolinfo_.lpReserved = NULL;
32  SetRectEmpty(&toolinfo_.rect);
33}
34
35TooltipWin::~TooltipWin() {
36  if (tooltip_hwnd_)
37    DestroyWindow(tooltip_hwnd_);
38}
39
40bool TooltipWin::HandleNotify(int w_param, NMHDR* l_param, LRESULT* l_result) {
41  if (tooltip_hwnd_ == NULL)
42    return false;
43
44  switch (l_param->code) {
45    case TTN_POP:
46      showing_ = false;
47      return true;
48    case TTN_SHOW:
49      *l_result = TRUE;
50      PositionTooltip();
51      showing_ = true;
52      return true;
53    default:
54      break;
55  }
56  return false;
57}
58
59bool TooltipWin::EnsureTooltipWindow() {
60  if (tooltip_hwnd_)
61    return true;
62
63  tooltip_hwnd_ = CreateWindowEx(
64      WS_EX_TRANSPARENT | l10n_util::GetExtendedTooltipStyles(),
65      TOOLTIPS_CLASS, NULL, TTS_NOPREFIX | WS_POPUP, 0, 0, 0, 0,
66      parent_hwnd_, NULL, NULL, NULL);
67  if (!tooltip_hwnd_) {
68    PLOG(WARNING) << "tooltip creation failed, disabling tooltips";
69    return false;
70  }
71
72  l10n_util::AdjustUIFontForWindow(tooltip_hwnd_);
73
74  SendMessage(tooltip_hwnd_, TTM_ADDTOOL, 0,
75              reinterpret_cast<LPARAM>(&toolinfo_));
76  return true;
77}
78
79void TooltipWin::PositionTooltip() {
80  // This code only runs for non-metro, so GetNativeScreen() is fine.
81  gfx::Point screen_point = gfx::win::DIPToScreenPoint(location_);
82  const int cursoroffset = GetCurrentCursorVisibleHeight();
83  screen_point.Offset(0, cursoroffset);
84
85  DWORD tooltip_size = SendMessage(tooltip_hwnd_, TTM_GETBUBBLESIZE, 0,
86                                   reinterpret_cast<LPARAM>(&toolinfo_));
87  const gfx::Size size(LOWORD(tooltip_size), HIWORD(tooltip_size));
88
89  const gfx::Display display(
90      gfx::Screen::GetNativeScreen()->GetDisplayNearestPoint(screen_point));
91
92  gfx::Rect tooltip_bounds(screen_point, size);
93  tooltip_bounds.AdjustToFit(gfx::win::DIPToScreenRect(display.work_area()));
94  SetWindowPos(tooltip_hwnd_, NULL, tooltip_bounds.x(), tooltip_bounds.y(), 0,
95               0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
96}
97
98void TooltipWin::SetText(aura::Window* window,
99                         const base::string16& tooltip_text,
100                         const gfx::Point& location) {
101  if (!EnsureTooltipWindow())
102    return;
103
104  // See comment in header for details on why |location_| is needed.
105  location_ = location;
106
107  // Without this we get a flicker of the tooltip appearing at 0x0. Not sure
108  // why.
109  SetWindowPos(tooltip_hwnd_, NULL, 0, 0, 0, 0,
110               SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOMOVE |
111               SWP_NOREPOSITION | SWP_NOSIZE | SWP_NOZORDER);
112
113  base::string16 adjusted_text(tooltip_text);
114  base::i18n::AdjustStringForLocaleDirection(&adjusted_text);
115  toolinfo_.lpszText = const_cast<WCHAR*>(adjusted_text.c_str());
116  SendMessage(tooltip_hwnd_, TTM_SETTOOLINFO, 0,
117              reinterpret_cast<LPARAM>(&toolinfo_));
118
119  // This code only runs for non-metro, so GetNativeScreen() is fine.
120  const gfx::Point screen_point = gfx::win::DIPToScreenPoint(location_);
121  gfx::Display display(
122      gfx::Screen::GetNativeScreen()->GetDisplayNearestPoint(screen_point));
123  const gfx::Rect monitor_bounds = display.bounds();
124  int max_width = (monitor_bounds.width() + 1) / 2;
125  SendMessage(tooltip_hwnd_, TTM_SETMAXTIPWIDTH, 0, max_width);
126}
127
128void TooltipWin::Show() {
129  if (!EnsureTooltipWindow())
130    return;
131
132  SendMessage(tooltip_hwnd_, TTM_TRACKACTIVATE,
133              TRUE, reinterpret_cast<LPARAM>(&toolinfo_));
134  SetWindowPos(tooltip_hwnd_, HWND_TOPMOST, 0, 0, 0, 0,
135               SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOSIZE);
136}
137
138void TooltipWin::Hide() {
139  if (!tooltip_hwnd_)
140    return;
141
142  SendMessage(tooltip_hwnd_, TTM_TRACKACTIVATE, FALSE,
143              reinterpret_cast<LPARAM>(&toolinfo_));
144}
145
146bool TooltipWin::IsVisible() {
147  return showing_;
148}
149
150}  // namespace corewm
151}  // namespace views
152