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.
4
5#include "ui/views/widget/aero_tooltip_manager.h"
6
7#include <windows.h>
8#include <commctrl.h>
9#include <shlobj.h>
10
11#include "base/bind.h"
12#include "base/message_loop/message_loop.h"
13#include "ui/base/l10n/l10n_util_win.h"
14#include "ui/gfx/point.h"
15#include "ui/gfx/win/dpi.h"
16#include "ui/gfx/win/hwnd_util.h"
17
18namespace views {
19
20///////////////////////////////////////////////////////////////////////////////
21// AeroTooltipManager, public:
22
23AeroTooltipManager::AeroTooltipManager(Widget* widget)
24    : TooltipManagerWin(widget),
25      initial_delay_(0) {
26}
27
28AeroTooltipManager::~AeroTooltipManager() {
29  if (initial_timer_)
30    initial_timer_->Disown();
31}
32
33void AeroTooltipManager::OnMouse(UINT u_msg, WPARAM w_param, LPARAM l_param) {
34  if (u_msg == WM_MOUSELEAVE) {
35    last_mouse_pos_.SetPoint(-1, -1);
36    UpdateTooltip();
37    return;
38  }
39
40  if (initial_timer_)
41    initial_timer_->Disown();
42
43  if (u_msg == WM_MOUSEMOVE || u_msg == WM_NCMOUSEMOVE) {
44    gfx::Point mouse_pos_in_pixels(l_param);
45    gfx::Point mouse_pos = gfx::win::ScreenToDIPPoint(mouse_pos_in_pixels);
46    if (u_msg == WM_NCMOUSEMOVE) {
47      // NC message coordinates are in screen coordinates.
48      POINT temp = mouse_pos_in_pixels.ToPOINT();
49      ::MapWindowPoints(HWND_DESKTOP, GetParent(), &temp, 1);
50      mouse_pos_in_pixels.SetPoint(temp.x, temp.y);
51      mouse_pos = gfx::win::ScreenToDIPPoint(mouse_pos_in_pixels);
52    }
53    if (last_mouse_pos_ != mouse_pos) {
54      last_mouse_pos_ = mouse_pos;
55      UpdateTooltip(mouse_pos);
56    }
57
58    // Delay opening of the tooltip just in case the user moves their
59    // mouse to another control. We defer this from Init because we get
60    // zero if we query it too soon.
61    if (!initial_delay_) {
62      initial_delay_ = static_cast<int>(
63          ::SendMessage(tooltip_hwnd_, TTM_GETDELAYTIME, TTDT_INITIAL, 0));
64    }
65    initial_timer_ = new InitialTimer(this);
66    initial_timer_->Start(initial_delay_);
67  } else {
68    // Hide the tooltip and cancel any timers.
69    ::SendMessage(tooltip_hwnd_, TTM_POP, 0, 0);
70    ::SendMessage(tooltip_hwnd_, TTM_TRACKACTIVATE, false, (LPARAM)&toolinfo_);
71    return;
72  }
73}
74
75///////////////////////////////////////////////////////////////////////////////
76// AeroTooltipManager, private:
77
78void AeroTooltipManager::OnTimer() {
79  initial_timer_ = NULL;
80
81  POINT pt = last_mouse_pos_.ToPOINT();
82  ::ClientToScreen(GetParent(), &pt);
83
84  // Set the position and visibility.
85  if (!tooltip_showing_) {
86    ::SendMessage(tooltip_hwnd_, TTM_POPUP, 0, 0);
87    ::SendMessage(tooltip_hwnd_, TTM_TRACKPOSITION, 0, MAKELPARAM(pt.x, pt.y));
88    ::SendMessage(tooltip_hwnd_, TTM_TRACKACTIVATE, true, (LPARAM)&toolinfo_);
89  }
90}
91
92///////////////////////////////////////////////////////////////////////////////
93// AeroTooltipManager::InitialTimer
94
95AeroTooltipManager::InitialTimer::InitialTimer(AeroTooltipManager* manager)
96    : manager_(manager) {
97}
98
99void AeroTooltipManager::InitialTimer::Start(int time) {
100  base::MessageLoop::current()->PostDelayedTask(
101      FROM_HERE,
102      base::Bind(&InitialTimer::Execute, this),
103      base::TimeDelta::FromMilliseconds(time));
104}
105
106void AeroTooltipManager::InitialTimer::Disown() {
107  manager_ = NULL;
108}
109
110void AeroTooltipManager::InitialTimer::Execute() {
111  if (manager_)
112    manager_->OnTimer();
113}
114
115}  // namespace views
116