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 "ui/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 "ui/views/widget/native_widget_gtk.h" | |
20 #include "ui/views/widget/root_view.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 |