Home | History | Annotate | Download | only in gtk
      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