Index: views/widget/tooltip_manager.cc |
=================================================================== |
--- views/widget/tooltip_manager.cc (revision 17063) |
+++ views/widget/tooltip_manager.cc (working copy) |
@@ -1,448 +0,0 @@ |
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-#include "views/widget/tooltip_manager.h" |
- |
-#include <windowsx.h> |
-#include <limits> |
- |
-#include "app/gfx/text_elider.h" |
-#include "app/l10n_util.h" |
-#include "app/l10n_util_win.h" |
-#include "app/win_util.h" |
-#include "base/logging.h" |
-#include "base/message_loop.h" |
-#include "views/view.h" |
-#include "views/widget/root_view.h" |
-#include "views/widget/widget.h" |
- |
-namespace views { |
- |
-//static |
-int TooltipManager::tooltip_height_ = 0; |
- |
-// Default timeout for the tooltip displayed using keyboard. |
-// Timeout is mentioned in milliseconds. |
-static const int kDefaultTimeout = 4000; |
- |
-// Maximum number of lines we allow in the tooltip. |
-static const int kMaxLines = 6; |
- |
-// Maximum number of characters we allow in a tooltip. |
-static const int kMaxTooltipLength = 1024; |
- |
-// Breaks |text| along line boundaries, placing each line of text into lines. |
-static void SplitTooltipString(const std::wstring& text, |
- std::vector<std::wstring>* lines) { |
- size_t index = 0; |
- size_t next_index; |
- while ((next_index = text.find(TooltipManager::GetLineSeparator(), index)) |
- != std::wstring::npos && lines->size() < kMaxLines) { |
- lines->push_back(text.substr(index, next_index - index)); |
- index = next_index + TooltipManager::GetLineSeparator().size(); |
- } |
- if (next_index != text.size() && lines->size() < kMaxLines) |
- lines->push_back(text.substr(index, text.size() - index)); |
-} |
- |
-// static |
-int TooltipManager::GetTooltipHeight() { |
- DCHECK(tooltip_height_ > 0); |
- return tooltip_height_; |
-} |
- |
-static gfx::Font DetermineDefaultFont() { |
- HWND window = CreateWindowEx( |
- WS_EX_TRANSPARENT | l10n_util::GetExtendedTooltipStyles(), |
- TOOLTIPS_CLASS, NULL, 0 , 0, 0, 0, 0, NULL, NULL, NULL, NULL); |
- HFONT hfont = reinterpret_cast<HFONT>(SendMessage(window, WM_GETFONT, 0, 0)); |
- gfx::Font font = hfont ? gfx::Font::CreateFont(hfont) : gfx::Font(); |
- DestroyWindow(window); |
- return font; |
-} |
- |
-// static |
-gfx::Font TooltipManager::GetDefaultFont() { |
- static gfx::Font* font = NULL; |
- if (!font) |
- font = new gfx::Font(DetermineDefaultFont()); |
- return *font; |
-} |
- |
-// static |
-const std::wstring& TooltipManager::GetLineSeparator() { |
- static const std::wstring* separator = NULL; |
- if (!separator) |
- separator = new std::wstring(L"\r\n"); |
- return *separator; |
-} |
- |
-TooltipManager::TooltipManager(Widget* widget, HWND parent) |
- : widget_(widget), |
- parent_(parent), |
- last_mouse_x_(-1), |
- last_mouse_y_(-1), |
- tooltip_showing_(false), |
- last_tooltip_view_(NULL), |
- last_view_out_of_sync_(false), |
- tooltip_width_(0), |
- keyboard_tooltip_hwnd_(NULL), |
-#pragma warning(suppress: 4355) |
- keyboard_tooltip_factory_(this) { |
- DCHECK(widget && parent); |
- Init(); |
-} |
- |
-TooltipManager::~TooltipManager() { |
- if (tooltip_hwnd_) |
- DestroyWindow(tooltip_hwnd_); |
- if (keyboard_tooltip_hwnd_) |
- DestroyWindow(keyboard_tooltip_hwnd_); |
-} |
- |
-void TooltipManager::Init() { |
- // Create the tooltip control. |
- tooltip_hwnd_ = CreateWindowEx( |
- WS_EX_TRANSPARENT | l10n_util::GetExtendedTooltipStyles(), |
- TOOLTIPS_CLASS, NULL, TTS_NOPREFIX, 0, 0, 0, 0, |
- parent_, NULL, NULL, NULL); |
- |
- l10n_util::AdjustUIFontForWindow(tooltip_hwnd_); |
- |
- // This effectively turns off clipping of tooltips. We need this otherwise |
- // multi-line text (\r\n) won't work right. The size doesn't really matter |
- // (just as long as its bigger than the monitor's width) as we clip to the |
- // screen size before rendering. |
- SendMessage(tooltip_hwnd_, TTM_SETMAXTIPWIDTH, 0, |
- std::numeric_limits<short>::max()); |
- |
- // Add one tool that is used for all tooltips. |
- toolinfo_.cbSize = sizeof(toolinfo_); |
- toolinfo_.uFlags = TTF_TRANSPARENT | TTF_IDISHWND; |
- toolinfo_.hwnd = parent_; |
- toolinfo_.uId = reinterpret_cast<UINT_PTR>(parent_); |
- // Setting this tells windows to call parent_ back (using a WM_NOTIFY |
- // message) for the actual tooltip contents. |
- toolinfo_.lpszText = LPSTR_TEXTCALLBACK; |
- SetRectEmpty(&toolinfo_.rect); |
- SendMessage(tooltip_hwnd_, TTM_ADDTOOL, 0, (LPARAM)&toolinfo_); |
-} |
- |
-void TooltipManager::UpdateTooltip() { |
- // Set last_view_out_of_sync_ to indicate the view is currently out of sync. |
- // This doesn't update the view under the mouse immediately as it may cause |
- // timing problems. |
- last_view_out_of_sync_ = true; |
- last_tooltip_view_ = NULL; |
- // Hide the tooltip. |
- SendMessage(tooltip_hwnd_, TTM_POP, 0, 0); |
-} |
- |
-void TooltipManager::TooltipTextChanged(View* view) { |
- if (view == last_tooltip_view_) |
- UpdateTooltip(last_mouse_x_, last_mouse_y_); |
-} |
- |
-LRESULT TooltipManager::OnNotify(int w_param, NMHDR* l_param, bool* handled) { |
- *handled = false; |
- if (l_param->hwndFrom == tooltip_hwnd_ && keyboard_tooltip_hwnd_ == NULL) { |
- switch (l_param->code) { |
- case TTN_GETDISPINFO: { |
- if (last_view_out_of_sync_) { |
- // View under the mouse is out of sync, determine it now. |
- RootView* root_view = widget_->GetRootView(); |
- last_tooltip_view_ = root_view->GetViewForPoint( |
- gfx::Point(last_mouse_x_, last_mouse_y_)); |
- last_view_out_of_sync_ = false; |
- } |
- // Tooltip control is asking for the tooltip to display. |
- NMTTDISPINFOW* tooltip_info = |
- reinterpret_cast<NMTTDISPINFOW*>(l_param); |
- // Initialize the string, if we have a valid tooltip the string will |
- // get reset below. |
- tooltip_info->szText[0] = TEXT('\0'); |
- tooltip_text_.clear(); |
- tooltip_info->lpszText = NULL; |
- clipped_text_.clear(); |
- if (last_tooltip_view_ != NULL) { |
- tooltip_text_.clear(); |
- // Mouse is over a View, ask the View for it's tooltip. |
- gfx::Point view_loc(last_mouse_x_, last_mouse_y_); |
- View::ConvertPointToView(widget_->GetRootView(), |
- last_tooltip_view_, &view_loc); |
- if (last_tooltip_view_->GetTooltipText(view_loc.x(), view_loc.y(), |
- &tooltip_text_) && |
- !tooltip_text_.empty()) { |
- // View has a valid tip, copy it into TOOLTIPINFO. |
- clipped_text_ = tooltip_text_; |
- TrimTooltipToFit(&clipped_text_, &tooltip_width_, &line_count_, |
- last_mouse_x_, last_mouse_y_, tooltip_hwnd_); |
- // Adjust the clipped tooltip text for locale direction. |
- l10n_util::AdjustStringForLocaleDirection(clipped_text_, |
- &clipped_text_); |
- tooltip_info->lpszText = const_cast<WCHAR*>(clipped_text_.c_str()); |
- } else { |
- tooltip_text_.clear(); |
- } |
- } |
- *handled = true; |
- return 0; |
- } |
- case TTN_POP: |
- tooltip_showing_ = false; |
- *handled = true; |
- return 0; |
- case TTN_SHOW: { |
- *handled = true; |
- tooltip_showing_ = true; |
- // The tooltip is about to show, allow the view to position it |
- gfx::Point text_origin; |
- if (tooltip_height_ == 0) |
- tooltip_height_ = CalcTooltipHeight(); |
- gfx::Point view_loc(last_mouse_x_, last_mouse_y_); |
- View::ConvertPointToView(widget_->GetRootView(), |
- last_tooltip_view_, &view_loc); |
- if (last_tooltip_view_->GetTooltipTextOrigin( |
- view_loc.x(), view_loc.y(), &text_origin) && |
- SetTooltipPosition(text_origin.x(), text_origin.y())) { |
- // Return true, otherwise the rectangle we specified is ignored. |
- return TRUE; |
- } |
- return 0; |
- } |
- default: |
- // Fall through. |
- break; |
- } |
- } |
- return 0; |
-} |
- |
-bool TooltipManager::SetTooltipPosition(int text_x, int text_y) { |
- // NOTE: this really only tests that the y location fits on screen, but that |
- // is good enough for our usage. |
- |
- // Calculate the bounds the tooltip will get. |
- gfx::Point view_loc; |
- View::ConvertPointToScreen(last_tooltip_view_, &view_loc); |
- RECT bounds = { view_loc.x() + text_x, |
- view_loc.y() + text_y, |
- view_loc.x() + text_x + tooltip_width_, |
- view_loc.y() + line_count_ * GetTooltipHeight() }; |
- SendMessage(tooltip_hwnd_, TTM_ADJUSTRECT, TRUE, (LPARAM)&bounds); |
- |
- // Make sure the rectangle completely fits on the current monitor. If it |
- // doesn't, return false so that windows positions the tooltip at the |
- // default location. |
- gfx::Rect monitor_bounds = |
- win_util::GetMonitorBoundsForRect(gfx::Rect(bounds.left,bounds.right, |
- 0, 0)); |
- if (!monitor_bounds.Contains(gfx::Rect(bounds))) { |
- return false; |
- } |
- |
- ::SetWindowPos(tooltip_hwnd_, NULL, bounds.left, bounds.top, 0, 0, |
- SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE); |
- return true; |
-} |
- |
-int TooltipManager::CalcTooltipHeight() { |
- // Ask the tooltip for it's font. |
- int height; |
- HFONT hfont = reinterpret_cast<HFONT>( |
- SendMessage(tooltip_hwnd_, WM_GETFONT, 0, 0)); |
- if (hfont != NULL) { |
- HDC dc = GetDC(tooltip_hwnd_); |
- HFONT previous_font = static_cast<HFONT>(SelectObject(dc, hfont)); |
- int last_map_mode = SetMapMode(dc, MM_TEXT); |
- TEXTMETRIC font_metrics; |
- GetTextMetrics(dc, &font_metrics); |
- height = font_metrics.tmHeight; |
- // To avoid the DC referencing font_handle_, select the previous font. |
- SelectObject(dc, previous_font); |
- SetMapMode(dc, last_map_mode); |
- ReleaseDC(NULL, dc); |
- } else { |
- // Tooltip is using the system font. Use gfx::Font, which should pick |
- // up the system font. |
- height = gfx::Font().height(); |
- } |
- // Get the margins from the tooltip |
- RECT tooltip_margin; |
- SendMessage(tooltip_hwnd_, TTM_GETMARGIN, 0, (LPARAM)&tooltip_margin); |
- return height + tooltip_margin.top + tooltip_margin.bottom; |
-} |
- |
-void TooltipManager::TrimTooltipToFit(std::wstring* text, |
- int* max_width, |
- int* line_count, |
- int position_x, |
- int position_y, |
- HWND window) { |
- *max_width = 0; |
- *line_count = 0; |
- |
- // Clamp the tooltip length to kMaxTooltipLength so that we don't |
- // accidentally DOS the user with a mega tooltip (since Windows doesn't seem |
- // to do this itself). |
- if (text->length() > kMaxTooltipLength) |
- *text = text->substr(0, kMaxTooltipLength); |
- |
- // Determine the available width for the tooltip. |
- gfx::Point screen_loc(position_x, position_y); |
- View::ConvertPointToScreen(widget_->GetRootView(), &screen_loc); |
- gfx::Rect monitor_bounds = |
- win_util::GetMonitorBoundsForRect(gfx::Rect(screen_loc.x(), |
- screen_loc.y(), |
- 0, 0)); |
- RECT tooltip_margin; |
- SendMessage(window, TTM_GETMARGIN, 0, (LPARAM)&tooltip_margin); |
- const int available_width = monitor_bounds.width() - tooltip_margin.left - |
- tooltip_margin.right; |
- if (available_width <= 0) |
- return; |
- |
- // Split the string. |
- std::vector<std::wstring> lines; |
- SplitTooltipString(*text, &lines); |
- *line_count = static_cast<int>(lines.size()); |
- |
- // Format each line to fit. |
- gfx::Font font = GetDefaultFont(); |
- std::wstring result; |
- for (std::vector<std::wstring>::iterator i = lines.begin(); i != lines.end(); |
- ++i) { |
- std::wstring elided_text = gfx::ElideText(*i, font, available_width); |
- *max_width = std::max(*max_width, font.GetStringWidth(elided_text)); |
- if (i == lines.begin() && i + 1 == lines.end()) { |
- *text = elided_text; |
- return; |
- } |
- if (!result.empty()) |
- result.append(GetLineSeparator()); |
- result.append(elided_text); |
- } |
- *text = result; |
-} |
- |
-void TooltipManager::UpdateTooltip(int x, int y) { |
- RootView* root_view = widget_->GetRootView(); |
- View* view = root_view->GetViewForPoint(gfx::Point(x, y)); |
- if (view != last_tooltip_view_) { |
- // NOTE: This *must* be sent regardless of the visibility of the tooltip. |
- // It triggers Windows to ask for the tooltip again. |
- SendMessage(tooltip_hwnd_, TTM_POP, 0, 0); |
- last_tooltip_view_ = view; |
- } else if (last_tooltip_view_ != NULL) { |
- // Tooltip is showing, and mouse is over the same view. See if the tooltip |
- // text has changed. |
- gfx::Point view_point(x, y); |
- View::ConvertPointToView(root_view, last_tooltip_view_, &view_point); |
- std::wstring new_tooltip_text; |
- if (last_tooltip_view_->GetTooltipText(view_point.x(), view_point.y(), |
- &new_tooltip_text) && |
- new_tooltip_text != tooltip_text_) { |
- // The text has changed, hide the popup. |
- SendMessage(tooltip_hwnd_, TTM_POP, 0, 0); |
- if (!new_tooltip_text.empty() && tooltip_showing_) { |
- // New text is valid, show the popup. |
- SendMessage(tooltip_hwnd_, TTM_POPUP, 0, 0); |
- } |
- } |
- } |
-} |
- |
-void TooltipManager::OnMouse(UINT u_msg, WPARAM w_param, LPARAM l_param) { |
- int x = GET_X_LPARAM(l_param); |
- int y = GET_Y_LPARAM(l_param); |
- |
- if (u_msg >= WM_NCMOUSEMOVE && u_msg <= WM_NCXBUTTONDBLCLK) { |
- // NC message coordinates are in screen coordinates. |
- gfx::Rect frame_bounds; |
- widget_->GetBounds(&frame_bounds, true); |
- x -= frame_bounds.x(); |
- y -= frame_bounds.y(); |
- } |
- |
- if (u_msg != WM_MOUSEMOVE || last_mouse_x_ != x || last_mouse_y_ != y) { |
- last_mouse_x_ = x; |
- last_mouse_y_ = y; |
- HideKeyboardTooltip(); |
- UpdateTooltip(x, y); |
- } |
- // Forward the message onto the tooltip. |
- MSG msg; |
- msg.hwnd = parent_; |
- msg.message = u_msg; |
- msg.wParam = w_param; |
- msg.lParam = l_param; |
- SendMessage(tooltip_hwnd_, TTM_RELAYEVENT, 0, (LPARAM)&msg); |
-} |
- |
-void TooltipManager::ShowKeyboardTooltip(View* focused_view) { |
- if (tooltip_showing_) { |
- SendMessage(tooltip_hwnd_, TTM_POP, 0, 0); |
- tooltip_text_.clear(); |
- } |
- HideKeyboardTooltip(); |
- std::wstring tooltip_text; |
- if (!focused_view->GetTooltipText(0, 0, &tooltip_text)) |
- return; |
- gfx::Rect focused_bounds = focused_view->bounds(); |
- gfx::Point screen_point; |
- focused_view->ConvertPointToScreen(focused_view, &screen_point); |
- gfx::Point relative_point_coordinates; |
- focused_view->ConvertPointToWidget(focused_view, &relative_point_coordinates); |
- keyboard_tooltip_hwnd_ = CreateWindowEx( |
- WS_EX_TRANSPARENT | l10n_util::GetExtendedTooltipStyles(), |
- TOOLTIPS_CLASS, NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL); |
- SendMessage(keyboard_tooltip_hwnd_, TTM_SETMAXTIPWIDTH, 0, |
- std::numeric_limits<short>::max()); |
- int tooltip_width; |
- int line_count; |
- TrimTooltipToFit(&tooltip_text, &tooltip_width, &line_count, |
- relative_point_coordinates.x(), |
- relative_point_coordinates.y(), keyboard_tooltip_hwnd_); |
- TOOLINFO keyboard_toolinfo; |
- memset(&keyboard_toolinfo, 0, sizeof(keyboard_toolinfo)); |
- keyboard_toolinfo.cbSize = sizeof(keyboard_toolinfo); |
- keyboard_toolinfo.hwnd = parent_; |
- keyboard_toolinfo.uFlags = TTF_TRACK | TTF_TRANSPARENT | TTF_IDISHWND ; |
- keyboard_toolinfo.lpszText = const_cast<WCHAR*>(tooltip_text.c_str()); |
- SendMessage(keyboard_tooltip_hwnd_, TTM_ADDTOOL, 0, |
- reinterpret_cast<LPARAM>(&keyboard_toolinfo)); |
- SendMessage(keyboard_tooltip_hwnd_, TTM_TRACKACTIVATE, TRUE, |
- reinterpret_cast<LPARAM>(&keyboard_toolinfo)); |
- if (!tooltip_height_) |
- tooltip_height_ = CalcTooltipHeight(); |
- RECT rect_bounds = {screen_point.x(), |
- screen_point.y() + focused_bounds.height(), |
- screen_point.x() + tooltip_width, |
- screen_point.y() + focused_bounds.height() + |
- line_count * tooltip_height_ }; |
- gfx::Rect monitor_bounds = |
- win_util::GetMonitorBoundsForRect(gfx::Rect(rect_bounds)); |
- rect_bounds = gfx::Rect(rect_bounds).AdjustToFit(monitor_bounds).ToRECT(); |
- ::SetWindowPos(keyboard_tooltip_hwnd_, NULL, rect_bounds.left, |
- rect_bounds.top, 0, 0, |
- SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE); |
- MessageLoop::current()->PostDelayedTask(FROM_HERE, |
- keyboard_tooltip_factory_.NewRunnableMethod( |
- &TooltipManager::DestroyKeyboardTooltipWindow, keyboard_tooltip_hwnd_), |
- kDefaultTimeout); |
-} |
- |
-void TooltipManager::HideKeyboardTooltip() { |
- if (keyboard_tooltip_hwnd_ != NULL) { |
- SendMessage(keyboard_tooltip_hwnd_, WM_CLOSE, 0, 0); |
- keyboard_tooltip_hwnd_ = NULL; |
- } |
-} |
- |
-void TooltipManager::DestroyKeyboardTooltipWindow(HWND window_to_destroy) { |
- if (keyboard_tooltip_hwnd_ == window_to_destroy) |
- HideKeyboardTooltip(); |
-} |
- |
-} // namespace views |