nautilus-3.6.3/cut-n-paste-code/libegg/eggtreemultidnd.c

No issues found

  1 /* eggtreemultidnd.c
  2  * Copyright (C) 2001  Red Hat, Inc.
  3  *
  4  * This library is free software; you can redistribute it and/or
  5  * modify it under the terms of the GNU Library 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  * Library General Public License for more details.
 13  *
 14  * You should have received a copy of the GNU Library General Public
 15  * License along with this library; if not, write to the
 16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 17  * Boston, MA 02111-1307, USA.
 18  */
 19 
 20 #ifdef HAVE_CONFIG_H
 21 #include <config.h>
 22 #endif
 23 
 24 #include <string.h>
 25 #include <gtk/gtk.h>
 26 #include "eggtreemultidnd.h"
 27 
 28 #define EGG_TREE_MULTI_DND_STRING "EggTreeMultiDndString"
 29 
 30 typedef struct
 31 {
 32   guint pressed_button;
 33   gint x;
 34   gint y;
 35   guint motion_notify_handler;
 36   guint button_release_handler;
 37   guint drag_data_get_handler;
 38   GSList *event_list;
 39 } EggTreeMultiDndData;
 40 
 41 /* CUT-N-PASTE from gtktreeview.c */
 42 typedef struct _TreeViewDragInfo TreeViewDragInfo;
 43 struct _TreeViewDragInfo
 44 {
 45   GdkModifierType start_button_mask;
 46   GtkTargetList *source_target_list;
 47   GdkDragAction source_actions;
 48 
 49   GtkTargetList *dest_target_list;
 50 
 51   guint source_set : 1;
 52   guint dest_set : 1;
 53 };
 54 
 55 
 56 GType
 57 egg_tree_multi_drag_source_get_type (void)
 58 {
 59   static GType our_type = 0;
 60 
 61   if (!our_type)
 62     {
 63       const GTypeInfo our_info =
 64       {
 65         sizeof (EggTreeMultiDragSourceIface), /* class_size */
 66 	NULL,		/* base_init */
 67 	NULL,		/* base_finalize */
 68 	NULL,
 69 	NULL,		/* class_finalize */
 70 	NULL,		/* class_data */
 71 	0,
 72 	0,              /* n_preallocs */
 73 	NULL
 74       };
 75 
 76       our_type = g_type_register_static (G_TYPE_INTERFACE, "EggTreeMultiDragSource", &our_info, 0);
 77     }
 78   
 79   return our_type;
 80 }
 81 
 82 
 83 /**
 84  * egg_tree_multi_drag_source_row_draggable:
 85  * @drag_source: a #EggTreeMultiDragSource
 86  * @path: row on which user is initiating a drag
 87  * 
 88  * Asks the #EggTreeMultiDragSource whether a particular row can be used as
 89  * the source of a DND operation. If the source doesn't implement
 90  * this interface, the row is assumed draggable.
 91  *
 92  * Return value: %TRUE if the row can be dragged
 93  **/
 94 gboolean
 95 egg_tree_multi_drag_source_row_draggable (EggTreeMultiDragSource *drag_source,
 96 					  GList                  *path_list)
 97 {
 98   EggTreeMultiDragSourceIface *iface = EGG_TREE_MULTI_DRAG_SOURCE_GET_IFACE (drag_source);
 99 
100   g_return_val_if_fail (EGG_IS_TREE_MULTI_DRAG_SOURCE (drag_source), FALSE);
101   g_return_val_if_fail (iface->row_draggable != NULL, FALSE);
102   g_return_val_if_fail (path_list != NULL, FALSE);
103 
104   if (iface->row_draggable)
105     return (* iface->row_draggable) (drag_source, path_list);
106   else
107     return TRUE;
108 }
109 
110 
111 /**
112  * egg_tree_multi_drag_source_drag_data_delete:
113  * @drag_source: a #EggTreeMultiDragSource
114  * @path: row that was being dragged
115  * 
116  * Asks the #EggTreeMultiDragSource to delete the row at @path, because
117  * it was moved somewhere else via drag-and-drop. Returns %FALSE
118  * if the deletion fails because @path no longer exists, or for
119  * some model-specific reason. Should robustly handle a @path no
120  * longer found in the model!
121  * 
122  * Return value: %TRUE if the row was successfully deleted
123  **/
124 gboolean
125 egg_tree_multi_drag_source_drag_data_delete (EggTreeMultiDragSource *drag_source,
126 					     GList                  *path_list)
127 {
128   EggTreeMultiDragSourceIface *iface = EGG_TREE_MULTI_DRAG_SOURCE_GET_IFACE (drag_source);
129 
130   g_return_val_if_fail (EGG_IS_TREE_MULTI_DRAG_SOURCE (drag_source), FALSE);
131   g_return_val_if_fail (iface->drag_data_delete != NULL, FALSE);
132   g_return_val_if_fail (path_list != NULL, FALSE);
133 
134   return (* iface->drag_data_delete) (drag_source, path_list);
135 }
136 
137 /**
138  * egg_tree_multi_drag_source_drag_data_get:
139  * @drag_source: a #EggTreeMultiDragSource
140  * @path: row that was dragged
141  * @selection_data: a #EggSelectionData to fill with data from the dragged row
142  * 
143  * Asks the #EggTreeMultiDragSource to fill in @selection_data with a
144  * representation of the row at @path. @selection_data->target gives
145  * the required type of the data.  Should robustly handle a @path no
146  * longer found in the model!
147  * 
148  * Return value: %TRUE if data of the required type was provided 
149  **/
150 gboolean
151 egg_tree_multi_drag_source_drag_data_get    (EggTreeMultiDragSource *drag_source,
152 					     GList                  *path_list,
153 					     GtkSelectionData  *selection_data)
154 {
155   EggTreeMultiDragSourceIface *iface = EGG_TREE_MULTI_DRAG_SOURCE_GET_IFACE (drag_source);
156 
157   g_return_val_if_fail (EGG_IS_TREE_MULTI_DRAG_SOURCE (drag_source), FALSE);
158   g_return_val_if_fail (iface->drag_data_get != NULL, FALSE);
159   g_return_val_if_fail (path_list != NULL, FALSE);
160   g_return_val_if_fail (selection_data != NULL, FALSE);
161 
162   return (* iface->drag_data_get) (drag_source, path_list, selection_data);
163 }
164 
165 static void
166 stop_drag_check (GtkWidget *widget)
167 {
168   EggTreeMultiDndData *priv_data;
169   GSList *l;
170 
171   priv_data = g_object_get_data (G_OBJECT (widget), EGG_TREE_MULTI_DND_STRING);
172   
173   for (l = priv_data->event_list; l != NULL; l = l->next)
174     gdk_event_free (l->data);
175   
176   g_slist_free (priv_data->event_list);
177   priv_data->event_list = NULL;
178   g_signal_handler_disconnect (widget, priv_data->motion_notify_handler);
179   g_signal_handler_disconnect (widget, priv_data->button_release_handler);
180 }
181 
182 static gboolean
183 egg_tree_multi_drag_button_release_event (GtkWidget      *widget,
184 					  GdkEventButton *event,
185 					  gpointer        data)
186 {
187   EggTreeMultiDndData *priv_data;
188   GSList *l;
189 
190   priv_data = g_object_get_data (G_OBJECT (widget), EGG_TREE_MULTI_DND_STRING);
191 
192   for (l = priv_data->event_list; l != NULL; l = l->next) 
193     gtk_propagate_event (widget, l->data);
194   
195   stop_drag_check (widget);
196 
197   return FALSE;
198 }
199 
200 static void
201 selection_foreach (GtkTreeModel *model,
202 		   GtkTreePath  *path,
203 		   GtkTreeIter  *iter,
204 		   gpointer      data)
205 {
206   GList **list_ptr;
207 
208   list_ptr = (GList **) data;
209 
210   *list_ptr = g_list_prepend (*list_ptr, gtk_tree_row_reference_new (model, path));
211 }
212 
213 static void
214 path_list_free (GList *path_list)
215 {
216   g_list_foreach (path_list, (GFunc) gtk_tree_row_reference_free, NULL);
217   g_list_free (path_list);
218 }
219 
220 static void
221 set_context_data (GdkDragContext *context,
222 		  GList          *path_list)
223 {
224   g_object_set_data_full (G_OBJECT (context),
225                           "egg-tree-view-multi-source-row",
226                           path_list,
227                           (GDestroyNotify) path_list_free);
228 }
229 
230 static GList *
231 get_context_data (GdkDragContext *context)
232 {
233   return g_object_get_data (G_OBJECT (context),
234 			    "egg-tree-view-multi-source-row");
235 }
236 
237 /* CUT-N-PASTE from gtktreeview.c */
238 static TreeViewDragInfo*
239 get_info (GtkTreeView *tree_view)
240 {
241   return g_object_get_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info");
242 }
243 
244 
245 static void
246 egg_tree_multi_drag_drag_data_get (GtkWidget        *widget,
247 				   GdkDragContext   *context,
248 				   GtkSelectionData *selection_data,
249 				   guint             info,
250 				   guint             time)
251 {
252   GtkTreeView *tree_view;
253   GtkTreeModel *model;
254   TreeViewDragInfo *di;
255   GList *path_list;
256 
257   tree_view = GTK_TREE_VIEW (widget);
258 
259   model = gtk_tree_view_get_model (tree_view);
260 
261   if (model == NULL)
262     return;
263 
264   di = get_info (GTK_TREE_VIEW (widget));
265 
266   if (di == NULL)
267     return;
268 
269   path_list = get_context_data (context);
270 
271   if (path_list == NULL)
272     return;
273 
274   /* We can implement the GTK_TREE_MODEL_ROW target generically for
275    * any model; for DragSource models there are some other targets
276    * we also support.
277    */
278 
279   if (EGG_IS_TREE_MULTI_DRAG_SOURCE (model))
280     {
281       egg_tree_multi_drag_source_drag_data_get (EGG_TREE_MULTI_DRAG_SOURCE (model),
282 						path_list,
283 						selection_data);
284     }
285 }
286 
287 static gboolean
288 egg_tree_multi_drag_motion_event (GtkWidget      *widget,
289 				  GdkEventMotion *event,
290 				  gpointer        data)
291 {
292   EggTreeMultiDndData *priv_data;
293 
294   priv_data = g_object_get_data (G_OBJECT (widget), EGG_TREE_MULTI_DND_STRING);
295 
296   if (gtk_drag_check_threshold (widget,
297 				priv_data->x,
298 				priv_data->y,
299 				event->x,
300 				event->y))
301     {
302       GList *path_list = NULL;
303       GtkTreeSelection *selection;
304       GtkTreeModel *model;
305       GdkDragContext *context;
306       TreeViewDragInfo *di;
307 
308       di = get_info (GTK_TREE_VIEW (widget));
309 
310       if (di == NULL)
311 	return FALSE;
312       
313       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
314       stop_drag_check (widget);
315       gtk_tree_selection_selected_foreach (selection, selection_foreach, &path_list);
316       path_list = g_list_reverse (path_list);
317       model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
318       if (egg_tree_multi_drag_source_row_draggable (EGG_TREE_MULTI_DRAG_SOURCE (model), path_list))
319 	{
320 
321 	  context = gtk_drag_begin (widget,
322                                     gtk_drag_source_get_target_list (widget),
323 				    di->source_actions,
324 				    priv_data->pressed_button,
325 				    (GdkEvent*)event);
326 	  set_context_data (context, path_list);
327 	  gtk_drag_set_icon_default (context);
328 
329 	}
330       else
331 	{
332 	  path_list_free (path_list);
333 	}
334     }
335 
336   return TRUE;
337 }
338 
339 static gboolean
340 egg_tree_multi_drag_button_press_event (GtkWidget      *widget,
341 					GdkEventButton *event,
342 					gpointer        data)
343 {
344   GtkTreeView *tree_view;
345   GtkTreePath *path = NULL;
346   GtkTreeViewColumn *column = NULL;
347   gint cell_x, cell_y;
348   GtkTreeSelection *selection;
349   EggTreeMultiDndData *priv_data;
350 
351   tree_view = GTK_TREE_VIEW (widget);
352   priv_data = g_object_get_data (G_OBJECT (tree_view), EGG_TREE_MULTI_DND_STRING);
353   if (priv_data == NULL)
354     {
355       priv_data = g_new0 (EggTreeMultiDndData, 1);
356       g_object_set_data (G_OBJECT (tree_view), EGG_TREE_MULTI_DND_STRING, priv_data);
357     }
358 
359   if (g_slist_find (priv_data->event_list, event)) 
360     return FALSE;
361 
362   if (priv_data->event_list) 
363     {
364       /* save the event to be propagated in order */
365       priv_data->event_list = g_slist_append (priv_data->event_list, gdk_event_copy ((GdkEvent*)event));
366       return TRUE;
367     }
368   
369   if (event->type == GDK_2BUTTON_PRESS)
370     return FALSE;
371 
372   gtk_tree_view_get_path_at_pos (tree_view,
373 				 event->x, event->y,
374 				 &path, &column,
375 				 &cell_x, &cell_y);
376 
377   selection = gtk_tree_view_get_selection (tree_view);
378 
379   if (path && gtk_tree_selection_path_is_selected (selection, path))
380     {
381       priv_data->pressed_button = event->button;
382       priv_data->x = event->x;
383       priv_data->y = event->y;
384       priv_data->event_list = g_slist_append (priv_data->event_list, gdk_event_copy ((GdkEvent*)event));
385       priv_data->motion_notify_handler =
386 	g_signal_connect (G_OBJECT (tree_view), "motion_notify_event", G_CALLBACK (egg_tree_multi_drag_motion_event), NULL);
387       priv_data->button_release_handler =
388 	g_signal_connect (G_OBJECT (tree_view), "button_release_event", G_CALLBACK (egg_tree_multi_drag_button_release_event), NULL);
389 
390       if (priv_data->drag_data_get_handler == 0) 
391 	{
392 	  priv_data->drag_data_get_handler =
393 	    g_signal_connect (G_OBJECT (tree_view), "drag_data_get", G_CALLBACK (egg_tree_multi_drag_drag_data_get), NULL);
394 	}
395 
396       gtk_tree_path_free (path);
397       
398       return TRUE;
399     }
400 
401   if (path) 
402     {
403       gtk_tree_path_free (path);
404     }
405 
406   return FALSE;
407 }
408 
409 void
410 egg_tree_multi_drag_add_drag_support (GtkTreeView *tree_view)
411 {
412   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
413   g_signal_connect (G_OBJECT (tree_view), "button_press_event", G_CALLBACK (egg_tree_multi_drag_button_press_event), NULL);
414 }