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