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 19 TabContentsContainerGtk::TabContentsContainerGtk(StatusBubbleGtk* status_bubble) 20 : tab_(NULL), 21 preview_(NULL), 22 status_bubble_(status_bubble) { 23 Init(); 24 } 25 26 TabContentsContainerGtk::~TabContentsContainerGtk() { 27 floating_.Destroy(); 28 } 29 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 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 85 TabContents* TabContentsContainerGtk::GetVisibleTabContents() { 86 if (preview_) 87 return preview_->tab_contents(); 88 return tab_ ? tab_->tab_contents() : NULL; 89 } 90 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 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 119 void TabContentsContainerGtk::PopPreview() { 120 if (!preview_) 121 return; 122 123 RemovePreview(); 124 125 PackTab(tab_); 126 } 127 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 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 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 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 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 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 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 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