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