1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1999 Peter Mattis, Spencer Kimball and Josh MacDonald
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 /*
19 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
20 * file for a list of people on the GTK+ Team. See the ChangeLog
21 * files for a list of changes. These files are distributed with
22 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
23 */
24
25 #include "config.h"
26
27 #include "gtkdragsource.h"
28
29 #include "gtkdnd.h"
30 #include "gtkdndprivate.h"
31 #include "gtkgesturedrag.h"
32 #include "gtkimagedefinitionprivate.h"
33 #include "gtkintl.h"
34
35
36 typedef struct _GtkDragSourceSite GtkDragSourceSite;
37
38 struct _GtkDragSourceSite
39 {
40 GdkModifierType start_button_mask;
41 GtkTargetList *target_list; /* Targets for drag data */
42 GdkDragAction actions; /* Possible actions */
43
44 GtkImageDefinition *image_def;
45 GtkGesture *drag_gesture;
46 };
47
48 static void
gtk_drag_source_gesture_begin(GtkGesture * gesture,GdkEventSequence * sequence,gpointer data)49 gtk_drag_source_gesture_begin (GtkGesture *gesture,
50 GdkEventSequence *sequence,
51 gpointer data)
52 {
53 GtkDragSourceSite *site = data;
54 guint button;
55
56 if (gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture)))
57 button = 1;
58 else
59 button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
60
61 g_assert (button >= 1);
62
63 if (!site->start_button_mask ||
64 !(site->start_button_mask & (GDK_BUTTON1_MASK << (button - 1))))
65 gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_DENIED);
66 }
67
68 static gboolean
gtk_drag_source_event_cb(GtkWidget * widget,GdkEvent * event,gpointer data)69 gtk_drag_source_event_cb (GtkWidget *widget,
70 GdkEvent *event,
71 gpointer data)
72 {
73 gdouble start_x, start_y, offset_x, offset_y;
74 GtkDragSourceSite *site = data;
75
76 gtk_event_controller_handle_event (GTK_EVENT_CONTROLLER (site->drag_gesture), event);
77
78 if (gtk_gesture_is_recognized (site->drag_gesture))
79 {
80 gtk_gesture_drag_get_start_point (GTK_GESTURE_DRAG (site->drag_gesture),
81 &start_x, &start_y);
82 gtk_gesture_drag_get_offset (GTK_GESTURE_DRAG (site->drag_gesture),
83 &offset_x, &offset_y);
84
85 if (gtk_drag_check_threshold (widget, start_x, start_y,
86 start_x + offset_x, start_y + offset_y))
87 {
88 GdkEventSequence *sequence;
89 GdkEvent *last_event;
90 guint button;
91 gboolean needs_icon;
92 GdkDragContext *context;
93
94 sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (site->drag_gesture));
95 last_event = gdk_event_copy (gtk_gesture_get_last_event (site->drag_gesture, sequence));
96
97 button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (site->drag_gesture));
98 gtk_event_controller_reset (GTK_EVENT_CONTROLLER (site->drag_gesture));
99
100 context = gtk_drag_begin_internal (widget, &needs_icon, site->target_list,
101 site->actions, button, last_event,
102 start_x, start_y);
103
104 if (context != NULL && needs_icon)
105 gtk_drag_set_icon_definition (context, site->image_def, 0, 0);
106
107 gdk_event_free (last_event);
108
109 return TRUE;
110 }
111 }
112
113 return FALSE;
114 }
115
116 static void
gtk_drag_source_site_destroy(gpointer data)117 gtk_drag_source_site_destroy (gpointer data)
118 {
119 GtkDragSourceSite *site = data;
120
121 if (site->target_list)
122 gtk_target_list_unref (site->target_list);
123
124 gtk_image_definition_unref (site->image_def);
125 g_clear_object (&site->drag_gesture);
126 g_slice_free (GtkDragSourceSite, site);
127 }
128
129 /**
130 * gtk_drag_source_set: (method)
131 * @widget: a #GtkWidget
132 * @start_button_mask: the bitmask of buttons that can start the drag
133 * @targets: (allow-none) (array length=n_targets): the table of targets
134 * that the drag will support, may be %NULL
135 * @n_targets: the number of items in @targets
136 * @actions: the bitmask of possible actions for a drag from this widget
137 *
138 * Sets up a widget so that GTK+ will start a drag operation when the user
139 * clicks and drags on the widget. The widget must have a window.
140 */
141 void
gtk_drag_source_set(GtkWidget * widget,GdkModifierType start_button_mask,const GtkTargetEntry * targets,gint n_targets,GdkDragAction actions)142 gtk_drag_source_set (GtkWidget *widget,
143 GdkModifierType start_button_mask,
144 const GtkTargetEntry *targets,
145 gint n_targets,
146 GdkDragAction actions)
147 {
148 GtkDragSourceSite *site;
149
150 g_return_if_fail (GTK_IS_WIDGET (widget));
151
152 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
153
154 gtk_widget_add_events (widget,
155 gtk_widget_get_events (widget) |
156 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
157 GDK_BUTTON_MOTION_MASK);
158
159 if (site)
160 {
161 if (site->target_list)
162 gtk_target_list_unref (site->target_list);
163 }
164 else
165 {
166 site = g_slice_new0 (GtkDragSourceSite);
167 site->image_def = gtk_image_definition_new_empty ();
168 site->drag_gesture = gtk_gesture_drag_new (widget);
169 gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (site->drag_gesture),
170 GTK_PHASE_NONE);
171 gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (site->drag_gesture), 0);
172 g_signal_connect (site->drag_gesture, "begin",
173 G_CALLBACK (gtk_drag_source_gesture_begin),
174 site);
175
176 g_signal_connect (widget, "button-press-event",
177 G_CALLBACK (gtk_drag_source_event_cb),
178 site);
179 g_signal_connect (widget, "button-release-event",
180 G_CALLBACK (gtk_drag_source_event_cb),
181 site);
182 g_signal_connect (widget, "motion-notify-event",
183 G_CALLBACK (gtk_drag_source_event_cb),
184 site);
185 g_object_set_data_full (G_OBJECT (widget),
186 I_("gtk-site-data"),
187 site, gtk_drag_source_site_destroy);
188 }
189
190 site->start_button_mask = start_button_mask;
191
192 site->target_list = gtk_target_list_new (targets, n_targets);
193
194 site->actions = actions;
195 }
196
197 /**
198 * gtk_drag_source_unset: (method)
199 * @widget: a #GtkWidget
200 *
201 * Undoes the effects of gtk_drag_source_set().
202 */
203 void
gtk_drag_source_unset(GtkWidget * widget)204 gtk_drag_source_unset (GtkWidget *widget)
205 {
206 GtkDragSourceSite *site;
207
208 g_return_if_fail (GTK_IS_WIDGET (widget));
209
210 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
211
212 if (site)
213 {
214 g_signal_handlers_disconnect_by_func (widget,
215 gtk_drag_source_event_cb,
216 site);
217 g_object_set_data (G_OBJECT (widget), I_("gtk-site-data"), NULL);
218 }
219 }
220
221 /**
222 * gtk_drag_source_get_target_list: (method)
223 * @widget: a #GtkWidget
224 *
225 * Gets the list of targets this widget can provide for
226 * drag-and-drop.
227 *
228 * Returns: (nullable) (transfer none): the #GtkTargetList, or %NULL if none
229 *
230 * Since: 2.4
231 */
232 GtkTargetList *
gtk_drag_source_get_target_list(GtkWidget * widget)233 gtk_drag_source_get_target_list (GtkWidget *widget)
234 {
235 GtkDragSourceSite *site;
236
237 g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
238
239 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
240
241 return site ? site->target_list : NULL;
242 }
243
244 /**
245 * gtk_drag_source_set_target_list: (method)
246 * @widget: a #GtkWidget that’s a drag source
247 * @target_list: (allow-none): list of draggable targets, or %NULL for none
248 *
249 * Changes the target types that this widget offers for drag-and-drop.
250 * The widget must first be made into a drag source with
251 * gtk_drag_source_set().
252 *
253 * Since: 2.4
254 */
255 void
gtk_drag_source_set_target_list(GtkWidget * widget,GtkTargetList * target_list)256 gtk_drag_source_set_target_list (GtkWidget *widget,
257 GtkTargetList *target_list)
258 {
259 GtkDragSourceSite *site;
260
261 g_return_if_fail (GTK_IS_WIDGET (widget));
262
263 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
264 if (site == NULL)
265 {
266 g_warning ("gtk_drag_source_set_target_list() requires the widget "
267 "to already be a drag source.");
268 return;
269 }
270
271 if (target_list)
272 gtk_target_list_ref (target_list);
273
274 if (site->target_list)
275 gtk_target_list_unref (site->target_list);
276
277 site->target_list = target_list;
278 }
279
280 /**
281 * gtk_drag_source_add_text_targets: (method)
282 * @widget: a #GtkWidget that’s is a drag source
283 *
284 * Add the text targets supported by #GtkSelectionData to
285 * the target list of the drag source. The targets
286 * are added with @info = 0. If you need another value,
287 * use gtk_target_list_add_text_targets() and
288 * gtk_drag_source_set_target_list().
289 *
290 * Since: 2.6
291 */
292 void
gtk_drag_source_add_text_targets(GtkWidget * widget)293 gtk_drag_source_add_text_targets (GtkWidget *widget)
294 {
295 GtkTargetList *target_list;
296
297 target_list = gtk_drag_source_get_target_list (widget);
298 if (target_list)
299 gtk_target_list_ref (target_list);
300 else
301 target_list = gtk_target_list_new (NULL, 0);
302 gtk_target_list_add_text_targets (target_list, 0);
303 gtk_drag_source_set_target_list (widget, target_list);
304 gtk_target_list_unref (target_list);
305 }
306
307 /**
308 * gtk_drag_source_add_image_targets: (method)
309 * @widget: a #GtkWidget that’s is a drag source
310 *
311 * Add the writable image targets supported by #GtkSelectionData to
312 * the target list of the drag source. The targets
313 * are added with @info = 0. If you need another value,
314 * use gtk_target_list_add_image_targets() and
315 * gtk_drag_source_set_target_list().
316 *
317 * Since: 2.6
318 */
319 void
gtk_drag_source_add_image_targets(GtkWidget * widget)320 gtk_drag_source_add_image_targets (GtkWidget *widget)
321 {
322 GtkTargetList *target_list;
323
324 target_list = gtk_drag_source_get_target_list (widget);
325 if (target_list)
326 gtk_target_list_ref (target_list);
327 else
328 target_list = gtk_target_list_new (NULL, 0);
329 gtk_target_list_add_image_targets (target_list, 0, TRUE);
330 gtk_drag_source_set_target_list (widget, target_list);
331 gtk_target_list_unref (target_list);
332 }
333
334 /**
335 * gtk_drag_source_add_uri_targets: (method)
336 * @widget: a #GtkWidget that’s is a drag source
337 *
338 * Add the URI targets supported by #GtkSelectionData to
339 * the target list of the drag source. The targets
340 * are added with @info = 0. If you need another value,
341 * use gtk_target_list_add_uri_targets() and
342 * gtk_drag_source_set_target_list().
343 *
344 * Since: 2.6
345 */
346 void
gtk_drag_source_add_uri_targets(GtkWidget * widget)347 gtk_drag_source_add_uri_targets (GtkWidget *widget)
348 {
349 GtkTargetList *target_list;
350
351 target_list = gtk_drag_source_get_target_list (widget);
352 if (target_list)
353 gtk_target_list_ref (target_list);
354 else
355 target_list = gtk_target_list_new (NULL, 0);
356 gtk_target_list_add_uri_targets (target_list, 0);
357 gtk_drag_source_set_target_list (widget, target_list);
358 gtk_target_list_unref (target_list);
359 }
360
361 /**
362 * gtk_drag_source_set_icon_pixbuf: (method)
363 * @widget: a #GtkWidget
364 * @pixbuf: the #GdkPixbuf for the drag icon
365 *
366 * Sets the icon that will be used for drags from a particular widget
367 * from a #GdkPixbuf. GTK+ retains a reference for @pixbuf and will
368 * release it when it is no longer needed.
369 */
370 void
gtk_drag_source_set_icon_pixbuf(GtkWidget * widget,GdkPixbuf * pixbuf)371 gtk_drag_source_set_icon_pixbuf (GtkWidget *widget,
372 GdkPixbuf *pixbuf)
373 {
374 GtkDragSourceSite *site;
375
376 g_return_if_fail (GTK_IS_WIDGET (widget));
377 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
378
379 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
380 g_return_if_fail (site != NULL);
381
382 g_clear_pointer (&site->image_def, gtk_image_definition_unref);
383 site->image_def = gtk_image_definition_new_pixbuf (pixbuf, 1);
384 }
385
386 /**
387 * gtk_drag_source_set_icon_stock: (method)
388 * @widget: a #GtkWidget
389 * @stock_id: the ID of the stock icon to use
390 *
391 * Sets the icon that will be used for drags from a particular source
392 * to a stock icon.
393 *
394 * Deprecated: 3.10: Use gtk_drag_source_set_icon_name() instead.
395 */
396 void
gtk_drag_source_set_icon_stock(GtkWidget * widget,const gchar * stock_id)397 gtk_drag_source_set_icon_stock (GtkWidget *widget,
398 const gchar *stock_id)
399 {
400 GtkDragSourceSite *site;
401
402 g_return_if_fail (GTK_IS_WIDGET (widget));
403 g_return_if_fail (stock_id != NULL);
404
405 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
406 g_return_if_fail (site != NULL);
407
408 gtk_image_definition_unref (site->image_def);
409 site->image_def = gtk_image_definition_new_stock (stock_id);
410 }
411
412 /**
413 * gtk_drag_source_set_icon_name: (method)
414 * @widget: a #GtkWidget
415 * @icon_name: name of icon to use
416 *
417 * Sets the icon that will be used for drags from a particular source
418 * to a themed icon. See the docs for #GtkIconTheme for more details.
419 *
420 * Since: 2.8
421 */
422 void
gtk_drag_source_set_icon_name(GtkWidget * widget,const gchar * icon_name)423 gtk_drag_source_set_icon_name (GtkWidget *widget,
424 const gchar *icon_name)
425 {
426 GtkDragSourceSite *site;
427
428 g_return_if_fail (GTK_IS_WIDGET (widget));
429 g_return_if_fail (icon_name != NULL);
430
431 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
432 g_return_if_fail (site != NULL);
433
434 gtk_image_definition_unref (site->image_def);
435 site->image_def = gtk_image_definition_new_icon_name (icon_name);
436 }
437
438 /**
439 * gtk_drag_source_set_icon_gicon: (method)
440 * @widget: a #GtkWidget
441 * @icon: A #GIcon
442 *
443 * Sets the icon that will be used for drags from a particular source
444 * to @icon. See the docs for #GtkIconTheme for more details.
445 *
446 * Since: 3.2
447 */
448 void
gtk_drag_source_set_icon_gicon(GtkWidget * widget,GIcon * icon)449 gtk_drag_source_set_icon_gicon (GtkWidget *widget,
450 GIcon *icon)
451 {
452 GtkDragSourceSite *site;
453
454 g_return_if_fail (GTK_IS_WIDGET (widget));
455 g_return_if_fail (icon != NULL);
456
457 site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
458 g_return_if_fail (site != NULL);
459
460 gtk_image_definition_unref (site->image_def);
461 site->image_def = gtk_image_definition_new_gicon (icon);
462 }
463
464