14e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
24e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
34e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// found in the LICENSE file.
44e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
54e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "ui/views/corewm/tooltip_win.h"
64e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
74e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include <winuser.h>
84e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
94e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "base/debug/stack_trace.h"
104e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "base/i18n/rtl.h"
114e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "base/logging.h"
124e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "ui/base/l10n/l10n_util_win.h"
134e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "ui/gfx/rect.h"
144e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "ui/gfx/screen.h"
15a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/gfx/win/dpi.h"
16cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "ui/views/corewm/cursor_height_provider_win.h"
174e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
184e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)namespace views {
194e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)namespace corewm {
204e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
214e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)TooltipWin::TooltipWin(HWND parent)
224e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    : parent_hwnd_(parent),
234e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      tooltip_hwnd_(NULL),
244e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      showing_(false) {
254e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  memset(&toolinfo_, 0, sizeof(toolinfo_));
264e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  toolinfo_.cbSize = sizeof(toolinfo_);
274e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  toolinfo_.uFlags = TTF_IDISHWND | TTF_TRACK | TTF_ABSOLUTE;
284e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  toolinfo_.uId = reinterpret_cast<UINT_PTR>(parent_hwnd_);
294e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  toolinfo_.hwnd = parent_hwnd_;
304e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  toolinfo_.lpszText = NULL;
314e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  toolinfo_.lpReserved = NULL;
324e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  SetRectEmpty(&toolinfo_.rect);
334e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
344e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
354e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)TooltipWin::~TooltipWin() {
364e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (tooltip_hwnd_)
374e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    DestroyWindow(tooltip_hwnd_);
384e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
394e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
404e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)bool TooltipWin::HandleNotify(int w_param, NMHDR* l_param, LRESULT* l_result) {
414e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (tooltip_hwnd_ == NULL)
424e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    return false;
434e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
444e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  switch (l_param->code) {
454e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    case TTN_POP:
464e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      showing_ = false;
474e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      return true;
484e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    case TTN_SHOW:
494e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      *l_result = TRUE;
504e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      PositionTooltip();
514e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      showing_ = true;
524e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      return true;
534e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    default:
544e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      break;
554e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  }
564e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  return false;
574e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
584e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
594e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)bool TooltipWin::EnsureTooltipWindow() {
604e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (tooltip_hwnd_)
614e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    return true;
624e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
634e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  tooltip_hwnd_ = CreateWindowEx(
648bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      WS_EX_TRANSPARENT | l10n_util::GetExtendedTooltipStyles(),
654e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      TOOLTIPS_CLASS, NULL, TTS_NOPREFIX | WS_POPUP, 0, 0, 0, 0,
664e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      parent_hwnd_, NULL, NULL, NULL);
674e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (!tooltip_hwnd_) {
68cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    PLOG(WARNING) << "tooltip creation failed, disabling tooltips";
694e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    return false;
704e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  }
714e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
724e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  l10n_util::AdjustUIFontForWindow(tooltip_hwnd_);
734e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
744e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  SendMessage(tooltip_hwnd_, TTM_ADDTOOL, 0,
754e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)              reinterpret_cast<LPARAM>(&toolinfo_));
764e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  return true;
774e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
784e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
794e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)void TooltipWin::PositionTooltip() {
804e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // This code only runs for non-metro, so GetNativeScreen() is fine.
81cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  gfx::Point screen_point = gfx::win::DIPToScreenPoint(location_);
82cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  const int cursoroffset = GetCurrentCursorVisibleHeight();
83cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  screen_point.Offset(0, cursoroffset);
844e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
854e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  DWORD tooltip_size = SendMessage(tooltip_hwnd_, TTM_GETBUBBLESIZE, 0,
864e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                                   reinterpret_cast<LPARAM>(&toolinfo_));
87cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  const gfx::Size size(LOWORD(tooltip_size), HIWORD(tooltip_size));
88cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
89cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  const gfx::Display display(
90cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      gfx::Screen::GetNativeScreen()->GetDisplayNearestPoint(screen_point));
91cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
92cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  gfx::Rect tooltip_bounds(screen_point, size);
93cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  tooltip_bounds.AdjustToFit(gfx::win::DIPToScreenRect(display.work_area()));
944e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  SetWindowPos(tooltip_hwnd_, NULL, tooltip_bounds.x(), tooltip_bounds.y(), 0,
954e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)               0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
964e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
974e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
984e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)void TooltipWin::SetText(aura::Window* window,
994e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                         const base::string16& tooltip_text,
1004e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                         const gfx::Point& location) {
1014e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (!EnsureTooltipWindow())
1024e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    return;
1034e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
1044e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // See comment in header for details on why |location_| is needed.
1054e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  location_ = location;
1064e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
1078bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  // Without this we get a flicker of the tooltip appearing at 0x0. Not sure
1088bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  // why.
1098bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  SetWindowPos(tooltip_hwnd_, NULL, 0, 0, 0, 0,
1108bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)               SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOMOVE |
1118bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)               SWP_NOREPOSITION | SWP_NOSIZE | SWP_NOZORDER);
1128bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
1134e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  base::string16 adjusted_text(tooltip_text);
1144e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  base::i18n::AdjustStringForLocaleDirection(&adjusted_text);
1154e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  toolinfo_.lpszText = const_cast<WCHAR*>(adjusted_text.c_str());
1164e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  SendMessage(tooltip_hwnd_, TTM_SETTOOLINFO, 0,
1174e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)              reinterpret_cast<LPARAM>(&toolinfo_));
1184e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
1194e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // This code only runs for non-metro, so GetNativeScreen() is fine.
120cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  const gfx::Point screen_point = gfx::win::DIPToScreenPoint(location_);
1214e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  gfx::Display display(
122cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      gfx::Screen::GetNativeScreen()->GetDisplayNearestPoint(screen_point));
1234e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  const gfx::Rect monitor_bounds = display.bounds();
1244e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  int max_width = (monitor_bounds.width() + 1) / 2;
1254e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  SendMessage(tooltip_hwnd_, TTM_SETMAXTIPWIDTH, 0, max_width);
1264e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
1274e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
1284e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)void TooltipWin::Show() {
1294e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (!EnsureTooltipWindow())
1304e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    return;
1314e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
1324e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  SendMessage(tooltip_hwnd_, TTM_TRACKACTIVATE,
1334e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)              TRUE, reinterpret_cast<LPARAM>(&toolinfo_));
1341e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  SetWindowPos(tooltip_hwnd_, HWND_TOPMOST, 0, 0, 0, 0,
1351e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)               SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOSIZE);
1364e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
1374e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
1384e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)void TooltipWin::Hide() {
1395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!tooltip_hwnd_)
1404e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    return;
1414e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
1424e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  SendMessage(tooltip_hwnd_, TTM_TRACKACTIVATE, FALSE,
1434e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)              reinterpret_cast<LPARAM>(&toolinfo_));
1444e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
1454e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
1464e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)bool TooltipWin::IsVisible() {
1474e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  return showing_;
1484e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
1494e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
1504e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}  // namespace corewm
1514e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}  // namespace views
152