1// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef CHROME_BROWSER_UI_GTK_STATUS_BUBBLE_GTK_H_
6#define CHROME_BROWSER_UI_GTK_STATUS_BUBBLE_GTK_H_
7#pragma once
8
9#include <gtk/gtk.h>
10
11#include <string>
12
13#include "base/memory/scoped_ptr.h"
14#include "base/timer.h"
15#include "chrome/browser/ui/gtk/owned_widget_gtk.h"
16#include "chrome/browser/ui/status_bubble.h"
17#include "content/common/notification_observer.h"
18#include "content/common/notification_registrar.h"
19#include "googleurl/src/gurl.h"
20#include "ui/base/animation/animation_delegate.h"
21#include "ui/base/gtk/gtk_signal.h"
22#include "ui/gfx/point.h"
23
24class GtkThemeService;
25class Profile;
26
27namespace ui {
28class SlideAnimation;
29}
30
31// GTK implementation of StatusBubble. Unlike Windows, our status bubble
32// doesn't have the nice leave-the-window effect since we can't rely on the
33// window manager to not try to be "helpful" and center our popups, etc.
34// We therefore position it absolutely in a GtkFixed, that we don't own.
35class StatusBubbleGtk : public StatusBubble,
36                        public NotificationObserver,
37                        public ui::AnimationDelegate {
38 public:
39  explicit StatusBubbleGtk(Profile* profile);
40  virtual ~StatusBubbleGtk();
41
42  bool flip_horizontally() const { return flip_horizontally_; }
43  int y_offset() const { return y_offset_; }
44
45  // StatusBubble implementation.
46  virtual void SetStatus(const string16& status);
47  virtual void SetURL(const GURL& url, const string16& languages);
48  virtual void Hide();
49  virtual void MouseMoved(const gfx::Point& location, bool left_content);
50
51  // ui::AnimationDelegate implementation.
52  virtual void AnimationEnded(const ui::Animation* animation);
53  virtual void AnimationProgressed(const ui::Animation* animation);
54
55  // Called when the download shelf becomes visible or invisible.
56  // This is used by to ensure that the status bubble does not obscure
57  // the download shelf, when it is visible.
58  virtual void UpdateDownloadShelfVisibility(bool visible);
59
60  // Overridden from NotificationObserver:
61  virtual void Observe(NotificationType type,
62                       const NotificationSource& source,
63                       const NotificationDetails& details);
64
65  // Top of the widget hierarchy for a StatusBubble. This top level widget is
66  // guarenteed to have its gtk_widget_name set to "status-bubble" for
67  // identification.
68  GtkWidget* widget() { return container_.get(); }
69
70 private:
71  // Sets the text of the label widget and controls visibility. (As contrasted
72  // with setting the current status or URL text, which may be ignored for now).
73  void SetStatusTextTo(const std::string& status_utf8);
74
75  // Sets the status text to the current value of |url_|, eliding it as
76  // necessary.
77  void SetStatusTextToURL();
78
79  // Sets the status bubble's location in the parent GtkFixed, shows the widget
80  // and makes sure that the status bubble has the highest z-order.
81  void Show();
82
83  // Builds the widgets, containers, etc.
84  void InitWidgets();
85
86  // Notification from the window that we should retheme ourself.
87  void UserChangedTheme();
88
89  // Sets whether the bubble should be flipped horizontally and displayed on the
90  // opposite side of the tab contents.  Reshapes the container and queues a
91  // redraw if necessary.
92  void SetFlipHorizontally(bool flip_horizontally);
93
94  // Expand the bubble up to the full width of the browser, so that the entire
95  // URL may be seen. Called after the user hovers over a link for sufficient
96  // time.
97  void ExpandURL();
98
99  // Adjust the actual size of the bubble by changing the label's size request.
100  void UpdateLabelSizeRequest();
101
102  // Returns true if the status bubble is in the expand-state (i.e., is
103  // currently expanded or in the process of expanding).
104  bool expanded() {
105    return expand_animation_.get();
106  }
107
108  CHROMEGTK_CALLBACK_1(StatusBubbleGtk, gboolean, HandleMotionNotify,
109                       GdkEventMotion*);
110
111  CHROMEGTK_CALLBACK_1(StatusBubbleGtk, gboolean, HandleEnterNotify,
112                       GdkEventCrossing*);
113
114  NotificationRegistrar registrar_;
115
116  // Provides colors.
117  GtkThemeService* theme_service_;
118
119  // The toplevel event box.
120  OwnedWidgetGtk container_;
121
122  // The GtkAlignment holding |label_|.
123  GtkWidget* padding_;
124
125  // The GtkLabel holding the text.
126  OwnedWidgetGtk label_;
127
128  // The status text we want to display when there are no URLs to display.
129  std::string status_text_;
130
131  // The URL we are displaying for.
132  GURL url_;
133
134  // The possibly elided url text we want to display.
135  std::string url_text_;
136
137  // Used to determine the character set that the user can read (for eliding
138  // the url text).
139  string16 languages_;
140
141  // A timer that hides our window after a delay.
142  base::OneShotTimer<StatusBubbleGtk> hide_timer_;
143
144  // A timer that expands our window after a delay.
145  base::OneShotTimer<StatusBubbleGtk> expand_timer_;
146
147  // The animation for resizing the status bubble on long hovers.
148  scoped_ptr<ui::SlideAnimation> expand_animation_;
149
150  // The start and end width of the current resize animation.
151  int start_width_;
152  int desired_width_;
153
154  // Should the bubble be flipped horizontally (e.g. displayed on the right for
155  // an LTR language)?  We move the bubble to the other side of the tab contents
156  // rather than sliding it down when the download shelf is visible.
157  bool flip_horizontally_;
158
159  // Vertical offset used to hide the status bubble as the pointer nears it.
160  int y_offset_;
161
162  // If the download shelf is visible, do not obscure it.
163  bool download_shelf_is_visible_;
164
165  // 'location' and 'left_content' values from the last invocation of
166  // MouseMoved().  We hang onto these so we can move the bubble if necessary
167  // when its text changes, triggering a size change.
168  gfx::Point last_mouse_location_;
169  bool last_mouse_left_content_;
170
171  // Shortly after the cursor enters the status bubble, we'll get a message
172  // that the cursor left the content area. This lets us ignore that.
173  bool ignore_next_left_content_;
174};
175
176#endif  // CHROME_BROWSER_UI_GTK_STATUS_BUBBLE_GTK_H_
177