• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "chrome/browser/ui/gtk/tab_contents_container_gtk.h"
6 
7 #include <algorithm>
8 
9 #include "base/i18n/rtl.h"
10 #include "chrome/browser/renderer_host/render_widget_host_view_gtk.h"
11 #include "chrome/browser/ui/gtk/gtk_expanded_container.h"
12 #include "chrome/browser/ui/gtk/gtk_floating_container.h"
13 #include "chrome/browser/ui/gtk/status_bubble_gtk.h"
14 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
15 #include "content/browser/tab_contents/tab_contents.h"
16 #include "content/common/notification_source.h"
17 #include "ui/gfx/native_widget_types.h"
18 
TabContentsContainerGtk(StatusBubbleGtk * status_bubble)19 TabContentsContainerGtk::TabContentsContainerGtk(StatusBubbleGtk* status_bubble)
20     : tab_(NULL),
21       preview_(NULL),
22       status_bubble_(status_bubble) {
23   Init();
24 }
25 
~TabContentsContainerGtk()26 TabContentsContainerGtk::~TabContentsContainerGtk() {
27   floating_.Destroy();
28 }
29 
Init()30 void TabContentsContainerGtk::Init() {
31   // A high level overview of the TabContentsContainer:
32   //
33   // +- GtkFloatingContainer |floating_| -------------------------------+
34   // |+- GtkExpandedContainer |expanded_| -----------------------------+|
35   // ||                                                                ||
36   // ||                                                                ||
37   // ||                                                                ||
38   // ||                                                                ||
39   // |+- (StatusBubble) ------+                                        ||
40   // |+                       +                                        ||
41   // |+-----------------------+----------------------------------------+|
42   // +------------------------------------------------------------------+
43 
44   floating_.Own(gtk_floating_container_new());
45   gtk_widget_set_name(floating_.get(), "chrome-tab-contents-container");
46   g_signal_connect(floating_.get(), "focus", G_CALLBACK(OnFocusThunk), this);
47 
48   expanded_ = gtk_expanded_container_new();
49   gtk_container_add(GTK_CONTAINER(floating_.get()), expanded_);
50 
51   if (status_bubble_) {
52     gtk_floating_container_add_floating(GTK_FLOATING_CONTAINER(floating_.get()),
53                                         status_bubble_->widget());
54     g_signal_connect(floating_.get(), "set-floating-position",
55                      G_CALLBACK(OnSetFloatingPosition), this);
56   }
57 
58   gtk_widget_show(expanded_);
59   gtk_widget_show(floating_.get());
60 
61   ViewIDUtil::SetDelegateForWidget(widget(), this);
62 }
63 
SetTab(TabContentsWrapper * tab)64 void TabContentsContainerGtk::SetTab(TabContentsWrapper* tab) {
65   HideTab(tab_);
66   if (tab_) {
67     registrar_.Remove(this, NotificationType::TAB_CONTENTS_DESTROYED,
68                       Source<TabContents>(tab_->tab_contents()));
69   }
70 
71   tab_ = tab;
72 
73   if (tab_ == preview_) {
74     // If the preview contents is becoming the new permanent tab contents, we
75     // just reassign some pointers.
76     preview_ = NULL;
77   } else if (tab_) {
78     // Otherwise we actually have to add it to the widget hierarchy.
79     PackTab(tab);
80     registrar_.Add(this, NotificationType::TAB_CONTENTS_DESTROYED,
81                    Source<TabContents>(tab_->tab_contents()));
82   }
83 }
84 
GetVisibleTabContents()85 TabContents* TabContentsContainerGtk::GetVisibleTabContents() {
86   if (preview_)
87     return preview_->tab_contents();
88   return tab_ ? tab_->tab_contents() : NULL;
89 }
90 
SetPreview(TabContentsWrapper * preview)91 void TabContentsContainerGtk::SetPreview(TabContentsWrapper* preview) {
92   if (preview_)
93     RemovePreview();
94   else
95     HideTab(tab_);
96 
97   preview_ = preview;
98 
99   PackTab(preview);
100   registrar_.Add(this, NotificationType::TAB_CONTENTS_DESTROYED,
101                  Source<TabContents>(preview_->tab_contents()));
102 }
103 
RemovePreview()104 void TabContentsContainerGtk::RemovePreview() {
105   if (!preview_)
106     return;
107 
108   HideTab(preview_);
109 
110   GtkWidget* preview_widget = preview_->tab_contents()->GetNativeView();
111   if (preview_widget)
112     gtk_container_remove(GTK_CONTAINER(expanded_), preview_widget);
113 
114   registrar_.Remove(this, NotificationType::TAB_CONTENTS_DESTROYED,
115                     Source<TabContents>(preview_->tab_contents()));
116   preview_ = NULL;
117 }
118 
PopPreview()119 void TabContentsContainerGtk::PopPreview() {
120   if (!preview_)
121     return;
122 
123   RemovePreview();
124 
125   PackTab(tab_);
126 }
127 
PackTab(TabContentsWrapper * tab)128 void TabContentsContainerGtk::PackTab(TabContentsWrapper* tab) {
129   if (!tab)
130     return;
131 
132   gfx::NativeView widget = tab->tab_contents()->GetNativeView();
133   if (widget) {
134     if (widget->parent != expanded_)
135       gtk_container_add(GTK_CONTAINER(expanded_), widget);
136     gtk_widget_show(widget);
137   }
138 
139   // We need to make sure that we are below the findbar.
140   // Sometimes the content native view will be null.
141   if (tab->tab_contents()->GetContentNativeView()) {
142     GdkWindow* content_gdk_window =
143         tab->tab_contents()->GetContentNativeView()->window;
144     if (content_gdk_window)
145       gdk_window_lower(content_gdk_window);
146   }
147 
148   tab->tab_contents()->ShowContents();
149 }
150 
HideTab(TabContentsWrapper * tab)151 void TabContentsContainerGtk::HideTab(TabContentsWrapper* tab) {
152   if (!tab)
153     return;
154 
155   gfx::NativeView widget = tab->tab_contents()->GetNativeView();
156   if (widget)
157     gtk_widget_hide(widget);
158 
159   tab->tab_contents()->WasHidden();
160 }
161 
DetachTab(TabContentsWrapper * tab)162 void TabContentsContainerGtk::DetachTab(TabContentsWrapper* tab) {
163   gfx::NativeView widget = tab->tab_contents()->GetNativeView();
164 
165   // It is possible to detach an unrealized, unparented TabContents if you
166   // slow things down enough in valgrind. Might happen in the real world, too.
167   if (widget && widget->parent) {
168     DCHECK_EQ(widget->parent, expanded_);
169     gtk_container_remove(GTK_CONTAINER(expanded_), widget);
170   }
171 }
172 
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)173 void TabContentsContainerGtk::Observe(NotificationType type,
174                                       const NotificationSource& source,
175                                       const NotificationDetails& details) {
176   DCHECK(type == NotificationType::TAB_CONTENTS_DESTROYED);
177 
178   TabContentsDestroyed(Source<TabContents>(source).ptr());
179 }
180 
TabContentsDestroyed(TabContents * contents)181 void TabContentsContainerGtk::TabContentsDestroyed(TabContents* contents) {
182   // Sometimes, a TabContents is destroyed before we know about it. This allows
183   // us to clean up our state in case this happens.
184   if (preview_ && contents == preview_->tab_contents())
185     PopPreview();
186   else if (tab_ && contents == tab_->tab_contents())
187     SetTab(NULL);
188   else
189     NOTREACHED();
190 }
191 
192 // Prevent |preview_| from getting focus via the tab key. If |tab_| exists, try
193 // to focus that. Otherwise, do nothing, but stop event propagation. See bug
194 // http://crbug.com/63365
OnFocus(GtkWidget * widget,GtkDirectionType focus)195 gboolean TabContentsContainerGtk::OnFocus(GtkWidget* widget,
196                                           GtkDirectionType focus) {
197   if (preview_) {
198     gtk_widget_child_focus(tab_->tab_contents()->GetContentNativeView(), focus);
199     return TRUE;
200   }
201 
202   // No preview contents; let the default handler run.
203   return FALSE;
204 }
205 
206 // -----------------------------------------------------------------------------
207 // ViewIDUtil::Delegate implementation
208 
GetWidgetForViewID(ViewID view_id)209 GtkWidget* TabContentsContainerGtk::GetWidgetForViewID(ViewID view_id) {
210   if (view_id == VIEW_ID_TAB_CONTAINER ||
211       view_id == VIEW_ID_TAB_CONTAINER_FOCUS_VIEW) {
212     return widget();
213   }
214 
215   return NULL;
216 }
217 
218 // -----------------------------------------------------------------------------
219 
220 // static
OnSetFloatingPosition(GtkFloatingContainer * floating_container,GtkAllocation * allocation,TabContentsContainerGtk * tab_contents_container)221 void TabContentsContainerGtk::OnSetFloatingPosition(
222     GtkFloatingContainer* floating_container, GtkAllocation* allocation,
223     TabContentsContainerGtk* tab_contents_container) {
224   StatusBubbleGtk* status = tab_contents_container->status_bubble_;
225 
226   // Look at the size request of the status bubble and tell the
227   // GtkFloatingContainer where we want it positioned.
228   GtkRequisition requisition;
229   gtk_widget_size_request(status->widget(), &requisition);
230 
231   bool ltr = !base::i18n::IsRTL();
232 
233   GValue value = { 0, };
234   g_value_init(&value, G_TYPE_INT);
235   if (ltr ^ status->flip_horizontally())  // Is it on the left?
236     g_value_set_int(&value, 0);
237   else
238     g_value_set_int(&value, allocation->width - requisition.width);
239   gtk_container_child_set_property(GTK_CONTAINER(floating_container),
240                                    status->widget(), "x", &value);
241 
242   int child_y = std::max(allocation->height - requisition.height, 0);
243   g_value_set_int(&value, child_y + status->y_offset());
244   gtk_container_child_set_property(GTK_CONTAINER(floating_container),
245                                    status->widget(), "y", &value);
246   g_value_unset(&value);
247 }
248