summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authorestade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-11 19:32:10 +0000
committerestade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-11 19:32:10 +0000
commitcbc9ed60c4959214cbbcff09035534e29eaed0db (patch)
tree327956baa5e08edbd4c576cebf860d849740e329 /chrome
parent25396dab596e7e47262a8a9569c7570d72b1a5ea (diff)
downloadchromium_src-cbc9ed60c4959214cbbcff09035534e29eaed0db.zip
chromium_src-cbc9ed60c4959214cbbcff09035534e29eaed0db.tar.gz
chromium_src-cbc9ed60c4959214cbbcff09035534e29eaed0db.tar.bz2
GTK: make browser actions container resizable.
TODO: persist the size of the browser actions container to the profile. TODO: implement the overflow menu. BUG=32101 TEST=manual Review URL: http://codereview.chromium.org/800003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@41305 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/extensions/extension_toolbar_model.h3
-rw-r--r--chrome/browser/gtk/browser_actions_toolbar_gtk.cc155
-rw-r--r--chrome/browser/gtk/browser_actions_toolbar_gtk.h69
-rw-r--r--chrome/browser/gtk/browser_toolbar_gtk.cc4
-rw-r--r--chrome/browser/gtk/gtk_chrome_shrinkable_hbox.cc14
-rw-r--r--chrome/browser/gtk/gtk_chrome_shrinkable_hbox.h3
-rw-r--r--chrome/browser/gtk/overflow_button.cc44
-rw-r--r--chrome/browser/gtk/overflow_button.h37
-rwxr-xr-xchrome/chrome_browser.gypi2
9 files changed, 271 insertions, 60 deletions
diff --git a/chrome/browser/extensions/extension_toolbar_model.h b/chrome/browser/extensions/extension_toolbar_model.h
index a4c7855..d930c86 100644
--- a/chrome/browser/extensions/extension_toolbar_model.h
+++ b/chrome/browser/extensions/extension_toolbar_model.h
@@ -37,6 +37,9 @@ class ExtensionToolbarModel : public NotificationObserver {
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
void MoveBrowserAction(Extension* extension, int index);
+ // TODO(estade): implement these.
+ void SetVisibleIconCount(int count) {}
+ int GetVisibleIconCount() { return -1; }
size_t size() const {
return toolitems_.size();
diff --git a/chrome/browser/gtk/browser_actions_toolbar_gtk.cc b/chrome/browser/gtk/browser_actions_toolbar_gtk.cc
index 78da006..a20a4921 100644
--- a/chrome/browser/gtk/browser_actions_toolbar_gtk.cc
+++ b/chrome/browser/gtk/browser_actions_toolbar_gtk.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -13,11 +13,14 @@
#include "chrome/browser/extensions/extension_browser_event_router.h"
#include "chrome/browser/extensions/extensions_service.h"
#include "chrome/browser/extensions/image_loading_tracker.h"
+#include "chrome/browser/gtk/cairo_cached_surface.h"
#include "chrome/browser/gtk/extension_popup_gtk.h"
#include "chrome/browser/gtk/gtk_chrome_button.h"
+#include "chrome/browser/gtk/gtk_chrome_shrinkable_hbox.h"
#include "chrome/browser/gtk/gtk_theme_provider.h"
#include "chrome/browser/gtk/gtk_util.h"
#include "chrome/browser/gtk/menu_gtk.h"
+#include "chrome/browser/gtk/view_id_util.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/extensions/extension.h"
@@ -26,6 +29,7 @@
#include "chrome/common/notification_service.h"
#include "chrome/common/notification_source.h"
#include "chrome/common/notification_type.h"
+#include "grit/app_resources.h"
namespace {
@@ -48,6 +52,43 @@ GtkTargetEntry GetDragTargetEntry() {
return drag_target;
}
+// The minimum width in pixels of the button hbox if |icon_count| icons are
+// showing.
+gint WidthForIconCount(gint icon_count) {
+ return (kButtonSize + kButtonPadding) * icon_count - kButtonPadding;
+}
+
+// These three signal handlers are used to give the gripper the resize
+// cursor. Since it doesn't have its own window, we have to set the cursor
+// whenever the pointer moves into the button or leaves the button, and be
+// sure to leave it on when the user is dragging.
+gboolean OnGripperEnterNotify(GtkWidget* gripper,
+ GdkEventCrossing* event,
+ gpointer unused) {
+ gdk_window_set_cursor(gripper->window,
+ gtk_util::GetCursor(GDK_SB_H_DOUBLE_ARROW));
+ return FALSE;
+}
+
+gboolean OnGripperLeaveNotify(GtkWidget* gripper,
+ GdkEventCrossing* event,
+ gpointer unused) {
+ if (!(event->state & GDK_BUTTON1_MASK))
+ gdk_window_set_cursor(gripper->window, NULL);
+ return FALSE;
+}
+
+gboolean OnGripperButtonRelease(GtkWidget* gripper,
+ GdkEventButton* event,
+ gpointer unused) {
+ gfx::Rect gripper_rect(0, 0,
+ gripper->allocation.width, gripper->allocation.height);
+ gfx::Point release_point(event->x, event->y);
+ if (!gripper_rect.Contains(release_point))
+ gdk_window_set_cursor(gripper->window, NULL);
+ return FALSE;
+}
+
} // namespace
class BrowserActionButton : public NotificationObserver,
@@ -254,8 +295,11 @@ class BrowserActionButton : public NotificationObserver,
BrowserActionsToolbarGtk::BrowserActionsToolbarGtk(Browser* browser)
: browser_(browser),
profile_(browser->profile()),
+ theme_provider_(GtkThemeProvider::GetFrom(browser->profile())),
model_(NULL),
- hbox_(gtk_hbox_new(FALSE, kButtonPadding)),
+ hbox_(gtk_hbox_new(FALSE, 0)),
+ button_hbox_(gtk_chrome_shrinkable_hbox_new(TRUE, FALSE, kButtonPadding)),
+ overflow_button_(browser->profile()),
drag_button_(NULL),
drop_index_(-1),
method_factory_(this) {
@@ -264,6 +308,25 @@ BrowserActionsToolbarGtk::BrowserActionsToolbarGtk(Browser* browser)
if (!extension_service)
return;
+ GtkWidget* gripper = gtk_button_new();
+ GTK_WIDGET_UNSET_FLAGS(gripper, GTK_CAN_FOCUS);
+ gtk_widget_add_events(gripper, GDK_POINTER_MOTION_MASK);
+ g_signal_connect(gripper, "motion-notify-event",
+ G_CALLBACK(OnGripperMotionNotifyThunk), this);
+ g_signal_connect(gripper, "expose-event",
+ G_CALLBACK(OnGripperExposeThunk), this);
+ g_signal_connect(gripper, "enter-notify-event",
+ G_CALLBACK(OnGripperEnterNotify), NULL);
+ g_signal_connect(gripper, "leave-notify-event",
+ G_CALLBACK(OnGripperLeaveNotify), NULL);
+ g_signal_connect(gripper, "button-release-event",
+ G_CALLBACK(OnGripperButtonRelease), NULL);
+
+ gtk_box_pack_start(GTK_BOX(hbox_.get()), gripper, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(hbox_.get()), button_hbox_, TRUE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(hbox_.get()), overflow_button_.widget(),
+ FALSE, FALSE, 0);
+
model_ = extension_service->toolbar_model();
model_->AddObserver(this);
SetupDrags();
@@ -273,6 +336,12 @@ BrowserActionsToolbarGtk::BrowserActionsToolbarGtk(Browser* browser)
// until we are added to a toplevel window to do so.
g_signal_connect(widget(), "hierarchy-changed",
G_CALLBACK(OnHierarchyChangedThunk), this);
+
+ int showing_actions = model_->GetVisibleIconCount();
+ if (showing_actions >= 0)
+ SetButtonHBoxWidth(WidthForIconCount(showing_actions));
+
+ ViewIDUtil::SetID(button_hbox_, VIEW_ID_BROWSER_ACTION_TOOLBAR);
}
BrowserActionsToolbarGtk::~BrowserActionsToolbarGtk() {
@@ -304,10 +373,10 @@ void BrowserActionsToolbarGtk::Update() {
void BrowserActionsToolbarGtk::SetupDrags() {
GtkTargetEntry drag_target = GetDragTargetEntry();
- gtk_drag_dest_set(widget(), GTK_DEST_DEFAULT_DROP, &drag_target, 1,
+ gtk_drag_dest_set(button_hbox_, GTK_DEST_DEFAULT_DROP, &drag_target, 1,
GDK_ACTION_MOVE);
- g_signal_connect(widget(), "drag-motion",
+ g_signal_connect(button_hbox_, "drag-motion",
G_CALLBACK(OnDragMotionThunk), this);
}
@@ -332,8 +401,9 @@ void BrowserActionsToolbarGtk::CreateButtonForExtension(Extension* extension,
RemoveButtonForExtension(extension);
linked_ptr<BrowserActionButton> button(
new BrowserActionButton(this, extension));
- gtk_box_pack_start(GTK_BOX(hbox_.get()), button->widget(), FALSE, FALSE, 0);
- gtk_box_reorder_child(GTK_BOX(hbox_.get()), button->widget(), index);
+ gtk_chrome_shrinkable_hbox_pack_start(
+ GTK_CHROME_SHRINKABLE_HBOX(button_hbox_), button->widget(), 0);
+ gtk_box_reorder_child(GTK_BOX(button_hbox_), button->widget(), index);
gtk_widget_show(button->widget());
extension_button_map_[extension->id()] = button;
@@ -392,7 +462,7 @@ void BrowserActionsToolbarGtk::BrowserActionAdded(Extension* extension,
void BrowserActionsToolbarGtk::BrowserActionRemoved(Extension* extension) {
if (drag_button_ != NULL) {
// Break the current drag.
- gtk_grab_remove(widget());
+ gtk_grab_remove(button_hbox_);
}
RemoveButtonForExtension(extension);
@@ -414,7 +484,7 @@ void BrowserActionsToolbarGtk::BrowserActionMoved(Extension* extension,
if (profile_->IsOffTheRecord())
index = model_->OriginalIndexToIncognito(index);
- gtk_box_reorder_child(GTK_BOX(hbox_.get()), button->widget(), index);
+ gtk_box_reorder_child(GTK_BOX(button_hbox_), button->widget(), index);
}
void BrowserActionsToolbarGtk::DragStarted(BrowserActionButton* button,
@@ -428,6 +498,34 @@ void BrowserActionsToolbarGtk::DragStarted(BrowserActionButton* button,
drag_button_ = button;
}
+void BrowserActionsToolbarGtk::SetButtonHBoxWidth(int new_width) {
+ gint max_width = WidthForIconCount(model_->size());
+ new_width = std::min(max_width, new_width);
+ new_width = std::max(new_width, 0);
+ gtk_widget_set_size_request(button_hbox_, new_width, -1);
+
+ int showing_icon_count =
+ gtk_chrome_shrinkable_hbox_get_visible_child_count(
+ GTK_CHROME_SHRINKABLE_HBOX(button_hbox_));
+
+ model_->SetVisibleIconCount(showing_icon_count);
+ if (model_->size() > static_cast<size_t>(showing_icon_count)) {
+ if (!GTK_WIDGET_VISIBLE(overflow_button_.widget())) {
+ // When the overflow chevron shows for the first time, take that
+ // much space away from |button_hbox_| to make the drag look smoother.
+ GtkRequisition req;
+ gtk_widget_size_request(overflow_button_.widget(), &req);
+ new_width -= req.width;
+ new_width = std::max(new_width, 0);
+ gtk_widget_set_size_request(button_hbox_, new_width, -1);
+
+ gtk_widget_show(overflow_button_.widget());
+ }
+ } else {
+ gtk_widget_hide(overflow_button_.widget());
+ }
+}
+
gboolean BrowserActionsToolbarGtk::OnDragMotion(GtkWidget* widget,
GdkDragContext* drag_context,
gint x, gint y, guint time) {
@@ -442,7 +540,7 @@ gboolean BrowserActionsToolbarGtk::OnDragMotion(GtkWidget* widget,
// We will go ahead and reorder the child in order to provide visual feedback
// to the user. We don't inform the model that it has moved until the drag
// ends.
- gtk_box_reorder_child(GTK_BOX(hbox_.get()), drag_button_->widget(),
+ gtk_box_reorder_child(GTK_BOX(button_hbox_), drag_button_->widget(),
drop_index_);
gdk_drag_status(drag_context, GDK_ACTION_MOVE, time);
@@ -468,18 +566,51 @@ gboolean BrowserActionsToolbarGtk::OnDragFailed(GtkWidget* widget,
return TRUE;
}
-void BrowserActionsToolbarGtk::OnHierarchyChanged() {
- GtkWidget* toplevel = gtk_widget_get_toplevel(widget());
+void BrowserActionsToolbarGtk::OnHierarchyChanged(GtkWidget* widget,
+ GtkWidget* previous_toplevel) {
+ GtkWidget* toplevel = gtk_widget_get_toplevel(widget);
if (!GTK_WIDGET_TOPLEVEL(toplevel))
return;
g_signal_connect(toplevel, "set-focus", G_CALLBACK(OnSetFocusThunk), this);
}
-void BrowserActionsToolbarGtk::OnSetFocus() {
+void BrowserActionsToolbarGtk::OnSetFocus(GtkWidget* widget,
+ GtkWidget* focus_widget) {
// The focus of the parent window has changed. Close the popup. Delay the hide
// because it will destroy the RenderViewHost, which may still be on the
// call stack.
+ if (!ExtensionPopupGtk::get_current_extension_popup())
+ return;
MessageLoop::current()->PostTask(FROM_HERE,
method_factory_.NewRunnableMethod(&BrowserActionsToolbarGtk::HidePopup));
}
+
+gboolean BrowserActionsToolbarGtk::OnGripperMotionNotify(
+ GtkWidget* widget, GdkEventMotion* event) {
+ if (!(event->state & GDK_BUTTON1_MASK))
+ return FALSE;
+
+ gint new_width = button_hbox_->allocation.width -
+ (event->x - widget->allocation.width);
+ SetButtonHBoxWidth(new_width);
+
+ return FALSE;
+}
+
+gboolean BrowserActionsToolbarGtk::OnGripperExpose(GtkWidget* gripper,
+ GdkEventExpose* expose) {
+ cairo_t* cr = gdk_cairo_create(GDK_DRAWABLE(expose->window));
+
+ CairoCachedSurface* surface = theme_provider_->GetSurfaceNamed(
+ IDR_RESIZE_GRIPPER, gripper);
+ gfx::Point center = gfx::Rect(gripper->allocation).CenterPoint();
+ center.Offset(-surface->Width() / 2, -surface->Height() / 2);
+ surface->SetSource(cr, center.x(), center.y());
+ gdk_cairo_rectangle(cr, &expose->area);
+ cairo_fill(cr);
+
+ cairo_destroy(cr);
+
+ return TRUE;
+}
diff --git a/chrome/browser/gtk/browser_actions_toolbar_gtk.h b/chrome/browser/gtk/browser_actions_toolbar_gtk.h
index 024e81b..2e03897 100644
--- a/chrome/browser/gtk/browser_actions_toolbar_gtk.h
+++ b/chrome/browser/gtk/browser_actions_toolbar_gtk.h
@@ -13,13 +13,16 @@
#include "base/linked_ptr.h"
#include "base/task.h"
#include "chrome/browser/extensions/extension_toolbar_model.h"
+#include "chrome/browser/gtk/overflow_button.h"
#include "chrome/common/notification_observer.h"
#include "chrome/common/notification_registrar.h"
#include "chrome/common/owned_widget_gtk.h"
+#include "chrome/common/gtk_signal.h"
class Browser;
class BrowserActionButton;
class Extension;
+class GtkThemeProvider;
class Profile;
typedef struct _GtkWidget GtkWidget;
@@ -27,7 +30,7 @@ typedef struct _GtkWidget GtkWidget;
class BrowserActionsToolbarGtk : public ExtensionToolbarModel::Observer {
public:
explicit BrowserActionsToolbarGtk(Browser* browser);
- ~BrowserActionsToolbarGtk();
+ virtual ~BrowserActionsToolbarGtk();
GtkWidget* widget() { return hbox_.get(); }
@@ -83,55 +86,39 @@ class BrowserActionsToolbarGtk : public ExtensionToolbarModel::Observer {
// Called by the BrowserActionButton in response to drag-begin.
void DragStarted(BrowserActionButton* button, GdkDragContext* drag_context);
- static gboolean OnDragMotionThunk(GtkWidget* widget,
- GdkDragContext* drag_context,
- gint x, gint y, guint time,
- BrowserActionsToolbarGtk* toolbar) {
- return toolbar->OnDragMotion(widget, drag_context, x, y, time);
- }
- gboolean OnDragMotion(GtkWidget* widget,
- GdkDragContext* drag_context,
- gint x, gint y, guint time);
-
- static void OnDragEndThunk(GtkWidget* button,
- GdkDragContext* drag_context,
- BrowserActionsToolbarGtk* toolbar) {
- toolbar->OnDragEnd(button, drag_context);
- }
- void OnDragEnd(GtkWidget* button, GdkDragContext* drag_context);
-
- static gboolean OnDragFailedThunk(GtkWidget* widget,
- GdkDragContext* drag_context,
- GtkDragResult result,
- BrowserActionsToolbarGtk* toolbar) {
- return toolbar->OnDragFailed(widget, drag_context, result);
- }
- gboolean OnDragFailed(GtkWidget* widget,
- GdkDragContext* drag_context,
- GtkDragResult result);
-
- static void OnHierarchyChangedThunk(GtkWidget* widget,
- GtkWidget* previous_toplevel,
- BrowserActionsToolbarGtk* toolbar) {
- toolbar->OnHierarchyChanged();
- }
- void OnHierarchyChanged();
-
- static void OnSetFocusThunk(GtkWindow* window,
- GtkWidget* widget,
- BrowserActionsToolbarGtk* toolbar) {
- toolbar->OnSetFocus();
- }
- void OnSetFocus();
+ // Sets the width of the button area of the toolbar to |new_width|, clamping
+ // it to appropriate values and updating the model.
+ void SetButtonHBoxWidth(int new_width);
+
+ CHROMEGTK_CALLBACK_4(BrowserActionsToolbarGtk, gboolean, OnDragMotion,
+ GdkDragContext*, gint, gint, guint);
+ CHROMEGTK_CALLBACK_1(BrowserActionsToolbarGtk, void, OnDragEnd,
+ GdkDragContext*);
+ CHROMEGTK_CALLBACK_2(BrowserActionsToolbarGtk, gboolean, OnDragFailed,
+ GdkDragContext*, GtkDragResult);
+ CHROMEGTK_CALLBACK_1(BrowserActionsToolbarGtk, void, OnHierarchyChanged,
+ GtkWidget*);
+ CHROMEGTK_CALLBACK_1(BrowserActionsToolbarGtk, void, OnSetFocus, GtkWidget*);
+ CHROMEGTK_CALLBACK_1(BrowserActionsToolbarGtk, gboolean,
+ OnGripperMotionNotify, GdkEventMotion*);
+ CHROMEGTK_CALLBACK_1(BrowserActionsToolbarGtk, gboolean, OnGripperExpose,
+ GdkEventExpose*);
Browser* browser_;
Profile* profile_;
+ GtkThemeProvider* theme_provider_;
ExtensionToolbarModel* model_;
+ // Contains the drag gripper, browser action buttons, and overflow chevron.
OwnedWidgetGtk hbox_;
+ // Contains the browser action buttons.
+ GtkWidget* button_hbox_;
+
+ OverflowButton overflow_button_;
+
// The button that is currently being dragged, or NULL.
BrowserActionButton* drag_button_;
diff --git a/chrome/browser/gtk/browser_toolbar_gtk.cc b/chrome/browser/gtk/browser_toolbar_gtk.cc
index 8650770..7eed210 100644
--- a/chrome/browser/gtk/browser_toolbar_gtk.cc
+++ b/chrome/browser/gtk/browser_toolbar_gtk.cc
@@ -264,10 +264,6 @@ void BrowserToolbarGtk::SetViewIDs() {
ViewIDUtil::SetID(go_->widget(), VIEW_ID_GO_BUTTON);
ViewIDUtil::SetID(page_menu_button_.get(), VIEW_ID_PAGE_MENU);
ViewIDUtil::SetID(app_menu_button_.get(), VIEW_ID_APP_MENU);
- if (actions_toolbar_.get()) {
- ViewIDUtil::SetID(actions_toolbar_->widget(),
- VIEW_ID_BROWSER_ACTION_TOOLBAR);
- }
}
void BrowserToolbarGtk::Show() {
diff --git a/chrome/browser/gtk/gtk_chrome_shrinkable_hbox.cc b/chrome/browser/gtk/gtk_chrome_shrinkable_hbox.cc
index b147f5f..0dccb7d 100644
--- a/chrome/browser/gtk/gtk_chrome_shrinkable_hbox.cc
+++ b/chrome/browser/gtk/gtk_chrome_shrinkable_hbox.cc
@@ -170,9 +170,9 @@ static void gtk_chrome_shrinkable_hbox_size_allocate(
(GTK_WIDGET_CLASS(gtk_chrome_shrinkable_hbox_parent_class)->size_allocate)
(widget, allocation);
- int visible_children_count = 0;
- gtk_container_foreach(GTK_CONTAINER(widget), CountVisibleChildren,
- &visible_children_count);
+ gint visible_children_count =
+ gtk_chrome_shrinkable_hbox_get_visible_child_count(
+ GTK_CHROME_SHRINKABLE_HBOX(widget));
if (visible_children_count == 0)
return;
@@ -238,4 +238,12 @@ void gtk_chrome_shrinkable_hbox_pack_end(GtkChromeShrinkableHBox* box,
gtk_box_pack_end(GTK_BOX(box), child, FALSE, FALSE, 0);
}
+gint gtk_chrome_shrinkable_hbox_get_visible_child_count(
+ GtkChromeShrinkableHBox* box) {
+ gint visible_children_count = 0;
+ gtk_container_foreach(GTK_CONTAINER(box), CountVisibleChildren,
+ &visible_children_count);
+ return visible_children_count;
+}
+
G_END_DECLS
diff --git a/chrome/browser/gtk/gtk_chrome_shrinkable_hbox.h b/chrome/browser/gtk/gtk_chrome_shrinkable_hbox.h
index 75f9ed2..02be55d 100644
--- a/chrome/browser/gtk/gtk_chrome_shrinkable_hbox.h
+++ b/chrome/browser/gtk/gtk_chrome_shrinkable_hbox.h
@@ -75,6 +75,9 @@ void gtk_chrome_shrinkable_hbox_pack_end(GtkChromeShrinkableHBox* box,
GtkWidget* child,
guint padding);
+gint gtk_chrome_shrinkable_hbox_get_visible_child_count(
+ GtkChromeShrinkableHBox* box);
+
G_END_DECLS
#endif // CHROME_BROWSER_GTK_GTK_CHROME_SHRINKABLE_HBOX_H_
diff --git a/chrome/browser/gtk/overflow_button.cc b/chrome/browser/gtk/overflow_button.cc
new file mode 100644
index 0000000..bd1e216
--- /dev/null
+++ b/chrome/browser/gtk/overflow_button.cc
@@ -0,0 +1,44 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/gtk/overflow_button.h"
+
+#include <gtk/gtk.h>
+
+#include "app/resource_bundle.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/gtk/gtk_theme_provider.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/common/notification_type.h"
+#include "grit/theme_resources.h"
+
+OverflowButton::OverflowButton(Profile* profile) : profile_(profile) {
+ widget_.Own(GtkThemeProvider::GetFrom(profile)->BuildChromeButton());
+ gtk_widget_set_no_show_all(widget_.get(), TRUE);
+
+ registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED,
+ NotificationService::AllSources());
+ GtkThemeProvider::GetFrom(profile)->InitThemesFor(this);
+}
+
+OverflowButton::~OverflowButton() {
+ widget_.Destroy();
+}
+
+void OverflowButton::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ GtkWidget* former_child = gtk_bin_get_child(GTK_BIN(widget()));
+ if (former_child)
+ gtk_widget_destroy(former_child);
+
+ GtkWidget* new_child =
+ GtkThemeProvider::GetFrom(profile_)->UseGtkTheme() ?
+ gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_NONE) :
+ gtk_image_new_from_pixbuf(ResourceBundle::GetSharedInstance().
+ GetRTLEnabledPixbufNamed(IDR_BOOKMARK_BAR_CHEVRONS));
+
+ gtk_container_add(GTK_CONTAINER(widget()), new_child);
+ gtk_widget_show(new_child);
+}
diff --git a/chrome/browser/gtk/overflow_button.h b/chrome/browser/gtk/overflow_button.h
new file mode 100644
index 0000000..5362e6b
--- /dev/null
+++ b/chrome/browser/gtk/overflow_button.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_GTK_OVERFLOW_BUTTON_H_
+#define CHROME_BROWSER_GTK_OVERFLOW_BUTTON_H_
+
+#include "chrome/common/notification_observer.h"
+#include "chrome/common/notification_registrar.h"
+#include "chrome/common/owned_widget_gtk.h"
+
+typedef struct _GtkWidget GtkWidget;
+class Profile;
+
+// An overflow chevron button. The button itself is a plain gtk_chrome_button,
+// and this class handles theming it.
+class OverflowButton : public NotificationObserver {
+ public:
+ explicit OverflowButton(Profile* profile);
+ virtual ~OverflowButton();
+
+ GtkWidget* widget() { return widget_.get(); }
+
+ private:
+ // NotificationObserver implementation.
+ void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ OwnedWidgetGtk widget_;
+
+ Profile* profile_;
+
+ NotificationRegistrar registrar_;
+};
+
+#endif // CHROME_BROWSER_GTK_OVERFLOW_BUTTON_H_
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index ddf754b..08441c5 100755
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -1194,6 +1194,8 @@
'browser/gtk/notifications/balloon_view_host_gtk.h',
'browser/gtk/notifications/notification_options_menu_model.cc',
'browser/gtk/notifications/notification_options_menu_model.h',
+ 'browser/gtk/overflow_button.cc',
+ 'browser/gtk/overflow_button.h',
'browser/gtk/options/advanced_contents_gtk.cc',
'browser/gtk/options/advanced_contents_gtk.h',
'browser/gtk/options/advanced_page_gtk.cc',