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