Home | History | Annotate | Download | only in dragdrop
      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 "ui/base/dragdrop/gtk_dnd_util.h"
      6 
      7 #include <string>
      8 
      9 #include "base/logging.h"
     10 #include "base/pickle.h"
     11 #include "base/strings/utf_string_conversions.h"
     12 #include "ui/base/clipboard/custom_data_helper.h"
     13 #include "url/gurl.h"
     14 
     15 namespace ui {
     16 
     17 namespace {
     18 
     19 const int kBitsPerByte = 8;
     20 
     21 void AddTargetToList(GtkTargetList* targets, int target_code) {
     22   switch (target_code) {
     23     case ui::TEXT_PLAIN:
     24       gtk_target_list_add_text_targets(targets, ui::TEXT_PLAIN);
     25       break;
     26 
     27     case ui::TEXT_URI_LIST:
     28       gtk_target_list_add_uri_targets(targets, ui::TEXT_URI_LIST);
     29       break;
     30 
     31     case ui::TEXT_HTML:
     32       gtk_target_list_add(
     33           targets, ui::GetAtomForTarget(ui::TEXT_HTML), 0, ui::TEXT_HTML);
     34       break;
     35 
     36     case ui::NETSCAPE_URL:
     37       gtk_target_list_add(targets,
     38           ui::GetAtomForTarget(ui::NETSCAPE_URL), 0, ui::NETSCAPE_URL);
     39       break;
     40 
     41     case ui::CHROME_TAB:
     42     case ui::CHROME_BOOKMARK_ITEM:
     43     case ui::CHROME_NAMED_URL:
     44       gtk_target_list_add(targets, ui::GetAtomForTarget(target_code),
     45                           GTK_TARGET_SAME_APP, target_code);
     46       break;
     47 
     48     case ui::DIRECT_SAVE_FILE:
     49       gtk_target_list_add(targets,
     50           ui::GetAtomForTarget(ui::DIRECT_SAVE_FILE), 0, ui::DIRECT_SAVE_FILE);
     51       break;
     52 
     53     case ui::CUSTOM_DATA:
     54       gtk_target_list_add(targets,
     55           ui::GetAtomForTarget(ui::CUSTOM_DATA), 0, ui::CUSTOM_DATA);
     56       break;
     57 
     58     default:
     59       NOTREACHED() << " Unexpected target code: " << target_code;
     60   }
     61 }
     62 
     63 }  // namespace
     64 
     65 GdkAtom GetAtomForTarget(int target) {
     66   switch (target) {
     67     case CHROME_TAB:
     68       static const GdkAtom kTabAtom = gdk_atom_intern(
     69           "application/x-chrome-tab", false);
     70       return kTabAtom;
     71 
     72     case TEXT_HTML:
     73       static const GdkAtom kHtmlAtom = gdk_atom_intern(
     74           "text/html", false);
     75       return kHtmlAtom;
     76 
     77     case CHROME_BOOKMARK_ITEM:
     78       static const GdkAtom kBookmarkAtom = gdk_atom_intern(
     79           "application/x-chrome-bookmark-item", false);
     80       return kBookmarkAtom;
     81 
     82     case TEXT_PLAIN:
     83       static const GdkAtom kTextAtom= gdk_atom_intern(
     84           "text/plain;charset=utf-8", false);
     85       return kTextAtom;
     86 
     87     case TEXT_URI_LIST:
     88       static const GdkAtom kUrisAtom = gdk_atom_intern(
     89           "text/uri-list", false);
     90       return kUrisAtom;
     91 
     92     case CHROME_NAMED_URL:
     93       static const GdkAtom kNamedUrl = gdk_atom_intern(
     94           "application/x-chrome-named-url", false);
     95       return kNamedUrl;
     96 
     97     case NETSCAPE_URL:
     98       static const GdkAtom kNetscapeUrl = gdk_atom_intern(
     99           "_NETSCAPE_URL", false);
    100       return kNetscapeUrl;
    101 
    102     case TEXT_PLAIN_NO_CHARSET:
    103       static const GdkAtom kTextNoCharsetAtom = gdk_atom_intern(
    104           "text/plain", false);
    105       return kTextNoCharsetAtom;
    106 
    107     case DIRECT_SAVE_FILE:
    108       static const GdkAtom kXdsAtom = gdk_atom_intern(
    109           "XdndDirectSave0", false);
    110       return kXdsAtom;
    111 
    112     case CUSTOM_DATA:
    113       static const GdkAtom kCustomData = gdk_atom_intern(
    114           kMimeTypeWebCustomData, false);
    115       return kCustomData;
    116 
    117     default:
    118       NOTREACHED();
    119   }
    120 
    121   return NULL;
    122 }
    123 
    124 GtkTargetList* GetTargetListFromCodeMask(int code_mask) {
    125   GtkTargetList* targets = gtk_target_list_new(NULL, 0);
    126 
    127   for (size_t i = 1; i < INVALID_TARGET; i = i << 1) {
    128     if (i == CHROME_WEBDROP_FILE_CONTENTS)
    129       continue;
    130 
    131     if (i & code_mask)
    132       AddTargetToList(targets, i);
    133   }
    134 
    135   return targets;
    136 }
    137 
    138 void SetSourceTargetListFromCodeMask(GtkWidget* source, int code_mask) {
    139   GtkTargetList* targets = GetTargetListFromCodeMask(code_mask);
    140   gtk_drag_source_set_target_list(source, targets);
    141   gtk_target_list_unref(targets);
    142 }
    143 
    144 void SetDestTargetList(GtkWidget* dest, const int* target_codes) {
    145   GtkTargetList* targets = gtk_target_list_new(NULL, 0);
    146 
    147   for (size_t i = 0; target_codes[i] != -1; ++i) {
    148     AddTargetToList(targets, target_codes[i]);
    149   }
    150 
    151   gtk_drag_dest_set_target_list(dest, targets);
    152   gtk_target_list_unref(targets);
    153 }
    154 
    155 void WriteURLWithName(GtkSelectionData* selection_data,
    156                       const GURL& url,
    157                       base::string16 title,
    158                       int type) {
    159   if (title.empty()) {
    160     // We prefer to not have empty titles. Set it to the filename extracted
    161     // from the URL.
    162     title = UTF8ToUTF16(url.ExtractFileName());
    163   }
    164 
    165   switch (type) {
    166     case TEXT_PLAIN: {
    167       gtk_selection_data_set_text(selection_data, url.spec().c_str(),
    168                                   url.spec().length());
    169       break;
    170     }
    171     case TEXT_URI_LIST: {
    172       gchar* uri_array[2];
    173       uri_array[0] = strdup(url.spec().c_str());
    174       uri_array[1] = NULL;
    175       gtk_selection_data_set_uris(selection_data, uri_array);
    176       free(uri_array[0]);
    177       break;
    178     }
    179     case CHROME_NAMED_URL: {
    180       Pickle pickle;
    181       pickle.WriteString(UTF16ToUTF8(title));
    182       pickle.WriteString(url.spec());
    183       gtk_selection_data_set(
    184           selection_data,
    185           GetAtomForTarget(ui::CHROME_NAMED_URL),
    186           kBitsPerByte,
    187           reinterpret_cast<const guchar*>(pickle.data()),
    188           pickle.size());
    189       break;
    190     }
    191     case NETSCAPE_URL: {
    192       // _NETSCAPE_URL format is URL + \n + title.
    193       std::string utf8_text = url.spec() + "\n" + UTF16ToUTF8(title);
    194       gtk_selection_data_set(selection_data,
    195                              gtk_selection_data_get_target(selection_data),
    196                              kBitsPerByte,
    197                              reinterpret_cast<const guchar*>(utf8_text.c_str()),
    198                              utf8_text.length());
    199       break;
    200     }
    201 
    202     default: {
    203       NOTREACHED();
    204       break;
    205     }
    206   }
    207 }
    208 
    209 bool ExtractNamedURL(GtkSelectionData* selection_data,
    210                      GURL* url,
    211                      base::string16* title) {
    212   if (!selection_data || gtk_selection_data_get_length(selection_data) <= 0)
    213     return false;
    214 
    215   Pickle data(
    216       reinterpret_cast<const char*>(
    217           gtk_selection_data_get_data(selection_data)),
    218       gtk_selection_data_get_length(selection_data));
    219   PickleIterator iter(data);
    220   std::string title_utf8, url_utf8;
    221   if (!data.ReadString(&iter, &title_utf8) ||
    222       !data.ReadString(&iter, &url_utf8)) {
    223     return false;
    224   }
    225 
    226   GURL gurl(url_utf8);
    227   if (!gurl.is_valid())
    228     return false;
    229 
    230   *url = gurl;
    231   *title = UTF8ToUTF16(title_utf8);
    232   return true;
    233 }
    234 
    235 bool ExtractURIList(GtkSelectionData* selection_data, std::vector<GURL>* urls) {
    236   gchar** uris = gtk_selection_data_get_uris(selection_data);
    237   if (!uris)
    238     return false;
    239 
    240   for (size_t i = 0; uris[i] != NULL; ++i) {
    241     GURL url(uris[i]);
    242     if (url.is_valid())
    243       urls->push_back(url);
    244   }
    245 
    246   g_strfreev(uris);
    247   return true;
    248 }
    249 
    250 bool ExtractNetscapeURL(GtkSelectionData* selection_data,
    251                         GURL* url,
    252                         base::string16* title) {
    253   if (!selection_data || gtk_selection_data_get_length(selection_data) <= 0)
    254     return false;
    255 
    256   // Find the first '\n' in the data. It is the separator between the url and
    257   // the title.
    258   std::string data(
    259       reinterpret_cast<const char*>(
    260           gtk_selection_data_get_data(selection_data)),
    261       gtk_selection_data_get_length(selection_data));
    262   std::string::size_type newline = data.find('\n');
    263   if (newline == std::string::npos)
    264     return false;
    265 
    266   GURL gurl(data.substr(0, newline));
    267   if (!gurl.is_valid())
    268     return false;
    269 
    270   *url = gurl;
    271   *title = UTF8ToUTF16(data.substr(newline + 1));
    272   return true;
    273 }
    274 
    275 }  // namespace ui
    276