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