172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be
3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file.
4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/ui/gtk/status_bubble_gtk.h"
6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
7c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <gtk/gtk.h>
8c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <algorithm>
10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
11c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/i18n/rtl.h"
12c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/message_loop.h"
13c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/utf_string_conversions.h"
14ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/browser/ui/gtk/gtk_theme_service.h"
1572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/ui/gtk/gtk_util.h"
1672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/ui/gtk/rounded_window.h"
1772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/ui/gtk/slide_animator_gtk.h"
18dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/common/notification_service.h"
193f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#include "ui/base/animation/slide_animation.h"
2072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/base/text/text_elider.h"
21c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
22c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace {
23c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
24c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Inner padding between the border and the text label.
25c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kInternalTopBottomPadding = 1;
26c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kInternalLeftRightPadding = 2;
27c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
28c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// The radius of the edges of our bubble.
29c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kCornerSize = 3;
30c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
31c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Milliseconds before we hide the status bubble widget when you mouseout.
32c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kHideDelay = 250;
33c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
34c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// How close the mouse can get to the infobubble before it starts sliding
35c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// off-screen.
36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kMousePadding = 20;
37c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
38c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}  // namespace
39c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
40c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochStatusBubbleGtk::StatusBubbleGtk(Profile* profile)
41ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    : theme_service_(GtkThemeService::GetFrom(profile)),
42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      padding_(NULL),
43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      flip_horizontally_(false),
44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      y_offset_(0),
45c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      download_shelf_is_visible_(false),
46c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      last_mouse_location_(0, 0),
473f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      last_mouse_left_content_(false),
483f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      ignore_next_left_content_(false) {
49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  InitWidgets();
50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
51ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  theme_service_->InitThemesFor(this);
52c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED,
53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                 NotificationService::AllSources());
54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
55c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
56c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochStatusBubbleGtk::~StatusBubbleGtk() {
57dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  label_.Destroy();
58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  container_.Destroy();
59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
613345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickvoid StatusBubbleGtk::SetStatus(const string16& status_text_wide) {
623345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  std::string status_text = UTF16ToUTF8(status_text_wide);
63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (status_text_ == status_text)
64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  status_text_ = status_text;
673f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  if (!status_text_.empty())
68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    SetStatusTextTo(status_text_);
693f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  else if (!url_text_.empty())
70c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    SetStatusTextTo(url_text_);
713f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  else
72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    SetStatusTextTo(std::string());
73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
74c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
753345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickvoid StatusBubbleGtk::SetURL(const GURL& url, const string16& languages) {
76c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  url_ = url;
77c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  languages_ = languages;
78c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
79c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // If we want to clear a displayed URL but there is a status still to
80c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // display, display that status instead.
81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (url.is_empty() && !status_text_.empty()) {
82c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    url_text_ = std::string();
83c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    SetStatusTextTo(status_text_);
84c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
85c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
86c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
87c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SetStatusTextToURL();
88c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
89c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
90c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid StatusBubbleGtk::SetStatusTextToURL() {
91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GtkWidget* parent = gtk_widget_get_parent(container_.get());
92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
93c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // It appears that parent can be NULL (probably only during shutdown).
94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!parent || !GTK_WIDGET_REALIZED(parent))
95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int desired_width = parent->allocation.width;
98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!expanded()) {
99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    expand_timer_.Stop();
100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    expand_timer_.Start(base::TimeDelta::FromMilliseconds(kExpandHoverDelay),
101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                        this, &StatusBubbleGtk::ExpandURL);
102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // When not expanded, we limit the size to one third the browser's
103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // width.
104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    desired_width /= 3;
105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // TODO(tc): We don't actually use gfx::Font as the font in the status
10872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // bubble.  We should extend ui::ElideUrl to take some sort of pango font.
10972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  url_text_ = UTF16ToUTF8(ui::ElideUrl(url_, gfx::Font(), desired_width,
110dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                          UTF16ToUTF8(languages_)));
111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SetStatusTextTo(url_text_);
112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid StatusBubbleGtk::Show() {
115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // If we were going to hide, stop.
116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  hide_timer_.Stop();
117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
11872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  gtk_widget_show(container_.get());
119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (container_->window)
120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gdk_window_raise(container_->window);
121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid StatusBubbleGtk::Hide() {
124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // If we were going to expand the bubble, stop.
125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  expand_timer_.Stop();
126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  expand_animation_.reset();
127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
12872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  gtk_widget_hide(container_.get());
129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid StatusBubbleGtk::SetStatusTextTo(const std::string& status_utf8) {
132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (status_utf8.empty()) {
133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    hide_timer_.Stop();
134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    hide_timer_.Start(base::TimeDelta::FromMilliseconds(kHideDelay),
135c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                      this, &StatusBubbleGtk::Hide);
136c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
137dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    gtk_label_set_text(GTK_LABEL(label_.get()), status_utf8.c_str());
138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    GtkRequisition req;
139dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    gtk_widget_size_request(label_.get(), &req);
140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    desired_width_ = req.width;
141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    UpdateLabelSizeRequest();
143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!last_mouse_left_content_) {
145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // Show the padding and label to update our requisition and then
146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // re-process the last mouse event -- if the label was empty before or the
147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // text changed, our size will have changed and we may need to move
148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // ourselves away from the pointer now.
149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      gtk_widget_show_all(padding_);
150c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      MouseMoved(last_mouse_location_, false);
151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    Show();
153c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
154c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
155c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
156c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid StatusBubbleGtk::MouseMoved(
157c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const gfx::Point& location, bool left_content) {
1583f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  if (left_content && ignore_next_left_content_) {
1593f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    ignore_next_left_content_ = false;
1603f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    return;
1613f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  }
1623f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen
163c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  last_mouse_location_ = location;
164c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  last_mouse_left_content_ = left_content;
165c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
166c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!GTK_WIDGET_REALIZED(container_.get()))
167c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
168c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
169c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GtkWidget* parent = gtk_widget_get_parent(container_.get());
170c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!parent || !GTK_WIDGET_REALIZED(parent))
171c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
172c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
173c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int old_y_offset = y_offset_;
174c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  bool old_flip_horizontally = flip_horizontally_;
175c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
176c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (left_content) {
177c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    SetFlipHorizontally(false);
178c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    y_offset_ = 0;
179c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
180c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    GtkWidget* toplevel = gtk_widget_get_toplevel(container_.get());
181c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!toplevel || !GTK_WIDGET_REALIZED(toplevel))
182c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return;
183c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
184c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    bool ltr = !base::i18n::IsRTL();
185c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
186c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    GtkRequisition requisition;
187c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gtk_widget_size_request(container_.get(), &requisition);
188c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
189c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Get our base position (that is, not including the current offset)
190c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // relative to the origin of the root window.
191c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gint toplevel_x = 0, toplevel_y = 0;
192c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gdk_window_get_position(toplevel->window, &toplevel_x, &toplevel_y);
193c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gfx::Rect parent_rect =
194c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        gtk_util::GetWidgetRectRelativeToToplevel(parent);
195c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gfx::Rect bubble_rect(
196c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        toplevel_x + parent_rect.x() +
1973f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen            (ltr ? 0 : parent->allocation.width - requisition.width),
198c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        toplevel_y + parent_rect.y() +
1993f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen            parent->allocation.height - requisition.height,
200c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        requisition.width,
201c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        requisition.height);
202c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
203c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int left_threshold =
204c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        bubble_rect.x() - bubble_rect.height() - kMousePadding;
205c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int right_threshold =
206c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        bubble_rect.right() + bubble_rect.height() + kMousePadding;
207c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int top_threshold = bubble_rect.y() - kMousePadding;
208c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
209c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (((ltr && location.x() < right_threshold) ||
210c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch         (!ltr && location.x() > left_threshold)) &&
211c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        location.y() > top_threshold) {
212c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (download_shelf_is_visible_) {
213c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        SetFlipHorizontally(true);
214c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        y_offset_ = 0;
215c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      } else {
216c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        SetFlipHorizontally(false);
217c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        int distance = std::max(ltr ?
2183f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen                                    location.x() - right_threshold :
2193f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen                                    left_threshold - location.x(),
220c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                top_threshold - location.y());
221c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        y_offset_ = std::min(-1 * distance, requisition.height);
222c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
223c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    } else {
224c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      SetFlipHorizontally(false);
225c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      y_offset_ = 0;
226c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
227c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
228c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
229c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (y_offset_ != old_y_offset || flip_horizontally_ != old_flip_horizontally)
230c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gtk_widget_queue_resize_no_redraw(parent);
231c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
232c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
233c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid StatusBubbleGtk::UpdateDownloadShelfVisibility(bool visible) {
234c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  download_shelf_is_visible_ = visible;
235c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
236c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
237c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid StatusBubbleGtk::Observe(NotificationType type,
238c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                              const NotificationSource& source,
239c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                              const NotificationDetails& details) {
240c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (type == NotificationType::BROWSER_THEME_CHANGED) {
241c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    UserChangedTheme();
242c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
243c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
244c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
245c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid StatusBubbleGtk::InitWidgets() {
246c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  bool ltr = !base::i18n::IsRTL();
247c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
248dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  label_.Own(gtk_label_new(NULL));
249c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
250c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  padding_ = gtk_alignment_new(0, 0, 1, 1);
251c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_alignment_set_padding(GTK_ALIGNMENT(padding_),
252c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      kInternalTopBottomPadding, kInternalTopBottomPadding,
253c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      kInternalLeftRightPadding + (ltr ? 0 : kCornerSize),
254c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      kInternalLeftRightPadding + (ltr ? kCornerSize : 0));
255dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  gtk_container_add(GTK_CONTAINER(padding_), label_.get());
25672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  gtk_widget_show_all(padding_);
257c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
258c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  container_.Own(gtk_event_box_new());
25972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  gtk_widget_set_no_show_all(container_.get(), TRUE);
260c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_util::ActAsRoundedWindow(
261513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch      container_.get(), gtk_util::kGdkWhite, kCornerSize,
262c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      gtk_util::ROUNDED_TOP_RIGHT,
263c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      gtk_util::BORDER_TOP | gtk_util::BORDER_RIGHT);
264c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_widget_set_name(container_.get(), "status-bubble");
265c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_container_add(GTK_CONTAINER(container_.get()), padding_);
266c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
267c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // We need to listen for mouse motion events, since a fast-moving pointer may
268c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // enter our window without us getting any motion events on the browser near
269c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // enough for us to run away.
2703f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  gtk_widget_add_events(container_.get(), GDK_POINTER_MOTION_MASK |
2713f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen                                          GDK_ENTER_NOTIFY_MASK);
272c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  g_signal_connect(container_.get(), "motion-notify-event",
273c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                   G_CALLBACK(HandleMotionNotifyThunk), this);
2743f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  g_signal_connect(container_.get(), "enter-notify-event",
2753f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen                   G_CALLBACK(HandleEnterNotifyThunk), this);
276c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
277c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  UserChangedTheme();
278c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
279c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
280c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid StatusBubbleGtk::UserChangedTheme() {
281ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (theme_service_->UseGtkTheme()) {
282dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    gtk_widget_modify_fg(label_.get(), GTK_STATE_NORMAL, NULL);
283c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gtk_widget_modify_bg(container_.get(), GTK_STATE_NORMAL, NULL);
284c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
285c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // TODO(erg): This is the closest to "text that will look good on a
286c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // toolbar" that I can find. Maybe in later iterations of the theme system,
287c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // there will be a better color to pick.
288c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    GdkColor bookmark_text =
289ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        theme_service_->GetGdkColor(ThemeService::COLOR_BOOKMARK_TEXT);
290dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    gtk_widget_modify_fg(label_.get(), GTK_STATE_NORMAL, &bookmark_text);
291c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
292c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    GdkColor toolbar_color =
293ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        theme_service_->GetGdkColor(ThemeService::COLOR_TOOLBAR);
294c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gtk_widget_modify_bg(container_.get(), GTK_STATE_NORMAL, &toolbar_color);
295c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
296c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
297c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_util::SetRoundedWindowBorderColor(container_.get(),
298ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                        theme_service_->GetBorderColor());
299c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
300c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
301c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid StatusBubbleGtk::SetFlipHorizontally(bool flip_horizontally) {
302c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (flip_horizontally == flip_horizontally_)
303c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
304c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
305c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  flip_horizontally_ = flip_horizontally;
306c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
307c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  bool ltr = !base::i18n::IsRTL();
308c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  bool on_left = (ltr && !flip_horizontally) || (!ltr && flip_horizontally);
309c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
310c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_alignment_set_padding(GTK_ALIGNMENT(padding_),
311c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      kInternalTopBottomPadding, kInternalTopBottomPadding,
312c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      kInternalLeftRightPadding + (on_left ? 0 : kCornerSize),
313c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      kInternalLeftRightPadding + (on_left ? kCornerSize : 0));
314c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The rounded window code flips these arguments if we're RTL.
315c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_util::SetRoundedWindowEdgesAndBorders(
316c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      container_.get(),
317c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      kCornerSize,
318c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      flip_horizontally ?
3193f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen          gtk_util::ROUNDED_TOP_LEFT :
3203f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen          gtk_util::ROUNDED_TOP_RIGHT,
321c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      gtk_util::BORDER_TOP |
3223f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen          (flip_horizontally ? gtk_util::BORDER_LEFT : gtk_util::BORDER_RIGHT));
323c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_widget_queue_draw(container_.get());
324c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
325c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
326c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid StatusBubbleGtk::ExpandURL() {
327dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  start_width_ = label_.get()->allocation.width;
3283f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  expand_animation_.reset(new ui::SlideAnimation(this));
3293f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  expand_animation_->SetTweenType(ui::Tween::LINEAR);
330c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  expand_animation_->Show();
331c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
332c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SetStatusTextToURL();
333c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
334c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
335c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid StatusBubbleGtk::UpdateLabelSizeRequest() {
336c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!expanded() || !expand_animation_->is_animating()) {
337dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    gtk_widget_set_size_request(label_.get(), -1, -1);
338c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
339c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
340c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
341c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int new_width = start_width_ +
342c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      (desired_width_ - start_width_) * expand_animation_->GetCurrentValue();
343dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  gtk_widget_set_size_request(label_.get(), new_width, -1);
344c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
345c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
3463f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen// See http://crbug.com/68897 for why we have to handle this event.
3473f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsengboolean StatusBubbleGtk::HandleEnterNotify(GtkWidget* sender,
3483f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen                                            GdkEventCrossing* event) {
3493f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  ignore_next_left_content_ = true;
3503f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  MouseMoved(gfx::Point(event->x_root, event->y_root), false);
3513f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  return FALSE;
3523f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen}
3533f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen
354c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochgboolean StatusBubbleGtk::HandleMotionNotify(GtkWidget* sender,
355c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                             GdkEventMotion* event) {
356c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  MouseMoved(gfx::Point(event->x_root, event->y_root), false);
357c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return FALSE;
358c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
359c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
3603f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsenvoid StatusBubbleGtk::AnimationEnded(const ui::Animation* animation) {
361c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  UpdateLabelSizeRequest();
362c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
363c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
3643f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsenvoid StatusBubbleGtk::AnimationProgressed(const ui::Animation* animation) {
365c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  UpdateLabelSizeRequest();
366c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
367