*** empty log message ***
[anjuta-git-plugin.git] / libegg / eggtreemultidnd.c
blob3a7da919c082fb034a3eb3ba2b5e71d4cc708c1c
1 /* eggtreemultidnd.c
2 * Copyright (C) 2001 Red Hat, Inc.
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.
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.
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.
20 #include <string.h>
21 #include <gtk/gtktreeselection.h>
22 #include <gtk/gtksignal.h>
23 #include <gtk/gtkwidget.h>
24 #include <gtk/gtkmain.h>
25 #include "eggtreemultidnd.h"
27 #define EGG_TREE_MULTI_DND_STRING "EggTreeMultiDndString"
29 typedef struct
31 guint pressed_button;
32 gint x;
33 gint y;
34 guint motion_notify_handler;
35 guint button_release_handler;
36 guint drag_data_get_handler;
37 GSList *event_list;
38 } EggTreeMultiDndData;
40 /* CUT-N-PASTE from gtktreeview.c */
41 typedef struct _TreeViewDragInfo TreeViewDragInfo;
42 struct _TreeViewDragInfo
44 GdkModifierType start_button_mask;
45 GtkTargetList *source_target_list;
46 GdkDragAction source_actions;
48 GtkTargetList *dest_target_list;
50 guint source_set : 1;
51 guint dest_set : 1;
55 GType
56 egg_tree_multi_drag_source_get_type (void)
58 static GType our_type = 0;
60 if (!our_type)
62 static const GTypeInfo our_info =
64 sizeof (EggTreeMultiDragSourceIface), /* class_size */
65 NULL, /* base_init */
66 NULL, /* base_finalize */
67 NULL,
68 NULL, /* class_finalize */
69 NULL, /* class_data */
71 0, /* n_preallocs */
72 NULL
75 our_type = g_type_register_static (G_TYPE_INTERFACE, "EggTreeMultiDragSource", &our_info, 0);
78 return our_type;
82 /**
83 * egg_tree_multi_drag_source_row_draggable:
84 * @drag_source: a #EggTreeMultiDragSource
85 * @path: row on which user is initiating a drag
87 * Asks the #EggTreeMultiDragSource whether a particular row can be used as
88 * the source of a DND operation. If the source doesn't implement
89 * this interface, the row is assumed draggable.
91 * Return value: %TRUE if the row can be dragged
92 **/
93 gboolean
94 egg_tree_multi_drag_source_row_draggable (EggTreeMultiDragSource *drag_source,
95 GList *path_list)
97 EggTreeMultiDragSourceIface *iface = EGG_TREE_MULTI_DRAG_SOURCE_GET_IFACE (drag_source);
99 g_return_val_if_fail (EGG_IS_TREE_MULTI_DRAG_SOURCE (drag_source), FALSE);
100 g_return_val_if_fail (iface->row_draggable != NULL, FALSE);
101 g_return_val_if_fail (path_list != NULL, FALSE);
103 if (iface->row_draggable)
104 return (* iface->row_draggable) (drag_source, path_list);
105 else
106 return TRUE;
111 * egg_tree_multi_drag_source_drag_data_delete:
112 * @drag_source: a #EggTreeMultiDragSource
113 * @path: row that was being dragged
115 * Asks the #EggTreeMultiDragSource to delete the row at @path, because
116 * it was moved somewhere else via drag-and-drop. Returns %FALSE
117 * if the deletion fails because @path no longer exists, or for
118 * some model-specific reason. Should robustly handle a @path no
119 * longer found in the model!
121 * Return value: %TRUE if the row was successfully deleted
123 gboolean
124 egg_tree_multi_drag_source_drag_data_delete (EggTreeMultiDragSource *drag_source,
125 GList *path_list)
127 EggTreeMultiDragSourceIface *iface = EGG_TREE_MULTI_DRAG_SOURCE_GET_IFACE (drag_source);
129 g_return_val_if_fail (EGG_IS_TREE_MULTI_DRAG_SOURCE (drag_source), FALSE);
130 g_return_val_if_fail (iface->drag_data_delete != NULL, FALSE);
131 g_return_val_if_fail (path_list != NULL, FALSE);
133 return (* iface->drag_data_delete) (drag_source, path_list);
137 * egg_tree_multi_drag_source_drag_data_get:
138 * @drag_source: a #EggTreeMultiDragSource
139 * @path: row that was dragged
140 * @selection_data: a #EggSelectionData to fill with data from the dragged row
142 * Asks the #EggTreeMultiDragSource to fill in @selection_data with a
143 * representation of the row at @path. @selection_data->target gives
144 * the required type of the data. Should robustly handle a @path no
145 * longer found in the model!
147 * Return value: %TRUE if data of the required type was provided
149 gboolean
150 egg_tree_multi_drag_source_drag_data_get (EggTreeMultiDragSource *drag_source,
151 GList *path_list,
152 GtkSelectionData *selection_data)
154 EggTreeMultiDragSourceIface *iface = EGG_TREE_MULTI_DRAG_SOURCE_GET_IFACE (drag_source);
156 g_return_val_if_fail (EGG_IS_TREE_MULTI_DRAG_SOURCE (drag_source), FALSE);
157 g_return_val_if_fail (iface->drag_data_get != NULL, FALSE);
158 g_return_val_if_fail (path_list != NULL, FALSE);
159 g_return_val_if_fail (selection_data != NULL, FALSE);
161 return (* iface->drag_data_get) (drag_source, path_list, selection_data);
164 static void
165 stop_drag_check (GtkWidget *widget)
167 EggTreeMultiDndData *priv_data;
168 GSList *l;
170 priv_data = g_object_get_data (G_OBJECT (widget), EGG_TREE_MULTI_DND_STRING);
172 for (l = priv_data->event_list; l != NULL; l = l->next)
173 gdk_event_free (l->data);
175 g_slist_free (priv_data->event_list);
176 priv_data->event_list = NULL;
177 g_signal_handler_disconnect (widget, priv_data->motion_notify_handler);
178 g_signal_handler_disconnect (widget, priv_data->button_release_handler);
181 static gboolean
182 egg_tree_multi_drag_button_release_event (GtkWidget *widget,
183 GdkEventButton *event,
184 gpointer data)
186 EggTreeMultiDndData *priv_data;
187 GSList *l;
189 priv_data = g_object_get_data (G_OBJECT (widget), EGG_TREE_MULTI_DND_STRING);
191 for (l = priv_data->event_list; l != NULL; l = l->next)
192 gtk_propagate_event (widget, l->data);
194 stop_drag_check (widget);
196 return FALSE;
199 static void
200 selection_foreach (GtkTreeModel *model,
201 GtkTreePath *path,
202 GtkTreeIter *iter,
203 gpointer data)
205 GList **list_ptr;
207 list_ptr = (GList **) data;
209 *list_ptr = g_list_prepend (*list_ptr, gtk_tree_row_reference_new (model, path));
212 static void
213 path_list_free (GList *path_list)
215 g_list_foreach (path_list, (GFunc) gtk_tree_row_reference_free, NULL);
216 g_list_free (path_list);
219 static void
220 set_context_data (GdkDragContext *context,
221 GList *path_list)
223 g_object_set_data_full (G_OBJECT (context),
224 "egg-tree-view-multi-source-row",
225 path_list,
226 (GDestroyNotify) path_list_free);
229 static GList *
230 get_context_data (GdkDragContext *context)
232 return g_object_get_data (G_OBJECT (context),
233 "egg-tree-view-multi-source-row");
236 /* CUT-N-PASTE from gtktreeview.c */
237 static TreeViewDragInfo*
238 get_info (GtkTreeView *tree_view)
240 return g_object_get_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info");
244 static void
245 egg_tree_multi_drag_drag_data_get (GtkWidget *widget,
246 GdkDragContext *context,
247 GtkSelectionData *selection_data,
248 guint info,
249 guint time)
251 GtkTreeView *tree_view;
252 GtkTreeModel *model;
253 TreeViewDragInfo *di;
254 GList *path_list;
256 tree_view = GTK_TREE_VIEW (widget);
258 model = gtk_tree_view_get_model (tree_view);
260 if (model == NULL)
261 return;
263 di = get_info (GTK_TREE_VIEW (widget));
265 if (di == NULL)
266 return;
268 path_list = get_context_data (context);
270 if (path_list == NULL)
271 return;
273 /* We can implement the GTK_TREE_MODEL_ROW target generically for
274 * any model; for DragSource models there are some other targets
275 * we also support.
278 if (EGG_IS_TREE_MULTI_DRAG_SOURCE (model))
280 egg_tree_multi_drag_source_drag_data_get (EGG_TREE_MULTI_DRAG_SOURCE (model),
281 path_list,
282 selection_data);
286 static gboolean
287 egg_tree_multi_drag_motion_event (GtkWidget *widget,
288 GdkEventMotion *event,
289 gpointer data)
291 EggTreeMultiDndData *priv_data;
293 priv_data = g_object_get_data (G_OBJECT (widget), EGG_TREE_MULTI_DND_STRING);
295 if (gtk_drag_check_threshold (widget,
296 priv_data->x,
297 priv_data->y,
298 event->x,
299 event->y))
301 GList *path_list = NULL;
302 GtkTreeSelection *selection;
303 GtkTreeModel *model;
304 GdkDragContext *context;
305 TreeViewDragInfo *di;
307 di = get_info (GTK_TREE_VIEW (widget));
309 if (di == NULL)
310 return FALSE;
312 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
313 stop_drag_check (widget);
314 gtk_tree_selection_selected_foreach (selection, selection_foreach, &path_list);
315 path_list = g_list_reverse (path_list);
316 model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
317 if (egg_tree_multi_drag_source_row_draggable (EGG_TREE_MULTI_DRAG_SOURCE (model), path_list))
320 context = gtk_drag_begin (widget,
321 di->source_target_list,
322 di->source_actions,
323 priv_data->pressed_button,
324 (GdkEvent*)event);
325 set_context_data (context, path_list);
326 gtk_drag_set_icon_default (context);
329 else
331 path_list_free (path_list);
335 return TRUE;
338 static gboolean
339 egg_tree_multi_drag_button_press_event (GtkWidget *widget,
340 GdkEventButton *event,
341 gpointer data)
343 GtkTreeView *tree_view;
344 GtkTreePath *path = NULL;
345 GtkTreeViewColumn *column = NULL;
346 gint cell_x, cell_y;
347 GtkTreeSelection *selection;
348 EggTreeMultiDndData *priv_data;
350 tree_view = GTK_TREE_VIEW (widget);
351 priv_data = g_object_get_data (G_OBJECT (tree_view), EGG_TREE_MULTI_DND_STRING);
352 if (priv_data == NULL)
354 priv_data = g_new0 (EggTreeMultiDndData, 1);
355 g_object_set_data (G_OBJECT (tree_view), EGG_TREE_MULTI_DND_STRING, priv_data);
358 if (g_slist_find (priv_data->event_list, event))
359 return FALSE;
361 if (priv_data->event_list)
363 /* save the event to be propagated in order */
364 priv_data->event_list = g_slist_append (priv_data->event_list, gdk_event_copy ((GdkEvent*)event));
365 return TRUE;
368 if (event->type == GDK_2BUTTON_PRESS)
369 return FALSE;
371 gtk_tree_view_get_path_at_pos (tree_view,
372 event->x, event->y,
373 &path, &column,
374 &cell_x, &cell_y);
376 selection = gtk_tree_view_get_selection (tree_view);
378 if (path && gtk_tree_selection_path_is_selected (selection, path))
380 priv_data->pressed_button = event->button;
381 priv_data->x = event->x;
382 priv_data->y = event->y;
383 priv_data->event_list = g_slist_append (priv_data->event_list, gdk_event_copy ((GdkEvent*)event));
384 priv_data->motion_notify_handler =
385 g_signal_connect (G_OBJECT (tree_view), "motion_notify_event", G_CALLBACK (egg_tree_multi_drag_motion_event), NULL);
386 priv_data->button_release_handler =
387 g_signal_connect (G_OBJECT (tree_view), "button_release_event", G_CALLBACK (egg_tree_multi_drag_button_release_event), NULL);
389 if (priv_data->drag_data_get_handler == 0)
391 priv_data->drag_data_get_handler =
392 g_signal_connect (G_OBJECT (tree_view), "drag_data_get", G_CALLBACK (egg_tree_multi_drag_drag_data_get), NULL);
395 gtk_tree_path_free (path);
397 return TRUE;
400 if (path)
402 gtk_tree_path_free (path);
405 return FALSE;
408 void
409 egg_tree_multi_drag_add_drag_support (GtkTreeView *tree_view)
411 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
412 g_signal_connect (G_OBJECT (tree_view), "button_press_event", G_CALLBACK (egg_tree_multi_drag_button_press_event), NULL);