15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/views/corewm/tooltip_controller.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <vector>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
91e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)#include "base/strings/string_util.h"
10eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/time/time.h"
11f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "ui/aura/client/capture_client.h"
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/aura/client/cursor_client.h"
131e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)#include "ui/aura/client/screen_position_client.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/aura/env.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/aura/window.h"
16d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "ui/events/event.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/font.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/rect.h"
191e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)#include "ui/gfx/screen.h"
204e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "ui/views/corewm/tooltip.h"
214e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "ui/views/widget/tooltip_manager.h"
22effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "ui/wm/public/drag_drop_client.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
241e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)namespace views {
251e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)namespace corewm {
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kTooltipTimeoutMs = 500;
2990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)const int kDefaultTooltipShownTimeoutMs = 10000;
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
311e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)// Returns true if |target| is a valid window to get the tooltip from.
321e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)// |event_target| is the original target from the event and |target| the window
331e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)// at the same location.
341e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)bool IsValidTarget(aura::Window* event_target, aura::Window* target) {
351e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  if (!target || (event_target == target))
361e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    return true;
371e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
381e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  void* event_target_grouping_id = event_target->GetNativeWindowProperty(
391e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      TooltipManager::kGroupingPropertyKey);
401e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  void* target_grouping_id = target->GetNativeWindowProperty(
411e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      TooltipManager::kGroupingPropertyKey);
421e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  return event_target_grouping_id &&
431e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      event_target_grouping_id == target_grouping_id;
441e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)}
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
461e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)// Returns the target (the Window tooltip text comes from) based on the event.
471e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)// If a Window other than event.target() is returned, |location| is adjusted
481e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)// to be in the coordinates of the returned Window.
491e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)aura::Window* GetTooltipTarget(const ui::MouseEvent& event,
501e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)                               gfx::Point* location) {
511e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  switch (event.type()) {
521e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    case ui::ET_MOUSE_CAPTURE_CHANGED:
531e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      // On windows we can get a capture changed without an exit. We need to
541e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      // reset state when this happens else the tooltip may incorrectly show.
551e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      return NULL;
561e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    case ui::ET_MOUSE_EXITED:
571e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      return NULL;
581e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    case ui::ET_MOUSE_MOVED:
591e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    case ui::ET_MOUSE_DRAGGED: {
601e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      aura::Window* event_target = static_cast<aura::Window*>(event.target());
61f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      if (!event_target)
62f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        return NULL;
63f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
64f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      // If a window other than |event_target| has capture, ignore the event.
65f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      // This can happen when RootWindow creates events when showing/hiding, or
66f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      // the system generates an extra event. We have to check
67f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      // GetGlobalCaptureWindow() as Windows does not use a singleton
68f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      // CaptureClient.
69f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      if (!event_target->HasCapture()) {
70f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        aura::Window* root = event_target->GetRootWindow();
71f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        if (root) {
72f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)          aura::client::CaptureClient* capture_client =
73f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)              aura::client::GetCaptureClient(root);
74f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)          if (capture_client) {
75f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)            aura::Window* capture_window =
76f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                capture_client->GetGlobalCaptureWindow();
77f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)            if (capture_window && event_target != capture_window)
78f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)              return NULL;
79f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)          }
80f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        }
811e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        return event_target;
82f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      }
831e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
841e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      // If |target| has capture all events go to it, even if the mouse is
851e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      // really over another window. Find the real window the mouse is over.
861e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      gfx::Point screen_loc(event.location());
871e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      aura::client::GetScreenPositionClient(event_target->GetRootWindow())->
881e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)          ConvertPointToScreen(event_target, &screen_loc);
891e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      gfx::Screen* screen = gfx::Screen::GetScreenFor(event_target);
901e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      aura::Window* target = screen->GetWindowAtScreenPoint(screen_loc);
911e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      if (!target)
921e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        return NULL;
931e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      gfx::Point target_loc(screen_loc);
941e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      aura::client::GetScreenPositionClient(target->GetRootWindow())->
951e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)          ConvertPointFromScreen(target, &target_loc);
961e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      aura::Window* screen_target = target->GetEventHandlerForPoint(target_loc);
971e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      if (!IsValidTarget(event_target, screen_target))
981e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        return NULL;
991e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
1001e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      aura::Window::ConvertPointToTarget(screen_target, target, &target_loc);
1011e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      *location = target_loc;
1021e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      return screen_target;
1031e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    }
1041e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    default:
1051e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      NOTREACHED();
1061e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      break;
1071e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  }
1081e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  return NULL;
1091e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)}
1101e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
1111e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)}  // namespace
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)////////////////////////////////////////////////////////////////////////////////
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// TooltipController public:
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1164e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)TooltipController::TooltipController(scoped_ptr<Tooltip> tooltip)
1174e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    : tooltip_window_(NULL),
118f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      tooltip_id_(NULL),
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tooltip_window_at_mouse_press_(NULL),
1204e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      tooltip_(tooltip.Pass()),
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tooltips_enabled_(true) {
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  tooltip_timer_.Start(FROM_HERE,
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::TimeDelta::FromMilliseconds(kTooltipTimeoutMs),
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this, &TooltipController::TooltipTimerFired);
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TooltipController::~TooltipController() {
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tooltip_window_)
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tooltip_window_->RemoveObserver(this);
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TooltipController::UpdateTooltip(aura::Window* target) {
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If tooltip is visible, we may want to hide it. If it is not, we are ok.
1344e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (tooltip_window_ == target && tooltip_->IsVisible())
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    UpdateIfRequired();
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Reset |tooltip_window_at_mouse_press_| if the moving within the same window
1385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // but over a region that has different tooltip text. By resetting
1395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // |tooltip_window_at_mouse_press_| we ensure the next time the timer fires
1405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // we'll requery for the tooltip text.
1415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // This handles the case of clicking on a view, moving within the same window
1425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // but over a different view, than back to the original.
1435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (tooltip_window_at_mouse_press_ &&
1445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      target == tooltip_window_at_mouse_press_ &&
1455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      aura::client::GetTooltipText(target) != tooltip_text_at_mouse_press_) {
1465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    tooltip_window_at_mouse_press_ = NULL;
1475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
1485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If we had stopped the tooltip timer for some reason, we must restart it if
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // there is a change in the tooltip.
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!tooltip_timer_.IsRunning()) {
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (tooltip_window_ != target || (tooltip_window_ &&
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        tooltip_text_ != aura::client::GetTooltipText(tooltip_window_))) {
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tooltip_timer_.Start(FROM_HERE,
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          base::TimeDelta::FromMilliseconds(kTooltipTimeoutMs),
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this, &TooltipController::TooltipTimerFired);
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)void TooltipController::SetTooltipShownTimeout(aura::Window* target,
16290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                                               int timeout_in_ms) {
16390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  tooltip_shown_timeout_map_[target] = timeout_in_ms;
16490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}
16590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TooltipController::SetTooltipsEnabled(bool enable) {
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tooltips_enabled_ == enable)
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  tooltips_enabled_ = enable;
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdateTooltip(tooltip_window_);
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TooltipController::OnKeyEvent(ui::KeyEvent* event) {
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // On key press, we want to hide the tooltip and not show it until change.
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // This is the same behavior as hiding tooltips on timeout. Hence, we can
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // simply simulate a timeout.
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tooltip_shown_timer_.IsRunning()) {
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tooltip_shown_timer_.Stop();
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    TooltipShownTimerFired();
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TooltipController::OnMouseEvent(ui::MouseEvent* event) {
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (event->type()) {
1851e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    case ui::ET_MOUSE_CAPTURE_CHANGED:
186424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    case ui::ET_MOUSE_EXITED:
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ui::ET_MOUSE_MOVED:
1881e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    case ui::ET_MOUSE_DRAGGED: {
1891e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      curr_mouse_loc_ = event->location();
190cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      aura::Window* target = NULL;
191cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      // Avoid a call to gfx::Screen::GetWindowAtScreenPoint() since it can be
192cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      // very expensive on X11 in cases when the tooltip is hidden anyway.
193cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      if (tooltips_enabled_ &&
194cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)          !aura::Env::GetInstance()->IsMouseButtonDown() &&
195cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)          !IsDragDropInProgress()) {
196cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        target = GetTooltipTarget(*event, &curr_mouse_loc_);
197cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      }
1985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      SetTooltipWindow(target);
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (tooltip_timer_.IsRunning())
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        tooltip_timer_.Reset();
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2024e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      if (tooltip_->IsVisible())
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        UpdateIfRequired();
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
2051e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    }
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ui::ET_MOUSE_PRESSED:
2072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if ((event->flags() & ui::EF_IS_NON_CLIENT) == 0) {
2081e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        aura::Window* target = static_cast<aura::Window*>(event->target());
2092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // We don't get a release for non-client areas.
2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        tooltip_window_at_mouse_press_ = target;
2112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (target)
2122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          tooltip_text_at_mouse_press_ = aura::client::GetTooltipText(target);
2132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
2144e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      tooltip_->Hide();
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ui::ET_MOUSEWHEEL:
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Hide the tooltip for click, release, drag, wheel events.
2184e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      if (tooltip_->IsVisible())
2194e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        tooltip_->Hide();
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TooltipController::OnTouchEvent(ui::TouchEvent* event) {
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(varunjain): need to properly implement tooltips for
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // touch events.
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Hide the tooltip for touch events.
2304e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  tooltip_->Hide();
2315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  SetTooltipWindow(NULL);
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TooltipController::OnCancelMode(ui::CancelModeEvent* event) {
2354e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  tooltip_->Hide();
2365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  SetTooltipWindow(NULL);
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TooltipController::OnWindowDestroyed(aura::Window* window) {
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tooltip_window_ == window) {
2414e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    tooltip_->Hide();
24290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    tooltip_shown_timeout_map_.erase(tooltip_window_);
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tooltip_window_ = NULL;
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)////////////////////////////////////////////////////////////////////////////////
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// TooltipController private:
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TooltipController::TooltipTimerFired() {
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdateIfRequired();
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TooltipController::TooltipShownTimerFired() {
2554e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  tooltip_->Hide();
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Since the user presumably no longer needs the tooltip, we also stop the
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // tooltip timer so that tooltip does not pop back up. We will restart this
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // timer if the tooltip changes (see UpdateTooltip()).
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  tooltip_timer_.Stop();
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TooltipController::UpdateIfRequired() {
2644e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (!tooltips_enabled_ ||
2654e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      aura::Env::GetInstance()->IsMouseButtonDown() ||
2664e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      IsDragDropInProgress() || !IsCursorVisible()) {
2674e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    tooltip_->Hide();
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::string16 tooltip_text;
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tooltip_window_)
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tooltip_text = aura::client::GetTooltipText(tooltip_window_);
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If the user pressed a mouse button. We will hide the tooltip and not show
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // it until there is a change in the tooltip.
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tooltip_window_at_mouse_press_) {
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (tooltip_window_ == tooltip_window_at_mouse_press_ &&
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        tooltip_text == tooltip_text_at_mouse_press_) {
2804e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      tooltip_->Hide();
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tooltip_window_at_mouse_press_ = NULL;
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
286f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // If the uniqueness indicator is different from the previously encountered
287f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // one, we should force tooltip update
288f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  const void* tooltip_id = aura::client::GetTooltipId(tooltip_window_);
289f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  bool ids_differ = false;
290f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  ids_differ = tooltip_id_ != tooltip_id;
291f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  tooltip_id_ = tooltip_id;
292f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
2934e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // We add the !tooltip_->IsVisible() below because when we come here from
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TooltipTimerFired(), the tooltip_text may not have changed but we still
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // want to update the tooltip because the timer has fired.
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If we come here from UpdateTooltip(), we have already checked for tooltip
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // visibility and this check below will have no effect.
298f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (tooltip_text_ != tooltip_text || !tooltip_->IsVisible() || ids_differ) {
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tooltip_shown_timer_.Stop();
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tooltip_text_ = tooltip_text;
3011e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    base::string16 trimmed_text(tooltip_text_);
3021e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    views::TooltipManager::TrimTooltipText(&trimmed_text);
3031e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    // If the string consists entirely of whitespace, then don't both showing it
3041e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    // (an empty tooltip is useless).
3051e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    base::string16 whitespace_removed_text;
306a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    base::TrimWhitespace(trimmed_text, base::TRIM_ALL,
307a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                         &whitespace_removed_text);
3081e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    if (whitespace_removed_text.empty()) {
3094e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      tooltip_->Hide();
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      gfx::Point widget_loc = curr_mouse_loc_ +
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          tooltip_window_->GetBoundsInScreen().OffsetFromOrigin();
3135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      tooltip_->SetText(tooltip_window_, whitespace_removed_text, widget_loc);
3144e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      tooltip_->Show();
3154e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      int timeout = GetTooltipShownTimeout();
3164e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      if (timeout > 0) {
3174e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        tooltip_shown_timer_.Start(FROM_HERE,
3184e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)            base::TimeDelta::FromMilliseconds(timeout),
3194e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)            this, &TooltipController::TooltipShownTimerFired);
3202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool TooltipController::IsTooltipVisible() {
3264e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  return tooltip_->IsVisible();
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool TooltipController::IsDragDropInProgress() {
3302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!tooltip_window_)
3312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
3322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  aura::client::DragDropClient* client =
3332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      aura::client::GetDragDropClient(tooltip_window_->GetRootWindow());
3342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return client && client->IsDragDropInProgress();
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool TooltipController::IsCursorVisible() {
3382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!tooltip_window_)
3392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
3401e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  aura::Window* root = tooltip_window_->GetRootWindow();
3412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!root)
3422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
3432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  aura::client::CursorClient* cursor_client =
3442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      aura::client::GetCursorClient(root);
3452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // |cursor_client| may be NULL in tests, treat NULL as always visible.
3462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return !cursor_client || cursor_client->IsCursorVisible();
3472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
34990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)int TooltipController::GetTooltipShownTimeout() {
35090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  std::map<aura::Window*, int>::const_iterator it =
35190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      tooltip_shown_timeout_map_.find(tooltip_window_);
35290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  if (it == tooltip_shown_timeout_map_.end())
35390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    return kDefaultTooltipShownTimeoutMs;
35490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  return it->second;
35590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}
35690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
3575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void TooltipController::SetTooltipWindow(aura::Window* target) {
3585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (tooltip_window_ == target)
3595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
3605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (tooltip_window_)
3615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    tooltip_window_->RemoveObserver(this);
3625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  tooltip_window_ = target;
3635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (tooltip_window_)
3645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    tooltip_window_->AddObserver(this);
3655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
3665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace corewm
3682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace views
369