diff options
author | erg@google.com <erg@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-15 22:33:13 +0000 |
---|---|---|
committer | erg@google.com <erg@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-15 22:33:13 +0000 |
commit | d90eaa38f5cd59970b50491b7e5bda8b230614b7 (patch) | |
tree | 747e5d4af61d9a97014036f7025df1b7fe5cdf6d | |
parent | 7f8bd32d382cc3f1cd70670d40f5f96d69e5d6ea (diff) | |
download | chromium_src-d90eaa38f5cd59970b50491b7e5bda8b230614b7.zip chromium_src-d90eaa38f5cd59970b50491b7e5bda8b230614b7.tar.gz chromium_src-d90eaa38f5cd59970b50491b7e5bda8b230614b7.tar.bz2 |
GTK: Modify the bookmark bar to actually use GTK drag and drop.
(Previously, DnD support was faked and would crash the application
in a variety of not-really corner cases.)
http://crbug.com/10964
Review URL: http://codereview.chromium.org/115388
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@16203 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/bookmarks/bookmark_drag_data.cc | 83 | ||||
-rw-r--r-- | chrome/browser/bookmarks/bookmark_drag_data.h | 20 | ||||
-rw-r--r-- | chrome/browser/gtk/bookmark_bar_gtk.cc | 145 | ||||
-rw-r--r-- | chrome/browser/gtk/bookmark_bar_gtk.h | 17 |
4 files changed, 202 insertions, 63 deletions
diff --git a/chrome/browser/bookmarks/bookmark_drag_data.cc b/chrome/browser/bookmarks/bookmark_drag_data.cc index c5a56ed..fcfcae4 100644 --- a/chrome/browser/bookmarks/bookmark_drag_data.cc +++ b/chrome/browser/bookmarks/bookmark_drag_data.cc @@ -19,16 +19,14 @@ #if defined(OS_WIN) static CLIPFORMAT clipboard_format = 0; -#endif static void RegisterFormat() { -#if defined(OS_WIN) if (clipboard_format == 0) { clipboard_format = RegisterClipboardFormat(L"chrome/x-bookmark-entries"); DCHECK(clipboard_format); } -#endif } +#endif BookmarkDragData::Element::Element(BookmarkNode* node) : is_url(node->is_url()), @@ -87,6 +85,7 @@ BookmarkDragData::BookmarkDragData(const std::vector<BookmarkNode*>& nodes) { elements.push_back(Element(nodes[i])); } +#if defined(OS_WIN) void BookmarkDragData::Write(Profile* profile, OSExchangeData* data) const { RegisterFormat(); @@ -103,19 +102,9 @@ void BookmarkDragData::Write(Profile* profile, OSExchangeData* data) const { } Pickle data_pickle; - data_pickle.WriteWString( - profile ? profile->GetPath().ToWStringHack() : std::wstring()); - data_pickle.WriteSize(elements.size()); - - for (size_t i = 0; i < elements.size(); ++i) - elements[i].WriteToPickle(&data_pickle); + WriteToPickle(profile, &data_pickle); -#if defined(OS_WIN) data->SetPickledData(clipboard_format, data_pickle); -#else - // TODO(port): Clipboard integration. - NOTIMPLEMENTED(); -#endif } bool BookmarkDragData::Read(const OSExchangeData& data) { @@ -125,24 +114,11 @@ bool BookmarkDragData::Read(const OSExchangeData& data) { profile_path_.clear(); -#if defined(OS_WIN) if (data.HasFormat(clipboard_format)) { Pickle drag_data_pickle; if (data.GetPickledData(clipboard_format, &drag_data_pickle)) { - void* data_iterator = NULL; - size_t element_count; - if (drag_data_pickle.ReadWString(&data_iterator, &profile_path_) && - drag_data_pickle.ReadSize(&data_iterator, &element_count)) { - std::vector<Element> tmp_elements; - tmp_elements.resize(element_count); - for (size_t i = 0; i < element_count; ++i) { - if (!tmp_elements[i].ReadFromPickle(&drag_data_pickle, - &data_iterator)) { - return false; - } - } - elements.swap(tmp_elements); - } + if (!ReadFromPickle(&drag_data_pickle)) + return false; } } else { // See if there is a URL on the clipboard. @@ -153,12 +129,51 @@ bool BookmarkDragData::Read(const OSExchangeData& data) { elements.push_back(element); } } + + return is_valid(); +} +#endif + +void BookmarkDragData::WriteToPickle(Profile* profile, Pickle* pickle) const { +#if defined(WCHAR_T_IS_UTF16) + pickle->WriteWString( + profile ? profile->GetPath().ToWStringHack() : std::wstring()); +#elif defined(WCHAR_T_IS_UTF32) + pickle->WriteString( + profile ? profile->GetPath().value() : std::string()); #else - // TODO(port): Clipboard integration. - NOTIMPLEMENTED(); + NOTIMPLEMENTED() << "Impossible encoding situation!"; #endif - return is_valid(); + pickle->WriteSize(elements.size()); + + for (size_t i = 0; i < elements.size(); ++i) + elements[i].WriteToPickle(pickle); +} + +bool BookmarkDragData::ReadFromPickle(Pickle* pickle) { + void* data_iterator = NULL; + size_t element_count; +#if defined(WCHAR_T_IS_UTF16) + if (pickle->ReadWString(&data_iterator, &profile_path_) && +#elif defined(WCHAR_T_IS_UTF32) + if (pickle->ReadString(&data_iterator, &profile_path_) && +#else + NOTIMPLEMENTED() << "Impossible encoding situation!"; + if (false && +#endif + pickle->ReadSize(&data_iterator, &element_count)) { + std::vector<Element> tmp_elements; + tmp_elements.resize(element_count); + for (size_t i = 0; i < element_count; ++i) { + if (!tmp_elements[i].ReadFromPickle(pickle, &data_iterator)) { + return false; + } + } + elements.swap(tmp_elements); + } + + return true; } std::vector<BookmarkNode*> BookmarkDragData::GetNodes(Profile* profile) const { @@ -187,5 +202,9 @@ BookmarkNode* BookmarkDragData::GetFirstNode(Profile* profile) const { bool BookmarkDragData::IsFromProfile(Profile* profile) const { // An empty path means the data is not associated with any profile. return (!profile_path_.empty() && +#if defined(WCHAR_T_IS_UTF16) profile->GetPath().ToWStringHack() == profile_path_); +#elif defined(WCHAR_T_IS_UTF32) + profile->GetPath().value() == profile_path_); +#endif } diff --git a/chrome/browser/bookmarks/bookmark_drag_data.h b/chrome/browser/bookmarks/bookmark_drag_data.h index b02be0c..18353e6 100644 --- a/chrome/browser/bookmarks/bookmark_drag_data.h +++ b/chrome/browser/bookmarks/bookmark_drag_data.h @@ -2,9 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROME_BROWSER_BOOKMARKS_BOOKMARK_DRAG_DATA_ -#define CHROME_BROWSER_BOOKMARKS_BOOKMARK_DRAG_DATA_ +#ifndef CHROME_BROWSER_BOOKMARKS_BOOKMARK_DRAG_DATA_H_ +#define CHROME_BROWSER_BOOKMARKS_BOOKMARK_DRAG_DATA_H_ +#include <string> #include <vector> #include "chrome/browser/history/history.h" @@ -35,7 +36,6 @@ class Profile; // // data is valid, contents are in elements. struct BookmarkDragData { - // Element represents a single node. struct Element { explicit Element(BookmarkNode* node); @@ -71,6 +71,7 @@ struct BookmarkDragData { explicit BookmarkDragData(BookmarkNode* node); explicit BookmarkDragData(const std::vector<BookmarkNode*>& nodes); +#if defined(OS_WIN) // Writes elements to data. If there is only one element and it is a URL // the URL and title are written to the clipboard in a format other apps can // use. @@ -80,6 +81,13 @@ struct BookmarkDragData { // Restores this data from the clipboard, returning true on success. bool Read(const OSExchangeData& data); +#endif + + // Writes the data for a drag to |pickle|. + void WriteToPickle(Profile* profile, Pickle* pickle) const; + + // Reads the data for a drag from a |pickle|. + bool ReadFromPickle(Pickle* pickle); // Returns the nodes represented by this DragData. If this DragData was // created from the same profile then the nodes from the model are returned. @@ -108,7 +116,11 @@ struct BookmarkDragData { private: // Path of the profile we originated from. +#if defined(WCHAR_T_IS_UTF16) std::wstring profile_path_; +#elif defined(WCHAR_T_IS_UTF32) + std::string profile_path_; +#endif }; -#endif // CHROME_BROWSER_BOOKMARKS_BOOKMARK_DRAG_DATA_ +#endif // CHROME_BROWSER_BOOKMARKS_BOOKMARK_DRAG_DATA_H_ diff --git a/chrome/browser/gtk/bookmark_bar_gtk.cc b/chrome/browser/gtk/bookmark_bar_gtk.cc index 649c2c64..01b95a2 100644 --- a/chrome/browser/gtk/bookmark_bar_gtk.cc +++ b/chrome/browser/gtk/bookmark_bar_gtk.cc @@ -9,7 +9,9 @@ #include "app/l10n_util.h" #include "app/resource_bundle.h" #include "base/gfx/gtk_util.h" +#include "base/pickle.h" #include "chrome/browser/bookmarks/bookmark_context_menu.h" +#include "chrome/browser/bookmarks/bookmark_drag_data.h" #include "chrome/browser/bookmarks/bookmark_menu_controller_gtk.h" #include "chrome/browser/bookmarks/bookmark_utils.h" #include "chrome/browser/browser.h" @@ -33,12 +35,31 @@ const int kBarPadding = 2; // Maximum number of characters on a bookmark button. const size_t kMaxCharsOnAButton = 15; -// Our custom draging type for bookmarks. -static GtkTargetEntry target_table[] = { - { const_cast<char*>("application/x-bookmark-toolbar-item"), - GTK_TARGET_SAME_APP, 0 } +// Used in gtk_selection_data_set(). (I assume from this parameter that gtk has +// to some really exotic hardware...) +const int kBitsInAByte = 8; + +// Dictionary key used to store a BookmarkNode* on a GtkWidget. +const char kBookmarkNode[] = "bookmark-node"; + +// Integers representing the various types of items that the bookmark bar +// accepts as DnD types. +enum BookmarkType { + DROP_TARGET_INTERNAL }; +// 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<char*>(kInternalURIType), GTK_TARGET_SAME_APP, + DROP_TARGET_INTERNAL } + // TODO(erg): Add "text/uri-list" support. +}; + +const int kTargetTableSize = G_N_ELEMENTS(kTargetTable); + } // namespace BookmarkBarGtk::BookmarkBarGtk(Profile* profile, Browser* browser) @@ -112,7 +133,7 @@ void BookmarkBarGtk::Init(Profile* profile) { TRUE, TRUE, 0); gtk_drag_dest_set(bookmark_toolbar_.get(), GTK_DEST_DEFAULT_DROP, - target_table, G_N_ELEMENTS(target_table), + kTargetTable, kTargetTableSize, GDK_ACTION_MOVE); g_signal_connect(bookmark_toolbar_.get(), "drag-motion", G_CALLBACK(&OnToolbarDragMotion), this); @@ -120,6 +141,8 @@ void BookmarkBarGtk::Init(Profile* profile) { G_CALLBACK(&OnToolbarDragLeave), this); g_signal_connect(bookmark_toolbar_.get(), "drag-drop", G_CALLBACK(&OnToolbarDragDrop), this); + g_signal_connect(bookmark_toolbar_.get(), "drag-data-received", + G_CALLBACK(&OnToolbarDragReceived), this); g_signal_connect(bookmark_toolbar_.get(), "button-press-event", G_CALLBACK(&OnButtonPressed), this); @@ -318,6 +341,9 @@ void BookmarkBarGtk::ConfigureButtonForNode(BookmarkNode* node, gtk_button_set_image(GTK_BUTTON(button), gtk_image_new_from_pixbuf(default_bookmark_icon)); } + + g_object_set_data(G_OBJECT(button), kBookmarkNode, + reinterpret_cast<void*>(node)); } GtkWidget* BookmarkBarGtk::CreateBookmarkButton( @@ -327,12 +353,17 @@ GtkWidget* BookmarkBarGtk::CreateBookmarkButton( // The tool item is also a source for dragging gtk_drag_source_set(button, GDK_BUTTON1_MASK, - target_table, G_N_ELEMENTS(target_table), + kTargetTable, kTargetTableSize, GDK_ACTION_MOVE); g_signal_connect(G_OBJECT(button), "drag-begin", G_CALLBACK(&OnButtonDragBegin), this); g_signal_connect(G_OBJECT(button), "drag-end", G_CALLBACK(&OnButtonDragEnd), this); + g_signal_connect(G_OBJECT(button), "drag-data-get", + G_CALLBACK(&OnButtonDragGet), this); + // We deliberately don't connect to "drag-data-delete" because the action of + // moving a button will regenerate all the contents of the bookmarks bar + // anyway. if (node->is_url()) { // Connect to 'button-release-event' instead of 'clicked' because we need @@ -509,7 +540,8 @@ void BookmarkBarGtk::OnButtonDragBegin(GtkWidget* button, gtk_container_add(GTK_CONTAINER(window), frame); gtk_widget_show(frame); - GtkWidget* floating_button = bar->CreateBookmarkButton(node); + GtkWidget* floating_button = gtk_chrome_button_new(); + bar->ConfigureButtonForNode(node, floating_button); gtk_container_add(GTK_CONTAINER(frame), floating_button); gtk_widget_show(floating_button); @@ -537,6 +569,34 @@ void BookmarkBarGtk::OnButtonDragEnd(GtkWidget* button, } // static +void BookmarkBarGtk::OnButtonDragGet(GtkWidget* widget, GdkDragContext* context, + GtkSelectionData* selection_data, + guint target_type, guint time, + BookmarkBarGtk* bar) { + BookmarkNode* node = + reinterpret_cast<BookmarkNode*>( + g_object_get_data(G_OBJECT(widget), kBookmarkNode)); + DCHECK(node); + + switch (target_type) { + case DROP_TARGET_INTERNAL: { + BookmarkDragData data(node); + Pickle pickle; + data.WriteToPickle(bar->profile_, &pickle); + + gtk_selection_data_set(selection_data, selection_data->target, + kBitsInAByte, + static_cast<const guchar*>(pickle.data()), + pickle.size()); + break; + } + default: { + DLOG(ERROR) << "Unsupported drag get type!"; + } + } +} + +// static gboolean BookmarkBarGtk::OnFolderButtonReleased(GtkWidget* sender, GdkEventButton* event, BookmarkBarGtk* bar) { @@ -614,22 +674,63 @@ void BookmarkBarGtk::OnToolbarDragLeave(GtkToolbar* toolbar, } // static -gboolean BookmarkBarGtk::OnToolbarDragDrop(GtkWidget* toolbar, - GdkDragContext* drag_context, - gint x, - gint y, - guint time, +gboolean BookmarkBarGtk::OnToolbarDragDrop( + GtkWidget* widget, GdkDragContext* context, + gint x, gint y, guint time, + BookmarkBarGtk* bar) { + gboolean is_valid_drop_site = FALSE; + + if (context->targets) { + GdkAtom target_type = + GDK_POINTER_TO_ATOM(g_list_nth_data(context->targets, + DROP_TARGET_INTERNAL)); + gtk_drag_get_data(widget, context, target_type, time); + + is_valid_drop_site = TRUE; + } + + return is_valid_drop_site; +} + +// static +void BookmarkBarGtk::OnToolbarDragReceived(GtkWidget* widget, + GdkDragContext* context, + gint x, gint y, + GtkSelectionData* selection_data, + guint target_type, guint time, BookmarkBarGtk* bar) { - // TODO(erg): This implementation only works within the same profile, which - // is OK for now because we're restricted to drags from within the same - // window. - if (bar->dragged_node_) { - gint index = gtk_toolbar_get_drop_index(GTK_TOOLBAR(toolbar), x, y); - // Drag from same profile, do a move. - bar->model_->Move(bar->dragged_node_, bar->model_->GetBookmarkBarNode(), - index); - return TRUE; + gboolean dnd_success = FALSE; + gboolean delete_selection_data = FALSE; + + // Deal with what we are given from source + if ((selection_data != NULL) && (selection_data->length >= 0)) { + if (context-> action == GDK_ACTION_MOVE) { + delete_selection_data = TRUE; + } + + switch (target_type) { + case DROP_TARGET_INTERNAL: { + Pickle pickle(reinterpret_cast<char*>(selection_data->data), + selection_data->length); + BookmarkDragData drag_data; + drag_data.ReadFromPickle(&pickle); + + std::vector<BookmarkNode*> nodes = drag_data.GetNodes(bar->profile_); + for (std::vector<BookmarkNode*>::iterator it = nodes.begin(); + it != nodes.end(); ++it) { + gint index = gtk_toolbar_get_drop_index( + GTK_TOOLBAR(bar->bookmark_toolbar_.get()), x, y); + bar->model_->Move(*it, bar->model_->GetBookmarkBarNode(), index); + } + + dnd_success = TRUE; + break; + } + default: { + DLOG(ERROR) << "Unsupported drag received type: " << target_type; + } + } } - return FALSE; + gtk_drag_finish(context, dnd_success, delete_selection_data, time); } diff --git a/chrome/browser/gtk/bookmark_bar_gtk.h b/chrome/browser/gtk/bookmark_bar_gtk.h index 697b55c..72f42c6 100644 --- a/chrome/browser/gtk/bookmark_bar_gtk.h +++ b/chrome/browser/gtk/bookmark_bar_gtk.h @@ -137,6 +137,10 @@ class BookmarkBarGtk : public BookmarkModelObserver { static void OnButtonDragEnd(GtkWidget* button, GdkDragContext* drag_context, BookmarkBarGtk* bar); + static void OnButtonDragGet(GtkWidget* widget, GdkDragContext* context, + GtkSelectionData* selection_data, + guint target_type, guint time, + BookmarkBarGtk* bar); // GtkButton callbacks for folder buttons static gboolean OnFolderButtonReleased(GtkWidget* sender, @@ -156,11 +160,14 @@ class BookmarkBarGtk : public BookmarkModelObserver { GdkDragContext* context, guint time, BookmarkBarGtk* bar); - static gboolean OnToolbarDragDrop(GtkWidget* toolbar, - GdkDragContext* drag_context, - gint x, - gint y, - guint time, + static gboolean OnToolbarDragDrop(GtkWidget* widget, GdkDragContext* context, + gint x, gint y, guint time, + BookmarkBarGtk* bar); + static void OnToolbarDragReceived(GtkWidget* widget, + GdkDragContext* context, + gint x, gint y, + GtkSelectionData* selection_data, + guint target_type, guint time, BookmarkBarGtk* bar); Profile* profile_; |