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.
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"
34 guint motion_notify_handler
;
35 guint button_release_handler
;
36 guint drag_data_get_handler
;
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
;
56 egg_tree_multi_drag_source_get_type (void)
58 static GType our_type
= 0;
62 static const GTypeInfo our_info
=
64 sizeof (EggTreeMultiDragSourceIface
), /* class_size */
66 NULL
, /* base_finalize */
68 NULL
, /* class_finalize */
69 NULL
, /* class_data */
75 our_type
= g_type_register_static (G_TYPE_INTERFACE
, "EggTreeMultiDragSource", &our_info
, 0);
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
94 egg_tree_multi_drag_source_row_draggable (EggTreeMultiDragSource
*drag_source
,
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
);
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
124 egg_tree_multi_drag_source_drag_data_delete (EggTreeMultiDragSource
*drag_source
,
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
150 egg_tree_multi_drag_source_drag_data_get (EggTreeMultiDragSource
*drag_source
,
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
);
165 stop_drag_check (GtkWidget
*widget
)
167 EggTreeMultiDndData
*priv_data
;
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
);
182 egg_tree_multi_drag_button_release_event (GtkWidget
*widget
,
183 GdkEventButton
*event
,
186 EggTreeMultiDndData
*priv_data
;
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
);
200 selection_foreach (GtkTreeModel
*model
,
207 list_ptr
= (GList
**) data
;
209 *list_ptr
= g_list_prepend (*list_ptr
, gtk_tree_row_reference_new (model
, path
));
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
);
220 set_context_data (GdkDragContext
*context
,
223 g_object_set_data_full (G_OBJECT (context
),
224 "egg-tree-view-multi-source-row",
226 (GDestroyNotify
) path_list_free
);
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");
245 egg_tree_multi_drag_drag_data_get (GtkWidget
*widget
,
246 GdkDragContext
*context
,
247 GtkSelectionData
*selection_data
,
251 GtkTreeView
*tree_view
;
253 TreeViewDragInfo
*di
;
256 tree_view
= GTK_TREE_VIEW (widget
);
258 model
= gtk_tree_view_get_model (tree_view
);
263 di
= get_info (GTK_TREE_VIEW (widget
));
268 path_list
= get_context_data (context
);
270 if (path_list
== NULL
)
273 /* We can implement the GTK_TREE_MODEL_ROW target generically for
274 * any model; for DragSource models there are some other targets
278 if (EGG_IS_TREE_MULTI_DRAG_SOURCE (model
))
280 egg_tree_multi_drag_source_drag_data_get (EGG_TREE_MULTI_DRAG_SOURCE (model
),
287 egg_tree_multi_drag_motion_event (GtkWidget
*widget
,
288 GdkEventMotion
*event
,
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
,
301 GList
*path_list
= NULL
;
302 GtkTreeSelection
*selection
;
304 GdkDragContext
*context
;
305 TreeViewDragInfo
*di
;
307 di
= get_info (GTK_TREE_VIEW (widget
));
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
,
323 priv_data
->pressed_button
,
325 set_context_data (context
, path_list
);
326 gtk_drag_set_icon_default (context
);
331 path_list_free (path_list
);
339 egg_tree_multi_drag_button_press_event (GtkWidget
*widget
,
340 GdkEventButton
*event
,
343 GtkTreeView
*tree_view
;
344 GtkTreePath
*path
= NULL
;
345 GtkTreeViewColumn
*column
= NULL
;
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
))
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
));
368 if (event
->type
== GDK_2BUTTON_PRESS
)
371 gtk_tree_view_get_path_at_pos (tree_view
,
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
);
402 gtk_tree_path_free (path
);
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
);