Correct blacklist entry message
[chromium-blink-merge.git] / ui / views / widget / tooltip_manager_win.cc
blobf9965f9cc378035ccdc8f4107bd105279d62b448
1 // Copyright (c) 2012 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.
5 #include "ui/views/widget/tooltip_manager_win.h"
7 #include <windowsx.h>
9 #include <limits>
10 #include <vector>
12 #include "base/bind.h"
13 #include "base/i18n/rtl.h"
14 #include "base/logging.h"
15 #include "base/message_loop/message_loop.h"
16 #include "base/strings/string_split.h"
17 #include "base/strings/string_util.h"
18 #include "base/win/scoped_hdc.h"
19 #include "base/win/scoped_select_object.h"
20 #include "ui/base/l10n/l10n_util_win.h"
21 #include "ui/gfx/font_list.h"
22 #include "ui/gfx/screen.h"
23 #include "ui/gfx/text_elider.h"
24 #include "ui/gfx/text_utils.h"
25 #include "ui/gfx/win/dpi.h"
26 #include "ui/gfx/win/hwnd_util.h"
27 #include "ui/gfx/win/scoped_set_map_mode.h"
28 #include "ui/views/view.h"
29 #include "ui/views/widget/monitor_win.h"
30 #include "ui/views/widget/widget.h"
32 namespace views {
34 namespace {
36 static int tooltip_height_ = 0;
38 // Maximum number of lines we allow in the tooltip.
39 const size_t kMaxLines = 6;
41 // Trims the tooltip to fit, setting |text| to the clipped result, |max_width|
42 // to the width (in pixels) of the clipped text and |line_count| to the number
43 // of lines of text in the tooltip. |available_width| gives the space available
44 // for the tooltip.
45 void TrimTooltipToFit(const gfx::FontList& font_list,
46 int available_width,
47 base::string16* text,
48 int* max_width,
49 int* line_count) {
50 *max_width = 0;
51 *line_count = 0;
53 TooltipManager::TrimTooltipText(text);
55 // Split the string into at most kMaxLines lines.
56 std::vector<base::string16> lines;
57 base::SplitString(*text, '\n', &lines);
58 if (lines.size() > kMaxLines)
59 lines.resize(kMaxLines);
60 *line_count = static_cast<int>(lines.size());
62 // Format each line to fit.
63 base::string16 result;
64 for (std::vector<base::string16>::iterator i = lines.begin();
65 i != lines.end(); ++i) {
66 base::string16 elided_text =
67 gfx::ElideText(*i, font_list, available_width, gfx::ELIDE_AT_END);
68 *max_width = std::max(*max_width,
69 gfx::GetStringWidth(elided_text, font_list));
70 if (!result.empty())
71 result.push_back('\n');
72 result.append(elided_text);
74 *text = result;
77 } // namespace
79 // static
80 int TooltipManager::GetTooltipHeight() {
81 DCHECK_GT(tooltip_height_, 0);
82 return tooltip_height_;
85 static gfx::Font DetermineDefaultFont() {
86 HWND window = CreateWindowEx(
87 WS_EX_TRANSPARENT | l10n_util::GetExtendedTooltipStyles(),
88 TOOLTIPS_CLASS, NULL, 0 , 0, 0, 0, 0, NULL, NULL, NULL, NULL);
89 if (!window)
90 return gfx::Font();
91 HFONT hfont = reinterpret_cast<HFONT>(SendMessage(window, WM_GETFONT, 0, 0));
92 gfx::Font font = hfont ? gfx::Font(hfont) : gfx::Font();
93 DestroyWindow(window);
94 return font;
97 TooltipManagerWin::TooltipManagerWin(Widget* widget)
98 : widget_(widget),
99 tooltip_hwnd_(NULL),
100 last_mouse_pos_(-1, -1),
101 tooltip_showing_(false),
102 last_tooltip_view_(NULL),
103 last_view_out_of_sync_(false),
104 tooltip_width_(0) {
105 DCHECK(widget);
106 DCHECK(widget->GetNativeView());
109 TooltipManagerWin::~TooltipManagerWin() {
110 if (tooltip_hwnd_)
111 DestroyWindow(tooltip_hwnd_);
114 bool TooltipManagerWin::Init() {
115 DCHECK(!tooltip_hwnd_);
116 // Create the tooltip control.
117 tooltip_hwnd_ = CreateWindowEx(
118 WS_EX_TRANSPARENT | l10n_util::GetExtendedTooltipStyles(),
119 TOOLTIPS_CLASS, NULL, TTS_NOPREFIX, 0, 0, 0, 0,
120 GetParent(), NULL, NULL, NULL);
121 if (!tooltip_hwnd_)
122 return false;
124 l10n_util::AdjustUIFontForWindow(tooltip_hwnd_);
126 // This effectively turns off clipping of tooltips. We need this otherwise
127 // multi-line text (\r\n) won't work right. The size doesn't really matter
128 // (just as long as its bigger than the monitor's width) as we clip to the
129 // screen size before rendering.
130 SendMessage(tooltip_hwnd_, TTM_SETMAXTIPWIDTH, 0,
131 std::numeric_limits<int16>::max());
133 // Add one tool that is used for all tooltips.
134 toolinfo_.cbSize = sizeof(toolinfo_);
135 toolinfo_.uFlags = TTF_TRANSPARENT | TTF_IDISHWND;
136 toolinfo_.hwnd = GetParent();
137 toolinfo_.uId = reinterpret_cast<UINT_PTR>(GetParent());
138 // Setting this tells windows to call GetParent() back (using a WM_NOTIFY
139 // message) for the actual tooltip contents.
140 toolinfo_.lpszText = LPSTR_TEXTCALLBACK;
141 toolinfo_.lpReserved = NULL;
142 SetRectEmpty(&toolinfo_.rect);
143 SendMessage(tooltip_hwnd_, TTM_ADDTOOL, 0, (LPARAM)&toolinfo_);
144 return true;
147 gfx::NativeView TooltipManagerWin::GetParent() {
148 return widget_->GetNativeView();
151 const gfx::FontList& TooltipManagerWin::GetFontList() const {
152 static gfx::FontList* font_list = NULL;
153 if (!font_list)
154 font_list = new gfx::FontList(DetermineDefaultFont());
155 return *font_list;
158 void TooltipManagerWin::UpdateTooltip() {
159 // Set last_view_out_of_sync_ to indicate the view is currently out of sync.
160 // This doesn't update the view under the mouse immediately as it may cause
161 // timing problems.
162 last_view_out_of_sync_ = true;
163 last_tooltip_view_ = NULL;
164 // Hide the tooltip.
165 SendMessage(tooltip_hwnd_, TTM_POP, 0, 0);
168 void TooltipManagerWin::TooltipTextChanged(View* view) {
169 if (view == last_tooltip_view_)
170 UpdateTooltip(last_mouse_pos_);
173 LRESULT TooltipManagerWin::OnNotify(int w_param,
174 NMHDR* l_param,
175 bool* handled) {
176 *handled = false;
177 if (l_param->hwndFrom != tooltip_hwnd_)
178 return 0;
180 switch (l_param->code) {
181 case TTN_GETDISPINFO: {
182 if (last_view_out_of_sync_) {
183 // View under the mouse is out of sync, determine it now.
184 View* root_view = widget_->GetRootView();
185 last_tooltip_view_ =
186 root_view->GetTooltipHandlerForPoint(last_mouse_pos_);
187 last_view_out_of_sync_ = false;
189 // Tooltip control is asking for the tooltip to display.
190 NMTTDISPINFOW* tooltip_info =
191 reinterpret_cast<NMTTDISPINFOW*>(l_param);
192 // Initialize the string, if we have a valid tooltip the string will
193 // get reset below.
194 tooltip_info->szText[0] = TEXT('\0');
195 tooltip_text_.clear();
196 tooltip_info->lpszText = NULL;
197 clipped_text_.clear();
198 if (last_tooltip_view_ != NULL) {
199 tooltip_text_.clear();
200 // Mouse is over a View, ask the View for its tooltip.
201 gfx::Point view_loc = last_mouse_pos_;
202 View::ConvertPointToTarget(widget_->GetRootView(),
203 last_tooltip_view_, &view_loc);
204 if (last_tooltip_view_->GetTooltipText(view_loc, &tooltip_text_) &&
205 !tooltip_text_.empty()) {
206 // View has a valid tip, copy it into TOOLTIPINFO.
207 clipped_text_ = tooltip_text_;
208 gfx::Point screen_loc = last_mouse_pos_;
209 View::ConvertPointToScreen(widget_->GetRootView(), &screen_loc);
210 TrimTooltipToFit(
211 GetFontList(),
212 GetMaxWidth(screen_loc.x(), screen_loc.y(),
213 widget_->GetNativeView()),
214 &clipped_text_, &tooltip_width_, &line_count_);
215 // Adjust the clipped tooltip text for locale direction.
216 base::i18n::AdjustStringForLocaleDirection(&clipped_text_);
217 tooltip_info->lpszText = const_cast<WCHAR*>(clipped_text_.c_str());
218 } else {
219 tooltip_text_.clear();
222 *handled = true;
223 return 0;
225 case TTN_POP:
226 tooltip_showing_ = false;
227 *handled = true;
228 return 0;
229 case TTN_SHOW: {
230 *handled = true;
231 tooltip_showing_ = true;
232 // The tooltip is about to show, allow the view to position it
233 gfx::Point text_origin;
234 if (tooltip_height_ == 0)
235 tooltip_height_ = CalcTooltipHeight();
236 gfx::Point view_loc = last_mouse_pos_;
237 View::ConvertPointToTarget(widget_->GetRootView(),
238 last_tooltip_view_, &view_loc);
239 if (last_tooltip_view_->GetTooltipTextOrigin(view_loc, &text_origin) &&
240 SetTooltipPosition(text_origin.x(), text_origin.y())) {
241 // Return true, otherwise the rectangle we specified is ignored.
242 return TRUE;
244 return 0;
246 default:
247 // Fall through.
248 break;
250 return 0;
253 bool TooltipManagerWin::SetTooltipPosition(int text_x, int text_y) {
254 // NOTE: this really only tests that the y location fits on screen, but that
255 // is good enough for our usage.
257 // Calculate the bounds the tooltip will get.
258 gfx::Point view_loc;
259 View::ConvertPointToScreen(last_tooltip_view_, &view_loc);
260 view_loc = gfx::win::DIPToScreenPoint(view_loc);
261 RECT bounds = { view_loc.x() + text_x,
262 view_loc.y() + text_y,
263 view_loc.x() + text_x + tooltip_width_,
264 view_loc.y() + line_count_ * GetTooltipHeight() };
265 SendMessage(tooltip_hwnd_, TTM_ADJUSTRECT, TRUE, (LPARAM)&bounds);
267 // Make sure the rectangle completely fits on the current monitor. If it
268 // doesn't, return false so that windows positions the tooltip at the
269 // default location.
270 gfx::Rect monitor_bounds =
271 views::GetMonitorBoundsForRect(gfx::Rect(bounds.left, bounds.right,
272 0, 0));
273 if (!monitor_bounds.Contains(gfx::Rect(bounds))) {
274 return false;
277 ::SetWindowPos(tooltip_hwnd_, NULL, bounds.left, bounds.top, 0, 0,
278 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE);
279 return true;
282 int TooltipManagerWin::CalcTooltipHeight() {
283 // Ask the tooltip for its font.
284 int height;
285 HFONT hfont = reinterpret_cast<HFONT>(
286 SendMessage(tooltip_hwnd_, WM_GETFONT, 0, 0));
287 if (hfont != NULL) {
288 base::win::ScopedGetDC dc(tooltip_hwnd_);
289 base::win::ScopedSelectObject font(dc, hfont);
290 gfx::ScopedSetMapMode mode(dc, MM_TEXT);
291 TEXTMETRIC font_metrics;
292 GetTextMetrics(dc, &font_metrics);
293 height = font_metrics.tmHeight;
294 } else {
295 // Tooltip is using the system font. Use gfx::Font, which should pick
296 // up the system font.
297 height = gfx::Font().GetHeight();
299 // Get the margins from the tooltip
300 RECT tooltip_margin;
301 SendMessage(tooltip_hwnd_, TTM_GETMARGIN, 0, (LPARAM)&tooltip_margin);
302 return height + tooltip_margin.top + tooltip_margin.bottom;
305 void TooltipManagerWin::UpdateTooltip(const gfx::Point& mouse_pos) {
306 View* root_view = widget_->GetRootView();
307 View* view = root_view->GetTooltipHandlerForPoint(mouse_pos);
308 if (view != last_tooltip_view_) {
309 // NOTE: This *must* be sent regardless of the visibility of the tooltip.
310 // It triggers Windows to ask for the tooltip again.
311 SendMessage(tooltip_hwnd_, TTM_POP, 0, 0);
312 last_tooltip_view_ = view;
313 } else if (last_tooltip_view_ != NULL) {
314 // Tooltip is showing, and mouse is over the same view. See if the tooltip
315 // text has changed.
316 gfx::Point view_point = mouse_pos;
317 View::ConvertPointToTarget(root_view, last_tooltip_view_, &view_point);
318 string16 new_tooltip_text;
319 bool has_tooltip_text =
320 last_tooltip_view_->GetTooltipText(view_point, &new_tooltip_text);
321 if (!has_tooltip_text || (new_tooltip_text != tooltip_text_)) {
322 // The text has changed, hide the popup.
323 SendMessage(tooltip_hwnd_, TTM_POP, 0, 0);
324 if (has_tooltip_text && !new_tooltip_text.empty() && tooltip_showing_) {
325 // New text is valid, show the popup.
326 SendMessage(tooltip_hwnd_, TTM_POPUP, 0, 0);
332 void TooltipManagerWin::OnMouse(UINT u_msg, WPARAM w_param, LPARAM l_param) {
333 gfx::Point mouse_pos_in_pixels(l_param);
334 gfx::Point mouse_pos = gfx::win::ScreenToDIPPoint(mouse_pos_in_pixels);
336 if (u_msg >= WM_NCMOUSEMOVE && u_msg <= WM_NCXBUTTONDBLCLK) {
337 // NC message coordinates are in screen coordinates.
338 POINT temp = mouse_pos_in_pixels.ToPOINT();
339 ::MapWindowPoints(HWND_DESKTOP, GetParent(), &temp, 1);
340 mouse_pos_in_pixels.SetPoint(temp.x, temp.y);
341 mouse_pos = gfx::win::ScreenToDIPPoint(mouse_pos_in_pixels);
344 if (u_msg != WM_MOUSEMOVE || last_mouse_pos_ != mouse_pos) {
345 last_mouse_pos_ = mouse_pos;
346 UpdateTooltip(mouse_pos);
348 // Forward the message onto the tooltip.
349 MSG msg;
350 msg.hwnd = GetParent();
351 msg.message = u_msg;
352 msg.wParam = w_param;
353 msg.lParam = l_param;
354 SendMessage(tooltip_hwnd_, TTM_RELAYEVENT, 0, (LPARAM)&msg);
357 } // namespace views