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