udapted vi.po
[rhythmbox.git] / lib / rb-tree-dnd.c
blob0d1e20e2186265aafe0f9fc9ae4c36b98276bc9d
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->rb_row_draggable != NULL, FALSE);
121 g_return_val_if_fail (path_list != NULL, FALSE);
123 if (iface->rb_row_draggable)
124 return (* iface->rb_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->rb_drag_data_delete != NULL, FALSE);
151 g_return_val_if_fail (path_list != NULL, FALSE);
153 return (* iface->rb_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->rb_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->rb_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->rb_drag_data_received != NULL, FALSE);
224 g_return_val_if_fail (selection_data != NULL, FALSE);
226 return (* iface->rb_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->rb_row_drop_possible != NULL, FALSE);
241 g_return_val_if_fail (selection_data != NULL, FALSE);
243 return (* iface->rb_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->rb_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->rb_row_drop_position) (drag_dest, dest_path, targets, pos);
263 static void
264 rb_tree_dnd_data_free (gpointer data)
266 RbTreeDndData *priv_data = data;
268 gtk_target_list_unref (priv_data->source_target_list);
270 g_free (priv_data);
273 RbTreeDndData *
274 init_rb_tree_dnd_data (GtkWidget *widget)
276 RbTreeDndData *priv_data;
278 priv_data = g_object_get_data (G_OBJECT (widget), RB_TREE_DND_STRING);
279 if (priv_data == NULL)
281 priv_data = g_new0 (RbTreeDndData, 1);
282 priv_data->pending_event = FALSE;
283 g_object_set_data_full (G_OBJECT (widget), RB_TREE_DND_STRING, priv_data, rb_tree_dnd_data_free);
284 priv_data->drag_motion_handler = 0;
285 priv_data->drag_leave_handler = 0;
286 priv_data->button_press_event_handler = 0;
287 priv_data->scroll_timeout = 0;
288 priv_data->previous_dest_path = NULL;
289 priv_data->select_on_drag_timeout = 0;
292 return priv_data;
295 static void
296 stop_drag_check (GtkWidget *widget)
298 RbTreeDndData *priv_data;
299 GSList *l;
301 priv_data = g_object_get_data (G_OBJECT (widget), RB_TREE_DND_STRING);
303 for (l = priv_data->event_list; l != NULL; l = l->next)
304 gdk_event_free (l->data);
306 g_slist_free (priv_data->event_list);
307 priv_data->event_list = NULL;
308 priv_data->pending_event = FALSE;
309 g_signal_handler_disconnect (widget, priv_data->motion_notify_handler);
310 g_signal_handler_disconnect (widget, priv_data->button_release_handler);
314 static gboolean
315 rb_tree_dnd_button_release_event_cb (GtkWidget *widget,
316 GdkEventButton *event,
317 gpointer data)
319 RbTreeDndData *priv_data;
320 GSList *l;
322 priv_data = g_object_get_data (G_OBJECT (widget), RB_TREE_DND_STRING);
324 for (l = priv_data->event_list; l != NULL; l = l->next)
325 gtk_propagate_event (widget, l->data);
327 stop_drag_check (widget);
329 return FALSE;
333 static void
334 selection_foreach (GtkTreeModel *model,
335 GtkTreePath *path,
336 GtkTreeIter *iter,
337 gpointer data)
339 GList **list_ptr;
341 list_ptr = (GList **) data;
342 *list_ptr = g_list_prepend (*list_ptr, gtk_tree_row_reference_new (model, path));
346 static void
347 path_list_free (GList *path_list)
349 g_list_foreach (path_list, (GFunc) gtk_tree_row_reference_free, NULL);
350 g_list_free (path_list);
353 static void
354 set_context_data (GdkDragContext *context,
355 GList *path_list)
357 g_object_set_data_full (G_OBJECT (context),
358 "rb-tree-view-multi-source-row",
359 path_list,
360 (GDestroyNotify) path_list_free);
362 rb_debug ("Setting path_list: index=%i", gtk_tree_path_get_indices(path_list->data)[0]);
365 GList *
366 get_context_data (GdkDragContext *context)
368 return g_object_get_data (G_OBJECT (context), "rb-tree-view-multi-source-row");
371 gboolean
372 filter_drop_position (GtkWidget *widget, GdkDragContext *context, GtkTreePath *path, GtkTreeViewDropPosition *pos)
374 GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
375 GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
376 RbTreeDndData *priv_data = g_object_get_data (G_OBJECT (widget), RB_TREE_DND_STRING);
377 gboolean ret;
379 if (!(priv_data->dest_flags & RB_TREE_DEST_CAN_DROP_INTO)) {
380 if (*pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)
381 *pos = GTK_TREE_VIEW_DROP_BEFORE;
382 else if (*pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER)
383 *pos = GTK_TREE_VIEW_DROP_AFTER;
384 } else if (!(priv_data->dest_flags & RB_TREE_DEST_CAN_DROP_BETWEEN)) {
385 if (*pos == GTK_TREE_VIEW_DROP_BEFORE)
386 *pos = GTK_TREE_VIEW_DROP_INTO_OR_BEFORE;
387 else if (*pos == GTK_TREE_VIEW_DROP_AFTER)
388 *pos = GTK_TREE_VIEW_DROP_INTO_OR_AFTER;
391 ret = rb_tree_drag_dest_row_drop_position (RB_TREE_DRAG_DEST (model),
392 path,
393 context->targets,
394 pos);
396 rb_debug ("filtered drop position: %s", ret ? "TRUE" : "FALSE");
397 return ret;
401 /* Scroll function taken/adapted from gtktreeview.c */
402 static gint
403 scroll_row_timeout (gpointer data)
405 GtkTreeView *tree_view = data;
406 GdkRectangle visible_rect;
407 gint y, x;
408 gint offset;
409 gfloat value;
410 GtkAdjustment* vadj;
411 RbTreeDndData *priv_data;
413 GDK_THREADS_ENTER ();
415 priv_data = g_object_get_data (G_OBJECT (tree_view), RB_TREE_DND_STRING);
416 g_return_val_if_fail(priv_data != NULL, TRUE);
418 gdk_window_get_pointer (gtk_tree_view_get_bin_window (tree_view), &x, &y, NULL);
419 gtk_tree_view_widget_to_tree_coords (tree_view, x, y, &x, &y);
420 gtk_tree_view_get_visible_rect (tree_view, &visible_rect);
422 /* see if we are near the edge. */
423 if (x < visible_rect.x && x > visible_rect.x + visible_rect.width)
425 GDK_THREADS_LEAVE ();
426 priv_data->scroll_timeout = 0;
427 return FALSE;
430 offset = y - (visible_rect.y + 2 * SCROLL_EDGE_SIZE);
431 if (offset > 0)
433 offset = y - (visible_rect.y + visible_rect.height - 2 * SCROLL_EDGE_SIZE);
434 if (offset < 0)
436 GDK_THREADS_LEAVE ();
437 priv_data->scroll_timeout = 0;
438 return FALSE;
442 vadj = gtk_tree_view_get_vadjustment (tree_view);
443 value = CLAMP (vadj->value + offset, vadj->lower, vadj->upper - vadj->page_size);
444 gtk_adjustment_set_value (vadj, value);
446 /* don't remove it if we're on the edge and not scrolling */
447 if (ABS (vadj->value - value) > 0.0001)
448 remove_select_on_drag_timeout(tree_view);
450 GDK_THREADS_LEAVE ();
452 return TRUE;
456 static gboolean
457 select_on_drag_timeout (gpointer data)
459 GtkTreeView *tree_view = data;
460 GtkTreeSelection *selection;
461 RbTreeDndData *priv_data;
463 GDK_THREADS_ENTER ();
465 priv_data = g_object_get_data (G_OBJECT (tree_view), RB_TREE_DND_STRING);
466 g_return_val_if_fail(priv_data != NULL, FALSE);
467 g_return_val_if_fail(priv_data->previous_dest_path != NULL, FALSE);
469 selection = gtk_tree_view_get_selection(tree_view);
470 if (!gtk_tree_selection_path_is_selected(selection,priv_data->previous_dest_path)) {
471 rb_debug("Changing selection because of drag timeout");
472 gtk_tree_view_set_cursor(tree_view,priv_data->previous_dest_path,NULL,FALSE);
475 priv_data->select_on_drag_timeout = 0;
476 gtk_tree_path_free(priv_data->previous_dest_path);
477 priv_data->previous_dest_path = NULL;
479 GDK_THREADS_LEAVE ();
480 return FALSE;
484 static void
485 remove_scroll_timeout (GtkTreeView *tree_view)
487 RbTreeDndData *priv_data;
489 priv_data = g_object_get_data (G_OBJECT (tree_view), RB_TREE_DND_STRING);
490 g_return_if_fail(priv_data != NULL);
492 if (priv_data->scroll_timeout != 0)
494 g_source_remove (priv_data->scroll_timeout);
495 priv_data->scroll_timeout = 0;
500 static void
501 remove_select_on_drag_timeout (GtkTreeView *tree_view)
503 RbTreeDndData *priv_data;
505 priv_data = g_object_get_data (G_OBJECT (tree_view), RB_TREE_DND_STRING);
506 g_return_if_fail(priv_data != NULL);
508 if (priv_data->select_on_drag_timeout != 0) {
509 rb_debug("Removing the select on drag timeout");
510 g_source_remove (priv_data->select_on_drag_timeout);
511 priv_data->select_on_drag_timeout = 0;
513 if (priv_data->previous_dest_path != NULL) {
514 gtk_tree_path_free(priv_data->previous_dest_path);
515 priv_data->previous_dest_path = NULL;
520 static void
521 rb_tree_dnd_drag_data_delete_cb (GtkWidget *widget,
522 GdkDragContext *drag_context,
523 gpointer user_data)
525 GList *path_list;
526 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW(widget));
528 path_list = get_context_data (drag_context);
529 rb_tree_drag_source_drag_data_delete (RB_TREE_DRAG_SOURCE (model),
530 path_list);
532 g_signal_stop_emission_by_name (widget, "drag_data_delete");
537 static void
538 rb_tree_dnd_drag_data_get_cb (GtkWidget *widget,
539 GdkDragContext *context,
540 GtkSelectionData *selection_data,
541 guint info,
542 guint time)
544 GtkTreeView *tree_view;
545 GtkTreeModel *model;
546 GList *path_list;
548 tree_view = GTK_TREE_VIEW (widget);
549 model = gtk_tree_view_get_model (tree_view);
551 if (model == NULL)
552 return;
554 path_list = get_context_data (context);
556 if (path_list == NULL)
557 return;
559 /* We can implement the GTK_TREE_MODEL_ROW target generically for
560 * any model; for DragSource models there are some other targets
561 * we also support.
563 if (RB_IS_TREE_DRAG_SOURCE (model))
565 rb_tree_drag_source_drag_data_get (RB_TREE_DRAG_SOURCE (model),
566 path_list,
567 selection_data);
572 static gboolean
573 rb_tree_dnd_motion_notify_event_cb (GtkWidget *widget,
574 GdkEventMotion *event,
575 gpointer data)
577 RbTreeDndData *priv_data;
579 priv_data = g_object_get_data (G_OBJECT (widget), RB_TREE_DND_STRING);
581 if (gtk_drag_check_threshold (widget,
582 priv_data->x,
583 priv_data->y,
584 event->x,
585 event->y))
587 GList *path_list = NULL;
588 GtkTreeSelection *selection;
589 GtkTreeModel *model;
590 GdkDragContext *context;
592 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
593 stop_drag_check (widget);
594 gtk_tree_selection_selected_foreach (selection, selection_foreach, &path_list);
595 path_list = g_list_reverse (path_list);
596 model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
598 if (rb_tree_drag_source_row_draggable (RB_TREE_DRAG_SOURCE (model), path_list))
600 rb_debug ("drag begin");
601 context = gtk_drag_begin (widget,
602 priv_data->source_target_list,
603 priv_data->source_actions,
604 priv_data->pressed_button,
605 (GdkEvent*)event);
606 set_context_data (context, path_list);
607 gtk_drag_set_icon_default (context);
609 } else {
610 path_list_free (path_list);
614 return TRUE;
617 static gboolean
618 rb_tree_dnd_drag_motion_cb (GtkWidget *widget,
619 GdkDragContext *context,
620 gint x,
621 gint y,
622 guint time)
624 GtkTreeView *tree_view;
625 GtkTreePath *path = NULL;
626 GtkTreeModel *model;
627 GtkTreeViewDropPosition pos;
628 RbTreeDndData *priv_data;
629 GdkDragAction action;
631 rb_debug ("drag and drop motion: (%i,%i)", x, y);
633 tree_view = GTK_TREE_VIEW (widget);
634 model = gtk_tree_view_get_model (tree_view);
636 priv_data = g_object_get_data (G_OBJECT (widget), RB_TREE_DND_STRING);
638 gtk_tree_view_get_dest_row_at_pos (tree_view, x, y, &path, &pos);
640 if ((priv_data->previous_dest_path == NULL)
641 || (path == NULL)
642 || gtk_tree_path_compare(path,priv_data->previous_dest_path)) {
643 remove_select_on_drag_timeout(tree_view);
646 if (path == NULL)
648 gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget),
649 NULL,
650 GTK_TREE_VIEW_DROP_BEFORE);
652 if (!(priv_data->dest_flags & RB_TREE_DEST_EMPTY_VIEW_DROP)) {
653 /* Can't drop here. */
654 gdk_drag_status (context, 0, time);
656 return TRUE;
657 } else if (!filter_drop_position (widget, context, path, &pos)) {
658 gdk_drag_status (context, 0, time);
659 return TRUE;
662 else
664 if (!filter_drop_position (widget, context, path, &pos)) {
665 gdk_drag_status (context, 0, time);
666 return TRUE;
669 if (priv_data->scroll_timeout == 0)
671 priv_data->scroll_timeout = g_timeout_add (150, scroll_row_timeout, tree_view);
675 if (GTK_WIDGET (tree_view) == gtk_drag_get_source_widget (context) &&
676 context->actions & GDK_ACTION_MOVE)
677 action = GDK_ACTION_MOVE;
678 else
679 action = context->suggested_action;
681 if (path) {
682 gtk_tree_view_set_drag_dest_row (tree_view, path, pos);
683 if (priv_data->dest_flags & RB_TREE_DEST_SELECT_ON_DRAG_TIMEOUT) {
684 if (priv_data->previous_dest_path != NULL) {
685 gtk_tree_path_free (priv_data->previous_dest_path);
687 priv_data->previous_dest_path = path;
688 if (priv_data->select_on_drag_timeout == 0) {
689 rb_debug("Setting up a new select on drag timeout");
690 priv_data->select_on_drag_timeout = g_timeout_add (2000, select_on_drag_timeout, tree_view);
692 } else {
693 gtk_tree_path_free (path);
697 gdk_drag_status (context, action, time);
699 return TRUE;
703 static gboolean
704 rb_tree_dnd_drag_leave_cb (GtkWidget *widget,
705 GdkDragContext *context,
706 gint x,
707 gint y,
708 guint time)
710 remove_select_on_drag_timeout(GTK_TREE_VIEW (widget));
711 return TRUE;
714 static gboolean
715 rb_tree_dnd_drag_drop_cb (GtkWidget *widget,
716 GdkDragContext *context,
717 gint x,
718 gint y,
719 guint time)
721 GtkTreeView *tree_view;
722 GtkTreePath *path;
723 GtkTreeModel *model;
724 GtkTreeViewDropPosition pos;
725 RbTreeDndData *priv_data;
727 tree_view = GTK_TREE_VIEW (widget);
728 model = gtk_tree_view_get_model (tree_view);
729 priv_data = g_object_get_data (G_OBJECT (widget), RB_TREE_DND_STRING);
730 gtk_tree_view_get_dest_row_at_pos (tree_view, x, y, &path, &pos);
732 remove_scroll_timeout (tree_view);
734 /* Unset this thing */
735 gtk_tree_view_set_drag_dest_row (tree_view,
736 NULL,
737 GTK_TREE_VIEW_DROP_BEFORE);
739 if (path || priv_data->dest_flags & RB_TREE_DEST_EMPTY_VIEW_DROP) {
741 GdkAtom target;
742 RbTreeDragDestIface *iface = RB_TREE_DRAG_DEST_GET_IFACE (model);
743 if (iface->rb_get_drag_target) {
744 RbTreeDragDest *dest = RB_TREE_DRAG_DEST (model);
745 target = (* iface->rb_get_drag_target) (dest, widget,
746 context, path,
747 priv_data->dest_target_list);
748 } else {
749 target = gtk_drag_dest_find_target (widget, context,
750 priv_data->dest_target_list);
753 if (path)
754 gtk_tree_path_free (path);
756 if (target != GDK_NONE) {
757 gtk_drag_get_data (widget, context, target, time);
758 return TRUE;
762 return FALSE;
766 static void
767 rb_tree_dnd_drag_data_received_cb (GtkWidget *widget,
768 GdkDragContext *context,
769 gint x,
770 gint y,
771 GtkSelectionData *selection_data,
772 guint info,
773 guint time)
775 GtkTreeView *tree_view;
776 GtkTreeModel *model;
777 GtkTreePath *dest_row;
778 GtkTreeViewDropPosition pos;
779 gboolean filtered = TRUE;
780 gboolean accepted = FALSE;
782 tree_view = GTK_TREE_VIEW (widget);
783 model = gtk_tree_view_get_model (tree_view);
785 gtk_tree_view_get_dest_row_at_pos (tree_view, x, y, &dest_row, &pos);
787 if (dest_row)
788 if (!filter_drop_position (widget, context, dest_row, &pos))
789 filtered = FALSE;
791 if (filtered && selection_data->length >= 0)
793 if (rb_tree_drag_dest_drag_data_received (RB_TREE_DRAG_DEST (model),
794 dest_row,
795 pos,
796 selection_data))
797 accepted = TRUE;
800 gtk_drag_finish (context,
801 accepted,
802 (context->action == GDK_ACTION_MOVE),
803 time);
805 if (dest_row)
806 gtk_tree_path_free (dest_row);
808 g_signal_stop_emission_by_name (widget, "drag_data_received");
813 static gboolean
814 rb_tree_dnd_button_press_event_cb (GtkWidget *widget,
815 GdkEventButton *event,
816 gpointer data)
818 GtkTreeView *tree_view;
819 GtkTreePath *path = NULL;
820 GtkTreeViewColumn *column = NULL;
821 gint cell_x, cell_y;
822 GtkTreeSelection *selection;
823 RbTreeDndData *priv_data;
825 if (event->button == 3)
826 return FALSE;
828 tree_view = GTK_TREE_VIEW (widget);
829 if (event->window != gtk_tree_view_get_bin_window (tree_view))
830 return FALSE;
832 priv_data = g_object_get_data (G_OBJECT (tree_view), RB_TREE_DND_STRING);
833 if (priv_data == NULL)
835 priv_data = g_new0 (RbTreeDndData, 1);
836 priv_data->pending_event = FALSE;
837 g_object_set_data (G_OBJECT (tree_view), RB_TREE_DND_STRING, priv_data);
840 if (g_slist_find (priv_data->event_list, event))
841 return FALSE;
843 if (priv_data->pending_event)
845 /* save the event to be propagated in order */
846 priv_data->event_list = g_slist_append (priv_data->event_list, gdk_event_copy ((GdkEvent*)event));
847 return TRUE;
850 if (event->type == GDK_2BUTTON_PRESS)
851 return FALSE;
853 gtk_tree_view_get_path_at_pos (tree_view,
854 event->x, event->y,
855 &path, &column,
856 &cell_x, &cell_y);
858 selection = gtk_tree_view_get_selection (tree_view);
860 if (path)
862 gboolean call_parent = (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ||
863 !gtk_tree_selection_path_is_selected (selection, path) ||
864 event->button != 1);
866 if (call_parent)
867 (GTK_WIDGET_GET_CLASS (tree_view))->button_press_event (widget, event);
869 if (gtk_tree_selection_path_is_selected (selection, path))
871 priv_data->pressed_button = event->button;
872 priv_data->x = event->x;
873 priv_data->y = event->y;
875 priv_data->pending_event = TRUE;
877 if (!call_parent)
878 priv_data->event_list = g_slist_append (priv_data->event_list,
879 gdk_event_copy ((GdkEvent*)event));
881 priv_data->motion_notify_handler = g_signal_connect (G_OBJECT (tree_view),
882 "motion_notify_event",
883 G_CALLBACK (rb_tree_dnd_motion_notify_event_cb),
884 NULL);
885 priv_data->button_release_handler = g_signal_connect (G_OBJECT (tree_view),
886 "button_release_event",
887 G_CALLBACK (rb_tree_dnd_button_release_event_cb),
888 NULL);
892 gtk_tree_path_free (path);
893 /* We called the default handler so we don't let the default handler run */
894 return TRUE;
897 return FALSE;
901 void
902 rb_tree_dnd_add_drag_source_support (GtkTreeView *tree_view,
903 GdkModifierType start_button_mask,
904 const GtkTargetEntry *targets,
905 gint n_targets,
906 GdkDragAction actions)
908 RbTreeDndData *priv_data = NULL;
909 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
911 priv_data = init_rb_tree_dnd_data (GTK_WIDGET(tree_view));
913 if (!priv_data->button_press_event_handler) {
915 priv_data->source_target_list = gtk_target_list_new (targets, n_targets);
916 priv_data->source_actions = actions;
917 priv_data->start_button_mask = start_button_mask;
919 priv_data->button_press_event_handler = g_signal_connect (G_OBJECT (tree_view),
920 "button_press_event",
921 G_CALLBACK (rb_tree_dnd_button_press_event_cb),
922 NULL);
923 priv_data->drag_data_get_handler = g_signal_connect (G_OBJECT (tree_view),
924 "drag_data_get",
925 G_CALLBACK (rb_tree_dnd_drag_data_get_cb),
926 NULL);
927 priv_data->drag_data_delete_handler = g_signal_connect (G_OBJECT (tree_view),
928 "drag_data_delete",
929 G_CALLBACK (rb_tree_dnd_drag_data_delete_cb),
930 NULL);
938 void
939 rb_tree_dnd_add_drag_dest_support (GtkTreeView *tree_view,
940 RbTreeDestFlag flags,
941 const GtkTargetEntry *targets,
942 gint n_targets,
943 GdkDragAction actions)
945 RbTreeDndData *priv_data = NULL;
946 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
948 priv_data = init_rb_tree_dnd_data (GTK_WIDGET(tree_view));
950 if (!priv_data->drag_motion_handler) {
952 priv_data->dest_target_list = gtk_target_list_new (targets, n_targets);
953 priv_data->dest_actions = actions;
954 priv_data->dest_flags = flags;
956 gtk_drag_dest_set (GTK_WIDGET (tree_view),
958 NULL,
960 actions);
962 priv_data->drag_motion_handler = g_signal_connect (G_OBJECT (tree_view),
963 "drag_motion",
964 G_CALLBACK (rb_tree_dnd_drag_motion_cb),
965 NULL);
966 priv_data->drag_leave_handler = g_signal_connect (G_OBJECT (tree_view),
967 "drag_leave",
968 G_CALLBACK (rb_tree_dnd_drag_leave_cb),
969 NULL);
970 priv_data->drag_drop_handler = g_signal_connect (G_OBJECT (tree_view),
971 "drag_drop",
972 G_CALLBACK (rb_tree_dnd_drag_drop_cb),
973 NULL);
974 priv_data->drag_data_received_handler = g_signal_connect (G_OBJECT (tree_view),
975 "drag_data_received",
976 G_CALLBACK (rb_tree_dnd_drag_data_received_cb), NULL);