Index: chrome/browser/gtk/browser_actions_toolbar_gtk.cc |
=================================================================== |
--- chrome/browser/gtk/browser_actions_toolbar_gtk.cc (revision 34134) |
+++ chrome/browser/gtk/browser_actions_toolbar_gtk.cc (working copy) |
@@ -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,14 +25,29 @@ |
#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 { |
public: |
@@ -61,11 +75,12 @@ |
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 @@ |
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 @@ |
} |
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 @@ |
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 @@ |
: 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::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::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; |
+} |