2006-06-07 James Livingston <doclivingston@gmail.com>
[rhythmbox.git] / lib / rb-tree-dnd.c
blob42edb51287b36f33f6646ed2bda2d20369c5993a
1 /* rbtreednd.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., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301 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 <gtk/gtktreednd.h>
26 #include "rb-tree-dnd.h"
28 #include "rb-debug.h"
30 #define RB_TREE_DND_STRING "RbTreeDndString"
31 /* must be the same value as in gtk_tree_view.c */
32 #define SCROLL_EDGE_SIZE 15
34 typedef struct
36 guint pressed_button;
37 gint x;
38 gint y;
39 guint button_press_event_handler;
40 guint motion_notify_handler;
41 guint button_release_handler;
42 guint drag_data_get_handler;
43 guint drag_data_delete_handler;
44 guint drag_motion_handler;
45 guint drag_leave_handler;
46 guint drag_drop_handler;
47 guint drag_data_received_handler;
48 GSList *event_list;
49 gboolean pending_event;
51 GtkTargetList *dest_target_list;
52 GdkDragAction dest_actions;
53 RbTreeDestFlag dest_flags;
55 GtkTargetList *source_target_list;
56 GdkDragAction source_actions;
57 GdkModifierType start_button_mask;
59 /* Scroll timeout (e.g. during dnd) */
60 guint scroll_timeout;
62 /* Select on drag timeout */
63 GtkTreePath * previous_dest_path;
64 guint select_on_drag_timeout;
65 } RbTreeDndData;
67 RbTreeDndData *init_rb_tree_dnd_data (GtkWidget *widget);
68 GList * get_context_data (GdkDragContext *context);
69 static gboolean filter_drop_position (GtkWidget *widget, GdkDragContext *context, GtkTreePath *path, GtkTreeViewDropPosition *pos);
70 static gint scroll_row_timeout (gpointer data);
71 static gboolean select_on_drag_timeout (gpointer data);
72 static void remove_scroll_timeout (GtkTreeView *tree_view);
73 static void remove_select_on_drag_timeout (GtkTreeView *tree_view);
75 GType
76 rb_tree_drag_source_get_type (void)
78 static GType our_type = 0;
80 if (!our_type)
82 static const GTypeInfo our_info =
84 sizeof (RbTreeDragSourceIface), /* class_size */
85 NULL, /* base_init */
86 NULL, /* base_finalize */
87 NULL,
88 NULL, /* class_finalize */
89 NULL, /* class_data */
91 0, /* n_preallocs */
92 NULL
95 our_type = g_type_register_static (G_TYPE_INTERFACE, "RbTreeDragSource", &our_info, 0);
98 return our_type;
103 * rb_tree_drag_source_row_draggable:
104 * @drag_source: a #EggTreeMultiDragSource
105 * @path: row on which user is initiating a drag
107 * Asks the #EggTreeMultiDragSource whether a particular row can be used as
108 * the source of a DND operation. If the source doesn't implement
109 * this interface, the row is assumed draggable.
111 * Return value: %TRUE if the row can be dragged
113 gboolean
114 rb_tree_drag_source_row_draggable (RbTreeDragSource *drag_source,
115 GList *path_list)
117 RbTreeDragSourceIface *iface = RB_TREE_DRAG_SOURCE_GET_IFACE (drag_source);
119 g_return_val_if_fail (RB_IS_TREE_DRAG_SOURCE (drag_source), FALSE);
120 g_return_val_if_fail (iface->row_draggable != NULL, FALSE);
121 g_return_val_if_fail (path_list != NULL, FALSE);
123 if (iface->row_draggable)
124 return (* iface->row_draggable) (drag_source, path_list);
125 else
126 return TRUE;
131 * rb_tree_drag_source_drag_data_delete:
132 * @drag_source: a #EggTreeMultiDragSource
133 * @path: row that was being dragged
135 * Asks the #EggTreeMultiDragSource to delete the row at @path, because
136 * it was moved somewhere else via drag-and-drop. Returns %FALSE
137 * if the deletion fails because @path no longer exists, or for
138 * some model-specific reason. Should robustly handle a @path no
139 * longer found in the model!
141 * Return value: %TRUE if the row was successfully deleted
143 gboolean
144 rb_tree_drag_source_drag_data_delete (RbTreeDragSource *drag_source,
145 GList *path_list)
147 RbTreeDragSourceIface *iface = RB_TREE_DRAG_SOURCE_GET_IFACE (drag_source);
149 g_return_val_if_fail (RB_IS_TREE_DRAG_SOURCE (drag_source), FALSE);
150 g_return_val_if_fail (iface->drag_data_delete != NULL, FALSE);
151 g_return_val_if_fail (path_list != NULL, FALSE);
153 return (* iface->drag_data_delete) (drag_source, path_list);
157 * rb_tree_drag_source_drag_data_get:
158 * @drag_source: a #EggTreeMultiDragSource
159 * @path: row that was dragged
160 * @selection_data: a #EggSelectionData to fill with data from the dragged row
162 * Asks the #EggTreeMultiDragSource to fill in @selection_data with a
163 * representation of the row at @path. @selection_data->target gives
164 * the required type of the data. Should robustly handle a @path no
165 * longer found in the model!
167 * Return value: %TRUE if data of the required type was provided
169 gboolean
170 rb_tree_drag_source_drag_data_get (RbTreeDragSource *drag_source,
171 GList *path_list,
172 GtkSelectionData *selection_data)
174 RbTreeDragSourceIface *iface = RB_TREE_DRAG_SOURCE_GET_IFACE (drag_source);
176 g_return_val_if_fail (RB_IS_TREE_DRAG_SOURCE (drag_source), FALSE);
177 g_return_val_if_fail (iface->drag_data_get != NULL, FALSE);
178 g_return_val_if_fail (path_list != NULL, FALSE);
179 g_return_val_if_fail (selection_data != NULL, FALSE);
181 return (* iface->drag_data_get) (drag_source, path_list, selection_data);
186 GType
187 rb_tree_drag_dest_get_type (void)
189 static GType our_type = 0;
191 if (!our_type)
193 static const GTypeInfo our_info =
195 sizeof (RbTreeDragDestIface), /* class_size */
196 NULL, /* base_init */
197 NULL, /* base_finalize */
198 NULL,
199 NULL, /* class_finalize */
200 NULL, /* class_data */
202 0, /* n_preallocs */
203 NULL
206 our_type = g_type_register_static (G_TYPE_INTERFACE, "RbTreeDragDest", &our_info, 0);
209 return our_type;
214 gboolean
215 rb_tree_drag_dest_drag_data_received (RbTreeDragDest *drag_dest,
216 GtkTreePath *dest,
217 GtkTreeViewDropPosition pos,
218 GtkSelectionData *selection_data)
220 RbTreeDragDestIface *iface = RB_TREE_DRAG_DEST_GET_IFACE (drag_dest);
222 g_return_val_if_fail (RB_IS_TREE_DRAG_DEST (drag_dest), FALSE);
223 g_return_val_if_fail (iface->drag_data_received != NULL, FALSE);
224 g_return_val_if_fail (selection_data != NULL, FALSE);
226 return (* iface->drag_data_received) (drag_dest, dest, pos, selection_data);
231 gboolean
232 rb_tree_drag_dest_row_drop_possible (RbTreeDragDest *drag_dest,
233 GtkTreePath *dest_path,
234 GtkTreeViewDropPosition pos,
235 GtkSelectionData *selection_data)
237 RbTreeDragDestIface *iface = RB_TREE_DRAG_DEST_GET_IFACE (drag_dest);
239 g_return_val_if_fail (RB_IS_TREE_DRAG_DEST (drag_dest), FALSE);
240 g_return_val_if_fail (iface->drag_data_received != NULL, FALSE);
241 g_return_val_if_fail (selection_data != NULL, FALSE);
243 return (* iface->row_drop_possible) (drag_dest, dest_path, pos, selection_data);
247 gboolean
248 rb_tree_drag_dest_row_drop_position (RbTreeDragDest *drag_dest,
249 GtkTreePath *dest_path,
250 GList *targets,
251 GtkTreeViewDropPosition *pos)
253 RbTreeDragDestIface *iface = RB_TREE_DRAG_DEST_GET_IFACE (drag_dest);
255 g_return_val_if_fail (RB_IS_TREE_DRAG_DEST (drag_dest), FALSE);
256 g_return_val_if_fail (iface->row_drop_position != NULL, FALSE);
257 g_return_val_if_fail (targets != NULL, FALSE);
258 g_return_val_if_fail (pos != NULL, FALSE);
260 return (* iface->row_drop_position) (drag_dest, dest_path, targets, pos);
263 RbTreeDndData *
264 init_rb_tree_dnd_data (GtkWidget *widget)
266 RbTreeDndData *priv_data;
268 priv_data = g_object_get_data (G_OBJECT (widget), RB_TREE_DND_STRING);
269 if (priv_data == NULL)
271 priv_data = g_new0 (RbTreeDndData, 1);
272 priv_data->pending_event = FALSE;
273 g_object_set_data (G_OBJECT (widget), RB_TREE_DND_STRING, priv_data);
274 priv_data->drag_motion_handler = 0;
275 priv_data->drag_leave_handler = 0;
276 priv_data->button_press_event_handler = 0;
277 priv_data->scroll_timeout = 0;
278 priv_data->previous_dest_path = NULL;
279 priv_data->select_on_drag_timeout = 0;
282 return priv_data;
285 static void
286 stop_drag_check (GtkWidget *widget)
288 RbTreeDndData *priv_data;
289 GSList *l;
291 priv_data = g_object_get_data (G_OBJECT (widget), RB_TREE_DND_STRING);
293 for (l = priv_data->event_list; l != NULL; l = l->next)
294 gdk_event_free (l->data);
296 g_slist_free (priv_data->event_list);
297 priv_data->event_list = NULL;
298 priv_data->pending_event = FALSE;
299 g_signal_handler_disconnect (widget, priv_data->motion_notify_handler);
300 g_signal_handler_disconnect (widget, priv_data->button_release_handler);
304 static gboolean
305 rb_tree_dnd_button_release_event_cb (GtkWidget *widget,
306 GdkEventButton *event,
307 gpointer data)
309 RbTreeDndData *priv_data;
310 GSList *l;
312 priv_data = g_object_get_data (G_OBJECT (widget), RB_TREE_DND_STRING);
314 for (l = priv_data->event_list; l != NULL; l = l->next)
315 gtk_propagate_event (widget, l->data);
317 stop_drag_check (widget);
319 return FALSE;
323 static void
324 selection_foreach (GtkTreeModel *model,
325 GtkTreePath *path,
326 GtkTreeIter *iter,
327 gpointer data)
329 GList **list_ptr;
331 list_ptr = (GList **) data;
332 *list_ptr = g_list_prepend (*list_ptr, gtk_tree_row_reference_new (model, path));
336 static void
337 path_list_free (GList *path_list)
339 g_list_foreach (path_list, (GFunc) gtk_tree_row_reference_free, NULL);
340 g_list_free (path_list);
343 static void
344 set_context_data (GdkDragContext *context,
345 GList *path_list)
347 g_object_set_data_full (G_OBJECT (context),
348 "rb-tree-view-multi-source-row",
349 path_list,
350 (GDestroyNotify) path_list_free);
352 rb_debug ("Setting path_list: index=%i", gtk_tree_path_get_indices(path_list->data)[0]);
355 GList *
356 get_context_data (GdkDragContext *context)
358 return g_object_get_data (G_OBJECT (context), "rb-tree-view-multi-source-row");
361 gboolean
362 filter_drop_position (GtkWidget *widget, GdkDragContext *context, GtkTreePath *path, GtkTreeViewDropPosition *pos)
364 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
365 GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
366 RbTreeDndData *priv_data = g_object_get_data (G_OBJECT (widget), RB_TREE_DND_STRING);
367 gboolean ret;
369 if (!(priv_data->dest_flags & RB_TREE_DEST_CAN_DROP_INTO)) {
370 if (*pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)
371 *pos = GTK_TREE_VIEW_DROP_BEFORE;
372 else if (*pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER)
373 *pos = GTK_TREE_VIEW_DROP_AFTER;
374 } else if (!(priv_data->dest_flags & RB_TREE_DEST_CAN_DROP_BETWEEN)) {
375 if (*pos == GTK_TREE_VIEW_DROP_BEFORE)
376 *pos = GTK_TREE_VIEW_DROP_INTO_OR_BEFORE;
377 else if (*pos == GTK_TREE_VIEW_DROP_AFTER)
378 *pos = GTK_TREE_VIEW_DROP_INTO_OR_AFTER;
381 ret = rb_tree_drag_dest_row_drop_position (RB_TREE_DRAG_DEST (model),
382 path,
383 context->targets,
384 pos);
386 rb_debug ("filtered drop position: %s", ret ? "TRUE" : "FALSE");
387 return ret;
391 /* Scroll function taken/adapted from gtktreeview.c */
392 static gint
393 scroll_row_timeout (gpointer data)
395 GtkTreeView *tree_view = data;
396 GdkRectangle visible_rect;
397 gint y, x;
398 gint offset;
399 gfloat value;
400 GtkAdjustment* vadj;
401 RbTreeDndData *priv_data;
403 GDK_THREADS_ENTER ();
405 priv_data = g_object_get_data (G_OBJECT (tree_view), RB_TREE_DND_STRING);
406 g_return_val_if_fail(priv_data != NULL, TRUE);
408 gdk_window_get_pointer (gtk_tree_view_get_bin_window (tree_view), &x, &y, NULL);
409 gtk_tree_view_widget_to_tree_coords (tree_view, x, y, &x, &y);
410 gtk_tree_view_get_visible_rect (tree_view, &visible_rect);
412 /* see if we are near the edge. */
413 if (x < visible_rect.x && x > visible_rect.x + visible_rect.width)
415 GDK_THREADS_LEAVE ();
416 priv_data->scroll_timeout = 0;
417 return FALSE;
420 offset = y - (visible_rect.y + 2 * SCROLL_EDGE_SIZE);
421 if (offset > 0)
423 offset = y - (visible_rect.y + visible_rect.height - 2 * SCROLL_EDGE_SIZE);
424 if (offset < 0)
426 GDK_THREADS_LEAVE ();
427 priv_data->scroll_timeout = 0;
428 return FALSE;
432 vadj = gtk_tree_view_get_vadjustment (tree_view);
433 value = CLAMP (vadj->value + offset, vadj->lower, vadj->upper - vadj->page_size);
434 gtk_adjustment_set_value (vadj, value);
436 remove_select_on_drag_timeout(tree_view);
438 GDK_THREADS_LEAVE ();
440 return TRUE;
444 static gboolean
445 select_on_drag_timeout (gpointer data)
447 GtkTreeView *tree_view = data;
448 GtkTreeSelection *selection;
449 RbTreeDndData *priv_data;
451 GDK_THREADS_ENTER ();
453 priv_data = g_object_get_data (G_OBJECT (tree_view), RB_TREE_DND_STRING);
454 g_return_val_if_fail(priv_data != NULL, FALSE);
455 g_return_val_if_fail(priv_data->previous_dest_path != NULL, FALSE);
457 selection = gtk_tree_view_get_selection(tree_view);
458 if (!gtk_tree_selection_path_is_selected(selection,priv_data->previous_dest_path)) {
459 rb_debug("Changing selection because of drag timeout");
460 gtk_tree_view_set_cursor(tree_view,priv_data->previous_dest_path,NULL,FALSE);
463 priv_data->select_on_drag_timeout = 0;
464 gtk_tree_path_free(priv_data->previous_dest_path);
465 priv_data->previous_dest_path = NULL;
467 GDK_THREADS_LEAVE ();
468 return FALSE;
472 static void
473 remove_scroll_timeout (GtkTreeView *tree_view)
475 RbTreeDndData *priv_data;
477 priv_data = g_object_get_data (G_OBJECT (tree_view), RB_TREE_DND_STRING);
478 g_return_if_fail(priv_data != NULL);
480 if (priv_data->scroll_timeout != 0)
482 g_source_remove (priv_data->scroll_timeout);
483 priv_data->scroll_timeout = 0;
488 static void
489 remove_select_on_drag_timeout (GtkTreeView *tree_view)
491 RbTreeDndData *priv_data;
493 priv_data = g_object_get_data (G_OBJECT (tree_view), RB_TREE_DND_STRING);
494 g_return_if_fail(priv_data != NULL);
496 if (priv_data->select_on_drag_timeout != 0) {
497 rb_debug("Removing the select on drag timeout");
498 g_source_remove (priv_data->select_on_drag_timeout);
499 priv_data->select_on_drag_timeout = 0;
501 if (priv_data->previous_dest_path != NULL) {
502 gtk_tree_path_free(priv_data->previous_dest_path);
503 priv_data->previous_dest_path = NULL;
508 static void
509 rb_tree_dnd_drag_data_delete_cb (GtkWidget *widget,
510 GdkDragContext *drag_context,
511 gpointer user_data)
513 GList *path_list;
514 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW(widget));
516 path_list = get_context_data (drag_context);
517 rb_tree_drag_source_drag_data_delete (RB_TREE_DRAG_SOURCE (model),
518 path_list);
520 g_signal_stop_emission_by_name (widget, "drag_data_delete");
525 static void
526 rb_tree_dnd_drag_data_get_cb (GtkWidget *widget,
527 GdkDragContext *context,
528 GtkSelectionData *selection_data,
529 guint info,
530 guint time)
532 GtkTreeView *tree_view;
533 GtkTreeModel *model;
534 GList *path_list;
536 tree_view = GTK_TREE_VIEW (widget);
537 model = gtk_tree_view_get_model (tree_view);
539 if (model == NULL)
540 return;
542 path_list = get_context_data (context);
544 if (path_list == NULL)
545 return;
547 /* We can implement the GTK_TREE_MODEL_ROW target generically for
548 * any model; for DragSource models there are some other targets
549 * we also support.
551 if (RB_IS_TREE_DRAG_SOURCE (model))
553 rb_tree_drag_source_drag_data_get (RB_TREE_DRAG_SOURCE (model),
554 path_list,
555 selection_data);
560 static gboolean
561 rb_tree_dnd_motion_notify_event_cb (GtkWidget *widget,
562 GdkEventMotion *event,
563 gpointer data)
565 RbTreeDndData *priv_data;
567 priv_data = g_object_get_data (G_OBJECT (widget), RB_TREE_DND_STRING);
569 if (gtk_drag_check_threshold (widget,
570 priv_data->x,
571 priv_data->y,
572 event->x,
573 event->y))
575 GList *path_list = NULL;
576 GtkTreeSelection *selection;
577 GtkTreeModel *model;
578 GdkDragContext *context;
580 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
581 stop_drag_check (widget);
582 gtk_tree_selection_selected_foreach (selection, selection_foreach, &path_list);
583 path_list = g_list_reverse (path_list);
584 model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
586 if (rb_tree_drag_source_row_draggable (RB_TREE_DRAG_SOURCE (model), path_list))
588 rb_debug ("drag begin");
589 context = gtk_drag_begin (widget,
590 priv_data->source_target_list,
591 priv_data->source_actions,
592 priv_data->pressed_button,
593 (GdkEvent*)event);
594 set_context_data (context, path_list);
595 gtk_drag_set_icon_default (context);
597 } else {
598 path_list_free (path_list);
602 return TRUE;
605 static gboolean
606 rb_tree_dnd_drag_motion_cb (GtkWidget *widget,
607 GdkDragContext *context,
608 gint x,
609 gint y,
610 guint time)
612 GtkTreeView *tree_view;
613 GtkTreePath *path = NULL;
614 GtkTreeModel *model;
615 GtkTreeViewDropPosition pos;
616 RbTreeDndData *priv_data;
617 GdkDragAction action;
619 rb_debug ("drag and drop motion: (%i,%i)", x, y);
621 tree_view = GTK_TREE_VIEW (widget);
622 model = gtk_tree_view_get_model (tree_view);
624 priv_data = g_object_get_data (G_OBJECT (widget), RB_TREE_DND_STRING);
626 gtk_tree_view_get_dest_row_at_pos (tree_view, x, y, &path, &pos);
628 if ((priv_data->previous_dest_path == NULL)
629 || (path == NULL)
630 || gtk_tree_path_compare(path,priv_data->previous_dest_path)) {
631 remove_select_on_drag_timeout(tree_view);
634 if (path == NULL)
636 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget),
637 NULL,
638 GTK_TREE_VIEW_DROP_BEFORE);
640 if (!(priv_data->dest_flags & RB_TREE_DEST_EMPTY_VIEW_DROP)) {
641 /* Can't drop here. */
642 gdk_drag_status (context, 0, time);
644 return TRUE;
645 } else if (!filter_drop_position (widget, context, path, &pos)) {
646 gdk_drag_status (context, 0, time);
647 return TRUE;
650 else
652 if (!filter_drop_position (widget, context, path, &pos)) {
653 gdk_drag_status (context, 0, time);
654 return TRUE;
657 if (priv_data->scroll_timeout == 0)
659 priv_data->scroll_timeout = g_timeout_add (150, scroll_row_timeout, tree_view);
663 if (GTK_WIDGET (tree_view) == gtk_drag_get_source_widget (context) &&
664 context->actions & GDK_ACTION_MOVE)
665 action = GDK_ACTION_MOVE;
666 else
667 action = context->suggested_action;
669 if (path) {
670 gtk_tree_view_set_drag_dest_row (tree_view, path, pos);
671 if (priv_data->dest_flags & RB_TREE_DEST_SELECT_ON_DRAG_TIMEOUT) {
672 if (priv_data->previous_dest_path != NULL) {
673 gtk_tree_path_free (priv_data->previous_dest_path);
675 priv_data->previous_dest_path = path;
676 if (priv_data->select_on_drag_timeout == 0) {
677 rb_debug("Setting up a new select on drag timeout");
678 priv_data->select_on_drag_timeout = g_timeout_add (2000, select_on_drag_timeout, tree_view);
680 } else {
681 gtk_tree_path_free (path);
685 gdk_drag_status (context, action, time);
687 return TRUE;
691 static gboolean
692 rb_tree_dnd_drag_leave_cb (GtkWidget *widget,
693 GdkDragContext *context,
694 gint x,
695 gint y,
696 guint time)
698 remove_select_on_drag_timeout(GTK_TREE_VIEW (widget));
699 return TRUE;
702 static gboolean
703 rb_tree_dnd_drag_drop_cb (GtkWidget *widget,
704 GdkDragContext *context,
705 gint x,
706 gint y,
707 guint time)
709 GtkTreeView *tree_view;
710 GtkTreePath *path;
711 GtkTreeModel *model;
712 GtkTreeViewDropPosition pos;
713 RbTreeDndData *priv_data;
715 tree_view = GTK_TREE_VIEW (widget);
716 model = gtk_tree_view_get_model (tree_view);
717 priv_data = g_object_get_data (G_OBJECT (widget), RB_TREE_DND_STRING);
718 gtk_tree_view_get_dest_row_at_pos (tree_view, x, y, &path, &pos);
720 remove_scroll_timeout (tree_view);
722 /* Unset this thing */
723 gtk_tree_view_set_drag_dest_row (tree_view,
724 NULL,
725 GTK_TREE_VIEW_DROP_BEFORE);
727 if (path || priv_data->dest_flags & RB_TREE_DEST_EMPTY_VIEW_DROP) {
729 GdkAtom target;
730 RbTreeDragDestIface *iface = RB_TREE_DRAG_DEST_GET_IFACE (model);
731 if (iface->get_drag_target) {
732 RbTreeDragDest *dest = RB_TREE_DRAG_DEST (model);
733 target = (* iface->get_drag_target) (dest, widget,
734 context, path,
735 priv_data->dest_target_list);
736 } else {
737 target = gtk_drag_dest_find_target (widget, context,
738 priv_data->dest_target_list);
741 if (path)
742 gtk_tree_path_free (path);
744 if (target != GDK_NONE) {
745 gtk_drag_get_data (widget, context, target, time);
746 return TRUE;
750 return FALSE;
754 static void
755 rb_tree_dnd_drag_data_received_cb (GtkWidget *widget,
756 GdkDragContext *context,
757 gint x,
758 gint y,
759 GtkSelectionData *selection_data,
760 guint info,
761 guint time)
763 GtkTreeView *tree_view;
764 GtkTreeModel *model;
765 GtkTreePath *dest_row;
766 GtkTreeViewDropPosition pos;
767 gboolean filtered = TRUE;
768 gboolean accepted = FALSE;
770 tree_view = GTK_TREE_VIEW (widget);
771 model = gtk_tree_view_get_model (tree_view);
773 gtk_tree_view_get_dest_row_at_pos (tree_view, x, y, &dest_row, &pos);
775 if (dest_row)
776 if (!filter_drop_position (widget, context, dest_row, &pos))
777 filtered = FALSE;
779 if (filtered && selection_data->length >= 0)
781 if (rb_tree_drag_dest_drag_data_received (RB_TREE_DRAG_DEST (model),
782 dest_row,
783 pos,
784 selection_data))
785 accepted = TRUE;
788 gtk_drag_finish (context,
789 accepted,
790 (context->action == GDK_ACTION_MOVE),
791 time);
793 if (dest_row)
794 gtk_tree_path_free (dest_row);
796 g_signal_stop_emission_by_name (widget, "drag_data_received");
801 static gboolean
802 rb_tree_dnd_button_press_event_cb (GtkWidget *widget,
803 GdkEventButton *event,
804 gpointer data)
806 GtkTreeView *tree_view;
807 GtkTreePath *path = NULL;
808 GtkTreeViewColumn *column = NULL;
809 gint cell_x, cell_y;
810 GtkTreeSelection *selection;
811 RbTreeDndData *priv_data;
813 if (event->button == 3)
814 return FALSE;
816 tree_view = GTK_TREE_VIEW (widget);
817 if (event->window != gtk_tree_view_get_bin_window (tree_view))
818 return FALSE;
820 priv_data = g_object_get_data (G_OBJECT (tree_view), RB_TREE_DND_STRING);
821 if (priv_data == NULL)
823 priv_data = g_new0 (RbTreeDndData, 1);
824 priv_data->pending_event = FALSE;
825 g_object_set_data (G_OBJECT (tree_view), RB_TREE_DND_STRING, priv_data);
828 if (g_slist_find (priv_data->event_list, event))
829 return FALSE;
831 if (priv_data->pending_event)
833 /* save the event to be propagated in order */
834 priv_data->event_list = g_slist_append (priv_data->event_list, gdk_event_copy ((GdkEvent*)event));
835 return TRUE;
838 if (event->type == GDK_2BUTTON_PRESS)
839 return FALSE;
841 gtk_tree_view_get_path_at_pos (tree_view,
842 event->x, event->y,
843 &path, &column,
844 &cell_x, &cell_y);
846 selection = gtk_tree_view_get_selection (tree_view);
848 if (path)
850 gboolean call_parent = (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ||
851 !gtk_tree_selection_path_is_selected (selection, path) ||
852 event->button != 1);
854 if (call_parent)
855 (GTK_WIDGET_GET_CLASS (tree_view))->button_press_event (widget, event);
857 if (gtk_tree_selection_path_is_selected (selection, path))
859 priv_data->pressed_button = event->button;
860 priv_data->x = event->x;
861 priv_data->y = event->y;
863 priv_data->pending_event = TRUE;
865 if (!call_parent)
866 priv_data->event_list = g_slist_append (priv_data->event_list,
867 gdk_event_copy ((GdkEvent*)event));
869 priv_data->motion_notify_handler = g_signal_connect (G_OBJECT (tree_view),
870 "motion_notify_event",
871 G_CALLBACK (rb_tree_dnd_motion_notify_event_cb),
872 NULL);
873 priv_data->button_release_handler = g_signal_connect (G_OBJECT (tree_view),
874 "button_release_event",
875 G_CALLBACK (rb_tree_dnd_button_release_event_cb),
876 NULL);
880 gtk_tree_path_free (path);
881 /* We called the default handler so we don't let the default handler run */
882 return TRUE;
885 return FALSE;
889 void
890 rb_tree_dnd_add_drag_source_support (GtkTreeView *tree_view,
891 GdkModifierType start_button_mask,
892 const GtkTargetEntry *targets,
893 gint n_targets,
894 GdkDragAction actions)
896 RbTreeDndData *priv_data = NULL;
897 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
899 priv_data = init_rb_tree_dnd_data (GTK_WIDGET(tree_view));
901 if (!priv_data->button_press_event_handler) {
903 priv_data->source_target_list = gtk_target_list_new (targets, n_targets);
904 priv_data->source_actions = actions;
905 priv_data->start_button_mask = start_button_mask;
907 priv_data->button_press_event_handler = g_signal_connect (G_OBJECT (tree_view),
908 "button_press_event",
909 G_CALLBACK (rb_tree_dnd_button_press_event_cb),
910 NULL);
911 priv_data->drag_data_get_handler = g_signal_connect (G_OBJECT (tree_view),
912 "drag_data_get",
913 G_CALLBACK (rb_tree_dnd_drag_data_get_cb),
914 NULL);
915 priv_data->drag_data_delete_handler = g_signal_connect (G_OBJECT (tree_view),
916 "drag_data_delete",
917 G_CALLBACK (rb_tree_dnd_drag_data_delete_cb),
918 NULL);
926 void
927 rb_tree_dnd_add_drag_dest_support (GtkTreeView *tree_view,
928 RbTreeDestFlag flags,
929 const GtkTargetEntry *targets,
930 gint n_targets,
931 GdkDragAction actions)
933 RbTreeDndData *priv_data = NULL;
934 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
936 priv_data = init_rb_tree_dnd_data (GTK_WIDGET(tree_view));
938 if (!priv_data->drag_motion_handler) {
940 priv_data->dest_target_list = gtk_target_list_new (targets, n_targets);
941 priv_data->dest_actions = actions;
942 priv_data->dest_flags = flags;
944 gtk_drag_dest_set (GTK_WIDGET (tree_view),
946 NULL,
948 actions);
950 priv_data->drag_motion_handler = g_signal_connect (G_OBJECT (tree_view),
951 "drag_motion",
952 G_CALLBACK (rb_tree_dnd_drag_motion_cb),
953 NULL);
954 priv_data->drag_leave_handler = g_signal_connect (G_OBJECT (tree_view),
955 "drag_leave",
956 G_CALLBACK (rb_tree_dnd_drag_leave_cb),
957 NULL);
958 priv_data->drag_drop_handler = g_signal_connect (G_OBJECT (tree_view),
959 "drag_drop",
960 G_CALLBACK (rb_tree_dnd_drag_drop_cb),
961 NULL);
962 priv_data->drag_data_received_handler = g_signal_connect (G_OBJECT (tree_view),
963 "drag_data_received",
964 G_CALLBACK (rb_tree_dnd_drag_data_received_cb), NULL);