summaryrefslogtreecommitdiffstats
path: root/chrome/browser/gtk
diff options
context:
space:
mode:
authorestade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-12-10 20:10:15 +0000
committerestade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-12-10 20:10:15 +0000
commitb2ba996f96dfd3bde403fce818258d97e3ca8002 (patch)
tree36f4f264fc2c8af5e6695ef31ac78afb7ebdc5c2 /chrome/browser/gtk
parent92b24c11f42b31158cca43921eac16f33c98839c (diff)
downloadchromium_src-b2ba996f96dfd3bde403fce818258d97e3ca8002.zip
chromium_src-b2ba996f96dfd3bde403fce818258d97e3ca8002.tar.gz
chromium_src-b2ba996f96dfd3bde403fce818258d97e3ca8002.tar.bz2
GTK: hook up drag and drop of browser actions (for reordering).
The changes are propagated across open chrome windows, but are not persisted between sessions yet. BUG=26990 Review URL: http://codereview.chromium.org/463056 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@34272 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/gtk')
-rw-r--r--chrome/browser/gtk/bookmark_bar_gtk.cc21
-rw-r--r--chrome/browser/gtk/bookmark_bar_gtk.h2
-rw-r--r--chrome/browser/gtk/browser_actions_toolbar_gtk.cc160
-rw-r--r--chrome/browser/gtk/browser_actions_toolbar_gtk.h38
4 files changed, 171 insertions, 50 deletions
diff --git a/chrome/browser/gtk/bookmark_bar_gtk.cc b/chrome/browser/gtk/bookmark_bar_gtk.cc
index 05db22b..cac9ed4 100644
--- a/chrome/browser/gtk/bookmark_bar_gtk.cc
+++ b/chrome/browser/gtk/bookmark_bar_gtk.cc
@@ -235,9 +235,7 @@ void BookmarkBarGtk::Init(Profile* profile) {
bookmark_toolbar_.Own(gtk_toolbar_new());
SetToolBarStyle();
gtk_widget_set_name(bookmark_toolbar_.get(), "chrome-bookmark-toolbar");
- gtk_widget_set_app_paintable(bookmark_toolbar_.get(), TRUE);
- g_signal_connect(bookmark_toolbar_.get(), "expose-event",
- G_CALLBACK(&OnToolbarExpose), this);
+ gtk_util::SuppressDefaultPainting(bookmark_toolbar_.get());
g_signal_connect(bookmark_toolbar_.get(), "size-allocate",
G_CALLBACK(&OnToolbarSizeAllocate), this);
gtk_box_pack_start(GTK_BOX(bookmark_hbox_), bookmark_toolbar_.get(),
@@ -974,23 +972,6 @@ void BookmarkBarGtk::OnFolderClicked(GtkWidget* sender,
}
// static
-gboolean BookmarkBarGtk::OnToolbarExpose(GtkWidget* widget,
- GdkEventExpose* event,
- BookmarkBarGtk* bar) {
- // A GtkToolbar's expose handler first draws a box. We don't want that so we
- // need to propagate the expose event to all the container's children.
- GList* children = gtk_container_get_children(GTK_CONTAINER(widget));
- for (GList* item = children; item; item = item->next) {
- gtk_container_propagate_expose(GTK_CONTAINER(widget),
- GTK_WIDGET(item->data),
- event);
- }
- g_list_free(children);
-
- return TRUE;
-}
-
-// static
gboolean BookmarkBarGtk::OnToolbarDragMotion(GtkToolbar* toolbar,
GdkDragContext* context,
gint x,
diff --git a/chrome/browser/gtk/bookmark_bar_gtk.h b/chrome/browser/gtk/bookmark_bar_gtk.h
index 571e43f..6a58e17 100644
--- a/chrome/browser/gtk/bookmark_bar_gtk.h
+++ b/chrome/browser/gtk/bookmark_bar_gtk.h
@@ -228,8 +228,6 @@ class BookmarkBarGtk : public AnimationDelegate,
BookmarkBarGtk* bar);
// GtkToolbar callbacks.
- static gboolean OnToolbarExpose(GtkWidget* widget, GdkEventExpose* event,
- BookmarkBarGtk* window);
static gboolean OnToolbarDragMotion(GtkToolbar* toolbar,
GdkDragContext* context,
gint x,
diff --git a/chrome/browser/gtk/browser_actions_toolbar_gtk.cc b/chrome/browser/gtk/browser_actions_toolbar_gtk.cc
index 91ea423..56424b3 100644
--- a/chrome/browser/gtk/browser_actions_toolbar_gtk.cc
+++ b/chrome/browser/gtk/browser_actions_toolbar_gtk.cc
@@ -4,7 +4,6 @@
#include "chrome/browser/gtk/browser_actions_toolbar_gtk.h"
-#include <gtk/gtk.h>
#include <vector>
#include "app/gfx/canvas_paint.h"
@@ -26,13 +25,28 @@
#include "chrome/common/notification_source.h"
#include "chrome/common/notification_type.h"
+namespace {
+
// The size of each button on the toolbar.
-static const int kButtonSize = 29;
+const int kButtonSize = 29;
// The padding between browser action buttons. Visually, the actual number of
// "empty" (non-drawing) pixels is this value + 2 when adjacent browser icons
// use their maximum allowed size.
-static const int kBrowserActionButtonPadding = 3;
+const int kButtonPadding = 3;
+
+const char* kDragTarget = "application/x-chrome-browseraction";
+
+GtkTargetEntry GetDragTargetEntry() {
+ static std::string drag_target_string(kDragTarget);
+ GtkTargetEntry drag_target;
+ drag_target.target = const_cast<char*>(drag_target_string.c_str());
+ drag_target.flags = GTK_TARGET_SAME_APP;
+ drag_target.info = 0;
+ return drag_target;
+}
+
+} // namespace
class BrowserActionButton : public NotificationObserver,
public ImageLoadingTracker::Observer {
@@ -61,11 +75,12 @@ class BrowserActionButton : public NotificationObserver,
Extension::kBrowserActionIconMaxSize));
}
- // We need to hook up extension popups here. http://crbug.com/23897
g_signal_connect(button_.get(), "clicked",
G_CALLBACK(OnButtonClicked), this);
g_signal_connect_after(button_.get(), "expose-event",
G_CALLBACK(OnExposeEvent), this);
+ g_signal_connect(button_.get(), "drag-begin",
+ G_CALLBACK(&OnDragBegin), this);
registrar_.Add(this, NotificationType::EXTENSION_BROWSER_ACTION_UPDATED,
Source<ExtensionAction>(extension->browser_action()));
@@ -90,6 +105,9 @@ class BrowserActionButton : public NotificationObserver,
GtkWidget* widget() { return button_.get(); }
+ Extension* extension() { return extension_; }
+
+ // NotificationObserver implementation.
void Observe(NotificationType type,
const NotificationSource& source,
const NotificationDetails& details) {
@@ -148,16 +166,16 @@ class BrowserActionButton : public NotificationObserver,
}
static void OnButtonClicked(GtkWidget* widget, BrowserActionButton* action) {
- if (action->extension_->browser_action()->has_popup()) {
- ExtensionPopupGtk::Show(
- action->extension_->browser_action()->popup_url(),
- action->toolbar_->browser(),
- gtk_util::GetWidgetRectRelativeToToplevel(widget));
- } else {
- ExtensionBrowserEventRouter::GetInstance()->BrowserActionExecuted(
- action->toolbar_->browser()->profile(), action->extension_->id(),
- action->toolbar_->browser());
- }
+ if (action->extension_->browser_action()->has_popup()) {
+ ExtensionPopupGtk::Show(
+ action->extension_->browser_action()->popup_url(),
+ action->toolbar_->browser(),
+ gtk_util::GetWidgetRectRelativeToToplevel(widget));
+ } else {
+ ExtensionBrowserEventRouter::GetInstance()->BrowserActionExecuted(
+ action->toolbar_->browser()->profile(), action->extension_->id(),
+ action->toolbar_->browser());
+ }
}
static gboolean OnExposeEvent(GtkWidget* widget,
@@ -177,6 +195,15 @@ class BrowserActionButton : public NotificationObserver,
return FALSE;
}
+ static void OnDragBegin(GtkWidget* widget,
+ GdkDragContext* drag_context,
+ BrowserActionButton* button) {
+ // Simply pass along the notification to the toolbar. The point of this
+ // function is to tell the toolbar which BrowserActionButton initiated the
+ // drag.
+ button->toolbar_->DragStarted(button, drag_context);
+ }
+
// The toolbar containing this button.
BrowserActionsToolbarGtk* toolbar_;
@@ -204,14 +231,18 @@ BrowserActionsToolbarGtk::BrowserActionsToolbarGtk(Browser* browser)
: browser_(browser),
profile_(browser->profile()),
model_(NULL),
- hbox_(gtk_hbox_new(FALSE, kBrowserActionButtonPadding)) {
+ hbox_(gtk_hbox_new(FALSE, kButtonPadding)),
+ drag_button_(NULL),
+ drop_index_(-1) {
ExtensionsService* extension_service = profile_->GetExtensionsService();
// The |extension_service| can be NULL in Incognito.
- if (extension_service) {
- model_ = extension_service->toolbar_model();
- model_->AddObserver(this);
- CreateAllButtons();
- }
+ if (!extension_service)
+ return;
+
+ model_ = extension_service->toolbar_model();
+ model_->AddObserver(this);
+ SetupDrags();
+ CreateAllButtons();
}
BrowserActionsToolbarGtk::~BrowserActionsToolbarGtk() {
@@ -235,21 +266,42 @@ void BrowserActionsToolbarGtk::Update() {
}
}
+void BrowserActionsToolbarGtk::SetupDrags() {
+ GtkTargetEntry drag_target = GetDragTargetEntry();
+ gtk_drag_dest_set(widget(), GTK_DEST_DEFAULT_DROP, &drag_target, 1,
+ GDK_ACTION_MOVE);
+
+ g_signal_connect(widget(), "drag-motion",
+ G_CALLBACK(OnDragMotionThunk), this);
+}
+
void BrowserActionsToolbarGtk::CreateAllButtons() {
+ extension_button_map_.clear();
+
+ int i = 0;
for (ExtensionList::iterator iter = model_->begin();
iter != model_->end(); ++iter) {
- CreateButtonForExtension(*iter);
+ CreateButtonForExtension(*iter, i++);
}
}
-void BrowserActionsToolbarGtk::CreateButtonForExtension(Extension* extension) {
+void BrowserActionsToolbarGtk::CreateButtonForExtension(Extension* extension,
+ int index) {
RemoveButtonForExtension(extension);
linked_ptr<BrowserActionButton> button(
new BrowserActionButton(this, extension));
- gtk_box_pack_end(GTK_BOX(hbox_.get()), button->widget(), FALSE, FALSE, 0);
+ 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_widget_show(button->widget());
extension_button_map_[extension->id()] = button;
+ GtkTargetEntry drag_target = GetDragTargetEntry();
+ gtk_drag_source_set(button->widget(), GDK_BUTTON1_MASK, &drag_target, 1,
+ GDK_ACTION_MOVE);
+ // We ignore whether the drag was a "success" or "failure" in Gtk's opinion.
+ g_signal_connect(button->widget(), "drag-end",
+ G_CALLBACK(&OnDragEndThunk), this);
+
UpdateVisibility();
}
@@ -267,10 +319,68 @@ void BrowserActionsToolbarGtk::UpdateVisibility() {
void BrowserActionsToolbarGtk::BrowserActionAdded(Extension* extension,
int index) {
- // TODO(estade): respect |index|.
- CreateButtonForExtension(extension);
+ CreateButtonForExtension(extension, index);
}
void BrowserActionsToolbarGtk::BrowserActionRemoved(Extension* extension) {
+ if (drag_button_ != NULL) {
+ // Break the current drag.
+ gtk_grab_remove(widget());
+
+ // Re-generate the toolbar to clean up unfinished drag business (i.e., we
+ // may have re-ordered some buttons; this will put them back where they
+ // belong).
+ CreateAllButtons();
+ }
+
RemoveButtonForExtension(extension);
}
+
+void BrowserActionsToolbarGtk::BrowserActionMoved(Extension* extension,
+ int index) {
+ // We initiated this move action, and have already moved the button.
+ if (drag_button_ != NULL)
+ return;
+
+ BrowserActionButton* button = extension_button_map_[extension->id()].get();
+ if (!button) {
+ NOTREACHED();
+ return;
+ }
+
+ gtk_box_reorder_child(GTK_BOX(hbox_.get()), button->widget(), index);
+}
+
+void BrowserActionsToolbarGtk::DragStarted(BrowserActionButton* button,
+ GdkDragContext* drag_context) {
+ // No representation of the widget following the cursor.
+ GdkPixbuf* pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, 1, 1);
+ gtk_drag_set_icon_pixbuf(drag_context, pixbuf, 0, 0);
+ g_object_unref(pixbuf);
+
+ DCHECK(!drag_button_);
+ drag_button_ = button;
+}
+
+gboolean BrowserActionsToolbarGtk::OnDragMotion(GtkWidget* widget,
+ GdkDragContext* drag_context,
+ gint x, gint y, guint time) {
+ drop_index_ = x < kButtonSize ? 0 : x / (kButtonSize + kButtonPadding);
+ // 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(),
+ drop_index_);
+
+ gdk_drag_status(drag_context, GDK_ACTION_MOVE, time);
+ return TRUE;
+}
+
+void BrowserActionsToolbarGtk::OnDragEnd(GtkWidget* button,
+ GdkDragContext* drag_context) {
+ if (drop_index_ != -1)
+ model_->MoveBrowserAction(drag_button_->extension(), drop_index_);
+
+ drag_button_ = NULL;
+ drop_index_ = -1;
+}
diff --git a/chrome/browser/gtk/browser_actions_toolbar_gtk.h b/chrome/browser/gtk/browser_actions_toolbar_gtk.h
index ceee599b..d2c27e6 100644
--- a/chrome/browser/gtk/browser_actions_toolbar_gtk.h
+++ b/chrome/browser/gtk/browser_actions_toolbar_gtk.h
@@ -5,6 +5,8 @@
#ifndef CHROME_BROWSER_GTK_BROWSER_ACTIONS_TOOLBAR_GTK_H_
#define CHROME_BROWSER_GTK_BROWSER_ACTIONS_TOOLBAR_GTK_H_
+#include <gtk/gtk.h>
+
#include <map>
#include <string>
@@ -39,15 +41,18 @@ class BrowserActionsToolbarGtk : public ExtensionToolbarModel::Observer {
void Update();
private:
+ friend class BrowserActionButton;
+
+ // Initialize drag and drop.
+ void SetupDrags();
+
// Query the extensions service for all extensions with browser actions,
// and create the UI for them.
void CreateAllButtons();
// Create the UI for a single browser action. This will stick the button
// at the end of the toolbar.
- // TODO(estade): is this OK, or does it need to place it in a specific
- // location on the toolbar?
- void CreateButtonForExtension(Extension* extension);
+ void CreateButtonForExtension(Extension* extension, int index);
// Delete resources associated with UI for a browser action.
void RemoveButtonForExtension(Extension* extension);
@@ -59,6 +64,27 @@ class BrowserActionsToolbarGtk : public ExtensionToolbarModel::Observer {
// ExtensionToolbarModel::Observer implementation.
virtual void BrowserActionAdded(Extension* extension, int index);
virtual void BrowserActionRemoved(Extension* extension);
+ virtual void BrowserActionMoved(Extension* extension, int index);
+
+ // 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);
Browser* browser_;
@@ -68,6 +94,12 @@ class BrowserActionsToolbarGtk : public ExtensionToolbarModel::Observer {
OwnedWidgetGtk hbox_;
+ // The button that is currently being dragged, or NULL.
+ BrowserActionButton* drag_button_;
+
+ // The new position of the button in the drag, or -1.
+ int drop_index_;
+
// Map from extension ID to BrowserActionButton, which is a wrapper for
// a chrome button and related functionality. There should be one entry
// for every extension that has a browser action.