Home | History | Annotate | Download | only in gtk
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/ui/gtk/custom_drag.h"
      6 
      7 #include "base/files/file_path.h"
      8 #include "base/strings/utf_string_conversions.h"
      9 #include "chrome/browser/ui/gtk/bookmarks/bookmark_utils_gtk.h"
     10 #include "content/public/browser/download_item.h"
     11 #include "net/base/net_util.h"
     12 #include "third_party/skia/include/core/SkBitmap.h"
     13 #include "ui/base/dragdrop/gtk_dnd_util.h"
     14 #include "ui/gfx/gtk_util.h"
     15 #include "ui/gfx/image/image.h"
     16 #include "url/gurl.h"
     17 
     18 using content::DownloadItem;
     19 
     20 namespace {
     21 
     22 const int kDownloadItemCodeMask = ui::TEXT_URI_LIST | ui::CHROME_NAMED_URL;
     23 const GdkDragAction kDownloadItemDragAction = GDK_ACTION_COPY;
     24 const GdkDragAction kBookmarkDragAction =
     25     static_cast<GdkDragAction>(GDK_ACTION_COPY | GDK_ACTION_MOVE);
     26 
     27 }  // namespace
     28 
     29 // CustomDrag ------------------------------------------------------------------
     30 
     31 CustomDrag::CustomDrag(gfx::Image* icon, int code_mask, GdkDragAction action)
     32     : drag_widget_(gtk_invisible_new()),
     33       image_(icon) {
     34   g_signal_connect(drag_widget_, "drag-data-get",
     35                    G_CALLBACK(OnDragDataGetThunk), this);
     36   g_signal_connect(drag_widget_, "drag-begin",
     37                    G_CALLBACK(OnDragBeginThunk), this);
     38   g_signal_connect(drag_widget_, "drag-end",
     39                    G_CALLBACK(OnDragEndThunk), this);
     40 
     41   GtkTargetList* list = ui::GetTargetListFromCodeMask(code_mask);
     42   GdkEvent* event = gtk_get_current_event();
     43   gtk_drag_begin(drag_widget_, list, action, 1, event);
     44   if (event)
     45     gdk_event_free(event);
     46   gtk_target_list_unref(list);
     47 }
     48 
     49 CustomDrag::~CustomDrag() {
     50   gtk_widget_destroy(drag_widget_);
     51 }
     52 
     53 void CustomDrag::OnDragBegin(GtkWidget* widget, GdkDragContext* drag_context) {
     54   if (image_)
     55     gtk_drag_set_icon_pixbuf(drag_context, image_->ToGdkPixbuf(), 0, 0);
     56 }
     57 
     58 void CustomDrag::OnDragEnd(GtkWidget* widget, GdkDragContext* drag_context) {
     59   delete this;
     60 }
     61 
     62 // DownloadItemDrag ------------------------------------------------------------
     63 
     64 // Stores metadata for a drag & drop operation.
     65 class DownloadItemDrag::DragData {
     66  public:
     67   // Constructs a DragData object based on the current state of |item|.
     68   explicit DragData(const DownloadItem* item);
     69 
     70   // 'drag-data-get' signal handler.
     71   CHROMEGTK_CALLBACK_4(DragData, void, OnDragDataGet, GdkDragContext*,
     72                        GtkSelectionData*, guint, guint);
     73 
     74   // Sets up a drag source and connects |drag_data| to 'drag-data-get' on
     75   // |widget|. If |icon| is non-NULL it will be used as the drag icon. The
     76   // object pointed to by |drag_data| will be deleted when the signal is
     77   // disconnected.
     78   static void AttachToWidget(scoped_ptr<DragData> drag_data,
     79                              GtkWidget* widget,
     80                              gfx::Image* icon);
     81 
     82  private:
     83   // GClosureNotify handler for destroying a DragData object. |data| is assumed
     84   // to be a DragData*.
     85   static void OnDestroy(gpointer data, GClosure* closure);
     86 
     87   GURL url_;
     88   string16 display_name_;
     89 };
     90 
     91 DownloadItemDrag::DragData::DragData(const DownloadItem* item)
     92     : url_(net::FilePathToFileURL(item->GetTargetFilePath())),
     93       display_name_(item->GetFileNameToReportUser().LossyDisplayName()) {
     94   DCHECK_EQ(DownloadItem::COMPLETE, item->GetState());
     95 }
     96 
     97 void DownloadItemDrag::DragData::OnDragDataGet(GtkWidget* widget,
     98                                                GdkDragContext* context,
     99                                                GtkSelectionData* selection_data,
    100                                                guint target_type,
    101                                                guint time) {
    102   ui::WriteURLWithName(selection_data, url_, display_name_, target_type);
    103 }
    104 
    105 // static
    106 void DownloadItemDrag::DragData::AttachToWidget(scoped_ptr<DragData> drag_data,
    107                                                 GtkWidget* widget,
    108                                                 gfx::Image* icon) {
    109   gtk_drag_source_set(widget, GDK_BUTTON1_MASK, NULL, 0,
    110                       kDownloadItemDragAction);
    111   ui::SetSourceTargetListFromCodeMask(widget, kDownloadItemCodeMask);
    112 
    113   // Disconnect previous signal handlers, if any.
    114   g_signal_handlers_disconnect_matched(
    115       widget, G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
    116       reinterpret_cast<gpointer>(&OnDragDataGetThunk),
    117       NULL);
    118 
    119   // Connect new signal handlers.
    120   g_signal_connect_data(
    121       widget, "drag-data-get",
    122       G_CALLBACK(&OnDragDataGetThunk),
    123       reinterpret_cast<gpointer>(drag_data.release()),
    124       &OnDestroy,
    125       static_cast<GConnectFlags>(0));
    126 
    127   if (icon)
    128     gtk_drag_source_set_icon_pixbuf(widget, icon->ToGdkPixbuf());
    129 }
    130 
    131 // static
    132 void DownloadItemDrag::DragData::OnDestroy(gpointer data, GClosure* closure) {
    133   DragData* drag_data = reinterpret_cast<DragData*>(data);
    134   delete drag_data;
    135 }
    136 
    137 DownloadItemDrag::DownloadItemDrag(const DownloadItem* item,
    138                                    gfx::Image* icon)
    139     : CustomDrag(icon, kDownloadItemCodeMask, kDownloadItemDragAction),
    140       drag_data_(new DragData(item)) {
    141 }
    142 
    143 DownloadItemDrag::~DownloadItemDrag() {
    144 }
    145 
    146 void DownloadItemDrag::OnDragDataGet(
    147     GtkWidget* widget, GdkDragContext* context,
    148     GtkSelectionData* selection_data,
    149     guint target_type, guint time) {
    150   drag_data_->OnDragDataGet(widget, context, selection_data, target_type, time);
    151 }
    152 
    153 // static
    154 void DownloadItemDrag::SetSource(GtkWidget* widget,
    155                                  const DownloadItem* item,
    156                                  gfx::Image* icon) {
    157   scoped_ptr<DragData> drag_data(new DragData(item));
    158   DragData::AttachToWidget(drag_data.Pass(), widget, icon);
    159 }
    160 
    161 // static
    162 void DownloadItemDrag::BeginDrag(const DownloadItem* item, gfx::Image* icon) {
    163   new DownloadItemDrag(item, icon);
    164 }
    165 
    166 // BookmarkDrag ----------------------------------------------------------------
    167 
    168 BookmarkDrag::BookmarkDrag(Profile* profile,
    169                            const std::vector<const BookmarkNode*>& nodes)
    170     : CustomDrag(NULL,
    171                  bookmark_utils::GetCodeMask(false),
    172                  kBookmarkDragAction),
    173       profile_(profile),
    174       nodes_(nodes) {
    175 }
    176 
    177 BookmarkDrag::~BookmarkDrag() {
    178 }
    179 
    180 void BookmarkDrag::OnDragDataGet(GtkWidget* widget, GdkDragContext* context,
    181                                  GtkSelectionData* selection_data,
    182                                  guint target_type, guint time) {
    183   bookmark_utils::WriteBookmarksToSelection(nodes_, selection_data,
    184                                             target_type, profile_);
    185 }
    186 
    187 // static
    188 void BookmarkDrag::BeginDrag(Profile* profile,
    189                              const std::vector<const BookmarkNode*>& nodes) {
    190   new BookmarkDrag(profile, nodes);
    191 }
    192