From dac443791cffe0c7681fffe2e1a405418474f5d9 Mon Sep 17 00:00:00 2001 From: "estade@chromium.org" Date: Mon, 29 Jun 2009 17:35:32 +0000 Subject: GTK DND: - Rearrange DND constants, create convenience functions - (partially) implement drag onto home button Note that the homepage is still hard-wired to the dev build splash page, so that it seems like this isn't working, but via debugging I ascertained that dragging a random file from the desktop onto the home button should successfully set that file as the homepage. TEST=drag a lot of stuff all over the place (tabs, bookmarks, what have you), make sure nothing crashes BUG=none Review URL: http://codereview.chromium.org/147256 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@19495 0039d316-1c4b-4281-b951-d872f2087c98 --- chrome/browser/gtk/bookmark_bar_gtk.cc | 14 ++++----- chrome/browser/gtk/bookmark_manager_gtk.cc | 23 ++++++++------- chrome/browser/gtk/bookmark_utils_gtk.cc | 13 --------- chrome/browser/gtk/browser_toolbar_gtk.cc | 42 ++++++++++++++++++++++++++ chrome/browser/gtk/browser_toolbar_gtk.h | 13 ++++++++- chrome/browser/gtk/dnd_registry.cc | 47 ++++++++++++++++++++++++++++++ chrome/browser/gtk/dnd_registry.h | 24 +++++++++------ chrome/browser/gtk/tabs/tab_gtk.cc | 10 ++----- chrome/browser/gtk/tabs/tab_strip_gtk.cc | 39 +++++++------------------ chrome/chrome.gyp | 2 ++ 10 files changed, 148 insertions(+), 79 deletions(-) create mode 100644 chrome/browser/gtk/dnd_registry.cc (limited to 'chrome') diff --git a/chrome/browser/gtk/bookmark_bar_gtk.cc b/chrome/browser/gtk/bookmark_bar_gtk.cc index dc86bab..d9b7ae9 100644 --- a/chrome/browser/gtk/bookmark_bar_gtk.cc +++ b/chrome/browser/gtk/bookmark_bar_gtk.cc @@ -45,9 +45,6 @@ const size_t kMaxCharsOnAButton = 15; // Dictionary key used to store a BookmarkNode* on a GtkWidget. const char kBookmarkNode[] = "bookmark-node"; -// Mime types for DnD. Used to synchronize across applications. -const char kInternalURIType[] = "application/x-chrome-bookmark-item"; - // Left-padding for the instructional text. const int kInstructionsPadding = 6; @@ -192,9 +189,9 @@ void BookmarkBarGtk::Init(Profile* profile) { TRUE, TRUE, 0); gtk_drag_dest_set(bookmark_toolbar_.get(), GTK_DEST_DEFAULT_DROP, - bookmark_utils::kTargetTable, - bookmark_utils::kTargetTableSize, - GDK_ACTION_MOVE); + NULL, 0, GDK_ACTION_MOVE); + dnd::SetDestTargetListFromCodeMask(bookmark_toolbar_.get(), + dnd::X_CHROME_BOOKMARK_ITEM); g_signal_connect(bookmark_toolbar_.get(), "drag-motion", G_CALLBACK(&OnToolbarDragMotion), this); g_signal_connect(bookmark_toolbar_.get(), "drag-leave", @@ -449,9 +446,8 @@ GtkWidget* BookmarkBarGtk::CreateBookmarkButton(const BookmarkNode* node) { // The tool item is also a source for dragging gtk_drag_source_set(button, GDK_BUTTON1_MASK, - bookmark_utils::kTargetTable, - bookmark_utils::kTargetTableSize, - GDK_ACTION_MOVE); + NULL, 0, GDK_ACTION_MOVE); + dnd::SetSourceTargetListFromCodeMask(button, dnd::X_CHROME_BOOKMARK_ITEM); g_signal_connect(G_OBJECT(button), "drag-begin", G_CALLBACK(&OnButtonDragBegin), this); g_signal_connect(G_OBJECT(button), "drag-end", diff --git a/chrome/browser/gtk/bookmark_manager_gtk.cc b/chrome/browser/gtk/bookmark_manager_gtk.cc index b1db3ad..bd781a1 100644 --- a/chrome/browser/gtk/bookmark_manager_gtk.cc +++ b/chrome/browser/gtk/bookmark_manager_gtk.cc @@ -17,6 +17,7 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/gtk/bookmark_tree_model.h" #include "chrome/browser/gtk/bookmark_utils_gtk.h" +#include "chrome/browser/gtk/dnd_registry.h" #include "chrome/browser/importer/importer.h" #include "chrome/browser/profile.h" #include "chrome/common/chrome_paths.h" @@ -379,9 +380,9 @@ GtkWidget* BookmarkManagerGtk::MakeLeftPane() { // The left side is only a drag destination (not a source). gtk_drag_dest_set(left_tree_view_, GTK_DEST_DEFAULT_DROP, - bookmark_utils::kTargetTable, - bookmark_utils::kTargetTableSize, - GDK_ACTION_MOVE); + NULL, 0, GDK_ACTION_MOVE); + dnd::SetDestTargetListFromCodeMask(left_tree_view_, + dnd::X_CHROME_BOOKMARK_ITEM); g_signal_connect(left_tree_view_, "drag-data-received", G_CALLBACK(&OnLeftTreeViewDragReceived), this); @@ -445,9 +446,9 @@ GtkWidget* BookmarkManagerGtk::MakeRightPane() { // any deleting following a succesful move, this should work. gtk_drag_source_set(right_tree_view_, GDK_BUTTON1_MASK, - bookmark_utils::kTargetTable, - bookmark_utils::kTargetTableSize, - GDK_ACTION_MOVE); + NULL, 0, GDK_ACTION_MOVE); + dnd::SetSourceTargetListFromCodeMask(right_tree_view_, + dnd::X_CHROME_BOOKMARK_ITEM); // We connect to drag dest signals, but we don't actually enable the widget // as a drag destination unless it corresponds to the contents of a folder. @@ -530,10 +531,10 @@ void BookmarkManagerGtk::BuildRightStore() { right_tree_model_.reset( BookmarkTableModel::CreateBookmarkTableModelForFolder(model_, node)); - gtk_drag_dest_set(right_tree_view_, GTK_DEST_DEFAULT_ALL, - bookmark_utils::kTargetTable, - bookmark_utils::kTargetTableSize, + gtk_drag_dest_set(right_tree_view_, GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_MOVE); + dnd::SetDestTargetListFromCodeMask(right_tree_view_, + dnd::X_CHROME_BOOKMARK_ITEM); } else { gtk_tree_view_column_set_visible(path_column_, TRUE); @@ -1018,8 +1019,8 @@ gboolean BookmarkManagerGtk::OnRightTreeViewMotion(GtkWidget* tree_view, if (gtk_drag_check_threshold(tree_view, bm->mousedown_event_.x, bm->mousedown_event_.y, event->x, event->y)) { bm->delaying_mousedown_ = false; - GtkTargetList* targets = gtk_target_list_new( - bookmark_utils::kTargetTable, bookmark_utils::kTargetTableSize); + GtkTargetList* targets = + dnd::GetTargetListFromCodeMask(dnd::X_CHROME_BOOKMARK_ITEM); gtk_drag_begin(tree_view, targets, GDK_ACTION_MOVE, 1, reinterpret_cast(event)); // The drag adds a ref; let it own the list. diff --git a/chrome/browser/gtk/bookmark_utils_gtk.cc b/chrome/browser/gtk/bookmark_utils_gtk.cc index 4551794..9e49716 100644 --- a/chrome/browser/gtk/bookmark_utils_gtk.cc +++ b/chrome/browser/gtk/bookmark_utils_gtk.cc @@ -21,19 +21,6 @@ const int kBitsInAByte = 8; namespace bookmark_utils { -// Mime types for DnD. Used to synchronize across applications. -const char kInternalURIType[] = "application/x-chrome-bookmark-item"; - -// Table of the mime types that we accept with their options. -const GtkTargetEntry kTargetTable[] = { - { const_cast(kInternalURIType), - GTK_TARGET_SAME_APP, - dnd::X_CHROME_BOOKMARK_ITEM } - // TODO(erg): Add "text/uri-list" support. -}; - -const int kTargetTableSize = G_N_ELEMENTS(kTargetTable); - GdkPixbuf* GetFolderIcon() { ResourceBundle& rb = ResourceBundle::GetSharedInstance(); static GdkPixbuf* default_folder_icon = rb.GetPixbufNamed( diff --git a/chrome/browser/gtk/browser_toolbar_gtk.cc b/chrome/browser/gtk/browser_toolbar_gtk.cc index c31d93e..31f2888 100644 --- a/chrome/browser/gtk/browser_toolbar_gtk.cc +++ b/chrome/browser/gtk/browser_toolbar_gtk.cc @@ -18,6 +18,7 @@ #include "chrome/browser/gtk/back_forward_button_gtk.h" #include "chrome/browser/gtk/browser_window_gtk.h" #include "chrome/browser/gtk/custom_button.h" +#include "chrome/browser/gtk/dnd_registry.h" #include "chrome/browser/gtk/go_button_gtk.h" #include "chrome/browser/gtk/gtk_chrome_button.h" #include "chrome/browser/gtk/location_bar_view_gtk.h" @@ -32,6 +33,7 @@ #include "chrome/common/notification_type.h" #include "chrome/common/pref_names.h" #include "chrome/common/pref_service.h" +#include "chrome/common/url_constants.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" @@ -127,6 +129,7 @@ void BrowserToolbarGtk::Init(Profile* profile, home_.reset(BuildToolbarButton(IDR_HOME, IDR_HOME_P, IDR_HOME_H, 0, l10n_util::GetStringUTF8(IDS_TOOLTIP_HOME))); gtk_util::SetButtonTriggersNavigation(home_->widget()); + SetUpDragForHomeButton(); // Group the start, omnibox, and go button into an hbox. GtkWidget* omnibox_hbox_ = gtk_hbox_new(FALSE, 0); @@ -341,6 +344,19 @@ GtkWidget* BrowserToolbarGtk::BuildToolbarMenuButton( return button; } +void BrowserToolbarGtk::SetUpDragForHomeButton() { + // TODO(estade): we should use a custom drag-drop handler so that we can + // prefer URIs over plain text when both are available. + gtk_drag_dest_set(home_->widget(), GTK_DEST_DEFAULT_ALL, + NULL, 0, GDK_ACTION_COPY); + dnd::SetDestTargetListFromCodeMask(home_->widget(), + dnd::X_CHROME_TEXT_PLAIN | + dnd::X_CHROME_TEXT_URI_LIST); + + g_signal_connect(home_->widget(), "drag-data-received", + G_CALLBACK(OnDragDataReceived), this); +} + // static gboolean BrowserToolbarGtk::OnToolbarExpose(GtkWidget* widget, GdkEventExpose* e, @@ -402,6 +418,32 @@ gboolean BrowserToolbarGtk::OnMenuButtonPressEvent(GtkWidget* button, return TRUE; } +// static +void BrowserToolbarGtk::OnDragDataReceived(GtkWidget* widget, + GdkDragContext* drag_context, gint x, gint y, + GtkSelectionData* data, guint info, guint time, + BrowserToolbarGtk* toolbar) { + if (info != dnd::X_CHROME_TEXT_PLAIN) { + NOTIMPLEMENTED() << "Only support plain text drops for now, sorry!"; + return; + } + + GURL url(reinterpret_cast(data->data)); + if (!url.is_valid()) { + // FIXME: remove this + NOTIMPLEMENTED() << "invalid url: " << url.spec(); + return; + } + + bool url_is_newtab = url.spec() == chrome::kChromeUINewTabURL; + toolbar->profile_->GetPrefs()->SetBoolean(prefs::kHomePageIsNewTabPage, + url_is_newtab); + if (!url_is_newtab) { + toolbar->profile_->GetPrefs()->SetString(prefs::kHomePage, + UTF8ToWide(url.spec())); + } +} + void BrowserToolbarGtk::InitNineBox() { // TODO(estade): use |profile_|? background_ninebox_.reset(new NineBox( diff --git a/chrome/browser/gtk/browser_toolbar_gtk.h b/chrome/browser/gtk/browser_toolbar_gtk.h index ffb2fdd..ed4dc17 100644 --- a/chrome/browser/gtk/browser_toolbar_gtk.h +++ b/chrome/browser/gtk/browser_toolbar_gtk.h @@ -102,6 +102,9 @@ class BrowserToolbarGtk : public CommandUpdater::CommandObserver, const std::string& localized_tooltip, OwnedWidgetGtk* owner); + // Connect signals for dragging a url onto the home button. + void SetUpDragForHomeButton(); + // Gtk callback for the "expose-event" signal. static gboolean OnToolbarExpose(GtkWidget* widget, GdkEventExpose* e, BrowserToolbarGtk* toolbar); @@ -118,6 +121,14 @@ class BrowserToolbarGtk : public CommandUpdater::CommandObserver, GdkEventButton* event, BrowserToolbarGtk* toolbar); + // Used for drags onto home button. + static void OnDragDataReceived(GtkWidget* widget, + GdkDragContext* drag_context, + gint x, gint y, + GtkSelectionData* data, + guint info, guint time, + BrowserToolbarGtk* toolbar); + // Initialize the background NineBox. void InitNineBox(); @@ -137,7 +148,7 @@ class BrowserToolbarGtk : public CommandUpdater::CommandObserver, // All the buttons in the toolbar. scoped_ptr back_, forward_; scoped_ptr reload_; - scoped_ptr home_; // May be NULL. + scoped_ptr home_; scoped_ptr star_; scoped_ptr go_; OwnedWidgetGtk page_menu_button_, app_menu_button_; diff --git a/chrome/browser/gtk/dnd_registry.cc b/chrome/browser/gtk/dnd_registry.cc new file mode 100644 index 0000000..42e935a --- /dev/null +++ b/chrome/browser/gtk/dnd_registry.cc @@ -0,0 +1,47 @@ +// Copyright (c) 2009 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 "dnd_registry.h" + +namespace dnd { + +GtkTargetList* GetTargetListFromCodeMask(int code_mask) { + GtkTargetList* targets = gtk_target_list_new(NULL, 0); + + if (code_mask & X_CHROME_TAB) { + GdkAtom tab_atom = gdk_atom_intern( + const_cast("application/x-chrome-tab"), false); + gtk_target_list_add(targets, tab_atom, GTK_TARGET_SAME_APP, + X_CHROME_TAB); + } + + if (code_mask & X_CHROME_TEXT_PLAIN) + gtk_target_list_add_text_targets(targets, X_CHROME_TEXT_PLAIN); + + if (code_mask & X_CHROME_TEXT_URI_LIST) + gtk_target_list_add_uri_targets(targets, X_CHROME_TEXT_URI_LIST); + + if (code_mask & X_CHROME_BOOKMARK_ITEM) { + GdkAtom bookmark_atom = gdk_atom_intern( + const_cast("application/x-chrome-bookmark-item"), false); + gtk_target_list_add(targets, bookmark_atom, GTK_TARGET_SAME_APP, + X_CHROME_BOOKMARK_ITEM); + } + + return targets; +} + +void SetDestTargetListFromCodeMask(GtkWidget* dest, int code_mask) { + GtkTargetList* targets = GetTargetListFromCodeMask(code_mask); + gtk_drag_dest_set_target_list(dest, targets); + gtk_target_list_unref(targets); +} + +void SetSourceTargetListFromCodeMask(GtkWidget* source, int code_mask) { + GtkTargetList* targets = GetTargetListFromCodeMask(code_mask); + gtk_drag_source_set_target_list(source, targets); + gtk_target_list_unref(targets); +} + +} // namespace dnd diff --git a/chrome/browser/gtk/dnd_registry.h b/chrome/browser/gtk/dnd_registry.h index 141e33a..92e06be 100644 --- a/chrome/browser/gtk/dnd_registry.h +++ b/chrome/browser/gtk/dnd_registry.h @@ -5,6 +5,8 @@ #ifndef CHROME_BROWSER_GTK_DND_REGISTRY_H_ #define CHROME_BROWSER_GTK_DND_REGISTRY_H_ +#include + // We wrap all of these constants in a namespace to prevent pollution. namespace dnd { @@ -13,18 +15,22 @@ namespace dnd { // These ids need to be unique app wide. Simply adding a GtkTargetEntry with an // id of 0 should be an error and it will conflict with X_CHROME_TAB below. enum { - // Tab DND items: X_CHROME_TAB = 0, + X_CHROME_TEXT_PLAIN = 1 << 0, + X_CHROME_TEXT_URI_LIST = 1 << 1, + X_CHROME_BOOKMARK_ITEM = 1 << 2, +}; - // Tabstrip DND items: - X_CHROME_STRING, - X_CHROME_TEXT_PLAIN, - X_CHROME_TEXT_URI_LIST, +// Creates a target list from the given mask. The mask should be an OR of +// X_CHROME_* values. The target list is returned with ref count 1; the caller +// is responsible for unreffing it when it is no longer needed. +GtkTargetList* GetTargetListFromCodeMask(int code_mask); - // Bookmark DND items: - X_CHROME_BOOKMARK_ITEM -}; +// Set the drag target list for |dest| or |source| with the target list that +// corresponds to |code_mask|. +void SetDestTargetListFromCodeMask(GtkWidget* dest, int code_mask); +void SetSourceTargetListFromCodeMask(GtkWidget* source, int code_mask); -}; +} // namespace dnd #endif // CHROME_BROWSER_GTK_DND_REGISTRY_H_ diff --git a/chrome/browser/gtk/tabs/tab_gtk.cc b/chrome/browser/gtk/tabs/tab_gtk.cc index b509685..10583e0 100644 --- a/chrome/browser/gtk/tabs/tab_gtk.cc +++ b/chrome/browser/gtk/tabs/tab_gtk.cc @@ -14,12 +14,6 @@ namespace { -// The targets available for drag n' drop. -GtkTargetEntry target_table[] = { - { const_cast("application/x-chrome-tab"), GTK_TARGET_SAME_APP, - dnd::X_CHROME_TAB } -}; - void SetEmptyDragIcon(GtkWidget* widget) { GdkPixbuf* pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, 1, 1); gtk_drag_source_set_icon_pixbuf(widget, pixbuf); @@ -113,8 +107,8 @@ TabGtk::TabGtk(TabDelegate* delegate) g_object_ref(event_box_); gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box_), FALSE); gtk_drag_source_set(event_box_, GDK_BUTTON1_MASK, - target_table, G_N_ELEMENTS(target_table), - GDK_ACTION_MOVE); + NULL, 0, GDK_ACTION_MOVE); + dnd::SetSourceTargetListFromCodeMask(event_box_, dnd::X_CHROME_TAB); g_signal_connect(G_OBJECT(event_box_), "button-press-event", G_CALLBACK(OnMousePress), this); g_signal_connect(G_OBJECT(event_box_), "button-release-event", diff --git a/chrome/browser/gtk/tabs/tab_strip_gtk.cc b/chrome/browser/gtk/tabs/tab_strip_gtk.cc index d5ec368..944323f 100755 --- a/chrome/browser/gtk/tabs/tab_strip_gtk.cc +++ b/chrome/browser/gtk/tabs/tab_strip_gtk.cc @@ -77,20 +77,6 @@ gfx::Rect GetInitialWidgetBounds(GtkWidget* widget) { return gfx::Rect(0, 0, request.width, request.height); } -// Mime types for DnD. Used to synchronize across applications. -const char kTargetString[] = "STRING"; -const char kTargetTextPlain[] = "text/plain"; -const char kTargetTextUriList[] = "text/uri-list"; - -// Table of the mime types that we accept with their options. -const GtkTargetEntry kTargetTable[] = { - { const_cast(kTargetString), 0, dnd::X_CHROME_STRING }, - { const_cast(kTargetTextPlain), 0, dnd::X_CHROME_TEXT_PLAIN }, - { const_cast(kTargetTextUriList), 0, dnd::X_CHROME_TEXT_URI_LIST } -}; - -const int kTargetTableSize = G_N_ELEMENTS(kTargetTable); - } // namespace //////////////////////////////////////////////////////////////////////////////// @@ -488,9 +474,13 @@ void TabStripGtk::Init() { TabGtk::GetMinimumUnselectedSize().height()); gtk_widget_set_app_paintable(tabstrip_.get(), TRUE); gtk_drag_dest_set(tabstrip_.get(), GTK_DEST_DEFAULT_ALL, - kTargetTable, kTargetTableSize, + NULL, 0, static_cast( GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK)); + dnd::SetDestTargetListFromCodeMask(tabstrip_.get(), + dnd::X_CHROME_TEXT_URI_LIST | + dnd::X_CHROME_TEXT_PLAIN); + g_signal_connect(G_OBJECT(tabstrip_.get()), "expose-event", G_CALLBACK(OnExpose), this); g_signal_connect(G_OBJECT(tabstrip_.get()), "size-allocate", @@ -1529,19 +1519,12 @@ gboolean TabStripGtk::OnDragDrop(GtkWidget* widget, GdkDragContext* context, if (!tabstrip->drop_info_.get()) return FALSE; - GtkTargetList* list = gtk_target_list_new(kTargetTable, kTargetTableSize); - DCHECK(list); - - GList* target = context->targets; - for (; target != NULL; target = target->next) { - guint info; - GdkAtom target_atom = GDK_POINTER_TO_ATOM(target->data); - if (gtk_target_list_find(list, target_atom, &info)) { - gtk_drag_get_data(widget, context, target_atom, time); - } - } + GdkAtom target = gtk_drag_dest_find_target(widget, context, NULL); + if (target != GDK_NONE) + gtk_drag_finish(context, FALSE, FALSE, time); + else + gtk_drag_get_data(widget, context, target, time); - g_free(list); return TRUE; } @@ -1570,7 +1553,7 @@ gboolean TabStripGtk::OnDragDataReceived(GtkWidget* widget, guint info, guint time, TabStripGtk* tabstrip) { // TODO(jhawkins): Parse URI lists. - if (info == dnd::X_CHROME_STRING || info == dnd::X_CHROME_TEXT_PLAIN) { + if (info == dnd::X_CHROME_TEXT_PLAIN) { tabstrip->CompleteDrop(data->data); gtk_drag_finish(context, TRUE, TRUE, time); } diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index e9c7441..7f3a51e 100644 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -972,6 +972,8 @@ 'browser/gtk/custom_button.cc', 'browser/gtk/custom_button.h', 'browser/gtk/dialogs_gtk.cc', + 'browser/gtk/dnd_registry.cc', + 'browser/gtk/dnd_registry.h', 'browser/gtk/download_item_gtk.cc', 'browser/gtk/download_item_gtk.h', 'browser/gtk/download_shelf_gtk.cc', -- cgit v1.1