hythmbox-2.98/lib/rb-tree-dnd.c

No issues found

   1 /* rbtreednd.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., 51 Franklin St, Fifth Floor,
  17  * Boston, MA 02110-1301  USA.
  18  */
  19 
  20 #include "config.h"
  21 
  22 #include <string.h>
  23 #include <gtk/gtk.h>
  24 #include "rb-tree-dnd.h"
  25 
  26 #include "rb-debug.h"
  27 
  28 #define RB_TREE_DND_STRING "RbTreeDndString"
  29 /* must be the same value as in gtk_tree_view.c */
  30 #define SCROLL_EDGE_SIZE 15
  31 
  32 
  33 /**
  34  * SECTION:rb-tree-dnd
  35  * @short_description: multi-row drag and drop support for GtkTreeViews
  36  *
  37  * Provides support for drag and drop operations to and from GtkTreeView
  38  * widgets that can include multiple rows.  The model backing the tree view
  39  * widgets must implement the #RbTreeDragSource and #RbTreeDragDest interfaces.
  40  */
  41 
  42 /**
  43  * RbTreeDestFlag:
  44  * @RB_TREE_DEST_EMPTY_VIEW_DROP: If set, drops into empty spaces in the view are accepted
  45  * @RB_TREE_DEST_CAN_DROP_INTO: If set, drops into existing rows are accepted
  46  * @RB_TREE_DEST_CAN_DROP_BETWEEN: If set, drops between existing rows are accepted
  47  * @RB_TREE_DEST_SELECT_ON_DRAG_TIMEOUT: If set, update the drag selection using a timeout
  48  *
  49  * Flags controlling drag destination behaviour.
  50  */
  51 
  52 typedef struct
  53 {
  54   guint pressed_button;
  55   gint x;
  56   gint y;
  57   guint button_press_event_handler;
  58   guint motion_notify_handler;
  59   guint button_release_handler;
  60   guint drag_data_get_handler;
  61   guint drag_data_delete_handler;
  62   guint drag_motion_handler;
  63   guint drag_leave_handler;
  64   guint drag_drop_handler;
  65   guint drag_data_received_handler;
  66   GSList *event_list;
  67   gboolean pending_event;
  68 
  69   GtkTargetList *dest_target_list;
  70   GdkDragAction dest_actions;
  71   RbTreeDestFlag dest_flags;
  72 
  73   GtkTargetList *source_target_list;
  74   GdkDragAction source_actions;
  75   GdkModifierType start_button_mask;
  76 
  77   /* Scroll timeout (e.g. during dnd) */
  78   guint scroll_timeout;
  79 
  80   /* Select on drag timeout */
  81   GtkTreePath * previous_dest_path;
  82   guint select_on_drag_timeout;
  83 } RbTreeDndData;
  84 
  85 static RbTreeDndData *init_rb_tree_dnd_data (GtkWidget *widget);
  86 static GList * get_context_data (GdkDragContext *context);
  87 static gboolean filter_drop_position (GtkWidget *widget, GdkDragContext *context, GtkTreePath *path, GtkTreeViewDropPosition *pos);
  88 static gint scroll_row_timeout (gpointer data);
  89 static gboolean select_on_drag_timeout (gpointer data);
  90 static void remove_scroll_timeout (GtkTreeView *tree_view);
  91 static void remove_select_on_drag_timeout (GtkTreeView *tree_view);
  92 
  93 GType
  94 rb_tree_drag_source_get_type (void)
  95 {
  96   static GType our_type = 0;
  97 
  98   if (!our_type)
  99     {
 100       static const GTypeInfo our_info =
 101       {
 102         sizeof (RbTreeDragSourceIface), /* class_size */
 103 	NULL,		/* base_init */
 104 	NULL,		/* base_finalize */
 105 	NULL,
 106 	NULL,		/* class_finalize */
 107 	NULL,		/* class_data */
 108 	0,
 109 	0,              /* n_preallocs */
 110 	NULL
 111       };
 112 
 113       our_type = g_type_register_static (G_TYPE_INTERFACE, "RbTreeDragSource", &our_info, 0);
 114     }
 115 
 116   return our_type;
 117 }
 118 
 119 
 120 /**
 121  * rb_tree_drag_source_row_draggable:
 122  * @drag_source: a #RbTreeDragSource
 123  * @path_list: row on which user is initiating a drag
 124  *
 125  * Asks the #RbTreeDragSource whether a particular row can be used as
 126  * the source of a DND operation. If the source doesn't implement
 127  * this interface, the row is assumed draggable.
 128  *
 129  * Return value: %TRUE if the row can be dragged
 130  **/
 131 gboolean
 132 rb_tree_drag_source_row_draggable (RbTreeDragSource *drag_source,
 133 				   GList            *path_list)
 134 {
 135   RbTreeDragSourceIface *iface = RB_TREE_DRAG_SOURCE_GET_IFACE (drag_source);
 136 
 137   g_return_val_if_fail (RB_IS_TREE_DRAG_SOURCE (drag_source), FALSE);
 138   g_return_val_if_fail (iface->rb_row_draggable != NULL, FALSE);
 139   g_return_val_if_fail (path_list != NULL, FALSE);
 140 
 141   if (iface->rb_row_draggable)
 142     return (* iface->rb_row_draggable) (drag_source, path_list);
 143   else
 144     return TRUE;
 145 }
 146 
 147 
 148 /**
 149  * rb_tree_drag_source_drag_data_delete:
 150  * @drag_source: a #RbTreeDragSource
 151  * @path_list: row that was being dragged
 152  *
 153  * Asks the #RbTreeDragSource to delete the row at @path, because
 154  * it was moved somewhere else via drag-and-drop. Returns %FALSE
 155  * if the deletion fails because @path no longer exists, or for
 156  * some model-specific reason. Should robustly handle a @path no
 157  * longer found in the model!
 158  *
 159  * Return value: %TRUE if the row was successfully deleted
 160  **/
 161 gboolean
 162 rb_tree_drag_source_drag_data_delete (RbTreeDragSource *drag_source,
 163 				      GList            *path_list)
 164 {
 165   RbTreeDragSourceIface *iface = RB_TREE_DRAG_SOURCE_GET_IFACE (drag_source);
 166 
 167   g_return_val_if_fail (RB_IS_TREE_DRAG_SOURCE (drag_source), FALSE);
 168   g_return_val_if_fail (iface->rb_drag_data_delete != NULL, FALSE);
 169   g_return_val_if_fail (path_list != NULL, FALSE);
 170 
 171   return (* iface->rb_drag_data_delete) (drag_source, path_list);
 172 }
 173 
 174 /**
 175  * rb_tree_drag_source_drag_data_get:
 176  * @drag_source: a #RbTreeDragSource
 177  * @path_list: row that was dragged
 178  * @selection_data: a #GtkSelectionData to fill with data from the dragged row
 179  *
 180  * Asks the #RbTreeDragSource to fill in @selection_data with a
 181  * representation of the row at @path. @selection_data->target gives
 182  * the required type of the data.  Should robustly handle a @path no
 183  * longer found in the model!
 184  *
 185  * Return value: %TRUE if data of the required type was provided
 186  **/
 187 gboolean
 188 rb_tree_drag_source_drag_data_get    (RbTreeDragSource *drag_source,
 189 				      GList            *path_list,
 190 				      GtkSelectionData *selection_data)
 191 {
 192   RbTreeDragSourceIface *iface = RB_TREE_DRAG_SOURCE_GET_IFACE (drag_source);
 193 
 194   g_return_val_if_fail (RB_IS_TREE_DRAG_SOURCE (drag_source), FALSE);
 195   g_return_val_if_fail (iface->rb_drag_data_get != NULL, FALSE);
 196   g_return_val_if_fail (path_list != NULL, FALSE);
 197   g_return_val_if_fail (selection_data != NULL, FALSE);
 198 
 199   return (* iface->rb_drag_data_get) (drag_source, path_list, selection_data);
 200 }
 201 
 202 
 203 
 204 GType
 205 rb_tree_drag_dest_get_type (void)
 206 {
 207   static GType our_type = 0;
 208 
 209   if (!our_type)
 210     {
 211       static const GTypeInfo our_info =
 212       {
 213         sizeof (RbTreeDragDestIface), /* class_size */
 214 	NULL,		/* base_init */
 215 	NULL,		/* base_finalize */
 216 	NULL,
 217 	NULL,		/* class_finalize */
 218 	NULL,		/* class_data */
 219 	0,
 220 	0,              /* n_preallocs */
 221 	NULL
 222       };
 223 
 224       our_type = g_type_register_static (G_TYPE_INTERFACE, "RbTreeDragDest", &our_info, 0);
 225     }
 226 
 227   return our_type;
 228 }
 229 
 230 
 231 /**
 232  * rb_tree_drag_dest_drag_data_received:
 233  * @drag_dest: a #RbTreeDragDest
 234  * @dest: the #GtkTreePath on which the data was dropped
 235  * @pos: the drop position relative to the row identified by @dest
 236  * @selection_data: a #GtkSelectionData containing the drag data
 237  *
 238  * Asks a #RbTreeDragDest to accept some drag and drop data.
 239  *
 240  * Return value: %TRUE if the data was accepted, %FALSE otherwise
 241  */
 242 gboolean
 243 rb_tree_drag_dest_drag_data_received (RbTreeDragDest   *drag_dest,
 244 				      GtkTreePath       *dest,
 245 				      GtkTreeViewDropPosition pos, 
 246 				      GtkSelectionData  *selection_data)
 247 {
 248   RbTreeDragDestIface *iface = RB_TREE_DRAG_DEST_GET_IFACE (drag_dest);
 249 
 250   g_return_val_if_fail (RB_IS_TREE_DRAG_DEST (drag_dest), FALSE);
 251   g_return_val_if_fail (iface->rb_drag_data_received != NULL, FALSE);
 252   g_return_val_if_fail (selection_data != NULL, FALSE);
 253 
 254   return (* iface->rb_drag_data_received) (drag_dest, dest, pos, selection_data);
 255 }
 256 
 257 
 258 /**
 259  * rb_tree_drag_dest_row_drop_possible:
 260  * @drag_dest: a #RbTreeDragDest
 261  * @dest_path: the #GtkTreePath on which the data may be dropped
 262  * @pos: the drop position relative to the row identified by @dest
 263  * @selection_data: a #GtkSelectionData containing the drag data
 264  *
 265  * Asks the #RbTreeDragDest whether data can be dropped on a particular
 266  * row.  This should probably check based on the format and the row.
 267  *
 268  * Return value: %TRUE if the data can be dropped there
 269  */
 270 gboolean
 271 rb_tree_drag_dest_row_drop_possible (RbTreeDragDest   *drag_dest,
 272 				     GtkTreePath       *dest_path,
 273 				     GtkTreeViewDropPosition pos,
 274 				     GtkSelectionData  *selection_data)
 275 {
 276   RbTreeDragDestIface *iface = RB_TREE_DRAG_DEST_GET_IFACE (drag_dest);
 277 
 278   g_return_val_if_fail (RB_IS_TREE_DRAG_DEST (drag_dest), FALSE);
 279   g_return_val_if_fail (iface->rb_row_drop_possible != NULL, FALSE);
 280   g_return_val_if_fail (selection_data != NULL, FALSE);
 281 
 282   return (* iface->rb_row_drop_possible) (drag_dest, dest_path, pos, selection_data);
 283 }
 284 
 285 
 286 /**
 287  * rb_tree_drag_dest_row_drop_position:
 288  * @drag_dest: a #RbTreeDragDest
 289  * @dest_path: a #GtkTreePath describing a possible drop row
 290  * @targets: a #GList containing possible drop target types
 291  * @pos: returns the #GtkTreeViewDropPosition to use relative to the row
 292  *
 293  * Asks the #RbTreeDragDest which drop position to use relative to the specified row.
 294  * The drag destination should decide which drop position to use based on the 
 295  * target row and the list of drag targets.
 296  *
 297  * Return value: %TRUE if a drop position has been set, %FALSE if a drop should not be
 298  *   allowed in the specified row
 299  */
 300 gboolean
 301 rb_tree_drag_dest_row_drop_position (RbTreeDragDest   *drag_dest,
 302 				     GtkTreePath       *dest_path,
 303 				     GList *targets,
 304 				     GtkTreeViewDropPosition *pos)
 305 {
 306   RbTreeDragDestIface *iface = RB_TREE_DRAG_DEST_GET_IFACE (drag_dest);
 307 
 308   g_return_val_if_fail (RB_IS_TREE_DRAG_DEST (drag_dest), FALSE);
 309   g_return_val_if_fail (iface->rb_row_drop_position != NULL, FALSE);
 310   g_return_val_if_fail (targets != NULL, FALSE);
 311   g_return_val_if_fail (pos != NULL, FALSE);
 312 
 313   return (* iface->rb_row_drop_position) (drag_dest, dest_path, targets, pos);
 314 }
 315 
 316 static void
 317 rb_tree_dnd_data_free (gpointer data)
 318 {
 319   RbTreeDndData *priv_data = data;
 320 
 321   if (priv_data->source_target_list != NULL) {
 322 	  gtk_target_list_unref (priv_data->source_target_list);
 323   }
 324   if (priv_data->dest_target_list != NULL) {
 325 	  gtk_target_list_unref (priv_data->dest_target_list);
 326   }
 327 
 328   g_free (priv_data);
 329 }
 330 
 331 static RbTreeDndData *
 332 init_rb_tree_dnd_data (GtkWidget *widget)
 333 {
 334 	RbTreeDndData *priv_data;
 335 
 336 	priv_data = g_object_get_data (G_OBJECT (widget), RB_TREE_DND_STRING);
 337 	if (priv_data == NULL)
 338 	{
 339 		priv_data = g_new0 (RbTreeDndData, 1);
 340 		priv_data->pending_event = FALSE;
 341 		g_object_set_data_full (G_OBJECT (widget), RB_TREE_DND_STRING, priv_data, rb_tree_dnd_data_free);
 342 		priv_data->drag_motion_handler = 0;
 343 		priv_data->drag_leave_handler = 0;
 344 		priv_data->button_press_event_handler = 0;
 345 		priv_data->scroll_timeout = 0;
 346 		priv_data->previous_dest_path = NULL;
 347 		priv_data->select_on_drag_timeout = 0;
 348 	}
 349 
 350 	return priv_data;
 351 }
 352 
 353 static void
 354 stop_drag_check (GtkWidget *widget)
 355 {
 356 	RbTreeDndData *priv_data;
 357 	GSList *l;
 358 
 359 	priv_data = g_object_get_data (G_OBJECT (widget), RB_TREE_DND_STRING);
 360 
 361 	for (l = priv_data->event_list; l != NULL; l = l->next)
 362 		gdk_event_free (l->data);
 363 
 364 	g_slist_free (priv_data->event_list);
 365 	priv_data->event_list = NULL;
 366 	priv_data->pending_event = FALSE;
 367 	g_signal_handler_disconnect (widget, priv_data->motion_notify_handler);
 368 	g_signal_handler_disconnect (widget, priv_data->button_release_handler);
 369 }
 370 
 371 
 372 static gboolean
 373 rb_tree_dnd_button_release_event_cb (GtkWidget      *widget,
 374 				   GdkEventButton *event,
 375 				   gpointer        data)
 376 {
 377 	RbTreeDndData *priv_data;
 378 	GSList *l;
 379 
 380 	priv_data = g_object_get_data (G_OBJECT (widget), RB_TREE_DND_STRING);
 381 
 382 	for (l = priv_data->event_list; l != NULL; l = l->next)
 383 		gtk_propagate_event (widget, l->data);
 384 
 385 	stop_drag_check (widget);
 386 
 387 	return FALSE;
 388 }
 389 
 390 
 391 static void
 392 selection_foreach (GtkTreeModel *model,
 393 		   GtkTreePath  *path,
 394 		   GtkTreeIter  *iter,
 395 		   gpointer      data)
 396 {
 397 	GList **list_ptr;
 398 
 399 	list_ptr = (GList **) data;
 400 	*list_ptr = g_list_prepend (*list_ptr, gtk_tree_row_reference_new (model, path));
 401 }
 402 
 403 
 404 static void
 405 path_list_free (GList *path_list)
 406 {
 407 	g_list_foreach (path_list, (GFunc) gtk_tree_row_reference_free, NULL);
 408 	g_list_free (path_list);
 409 }
 410 
 411 static void
 412 set_context_data (GdkDragContext *context,
 413 		  GList          *path_list)
 414 {
 415 	g_object_set_data_full (G_OBJECT (context),
 416 				"rb-tree-view-multi-source-row",
 417 				path_list,
 418 				(GDestroyNotify) path_list_free);
 419 
 420 	rb_debug ("Setting path_list: index=%i", gtk_tree_path_get_indices(path_list->data)[0]);
 421 }
 422 
 423 static GList *
 424 get_context_data (GdkDragContext *context)
 425 {
 426 	return g_object_get_data (G_OBJECT (context), "rb-tree-view-multi-source-row");
 427 }
 428 
 429 static gboolean
 430 filter_drop_position (GtkWidget *widget, GdkDragContext *context, GtkTreePath *path, GtkTreeViewDropPosition *pos)
 431 {
 432 	GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
 433 	GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
 434 	RbTreeDndData *priv_data = g_object_get_data (G_OBJECT (widget), RB_TREE_DND_STRING);
 435 	gboolean ret;
 436 
 437 	if (!(priv_data->dest_flags & RB_TREE_DEST_CAN_DROP_INTO)) {
 438 		if (*pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)
 439 			*pos = GTK_TREE_VIEW_DROP_BEFORE;
 440 		else if (*pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER)
 441 			*pos = GTK_TREE_VIEW_DROP_AFTER;
 442 	} else if (!(priv_data->dest_flags & RB_TREE_DEST_CAN_DROP_BETWEEN)) {
 443 		if (*pos == GTK_TREE_VIEW_DROP_BEFORE)
 444 			*pos = GTK_TREE_VIEW_DROP_INTO_OR_BEFORE;
 445 		else if (*pos == GTK_TREE_VIEW_DROP_AFTER)
 446 			*pos = GTK_TREE_VIEW_DROP_INTO_OR_AFTER;
 447 	}
 448 
 449 	ret = rb_tree_drag_dest_row_drop_position (RB_TREE_DRAG_DEST (model),
 450 						   path,
 451 						   gdk_drag_context_list_targets (context),
 452 						   pos);
 453   
 454 	rb_debug ("filtered drop position: %s", ret ? "TRUE" : "FALSE");	
 455 	return ret;
 456 }
 457 
 458 
 459 /* Scroll function taken/adapted from gtktreeview.c */
 460 static gint
 461 scroll_row_timeout (gpointer data)
 462 {
 463 	GtkTreeView *tree_view = data;
 464 	GdkRectangle visible_rect;
 465 	gint y, x;
 466 	gint offset;
 467 	gfloat value;
 468 	gdouble vadj_value;
 469 	GtkAdjustment* vadj;
 470 	RbTreeDndData *priv_data;
 471 	GdkWindow *window;
 472 	GdkDeviceManager *device_manager;
 473 
 474 	GDK_THREADS_ENTER ();
 475 
 476 	priv_data = g_object_get_data (G_OBJECT (tree_view), RB_TREE_DND_STRING);
 477 	g_return_val_if_fail(priv_data != NULL, TRUE);
 478 
 479 	window = gtk_tree_view_get_bin_window (tree_view);
 480 	device_manager = gdk_display_get_device_manager (gdk_window_get_display (window));
 481 	gdk_window_get_device_position (window, gdk_device_manager_get_client_pointer (device_manager), &x, &y, NULL);
 482 	gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, x, y, &x, &y);
 483 	gtk_tree_view_convert_bin_window_to_tree_coords (tree_view, x, y, &x, &y);
 484 
 485 	gtk_tree_view_get_visible_rect (tree_view, &visible_rect);
 486 
 487 	/* see if we are near the edge. */
 488 	if (x < visible_rect.x && x > visible_rect.x + visible_rect.width)
 489 	{
 490 		GDK_THREADS_LEAVE ();
 491 		priv_data->scroll_timeout = 0;
 492 		return FALSE;
 493 	}
 494 
 495 	offset = y - (visible_rect.y + 2 * SCROLL_EDGE_SIZE);
 496 	if (offset > 0)
 497 	{
 498 		offset = y - (visible_rect.y + visible_rect.height - 2 * SCROLL_EDGE_SIZE);
 499 		if (offset < 0) 
 500 		{
 501 			GDK_THREADS_LEAVE ();
 502 			priv_data->scroll_timeout = 0;
 503 			return FALSE;
 504 		}
 505 	}
 506 
 507 	vadj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (tree_view));
 508 	vadj_value = gtk_adjustment_get_value (vadj);
 509 	value = CLAMP (vadj_value + offset,
 510 		       gtk_adjustment_get_lower (vadj),
 511 		       gtk_adjustment_get_upper (vadj) - gtk_adjustment_get_page_size (vadj));
 512 	gtk_adjustment_set_value (vadj, value);
 513 
 514 	/* don't remove it if we're on the edge and not scrolling */
 515 	if (ABS (vadj_value - value) > 0.0001)
 516 		remove_select_on_drag_timeout(tree_view);
 517 
 518 	GDK_THREADS_LEAVE ();
 519 
 520 	return TRUE;
 521 }
 522 
 523 
 524 static gboolean
 525 select_on_drag_timeout (gpointer data)
 526 {
 527 	GtkTreeView *tree_view = data;
 528 	GtkTreeSelection *selection;
 529 	RbTreeDndData *priv_data;
 530 
 531 	GDK_THREADS_ENTER ();
 532 
 533 	priv_data = g_object_get_data (G_OBJECT (tree_view), RB_TREE_DND_STRING);
 534 	g_return_val_if_fail(priv_data != NULL, FALSE);
 535 	g_return_val_if_fail(priv_data->previous_dest_path != NULL, FALSE);
 536 
 537 	selection = gtk_tree_view_get_selection(tree_view);
 538 	if (!gtk_tree_selection_path_is_selected(selection,priv_data->previous_dest_path)) {
 539 		rb_debug("Changing selection because of drag timeout");
 540 		gtk_tree_view_set_cursor(tree_view,priv_data->previous_dest_path,NULL,FALSE);
 541 	}
 542 
 543 	priv_data->select_on_drag_timeout = 0;
 544 	gtk_tree_path_free(priv_data->previous_dest_path);
 545 	priv_data->previous_dest_path = NULL;
 546 
 547 	GDK_THREADS_LEAVE ();
 548 	return FALSE;
 549 }
 550 
 551 
 552 static void
 553 remove_scroll_timeout (GtkTreeView *tree_view)
 554  {
 555 	 RbTreeDndData *priv_data;
 556 
 557 	 priv_data = g_object_get_data (G_OBJECT (tree_view), RB_TREE_DND_STRING);
 558 	 g_return_if_fail(priv_data != NULL);
 559 
 560 	 if (priv_data->scroll_timeout != 0)
 561 	 {
 562 		 g_source_remove (priv_data->scroll_timeout);
 563 		 priv_data->scroll_timeout = 0;
 564 	 }
 565 }
 566 
 567 
 568 static void
 569 remove_select_on_drag_timeout (GtkTreeView *tree_view)
 570 {
 571 	RbTreeDndData *priv_data;
 572 
 573 	priv_data = g_object_get_data (G_OBJECT (tree_view), RB_TREE_DND_STRING);
 574 	g_return_if_fail(priv_data != NULL);
 575 
 576 	if (priv_data->select_on_drag_timeout != 0) {
 577 		rb_debug("Removing the select on drag timeout");
 578 		g_source_remove (priv_data->select_on_drag_timeout);
 579 		priv_data->select_on_drag_timeout = 0;
 580 	}
 581 	if (priv_data->previous_dest_path != NULL) {
 582 		gtk_tree_path_free(priv_data->previous_dest_path);
 583 		priv_data->previous_dest_path = NULL;
 584 	}
 585 }
 586 
 587 
 588 static void
 589 rb_tree_dnd_drag_data_delete_cb (GtkWidget *widget,
 590 			  GdkDragContext *drag_context,
 591 			  gpointer user_data)
 592 {
 593 	GList *path_list;
 594 	GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW(widget));
 595 
 596 	path_list = get_context_data (drag_context);
 597 	rb_tree_drag_source_drag_data_delete (RB_TREE_DRAG_SOURCE (model),
 598 					      path_list);
 599 
 600 	g_signal_stop_emission_by_name (widget, "drag_data_delete");
 601 }
 602 
 603 
 604 
 605 static void
 606 rb_tree_dnd_drag_data_get_cb (GtkWidget        *widget,
 607 			    GdkDragContext   *context,
 608 			    GtkSelectionData *selection_data,
 609 			    guint             info,
 610 			    guint             time)
 611 {
 612 	GtkTreeView *tree_view;
 613 	GtkTreeModel *model;
 614 	GList *path_list;
 615 
 616 	tree_view = GTK_TREE_VIEW (widget);
 617 	model = gtk_tree_view_get_model (tree_view);
 618 
 619 	if (model == NULL)
 620 		return;
 621 
 622 	path_list = get_context_data (context);
 623 
 624 	if (path_list == NULL)
 625 		return;
 626 
 627 	/* We can implement the GTK_TREE_MODEL_ROW target generically for
 628 	* any model; for DragSource models there are some other targets
 629 	* we also support.
 630 	*/
 631 	if (RB_IS_TREE_DRAG_SOURCE (model))
 632 	{
 633 		rb_tree_drag_source_drag_data_get (RB_TREE_DRAG_SOURCE (model),
 634 						   path_list,
 635 						   selection_data);
 636     	}
 637 }
 638 
 639 
 640 static gboolean
 641 rb_tree_dnd_motion_notify_event_cb (GtkWidget      *widget,
 642 				     GdkEventMotion *event,
 643                  		     gpointer        data)
 644 {
 645 	RbTreeDndData *priv_data;
 646 
 647 	priv_data = g_object_get_data (G_OBJECT (widget), RB_TREE_DND_STRING);
 648 
 649 	if (gtk_drag_check_threshold (widget,
 650 				      priv_data->x,
 651 				      priv_data->y,
 652 				      event->x,
 653 				      event->y))
 654 	{
 655 		GList *path_list = NULL;
 656 		GtkTreeSelection *selection;
 657 		GtkTreeModel *model;
 658 		GdkDragContext *context;
 659 
 660 		selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
 661 		stop_drag_check (widget);
 662 		gtk_tree_selection_selected_foreach (selection, selection_foreach, &path_list);
 663 		path_list = g_list_reverse (path_list);
 664 		model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
 665 
 666 		if (rb_tree_drag_source_row_draggable (RB_TREE_DRAG_SOURCE (model), path_list))
 667 		{
 668 			rb_debug ("drag begin");
 669 			context = gtk_drag_begin (widget,
 670 						  priv_data->source_target_list,
 671 						  priv_data->source_actions,
 672 						  priv_data->pressed_button,
 673 						  (GdkEvent*)event);
 674 	  		set_context_data (context, path_list);
 675 	  		gtk_drag_set_icon_default (context);
 676 
 677 		} else {
 678 			path_list_free (path_list);
 679 		}
 680 	}
 681 
 682 	return TRUE;
 683 }
 684 
 685 static gboolean
 686 rb_tree_dnd_drag_motion_cb (GtkWidget        *widget,
 687                            GdkDragContext   *context,
 688                            gint              x,
 689                            gint              y,
 690                            guint             time)
 691 {
 692 	GtkTreeView *tree_view;
 693 	GtkTreePath *path = NULL;
 694 	GtkTreeViewDropPosition pos;
 695 	RbTreeDndData *priv_data;
 696 	GdkDragAction action;
 697 
 698 	rb_debug ("drag and drop motion: (%i,%i)", x, y);
 699 
 700   	tree_view = GTK_TREE_VIEW (widget);
 701 
 702 	priv_data = g_object_get_data (G_OBJECT (widget), RB_TREE_DND_STRING);
 703 
 704 	gtk_tree_view_get_dest_row_at_pos (tree_view, x, y, &path, &pos);
 705 
 706 	if ((priv_data->previous_dest_path == NULL)
 707 	    || (path == NULL)
 708 	    || gtk_tree_path_compare(path,priv_data->previous_dest_path)) {
 709 				remove_select_on_drag_timeout(tree_view);
 710 	}
 711 
 712 	if (path == NULL)
 713 	{
 714 	  	gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget),
 715 						 NULL,
 716 						 GTK_TREE_VIEW_DROP_BEFORE);
 717 
 718 		if (!(priv_data->dest_flags & RB_TREE_DEST_EMPTY_VIEW_DROP)) {
 719 			/* Can't drop here. */
 720 			gdk_drag_status (context, 0, time);
 721 
 722 			return TRUE;
 723 		} else if (!filter_drop_position (widget, context, path, &pos)) {
 724 			gdk_drag_status (context, 0, time);
 725 			return TRUE;
 726 		}
 727 	}
 728 	else
 729 	{
 730 		if (!filter_drop_position (widget, context, path, &pos)) {
 731 			gdk_drag_status (context, 0, time);
 732 			return TRUE;
 733 		}
 734 
 735 	  if (priv_data->scroll_timeout == 0)
 736 	  {
 737 		  priv_data->scroll_timeout = g_timeout_add (150, scroll_row_timeout, tree_view);
 738 	  }
 739 	}
 740 
 741 	if (GTK_WIDGET (tree_view) == gtk_drag_get_source_widget (context) &&
 742 	    gdk_drag_context_get_actions (context) & GDK_ACTION_MOVE)
 743 		action = GDK_ACTION_MOVE;
 744 	else
 745 		action = gdk_drag_context_get_suggested_action (context);
 746 
 747 	if (path) {
 748 		gtk_tree_view_set_drag_dest_row (tree_view, path, pos);
 749 		if (priv_data->dest_flags & RB_TREE_DEST_SELECT_ON_DRAG_TIMEOUT) {
 750 			if (priv_data->previous_dest_path != NULL) {
 751 				gtk_tree_path_free (priv_data->previous_dest_path);
 752 			}
 753 			priv_data->previous_dest_path = path;
 754 			if (priv_data->select_on_drag_timeout == 0) {
 755 				rb_debug("Setting up a new select on drag timeout");
 756 				priv_data->select_on_drag_timeout = g_timeout_add_seconds (2, select_on_drag_timeout, tree_view);
 757 			}
 758 		} else {
 759 			gtk_tree_path_free (path);
 760 		}
 761 	}
 762 
 763 	gdk_drag_status (context, action, time);
 764 
 765 	return TRUE;
 766 }
 767 
 768 
 769 static gboolean
 770 rb_tree_dnd_drag_leave_cb (GtkWidget        *widget,
 771                            GdkDragContext   *context,
 772                            gint              x,
 773                            gint              y,
 774                            guint             time)
 775 {
 776 	remove_select_on_drag_timeout(GTK_TREE_VIEW (widget));
 777 	return TRUE;
 778 }
 779 
 780 static gboolean
 781 rb_tree_dnd_drag_drop_cb (GtkWidget        *widget,
 782                          GdkDragContext   *context,
 783                          gint              x,
 784                          gint              y,
 785                          guint             time)
 786 {
 787 	GtkTreeView *tree_view;
 788 	GtkTreePath *path;
 789 	GtkTreeModel *model;
 790 	GtkTreeViewDropPosition pos;
 791 	RbTreeDndData *priv_data;
 792 
 793 	tree_view = GTK_TREE_VIEW (widget);
 794 	model = gtk_tree_view_get_model (tree_view);
 795 	priv_data = g_object_get_data (G_OBJECT (widget), RB_TREE_DND_STRING);
 796 	gtk_tree_view_get_dest_row_at_pos (tree_view, x, y, &path, &pos);
 797 
 798 	remove_scroll_timeout (tree_view);
 799 
 800 	/* Unset this thing */
 801 	gtk_tree_view_set_drag_dest_row (tree_view,
 802                				 NULL,
 803 					 GTK_TREE_VIEW_DROP_BEFORE);
 804 
 805 	if (path || priv_data->dest_flags & RB_TREE_DEST_EMPTY_VIEW_DROP) {
 806 
 807 		GdkAtom target;
 808 		RbTreeDragDestIface *iface = RB_TREE_DRAG_DEST_GET_IFACE (model);
 809 		if (iface->rb_get_drag_target) {
 810 			RbTreeDragDest *dest = RB_TREE_DRAG_DEST (model);
 811 			target = (* iface->rb_get_drag_target) (dest, widget, 
 812 								context, path, 
 813 								priv_data->dest_target_list);
 814 		} else {
 815 			target = gtk_drag_dest_find_target (widget, context, 
 816 							    priv_data->dest_target_list);
 817 		}
 818 
 819 		if (path)
 820 			gtk_tree_path_free (path);
 821 
 822 		if (target != GDK_NONE) {
 823 			gtk_drag_get_data (widget, context, target, time);
 824 			return TRUE;
 825 		}
 826 	}
 827 
 828 	return FALSE;
 829 }
 830 
 831 
 832 static void
 833 rb_tree_dnd_drag_data_received_cb (GtkWidget        *widget,
 834                                   GdkDragContext   *context,
 835                                   gint              x,
 836                                   gint              y,
 837                                   GtkSelectionData *selection_data,
 838                                   guint             info,
 839                                   guint             time)
 840 {
 841 	GtkTreeView *tree_view;
 842 	GtkTreeModel *model;
 843 	GtkTreePath *dest_row;
 844 	GtkTreeViewDropPosition pos;
 845 	gboolean filtered = TRUE;
 846 	gboolean accepted = FALSE;
 847 
 848 	tree_view = GTK_TREE_VIEW (widget);
 849 	model = gtk_tree_view_get_model (tree_view);
 850 
 851 	gtk_tree_view_get_dest_row_at_pos (tree_view, x, y, &dest_row, &pos);
 852 
 853 	if (dest_row)
 854 		if (!filter_drop_position (widget, context, dest_row, &pos))
 855 			filtered = FALSE;
 856 
 857 	if (filtered && (gtk_selection_data_get_length (selection_data) >= 0))
 858 	{
 859 		if (rb_tree_drag_dest_drag_data_received (RB_TREE_DRAG_DEST (model),
 860                 					  dest_row,
 861 							  pos,
 862                 					  selection_data))
 863         		accepted = TRUE;
 864 	}
 865 
 866 	gtk_drag_finish (context,
 867         		 accepted,
 868 			 (gdk_drag_context_get_selected_action (context) == GDK_ACTION_MOVE),
 869 			 time);
 870 
 871 	if (dest_row)
 872   		gtk_tree_path_free (dest_row);
 873 
 874 	g_signal_stop_emission_by_name (widget, "drag_data_received");
 875 
 876 }
 877 
 878 
 879 static gboolean
 880 rb_tree_dnd_button_press_event_cb (GtkWidget      *widget,
 881 					GdkEventButton *event,
 882 					gpointer        data)
 883 {
 884 	GtkTreeView *tree_view;
 885 	GtkTreePath *path = NULL;
 886 	GtkTreeViewColumn *column = NULL;
 887 	gint cell_x, cell_y;
 888 	GtkTreeSelection *selection;
 889 	RbTreeDndData *priv_data;
 890 
 891 	if (event->button == 3)
 892 		return FALSE;
 893 
 894 	tree_view = GTK_TREE_VIEW (widget);
 895 	if (event->window != gtk_tree_view_get_bin_window (tree_view))
 896 		return FALSE;
 897 
 898 	priv_data = g_object_get_data (G_OBJECT (tree_view), RB_TREE_DND_STRING);
 899 	if (priv_data == NULL)
 900 	{
 901 		priv_data = g_new0 (RbTreeDndData, 1);
 902 		priv_data->pending_event = FALSE;
 903 		g_object_set_data (G_OBJECT (tree_view), RB_TREE_DND_STRING, priv_data);
 904 	}
 905 
 906 	if (g_slist_find (priv_data->event_list, event))
 907 		return FALSE;
 908 
 909 	if (priv_data->pending_event)
 910 	{
 911 		/* save the event to be propagated in order */
 912 		priv_data->event_list = g_slist_append (priv_data->event_list, gdk_event_copy ((GdkEvent*)event));
 913 		return TRUE;
 914 	}
 915 
 916 	if (event->type == GDK_2BUTTON_PRESS)
 917 		return FALSE;
 918 
 919 	gtk_tree_view_get_path_at_pos (tree_view,
 920 				       event->x, event->y,
 921 				       &path, &column,
 922 				       &cell_x, &cell_y);
 923 
 924 	selection = gtk_tree_view_get_selection (tree_view);
 925 
 926 	if (path)
 927 	{
 928 		gboolean call_parent = (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ||
 929 					!gtk_tree_selection_path_is_selected (selection, path) ||
 930 					event->button != 1);
 931 
 932 		if (call_parent)
 933 			(GTK_WIDGET_GET_CLASS (tree_view))->button_press_event (widget, event);
 934 
 935 		if (gtk_tree_selection_path_is_selected (selection, path))
 936 		{
 937 			priv_data->pressed_button = event->button;
 938 			priv_data->x = event->x;
 939 			priv_data->y = event->y;
 940 
 941 			priv_data->pending_event = TRUE;
 942 
 943 			if (!call_parent)
 944 				priv_data->event_list = g_slist_append (priv_data->event_list,
 945 									gdk_event_copy ((GdkEvent*)event));
 946 
 947 			priv_data->motion_notify_handler = g_signal_connect (G_OBJECT (tree_view),
 948 									     "motion_notify_event",
 949 									     G_CALLBACK (rb_tree_dnd_motion_notify_event_cb),
 950 									     NULL);
 951 			priv_data->button_release_handler = g_signal_connect (G_OBJECT (tree_view),
 952 									      "button_release_event",
 953 									      G_CALLBACK (rb_tree_dnd_button_release_event_cb),
 954 									      NULL);
 955 
 956 		}
 957 
 958 		gtk_tree_path_free (path);
 959 		/* We called the default handler so we don't let the default handler run */
 960 		return TRUE;
 961 	}
 962 
 963 	return FALSE;
 964 }
 965 
 966 /**
 967  * rb_tree_dnd_add_drag_source_support:
 968  * @tree_view: a #GtkTreeView that wants to be a drag source
 969  * @start_button_mask: a mask describing modifier keys to handle when dragging
 970  * @targets: an array of #GtkTargetEntry structures describing drag data types
 971  * @n_targets: the number of elements in @targets
 972  * @actions: a mask describing drag actions that are allowed from this source
 973  *
 974  * Adds event handlers to perform multi-row drag and drop operations from the
 975  * specified #GtkTreeView widget.  The model backing the #GtkTreeView must
 976  * implement the #RbTreeDragSource interface.  This should be called immediately
 977  * after the tree view is created.
 978  */
 979 void
 980 rb_tree_dnd_add_drag_source_support (GtkTreeView *tree_view,
 981 				     GdkModifierType start_button_mask,
 982 				     const GtkTargetEntry *targets,
 983 				     gint n_targets,
 984 				     GdkDragAction actions)
 985 {
 986 	RbTreeDndData *priv_data = NULL;
 987  	g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
 988 
 989 	priv_data = init_rb_tree_dnd_data (GTK_WIDGET(tree_view));
 990 
 991 	if (!priv_data->button_press_event_handler) {
 992 
 993 		priv_data->source_target_list = gtk_target_list_new (targets, n_targets);
 994 		priv_data->source_actions = actions;
 995 		priv_data->start_button_mask = start_button_mask;
 996 
 997 		priv_data->button_press_event_handler = g_signal_connect (G_OBJECT (tree_view),
 998 							"button_press_event",
 999 							G_CALLBACK (rb_tree_dnd_button_press_event_cb),
1000 							NULL);
1001 		priv_data->drag_data_get_handler = g_signal_connect (G_OBJECT (tree_view),
1002 								     "drag_data_get",
1003 								     G_CALLBACK (rb_tree_dnd_drag_data_get_cb),
1004 								     NULL);
1005 		priv_data->drag_data_delete_handler = g_signal_connect (G_OBJECT (tree_view),
1006 									"drag_data_delete",
1007 									G_CALLBACK (rb_tree_dnd_drag_data_delete_cb),
1008 									NULL);
1009 	}
1010 }
1011 
1012 /**
1013  * rb_tree_dnd_add_drag_dest_support:
1014  * @tree_view: a #GtkTreeView that wants to be a drag destination
1015  * @flags: #RbTreeDestFlags for this drag destination
1016  * @targets: an array of #GtkTargetEntry structures describing the allowed drag targets
1017  * @n_targets: the number of elements in @targets
1018  * @actions: the allowable drag actions for this destination
1019  *
1020  * Adds event handlers to perform multi-row drag and drop operations to the specified
1021  * #GtkTreeView.  The model backing the tree view should implement the #RbTreeDragDest
1022  * interface.  This should be called immediately after the tree view is created.
1023  */
1024 void
1025 rb_tree_dnd_add_drag_dest_support (GtkTreeView *tree_view,
1026 				   RbTreeDestFlag flags,
1027 				   const GtkTargetEntry *targets,
1028 				   gint n_targets,
1029 				   GdkDragAction actions)
1030 {
1031 	RbTreeDndData *priv_data = NULL;
1032 	g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
1033 
1034 	priv_data = init_rb_tree_dnd_data (GTK_WIDGET(tree_view));
1035 
1036 	if (!priv_data->drag_motion_handler) {
1037 
1038 		priv_data->dest_target_list = gtk_target_list_new (targets, n_targets);
1039 		priv_data->dest_actions = actions;
1040 		priv_data->dest_flags = flags;
1041 
1042 		gtk_drag_dest_set (GTK_WIDGET (tree_view),
1043 				   0,
1044 				   NULL,
1045 				   0,
1046 				   actions);
1047 
1048 		priv_data->drag_motion_handler = g_signal_connect (G_OBJECT (tree_view),
1049 								  "drag_motion",
1050 								  G_CALLBACK (rb_tree_dnd_drag_motion_cb),
1051 								  NULL);
1052 		priv_data->drag_leave_handler = g_signal_connect (G_OBJECT (tree_view),
1053 								  "drag_leave",
1054 								  G_CALLBACK (rb_tree_dnd_drag_leave_cb),
1055 								  NULL);
1056 		priv_data->drag_drop_handler = g_signal_connect (G_OBJECT (tree_view),
1057 								 "drag_drop",
1058 								 G_CALLBACK (rb_tree_dnd_drag_drop_cb),
1059 								 NULL);
1060 	  	priv_data->drag_data_received_handler = g_signal_connect (G_OBJECT (tree_view),
1061 									  "drag_data_received",
1062 									  G_CALLBACK (rb_tree_dnd_drag_data_received_cb),
1063 									  NULL);
1064 	}
1065 }