OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2011 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 "views/widget/drop_target_gtk.h" |
| 6 |
| 7 #include <algorithm> |
| 8 #include <iterator> |
| 9 #include <string> |
| 10 #include <vector> |
| 11 |
| 12 #include "base/file_path.h" |
| 13 #include "base/utf_string_conversions.h" |
| 14 #include "net/base/net_util.h" |
| 15 #include "ui/base/dragdrop/drag_drop_types.h" |
| 16 #include "ui/base/dragdrop/gtk_dnd_util.h" |
| 17 #include "ui/base/dragdrop/os_exchange_data_provider_gtk.h" |
| 18 #include "ui/gfx/point.h" |
| 19 #include "views/widget/root_view.h" |
| 20 #include "views/widget/native_widget_gtk.h" |
| 21 |
| 22 using ui::OSExchangeData; |
| 23 |
| 24 namespace { |
| 25 |
| 26 std::string GdkAtomToString(GdkAtom atom) { |
| 27 gchar* c_name = gdk_atom_name(atom); |
| 28 std::string name(c_name); |
| 29 g_free(c_name); |
| 30 return name; |
| 31 } |
| 32 |
| 33 // Returns true if |name| is a known name of plain text. |
| 34 bool IsTextType(const std::string& name) { |
| 35 return name == "text/plain" || name == "TEXT" || |
| 36 name == "STRING" || name == "UTF8_STRING" || |
| 37 name == "text/plain;charset=utf-8"; |
| 38 } |
| 39 |
| 40 // Returns the OSExchangeData::Formats in |targets| and all the |
| 41 // OSExchangeData::CustomFormats in |type_set|. |
| 42 int CalculateTypes(GList* targets, std::set<GdkAtom>* type_set) { |
| 43 int types = 0; |
| 44 for (GList* element = targets; element; |
| 45 element = g_list_next(element)) { |
| 46 GdkAtom atom = static_cast<GdkAtom>(element->data); |
| 47 type_set->insert(atom); |
| 48 if (atom == GDK_TARGET_STRING) { |
| 49 types |= OSExchangeData::STRING; |
| 50 } else if (atom == ui::GetAtomForTarget(ui::CHROME_NAMED_URL)) { |
| 51 types |= OSExchangeData::URL; |
| 52 } else if (atom == ui::GetAtomForTarget(ui::TEXT_URI_LIST)) { |
| 53 // TEXT_URI_LIST is used for files as well as urls. |
| 54 types |= OSExchangeData::URL | OSExchangeData::FILE_NAME; |
| 55 } else { |
| 56 std::string target_name = GdkAtomToString(atom); |
| 57 if (IsTextType(target_name)) { |
| 58 types |= OSExchangeData::STRING; |
| 59 } else { |
| 60 // Assume any unknown data is pickled. |
| 61 types |= OSExchangeData::PICKLED_DATA; |
| 62 } |
| 63 } |
| 64 } |
| 65 return types; |
| 66 } |
| 67 |
| 68 } // namespace |
| 69 |
| 70 namespace views { |
| 71 |
| 72 DropTargetGtk::DropTargetGtk(internal::RootView* root_view, |
| 73 GdkDragContext* context) |
| 74 : helper_(root_view), |
| 75 requested_formats_(0), |
| 76 waiting_for_data_(false), |
| 77 received_drop_(false), |
| 78 pending_view_(NULL) { |
| 79 std::set<GdkAtom> all_formats; |
| 80 int source_formats = CalculateTypes(context->targets, &all_formats); |
| 81 data_.reset(new OSExchangeData(new OSExchangeDataProviderGtk( |
| 82 source_formats, all_formats))); |
| 83 } |
| 84 |
| 85 DropTargetGtk::~DropTargetGtk() { |
| 86 } |
| 87 |
| 88 void DropTargetGtk::ResetTargetViewIfEquals(View* view) { |
| 89 helper_.ResetTargetViewIfEquals(view); |
| 90 } |
| 91 |
| 92 void DropTargetGtk::OnDragDataReceived(GdkDragContext* context, |
| 93 gint x, |
| 94 gint y, |
| 95 GtkSelectionData* data, |
| 96 guint info, |
| 97 guint time) { |
| 98 std::string target_name = GdkAtomToString(data->type); |
| 99 if (data->type == GDK_TARGET_STRING || IsTextType(target_name)) { |
| 100 guchar* text_data = gtk_selection_data_get_text(data); |
| 101 string16 result; |
| 102 if (text_data) { |
| 103 char* as_char = reinterpret_cast<char*>(text_data); |
| 104 UTF8ToUTF16(as_char, strlen(as_char), &result); |
| 105 g_free(text_data); |
| 106 } |
| 107 data_provider().SetString(result); |
| 108 } else if (requested_custom_formats_.find(data->type) != |
| 109 requested_custom_formats_.end()) { |
| 110 Pickle result; |
| 111 if (data->length > 0) |
| 112 result = Pickle(reinterpret_cast<char*>(data->data), data->length); |
| 113 data_provider().SetPickledData(data->type, result); |
| 114 } else if (data->type == ui::GetAtomForTarget(ui::CHROME_NAMED_URL)) { |
| 115 GURL url; |
| 116 string16 title; |
| 117 ui::ExtractNamedURL(data, &url, &title); |
| 118 data_provider().SetURL(url, title); |
| 119 } else if (data->type == ui::GetAtomForTarget(ui::TEXT_URI_LIST)) { |
| 120 std::vector<GURL> urls; |
| 121 ui::ExtractURIList(data, &urls); |
| 122 if (urls.size() == 1 && urls[0].is_valid()) { |
| 123 data_provider().SetURL(urls[0], string16()); |
| 124 |
| 125 // TEXT_URI_LIST is used for files as well as urls. |
| 126 if (urls[0].SchemeIsFile()) { |
| 127 FilePath file_path; |
| 128 if (net::FileURLToFilePath(urls[0], &file_path)) |
| 129 data_provider().SetFilename(file_path); |
| 130 } |
| 131 } else { |
| 132 // Consumers of OSExchangeData will see this as an invalid URL. That is, |
| 133 // when GetURL is invoked on the OSExchangeData this triggers false to |
| 134 // be returned. |
| 135 data_provider().SetURL(GURL(), string16()); |
| 136 } |
| 137 } |
| 138 |
| 139 if (!data_->HasAllFormats(requested_formats_, requested_custom_formats_)) |
| 140 return; // Waiting on more data. |
| 141 |
| 142 int drag_operation = ui::DragDropTypes::GdkDragActionToDragOperation( |
| 143 context->actions); |
| 144 gfx::Point root_view_location(x, y); |
| 145 drag_operation = helper_.OnDragOver(*data_, root_view_location, |
| 146 drag_operation); |
| 147 GdkDragAction gdk_action = static_cast<GdkDragAction>( |
| 148 ui::DragDropTypes::DragOperationToGdkDragAction(drag_operation)); |
| 149 if (!received_drop_) |
| 150 gdk_drag_status(context, gdk_action, time); |
| 151 |
| 152 waiting_for_data_ = false; |
| 153 |
| 154 if (pending_view_ && received_drop_) { |
| 155 FinishDrop(context, x, y, time); |
| 156 // WARNING: we've been deleted. |
| 157 return; |
| 158 } |
| 159 } |
| 160 |
| 161 gboolean DropTargetGtk::OnDragDrop(GdkDragContext* context, |
| 162 gint x, |
| 163 gint y, |
| 164 guint time) { |
| 165 received_drop_ = true; |
| 166 OnDragMotion(context, x, y, time); |
| 167 if (!pending_view_) { |
| 168 // User isn't over a view, no drop can occur. |
| 169 static_cast<NativeWidgetGtk*>( |
| 170 helper_.root_view()->GetWidget()->native_widget())->ResetDropTarget(); |
| 171 // WARNING: we've been deleted. |
| 172 return FALSE; |
| 173 } |
| 174 |
| 175 if (!waiting_for_data_) { |
| 176 // We've got all the data now. |
| 177 FinishDrop(context, x, y, time); |
| 178 // WARNING: we've been deleted. |
| 179 return TRUE; |
| 180 } |
| 181 // We're waiting on data. |
| 182 return TRUE; |
| 183 } |
| 184 |
| 185 void DropTargetGtk::OnDragLeave(GdkDragContext* context, guint time) { |
| 186 helper_.OnDragExit(); |
| 187 } |
| 188 |
| 189 gboolean DropTargetGtk::OnDragMotion(GdkDragContext* context, |
| 190 gint x, |
| 191 gint y, |
| 192 guint time) { |
| 193 waiting_for_data_ = false; |
| 194 gfx::Point root_view_location(x, y); |
| 195 pending_view_ = |
| 196 helper_.CalculateTargetView(root_view_location, *data_, false); |
| 197 if (pending_view_ && |
| 198 (received_drop_ || (pending_view_ != helper_.target_view() && |
| 199 pending_view_->AreDropTypesRequired()))) { |
| 200 // The target requires drop types before it can answer CanDrop, |
| 201 // ask for the data now. |
| 202 int formats = 0; |
| 203 std::set<GdkAtom> custom_formats; |
| 204 pending_view_->GetDropFormats(&formats, &custom_formats); |
| 205 IntersectFormats(data_provider().known_formats(), |
| 206 data_provider().known_custom_formats(), |
| 207 &formats, &custom_formats); |
| 208 if (!data_provider().HasDataForAllFormats(formats, custom_formats)) { |
| 209 if (!received_drop_) |
| 210 helper_.OnDragExit(); |
| 211 |
| 212 // The target needs data for all the types before it can test if the |
| 213 // drop is valid, but we don't have all the data. Request the data |
| 214 // now. When we get back the data we'll update the target. |
| 215 RequestFormats(context, formats, custom_formats, time); |
| 216 |
| 217 waiting_for_data_ = true; |
| 218 |
| 219 return TRUE; |
| 220 } |
| 221 } |
| 222 |
| 223 int drag_operation = ui::DragDropTypes::GdkDragActionToDragOperation( |
| 224 context->actions); |
| 225 drag_operation = helper_.OnDragOver(*data_, root_view_location, |
| 226 drag_operation); |
| 227 if (!received_drop_) { |
| 228 GdkDragAction gdk_action = |
| 229 static_cast<GdkDragAction>( |
| 230 ui::DragDropTypes::DragOperationToGdkDragAction(drag_operation)); |
| 231 gdk_drag_status(context, gdk_action, time); |
| 232 } |
| 233 return TRUE; |
| 234 } |
| 235 |
| 236 void DropTargetGtk::FinishDrop(GdkDragContext* context, |
| 237 gint x, gint y, guint time) { |
| 238 gfx::Point root_view_location(x, y); |
| 239 int drag_operation = ui::DragDropTypes::GdkDragActionToDragOperation( |
| 240 context->actions); |
| 241 drag_operation = helper_.OnDrop(*data_, root_view_location, |
| 242 drag_operation); |
| 243 GdkDragAction gdk_action = |
| 244 static_cast<GdkDragAction>( |
| 245 ui::DragDropTypes::DragOperationToGdkDragAction(drag_operation)); |
| 246 gtk_drag_finish(context, gdk_action != 0, (gdk_action & GDK_ACTION_MOVE), |
| 247 time); |
| 248 |
| 249 static_cast<NativeWidgetGtk*>(helper_.root_view()->GetWidget()-> |
| 250 native_widget())->ResetDropTarget(); |
| 251 // WARNING: we've been deleted. |
| 252 } |
| 253 |
| 254 void DropTargetGtk::IntersectFormats(int f1, const std::set<GdkAtom>& cf1, |
| 255 int* f2, std::set<GdkAtom>* cf2) { |
| 256 *f2 = (*f2 & f1); |
| 257 std::set<GdkAtom> cf; |
| 258 std::set_intersection( |
| 259 cf1.begin(), cf1.end(), cf2->begin(), cf2->end(), |
| 260 std::insert_iterator<std::set<GdkAtom> >(cf, cf.begin())); |
| 261 cf.swap(*cf2); |
| 262 } |
| 263 |
| 264 void DropTargetGtk::RequestFormats(GdkDragContext* context, |
| 265 int formats, |
| 266 const std::set<GdkAtom>& custom_formats, |
| 267 guint time) { |
| 268 GtkWidget* widget = static_cast<NativeWidgetGtk*>(helper_.root_view()-> |
| 269 GetWidget()->native_widget())->window_contents(); |
| 270 |
| 271 const std::set<GdkAtom>& known_formats = |
| 272 data_provider().known_custom_formats(); |
| 273 if ((formats & OSExchangeData::STRING) != 0 && |
| 274 (requested_formats_ & OSExchangeData::STRING) == 0) { |
| 275 requested_formats_ |= OSExchangeData::STRING; |
| 276 if (known_formats.count(gdk_atom_intern("UTF8_STRING", false))) { |
| 277 gtk_drag_get_data(widget, context, |
| 278 gdk_atom_intern("UTF8_STRING", false), time); |
| 279 } else if (known_formats.count(gdk_atom_intern("text/plain;charset=utf-8", |
| 280 false))) { |
| 281 gtk_drag_get_data(widget, context, |
| 282 gdk_atom_intern("text/plain;charset=utf-8", false), |
| 283 time); |
| 284 } else if (known_formats.count(GDK_TARGET_STRING)) { |
| 285 gtk_drag_get_data(widget, context, GDK_TARGET_STRING, time); |
| 286 } else if (known_formats.count(gdk_atom_intern("text/plain", false))) { |
| 287 gtk_drag_get_data(widget, context, gdk_atom_intern("text/plain", false), |
| 288 time); |
| 289 } else if (known_formats.count(gdk_atom_intern("TEXT", false))) { |
| 290 gtk_drag_get_data(widget, context, gdk_atom_intern("TEXT", false), |
| 291 time); |
| 292 } else if (known_formats.count(gdk_atom_intern("STRING", false))) { |
| 293 gtk_drag_get_data(widget, context, gdk_atom_intern("STRING", false), |
| 294 time); |
| 295 } |
| 296 } |
| 297 if ((formats & OSExchangeData::URL) != 0 && |
| 298 (requested_formats_ & OSExchangeData::URL) == 0) { |
| 299 requested_formats_ |= OSExchangeData::URL; |
| 300 if (known_formats.count(ui::GetAtomForTarget(ui::CHROME_NAMED_URL))) { |
| 301 gtk_drag_get_data(widget, context, |
| 302 ui::GetAtomForTarget(ui::CHROME_NAMED_URL), time); |
| 303 } else if (known_formats.count( |
| 304 ui::GetAtomForTarget(ui::TEXT_URI_LIST))) { |
| 305 gtk_drag_get_data(widget, context, |
| 306 ui::GetAtomForTarget(ui::TEXT_URI_LIST), time); |
| 307 } |
| 308 } |
| 309 if (((formats & OSExchangeData::FILE_NAME) != 0) && |
| 310 (requested_formats_ & OSExchangeData::FILE_NAME) == 0) { |
| 311 requested_formats_ |= OSExchangeData::FILE_NAME; |
| 312 gtk_drag_get_data(widget, context, |
| 313 ui::GetAtomForTarget(ui::TEXT_URI_LIST), time); |
| 314 } |
| 315 for (std::set<GdkAtom>::const_iterator i = custom_formats.begin(); |
| 316 i != custom_formats.end(); ++i) { |
| 317 if (requested_custom_formats_.find(*i) == |
| 318 requested_custom_formats_.end()) { |
| 319 requested_custom_formats_.insert(*i); |
| 320 gtk_drag_get_data(widget, context, *i, time); |
| 321 } |
| 322 } |
| 323 } |
| 324 |
| 325 OSExchangeDataProviderGtk& DropTargetGtk::data_provider() const { |
| 326 return static_cast<OSExchangeDataProviderGtk&>(data_->provider()); |
| 327 } |
| 328 |
| 329 } // namespace views |
OLD | NEW |