gtktreeselection.c 38.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/* gtktreeselection.h
 * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

20
#include "config.h"
21
#include <string.h>
22 23 24
#include "gtktreeselection.h"
#include "gtktreeprivate.h"
#include "gtkrbtree.h"
25
#include "gtkmarshalers.h"
26
#include "gtkintl.h"
27
#include "gtkalias.h"
28

29
static void gtk_tree_selection_finalize          (GObject               *object);
30 31 32 33 34 35
static gint gtk_tree_selection_real_select_all   (GtkTreeSelection      *selection);
static gint gtk_tree_selection_real_unselect_all (GtkTreeSelection      *selection);
static gint gtk_tree_selection_real_select_node  (GtkTreeSelection      *selection,
						  GtkRBTree             *tree,
						  GtkRBNode             *node,
						  gboolean               select);
36

37 38
enum
{
39
  CHANGED,
40 41 42
  LAST_SIGNAL
};

43
static guint tree_selection_signals [LAST_SIGNAL] = { 0 };
44

Matthias Clasen's avatar
Matthias Clasen committed
45
G_DEFINE_TYPE (GtkTreeSelection, gtk_tree_selection, G_TYPE_OBJECT)
46 47 48 49

static void
gtk_tree_selection_class_init (GtkTreeSelectionClass *class)
{
50
  GObjectClass *object_class;
51

52
  object_class = (GObjectClass*) class;
53

54
  object_class->finalize = gtk_tree_selection_finalize;
55
  class->changed = NULL;
56

57
  tree_selection_signals[CHANGED] =
58
    g_signal_new (I_("changed"),
Manish Singh's avatar
Manish Singh committed
59 60 61 62 63 64
		  G_OBJECT_CLASS_TYPE (object_class),
		  G_SIGNAL_RUN_FIRST,
		  G_STRUCT_OFFSET (GtkTreeSelectionClass, changed),
		  NULL, NULL,
		  _gtk_marshal_VOID__VOID,
		  G_TYPE_NONE, 0);
65 66 67 68 69
}

static void
gtk_tree_selection_init (GtkTreeSelection *selection)
{
70
  selection->type = GTK_SELECTION_SINGLE;
71 72
}

73 74 75
static void
gtk_tree_selection_finalize (GObject *object)
{
76 77 78 79
  GtkTreeSelection *selection = GTK_TREE_SELECTION (object);

  if (selection->destroy)
    {
80
      GDestroyNotify d = selection->destroy;
81 82 83 84

      selection->destroy = NULL;
      d (selection->user_data);
    }
85 86

  /* chain parent_class' handler */
Matthias Clasen's avatar
Matthias Clasen committed
87
  G_OBJECT_CLASS (gtk_tree_selection_parent_class)->finalize (object);
88 89
}

90
/**
91
 * _gtk_tree_selection_new:
92
 *
93
 * Creates a new #GtkTreeSelection object.  This function should not be invoked,
94
 * as each #GtkTreeView will create its own #GtkTreeSelection.
95
 *
96 97
 * Return value: A newly created #GtkTreeSelection object.
 **/
98 99
GtkTreeSelection*
_gtk_tree_selection_new (void)
100
{
101
  GtkTreeSelection *selection;
102

Manish Singh's avatar
Manish Singh committed
103
  selection = g_object_new (GTK_TYPE_TREE_SELECTION, NULL);
104 105 106 107

  return selection;
}

108
/**
109
 * _gtk_tree_selection_new_with_tree_view:
110
 * @tree_view: The #GtkTreeView.
111
 *
112
 * Creates a new #GtkTreeSelection object.  This function should not be invoked,
113
 * as each #GtkTreeView will create its own #GtkTreeSelection.
114
 *
115 116
 * Return value: A newly created #GtkTreeSelection object.
 **/
117 118
GtkTreeSelection*
_gtk_tree_selection_new_with_tree_view (GtkTreeView *tree_view)
119
{
120
  GtkTreeSelection *selection;
121 122 123

  g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);

124 125
  selection = _gtk_tree_selection_new ();
  _gtk_tree_selection_set_tree_view (selection, tree_view);
126 127 128 129

  return selection;
}

130
/**
131
 * _gtk_tree_selection_set_tree_view:
132 133
 * @selection: A #GtkTreeSelection.
 * @tree_view: The #GtkTreeView.
134
 *
135 136 137
 * Sets the #GtkTreeView of @selection.  This function should not be invoked, as
 * it is used internally by #GtkTreeView.
 **/
138
void
139 140
_gtk_tree_selection_set_tree_view (GtkTreeSelection *selection,
                                   GtkTreeView      *tree_view)
141 142 143 144 145 146 147 148
{
  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
  if (tree_view != NULL)
    g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));

  selection->tree_view = tree_view;
}

149
/**
Havoc Pennington's avatar
Havoc Pennington committed
150
 * gtk_tree_selection_set_mode:
151
 * @selection: A #GtkTreeSelection.
152
 * @type: The selection mode
153
 *
154
 * Sets the selection mode of the @selection.  If the previous type was
Matthias Clasen's avatar
Matthias Clasen committed
155
 * #GTK_SELECTION_MULTIPLE, then the anchor is kept selected, if it was
156
 * previously selected.
157
 **/
158
void
159 160
gtk_tree_selection_set_mode (GtkTreeSelection *selection,
			     GtkSelectionMode  type)
161
{
162
  GtkTreeSelectionFunc tmp_func;
163 164 165 166 167
  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));

  if (selection->type == type)
    return;

168 169 170 171 172 173 174 175 176
  
  if (type == GTK_SELECTION_NONE)
    {
      /* We do this so that we unconditionally unset all rows
       */
      tmp_func = selection->user_func;
      selection->user_func = NULL;
      gtk_tree_selection_unselect_all (selection);
      selection->user_func = tmp_func;
177 178 179

      gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
      selection->tree_view->priv->anchor = NULL;
180 181 182
    }
  else if (type == GTK_SELECTION_SINGLE ||
	   type == GTK_SELECTION_BROWSE)
183 184 185 186
    {
      GtkRBTree *tree = NULL;
      GtkRBNode *node = NULL;
      gint selected = FALSE;
187
      GtkTreePath *anchor_path = NULL;
188 189 190

      if (selection->tree_view->priv->anchor)
	{
191 192 193 194 195 196 197 198
          anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor);

          if (anchor_path)
            {
              _gtk_tree_view_find_node (selection->tree_view,
                                        anchor_path,
                                        &tree,
                                        &node);
199

200 201 202
              if (node && GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
                selected = TRUE;
            }
203
	}
204 205

      /* We do this so that we unconditionally unset all rows
206
       */
207 208
      tmp_func = selection->user_func;
      selection->user_func = NULL;
209
      gtk_tree_selection_unselect_all (selection);
210
      selection->user_func = tmp_func;
211

212
      if (node && selected)
213 214 215 216
	_gtk_tree_selection_internal_select_node (selection,
						  node,
						  tree,
						  anchor_path,
Kristian Rietveld's avatar
Kristian Rietveld committed
217
                                                  0,
Kristian Rietveld's avatar
Kristian Rietveld committed
218
						  FALSE);
219 220
      if (anchor_path)
	gtk_tree_path_free (anchor_path);
221
    }
222

223 224 225
  selection->type = type;
}

226 227 228 229 230 231 232 233 234
/**
 * gtk_tree_selection_get_mode:
 * @selection: a #GtkTreeSelection
 *
 * Gets the selection mode for @selection. See
 * gtk_tree_selection_set_mode().
 *
 * Return value: the current selection mode
 **/
235
GtkSelectionMode
236 237
gtk_tree_selection_get_mode (GtkTreeSelection *selection)
{
238
  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), GTK_SELECTION_SINGLE);
239 240 241 242

  return selection->type;
}

243 244 245 246 247
/**
 * gtk_tree_selection_set_select_function:
 * @selection: A #GtkTreeSelection.
 * @func: The selection function.
 * @data: The selection function's data.
248
 * @destroy: The destroy function for user data.  May be NULL.
249
 *
250 251
 * Sets the selection function.  If set, this function is called before any node
 * is selected or unselected, giving some control over which nodes are selected.
Havoc Pennington's avatar
Havoc Pennington committed
252 253
 * The select function should return %TRUE if the state of the node may be toggled,
 * and %FALSE if the state of the node should be left unchanged.
254
 **/
255 256 257
void
gtk_tree_selection_set_select_function (GtkTreeSelection     *selection,
					GtkTreeSelectionFunc  func,
258
					gpointer              data,
259
					GDestroyNotify        destroy)
260 261 262 263
{
  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
  g_return_if_fail (func != NULL);

264 265
  if (selection->destroy)
    {
266
      GDestroyNotify d = selection->destroy;
267 268 269 270 271

      selection->destroy = NULL;
      d (selection->user_data);
    }

272 273
  selection->user_func = func;
  selection->user_data = data;
274
  selection->destroy = destroy;
275 276
}

277 278 279 280 281 282 283 284
/**
 * gtk_tree_selection_get_select_function:
 * @selection: A #GtkTreeSelection.
 *
 * Returns the current selection function.
 *
 * Return value: The function.
 *
285
 * Since: 2.14
286 287 288 289 290 291 292 293 294
 **/
GtkTreeSelectionFunc
gtk_tree_selection_get_select_function (GtkTreeSelection *selection)
{
  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), NULL);

  return selection->user_func;
}

295 296 297
/**
 * gtk_tree_selection_get_user_data:
 * @selection: A #GtkTreeSelection.
298
 *
299
 * Returns the user data for the selection function.
300
 *
301 302
 * Return value: The user data.
 **/
303 304 305 306 307 308 309 310
gpointer
gtk_tree_selection_get_user_data (GtkTreeSelection *selection)
{
  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), NULL);

  return selection->user_data;
}

311 312 313 314 315 316 317 318 319
/**
 * gtk_tree_selection_get_tree_view:
 * @selection: A #GtkTreeSelection
 * 
 * Returns the tree view associated with @selection.
 * 
 * Return value: A #GtkTreeView
 **/
GtkTreeView *
Havoc Pennington's avatar
Havoc Pennington committed
320 321 322 323 324 325 326
gtk_tree_selection_get_tree_view (GtkTreeSelection *selection)
{
  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), NULL);

  return selection->tree_view;
}

327 328 329
/**
 * gtk_tree_selection_get_selected:
 * @selection: A #GtkTreeSelection.
330
 * @model: A pointer to set to the #GtkTreeModel, or NULL.
Jonathan Blandford's avatar
Jonathan Blandford committed
331
 * @iter: The #GtkTreeIter, or NULL.
332
 *
333
 * Sets @iter to the currently selected node if @selection is set to
334 335 336 337
 * #GTK_SELECTION_SINGLE or #GTK_SELECTION_BROWSE.  @iter may be NULL if you
 * just want to test if @selection has any selected nodes.  @model is filled
 * with the current model as a convenience.  This function will not work if you
 * use @selection is #GTK_SELECTION_MULTIPLE.
338
 *
Jonathan Blandford's avatar
Jonathan Blandford committed
339
 * Return value: TRUE, if there is a selected node.
340
 **/
341
gboolean
342 343 344
gtk_tree_selection_get_selected (GtkTreeSelection  *selection,
				 GtkTreeModel     **model,
				 GtkTreeIter       *iter)
345 346 347
{
  GtkRBTree *tree;
  GtkRBNode *node;
348 349
  GtkTreePath *anchor_path;
  gboolean retval;
Kristian Rietveld's avatar
Kristian Rietveld committed
350
  gboolean found_node;
351

352
  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), FALSE);
353
  g_return_val_if_fail (selection->type != GTK_SELECTION_MULTIPLE, FALSE);
354
  g_return_val_if_fail (selection->tree_view != NULL, FALSE);
355

356 357 358 359
  /* Clear the iter */
  if (iter)
    memset (iter, 0, sizeof (GtkTreeIter));

360 361
  if (model)
    *model = selection->tree_view->priv->model;
362

363
  if (selection->tree_view->priv->anchor == NULL)
364
    return FALSE;
365 366 367 368 369

  anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor);

  if (anchor_path == NULL)
    return FALSE;
370

371
  retval = FALSE;
372

Kristian Rietveld's avatar
Kristian Rietveld committed
373 374 375 376 377 378
  found_node = !_gtk_tree_view_find_node (selection->tree_view,
                                          anchor_path,
                                          &tree,
                                          &node);

  if (found_node && GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
379
    {
Kristian Rietveld's avatar
Kristian Rietveld committed
380 381
      /* we only want to return the anchor if it exists in the rbtree and
       * is selected.
382
       */
Kristian Rietveld's avatar
Kristian Rietveld committed
383 384 385 386 387 388
      if (iter == NULL)
	retval = TRUE;
      else
        retval = gtk_tree_model_get_iter (selection->tree_view->priv->model,
                                          iter,
                                          anchor_path);
389 390 391
    }
  else
    {
Kristian Rietveld's avatar
Kristian Rietveld committed
392 393 394
      /* We don't want to return the anchor if it isn't actually selected.
       */
      retval = FALSE;
395
    }
396

397
  gtk_tree_path_free (anchor_path);
398

399
  return retval;
400 401
}

402 403 404 405 406 407 408
/**
 * gtk_tree_selection_get_selected_rows:
 * @selection: A #GtkTreeSelection.
 * @model: A pointer to set to the #GtkTreeModel, or NULL.
 *
 * Creates a list of path of all selected rows. Additionally, if you are
 * planning on modifying the model after calling this function, you may
Matthias Clasen's avatar
Matthias Clasen committed
409
 * want to convert the returned list into a list of #GtkTreeRowReference<!-- -->s.
410
 * To do this, you can use gtk_tree_row_reference_new().
411 412
 *
 * To free the return value, use:
Matthias Clasen's avatar
Matthias Clasen committed
413
 * |[
414 415
 * g_list_foreach (list, gtk_tree_path_free, NULL);
 * g_list_free (list);
Matthias Clasen's avatar
Matthias Clasen committed
416
 * ]|
417 418
 *
 * Return value: A #GList containing a #GtkTreePath for each selected row.
419 420
 *
 * Since: 2.2
421 422 423 424 425 426 427 428 429 430 431 432 433
 **/
GList *
gtk_tree_selection_get_selected_rows (GtkTreeSelection   *selection,
                                      GtkTreeModel      **model)
{
  GList *list = NULL;
  GtkRBTree *tree = NULL;
  GtkRBNode *node = NULL;
  GtkTreePath *path;

  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), NULL);
  g_return_val_if_fail (selection->tree_view != NULL, NULL);

434 435 436
  if (model)
    *model = selection->tree_view->priv->model;

437 438 439 440 441 442 443 444 445 446 447 448 449 450
  if (selection->tree_view->priv->tree == NULL ||
      selection->tree_view->priv->tree->root == NULL)
    return NULL;

  if (selection->type == GTK_SELECTION_NONE)
    return NULL;
  else if (selection->type != GTK_SELECTION_MULTIPLE)
    {
      GtkTreeIter iter;

      if (gtk_tree_selection_get_selected (selection, NULL, &iter))
        {
	  GtkTreePath *path;

451
	  path = gtk_tree_model_get_path (selection->tree_view->priv->model, &iter);
452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469
	  list = g_list_append (list, path);

	  return list;
	}

      return NULL;
    }

  tree = selection->tree_view->priv->tree;
  node = selection->tree_view->priv->tree->root;

  while (node->left != tree->nil)
    node = node->left;
  path = gtk_tree_path_new_first ();

  do
    {
      if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
470
	list = g_list_prepend (list, gtk_tree_path_copy (path));
471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501

      if (node->children)
        {
	  tree = node->children;
	  node = tree->root;

	  while (node->left != tree->nil)
	    node = node->left;

	  gtk_tree_path_append_index (path, 0);
	}
      else
        {
	  gboolean done = FALSE;

	  do
	    {
	      node = _gtk_rbtree_next (tree, node);
	      if (node != NULL)
	        {
		  done = TRUE;
		  gtk_tree_path_next (path);
		}
	      else
	        {
		  node = tree->parent_node;
		  tree = tree->parent_tree;

		  if (!tree)
		    {
		      gtk_tree_path_free (path);
502 503

		      goto done; 
504 505 506 507 508 509 510 511 512 513 514 515
		    }

		  gtk_tree_path_up (path);
		}
	    }
	  while (!done);
	}
    }
  while (TRUE);

  gtk_tree_path_free (path);

516 517
 done:
  return g_list_reverse (list);
518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542
}

static void
gtk_tree_selection_count_selected_rows_helper (GtkRBTree *tree,
					       GtkRBNode *node,
					       gpointer   data)
{
  gint *count = (gint *)data;

  if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
    (*count)++;

  if (node->children)
    _gtk_rbtree_traverse (node->children, node->children->root,
			  G_PRE_ORDER,
			  gtk_tree_selection_count_selected_rows_helper, data);
}

/**
 * gtk_tree_selection_count_selected_rows:
 * @selection: A #GtkTreeSelection.
 *
 * Returns the number of rows that have been selected in @tree.
 *
 * Return value: The number of rows selected.
543 544
 * 
 * Since: 2.2
545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575
 **/
gint
gtk_tree_selection_count_selected_rows (GtkTreeSelection *selection)
{
  gint count = 0;

  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), 0);
  g_return_val_if_fail (selection->tree_view != NULL, 0);

  if (selection->tree_view->priv->tree == NULL ||
      selection->tree_view->priv->tree->root == NULL)
    return 0;

  if (selection->type == GTK_SELECTION_SINGLE ||
      selection->type == GTK_SELECTION_BROWSE)
    {
      if (gtk_tree_selection_get_selected (selection, NULL, NULL))
	return 1;
      else
	return 0;
    }

  _gtk_rbtree_traverse (selection->tree_view->priv->tree,
                        selection->tree_view->priv->tree->root,
			G_PRE_ORDER,
			gtk_tree_selection_count_selected_rows_helper,
			&count);

  return count;
}

576 577 578 579 580 581 582 583 584
/* gtk_tree_selection_selected_foreach helper */
static void
model_changed (gpointer data)
{
  gboolean *stop = (gboolean *)data;

  *stop = TRUE;
}

585 586 587 588 589
/**
 * gtk_tree_selection_selected_foreach:
 * @selection: A #GtkTreeSelection.
 * @func: The function to call for each selected node.
 * @data: user data to pass to the function.
590
 *
591 592 593
 * Calls a function for each selected node. Note that you cannot modify
 * the tree or selection from within this function. As a result,
 * gtk_tree_selection_get_selected_rows() might be more useful.
594
 **/
595 596 597 598 599 600 601 602
void
gtk_tree_selection_selected_foreach (GtkTreeSelection            *selection,
				     GtkTreeSelectionForeachFunc  func,
				     gpointer                     data)
{
  GtkTreePath *path;
  GtkRBTree *tree;
  GtkRBNode *node;
603
  GtkTreeIter iter;
604
  GtkTreeModel *model;
605

606
  gulong inserted_id, deleted_id, reordered_id, changed_id;
607
  gboolean stop = FALSE;
608

609 610 611 612 613 614 615 616
  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
  g_return_if_fail (selection->tree_view != NULL);

  if (func == NULL ||
      selection->tree_view->priv->tree == NULL ||
      selection->tree_view->priv->tree->root == NULL)
    return;

617 618
  if (selection->type == GTK_SELECTION_SINGLE ||
      selection->type == GTK_SELECTION_BROWSE)
619
    {
620 621 622 623 624 625 626
      if (gtk_tree_row_reference_valid (selection->tree_view->priv->anchor))
	{
	  path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor);
	  gtk_tree_model_get_iter (selection->tree_view->priv->model, &iter, path);
	  (* func) (selection->tree_view->priv->model, path, &iter, data);
	  gtk_tree_path_free (path);
	}
627 628 629
      return;
    }

630 631
  tree = selection->tree_view->priv->tree;
  node = selection->tree_view->priv->tree->root;
632
  
633 634 635
  while (node->left != tree->nil)
    node = node->left;

636 637 638
  model = selection->tree_view->priv->model;
  g_object_ref (model);

639
  /* connect to signals to monitor changes in treemodel */
640
  inserted_id = g_signal_connect_swapped (model, "row_inserted",
641 642
					  G_CALLBACK (model_changed),
				          &stop);
643
  deleted_id = g_signal_connect_swapped (model, "row_deleted",
644 645
					 G_CALLBACK (model_changed),
				         &stop);
646
  reordered_id = g_signal_connect_swapped (model, "rows_reordered",
647 648
					   G_CALLBACK (model_changed),
				           &stop);
649 650 651
  changed_id = g_signal_connect_swapped (selection->tree_view, "notify::model",
					 G_CALLBACK (model_changed), 
					 &stop);
652

653
  /* find the node internally */
654
  path = gtk_tree_path_new_first ();
655 656 657 658

  do
    {
      if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
659
        {
660 661
          gtk_tree_model_get_iter (model, &iter, path);
	  (* func) (model, path, &iter, data);
662
        }
663 664 665 666

      if (stop)
	goto out;

667 668 669 670
      if (node->children)
	{
	  tree = node->children;
	  node = tree->root;
671

672 673
	  while (node->left != tree->nil)
	    node = node->left;
674

675
	  gtk_tree_path_append_index (path, 0);
676 677 678 679
	}
      else
	{
	  gboolean done = FALSE;
680

681 682 683 684 685 686
	  do
	    {
	      node = _gtk_rbtree_next (tree, node);
	      if (node != NULL)
		{
		  done = TRUE;
687
		  gtk_tree_path_next (path);
688 689 690 691 692
		}
	      else
		{
		  node = tree->parent_node;
		  tree = tree->parent_tree;
693

694
		  if (tree == NULL)
695 696 697
		    {
		      /* we've run out of tree */
		      /* We're done with this function */
698 699

		      goto out;
700
		    }
701

702
		  gtk_tree_path_up (path);
703 704 705 706 707 708
		}
	    }
	  while (!done);
	}
    }
  while (TRUE);
709 710 711 712 713

out:
  if (path)
    gtk_tree_path_free (path);

714 715 716 717 718
  g_signal_handler_disconnect (model, inserted_id);
  g_signal_handler_disconnect (model, deleted_id);
  g_signal_handler_disconnect (model, reordered_id);
  g_signal_handler_disconnect (selection->tree_view, changed_id);
  g_object_unref (model);
719 720 721

  /* check if we have to spew a scary message */
  if (stop)
722 723 724 725
    g_warning ("The model has been modified from within gtk_tree_selection_selected_foreach.\n"
	       "This function is for observing the selections of the tree only.  If\n"
	       "you are trying to get all selected items from the tree, try using\n"
	       "gtk_tree_selection_get_selected_rows instead.\n");
726 727
}

728 729 730 731
/**
 * gtk_tree_selection_select_path:
 * @selection: A #GtkTreeSelection.
 * @path: The #GtkTreePath to be selected.
732
 *
733 734
 * Select the row at @path.
 **/
735 736 737 738 739 740
void
gtk_tree_selection_select_path (GtkTreeSelection *selection,
				GtkTreePath      *path)
{
  GtkRBNode *node;
  GtkRBTree *tree;
741
  gboolean ret;
Kristian Rietveld's avatar
Kristian Rietveld committed
742
  GtkTreeSelectMode mode = 0;
743 744 745 746 747

  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
  g_return_if_fail (selection->tree_view != NULL);
  g_return_if_fail (path != NULL);

748 749 750 751
  ret = _gtk_tree_view_find_node (selection->tree_view,
				  path,
				  &tree,
				  &node);
752

753 754
  if (node == NULL || GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED) ||
      ret == TRUE)
755 756
    return;

757
  if (selection->type == GTK_SELECTION_MULTIPLE)
Kristian Rietveld's avatar
Kristian Rietveld committed
758
    mode = GTK_TREE_SELECT_MODE_TOGGLE;
759 760 761 762 763

  _gtk_tree_selection_internal_select_node (selection,
					    node,
					    tree,
					    path,
Kristian Rietveld's avatar
Kristian Rietveld committed
764
                                            mode,
Kristian Rietveld's avatar
Kristian Rietveld committed
765
					    FALSE);
766 767
}

768 769 770 771
/**
 * gtk_tree_selection_unselect_path:
 * @selection: A #GtkTreeSelection.
 * @path: The #GtkTreePath to be unselected.
772
 *
773 774
 * Unselects the row at @path.
 **/
775 776 777 778 779 780
void
gtk_tree_selection_unselect_path (GtkTreeSelection *selection,
				  GtkTreePath      *path)
{
  GtkRBNode *node;
  GtkRBTree *tree;
781
  gboolean ret;
782 783 784 785 786

  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
  g_return_if_fail (selection->tree_view != NULL);
  g_return_if_fail (path != NULL);

787 788 789 790
  ret = _gtk_tree_view_find_node (selection->tree_view,
				  path,
				  &tree,
				  &node);
791

792 793
  if (node == NULL || !GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED) ||
      ret == TRUE)
794 795 796 797 798 799
    return;

  _gtk_tree_selection_internal_select_node (selection,
					    node,
					    tree,
					    path,
Kristian Rietveld's avatar
Kristian Rietveld committed
800
                                            GTK_TREE_SELECT_MODE_TOGGLE,
Kristian Rietveld's avatar
Kristian Rietveld committed
801
					    TRUE);
802 803
}

804
/**
805
 * gtk_tree_selection_select_iter:
806
 * @selection: A #GtkTreeSelection.
807
 * @iter: The #GtkTreeIter to be selected.
808
 *
809
 * Selects the specified iterator.
810
 **/
811
void
812 813
gtk_tree_selection_select_iter (GtkTreeSelection *selection,
				GtkTreeIter      *iter)
814 815 816 817 818 819
{
  GtkTreePath *path;

  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
  g_return_if_fail (selection->tree_view != NULL);
  g_return_if_fail (selection->tree_view->priv->model != NULL);
820
  g_return_if_fail (iter != NULL);
821 822

  path = gtk_tree_model_get_path (selection->tree_view->priv->model,
823
				  iter);
824 825 826 827 828 829 830 831 832

  if (path == NULL)
    return;

  gtk_tree_selection_select_path (selection, path);
  gtk_tree_path_free (path);
}


833
/**
834
 * gtk_tree_selection_unselect_iter:
835
 * @selection: A #GtkTreeSelection.
836
 * @iter: The #GtkTreeIter to be unselected.
837
 *
838
 * Unselects the specified iterator.
839
 **/
840
void
841 842
gtk_tree_selection_unselect_iter (GtkTreeSelection *selection,
				  GtkTreeIter      *iter)
843 844 845 846 847
{
  GtkTreePath *path;

  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
  g_return_if_fail (selection->tree_view != NULL);
848
  g_return_if_fail (selection->tree_view->priv->model != NULL);
849
  g_return_if_fail (iter != NULL);
850 851

  path = gtk_tree_model_get_path (selection->tree_view->priv->model,
852
				  iter);
853 854 855 856

  if (path == NULL)
    return;

857
  gtk_tree_selection_unselect_path (selection, path);
858 859 860
  gtk_tree_path_free (path);
}

861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876
/**
 * gtk_tree_selection_path_is_selected:
 * @selection: A #GtkTreeSelection.
 * @path: A #GtkTreePath to check selection on.
 * 
 * Returns %TRUE if the row pointed to by @path is currently selected.  If @path
 * does not point to a valid location, %FALSE is returned
 * 
 * Return value: %TRUE if @path is selected.
 **/
gboolean
gtk_tree_selection_path_is_selected (GtkTreeSelection *selection,
				     GtkTreePath      *path)
{
  GtkRBNode *node;
  GtkRBTree *tree;
877
  gboolean ret;
878 879 880 881

  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), FALSE);
  g_return_val_if_fail (path != NULL, FALSE);
  g_return_val_if_fail (selection->tree_view != NULL, FALSE);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
882 883 884

  if (selection->tree_view->priv->model == NULL)
    return FALSE;
885

886 887 888 889
  ret = _gtk_tree_view_find_node (selection->tree_view,
				  path,
				  &tree,
				  &node);
890

891 892
  if ((node == NULL) || !GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED) ||
      ret == TRUE)
893 894 895 896 897 898 899 900 901 902
    return FALSE;

  return TRUE;
}

/**
 * gtk_tree_selection_iter_is_selected:
 * @selection: A #GtkTreeSelection
 * @iter: A valid #GtkTreeIter
 * 
903
 * Returns %TRUE if the row at @iter is currently selected.
904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929
 * 
 * Return value: %TRUE, if @iter is selected
 **/
gboolean
gtk_tree_selection_iter_is_selected (GtkTreeSelection *selection,
				     GtkTreeIter      *iter)
{
  GtkTreePath *path;
  gboolean retval;

  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), FALSE);
  g_return_val_if_fail (iter != NULL, FALSE);
  g_return_val_if_fail (selection->tree_view != NULL, FALSE);
  g_return_val_if_fail (selection->tree_view->priv->model != NULL, FALSE);

  path = gtk_tree_model_get_path (selection->tree_view->priv->model, iter);
  if (path == NULL)
    return FALSE;

  retval = gtk_tree_selection_path_is_selected (selection, path);
  gtk_tree_path_free (path);

  return retval;
}


930 931 932 933 934 935 936 937 938
/* Wish I was in python, right now... */
struct _TempTuple {
  GtkTreeSelection *selection;
  gint dirty;
};

static void
select_all_helper (GtkRBTree  *tree,
		   GtkRBNode  *node,
939
		   gpointer    data)
940 941 942 943 944 945 946 947 948 949 950
{
  struct _TempTuple *tuple = data;

  if (node->children)
    _gtk_rbtree_traverse (node->children,
			  node->children->root,
			  G_PRE_ORDER,
			  select_all_helper,
			  data);
  if (!GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
    {
951
      tuple->dirty = gtk_tree_selection_real_select_node (tuple->selection, tree, node, TRUE) || tuple->dirty;
952 953 954
    }
}

955 956 957 958 959 960

/* We have a real_{un,}select_all function that doesn't emit the signal, so we
 * can use it in other places without fear of the signal being emitted.
 */
static gint
gtk_tree_selection_real_select_all (GtkTreeSelection *selection)
961 962
{
  struct _TempTuple *tuple;
963

964 965
  if (selection->tree_view->priv->tree == NULL)
    return FALSE;
966

967 968 969 970 971 972 973 974 975 976 977
  /* Mark all nodes selected */
  tuple = g_new (struct _TempTuple, 1);
  tuple->selection = selection;
  tuple->dirty = FALSE;

  _gtk_rbtree_traverse (selection->tree_view->priv->tree,
			selection->tree_view->priv->tree->root,
			G_PRE_ORDER,
			select_all_helper,
			tuple);
  if (tuple->dirty)
978 979
    {
      g_free (tuple);
980
      return TRUE;
981
    }
982 983
  g_free (tuple);
  return FALSE;
984 985
}

986 987 988
/**
 * gtk_tree_selection_select_all:
 * @selection: A #GtkTreeSelection.
989
 *
990
 * Selects all the nodes. @selection must be set to #GTK_SELECTION_MULTIPLE
991
 * mode.
992
 **/
993 994 995 996 997
void
gtk_tree_selection_select_all (GtkTreeSelection *selection)
{
  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
  g_return_if_fail (selection->tree_view != NULL);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
998 999

  if (selection->tree_view->priv->tree == NULL || selection->tree_view->priv->model == NULL)
1000
    return;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
1001

1002
  g_return_if_fail (selection->type == GTK_SELECTION_MULTIPLE);
1003 1004

  if (gtk_tree_selection_real_select_all (selection))
Manish Singh's avatar
Manish Singh committed
1005
    g_signal_emit (selection, tree_selection_signals[CHANGED], 0);
1006 1007 1008 1009 1010
}

static void
unselect_all_helper (GtkRBTree  *tree,
		     GtkRBNode  *node,
1011
		     gpointer    data)
1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022
{
  struct _TempTuple *tuple = data;

  if (node->children)
    _gtk_rbtree_traverse (node->children,
			  node->children->root,
			  G_PRE_ORDER,
			  unselect_all_helper,
			  data);
  if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
    {
1023
      tuple->dirty = gtk_tree_selection_real_select_node (tuple->selection, tree, node, FALSE) || tuple->dirty;
1024 1025 1026
    }
}

1027 1028
static gint
gtk_tree_selection_real_unselect_all (GtkTreeSelection *selection)
1029 1030 1031
{
  struct _TempTuple *tuple;

1032 1033
  if (selection->type == GTK_SELECTION_SINGLE ||
      selection->type == GTK_SELECTION_BROWSE)
1034 1035 1036
    {
      GtkRBTree *tree = NULL;
      GtkRBNode *node = NULL;
1037
      GtkTreePath *anchor_path;
1038

1039
      if (selection->tree_view->priv->anchor == NULL)
1040
	return FALSE;
1041

1042 1043 1044 1045
      anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor);

      if (anchor_path == NULL)
        return FALSE;
1046

1047
      _gtk_tree_view_find_node (selection->tree_view,
1048
                                anchor_path,
1049 1050
				&tree,
				&node);
1051 1052

      gtk_tree_path_free (anchor_path);
1053 1054 1055

      if (tree == NULL)
        return FALSE;
1056

1057
      if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
1058
	{
1059 1060 1061 1062 1063 1064
	  if (gtk_tree_selection_real_select_node (selection, tree, node, FALSE))
	    {
	      gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
	      selection->tree_view->priv->anchor = NULL;
	      return TRUE;
	    }
1065 1066
	}
      return FALSE;
1067
    }
1068
  else
1069
    {
1070 1071 1072
      tuple = g_new (struct _TempTuple, 1);
      tuple->selection = selection;
      tuple->dirty = FALSE;
1073

1074 1075 1076 1077 1078
      _gtk_rbtree_traverse (selection->tree_view->priv->tree,
                            selection->tree_view->priv->tree->root,
                            G_PRE_ORDER,
                            unselect_all_helper,
                            tuple);
1079

1080 1081 1082 1083 1084
      if (tuple->dirty)
        {
          g_free (tuple);
          return TRUE;
        }
1085
      g_free (tuple);
1086
      return FALSE;
1087
    }
1088 1089
}

1090 1091 1092
/**
 * gtk_tree_selection_unselect_all:
 * @selection: A #GtkTreeSelection.
1093
 *
1094 1095
 * Unselects all the nodes.
 **/
1096
void
1097
gtk_tree_selection_unselect_all (GtkTreeSelection *selection)
1098 1099 1100
{
  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
  g_return_if_fail (selection->tree_view != NULL);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
1101 1102

  if (selection->tree_view->priv->tree == NULL || selection->tree_view->priv->model == NULL)
1103 1104
    return;
  
1105
  if (gtk_tree_selection_real_unselect_all (selection))
Manish Singh's avatar
Manish Singh committed
1106
    g_signal_emit (selection, tree_selection_signals[CHANGED], 0);
1107 1108
}

1109 1110 1111 1112 1113 1114
enum
{
  RANGE_SELECT,
  RANGE_UNSELECT
};

1115
static gint
1116 1117
gtk_tree_selection_real_modify_range (GtkTreeSelection *selection,
                                      gint              mode,
1118 1119 1120 1121 1122
				      GtkTreePath      *start_path,
				      GtkTreePath      *end_path)
{
  GtkRBNode *start_node, *end_node;
  GtkRBTree *start_tree, *end_tree;
1123
  GtkTreePath *anchor_path = NULL;
1124
  gboolean dirty = FALSE;
1125 1126 1127

  switch (gtk_tree_path_compare (start_path, end_path))
    {
1128
    case 1:
1129 1130 1131 1132 1133 1134 1135 1136
      _gtk_tree_view_find_node (selection->tree_view,
				end_path,
				&start_tree,
				&start_node);
      _gtk_tree_view_find_node (selection->tree_view,
				start_path,
				&end_tree,
				&end_node);
1137
      anchor_path = start_path;
1138 1139 1140 1141 1142 1143 1144 1145
      break;
    case 0:
      _gtk_tree_view_find_node (selection->tree_view,
				start_path,
				&start_tree,
				&start_node);
      end_tree = start_tree;
      end_node = start_node;
1146
      anchor_path = start_path;
1147
      break;
1148
    case -1:
1149 1150 1151 1152 1153 1154 1155 1156
      _gtk_tree_view_find_node (selection->tree_view,
				start_path,
				&start_tree,
				&start_node);
      _gtk_tree_view_find_node (selection->tree_view,
				end_path,
				&end_tree,
				&end_node);
1157
      anchor_path = start_path;
1158 1159 1160
      break;
    }

1161 1162
  g_return_val_if_fail (start_node != NULL, FALSE);
  g_return_val_if_fail (end_node != NULL, FALSE);
1163

1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174
  if (anchor_path)
    {
      if (selection->tree_view->priv->anchor)
	gtk_tree_row_reference_free (selection->tree_view->priv->anchor);

      selection->tree_view->priv->anchor =
	gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view),
	                                  selection->tree_view->priv->model,
					  anchor_path);
    }

1175 1176
  do
    {
1177
      dirty |= gtk_tree_selection_real_select_node (selection, start_tree, start_node, (mode == RANGE_SELECT)?TRUE:FALSE);
1178 1179

      if (start_node == end_node)
1180
	break;
1181 1182 1183 1184 1185 1186 1187 1188 1189 1190

      if (start_node->children)
	{
	  start_tree = start_node->children;
	  start_node = start_tree->root;
	  while (start_node->left != start_tree->nil)
	    start_node = start_node->left;
	}
      else
	{
1191 1192
	  _gtk_rbtree_next_full (start_tree, start_node, &start_tree, &start_node);
	  if (start_tree == NULL)
1193
	    {
1194 1195 1196
	      /* we just ran out of tree.  That means someone passed in bogus values.
	       */
	      return dirty;
1197 1198 1199 1200
	    }
	}
    }
  while (TRUE);
1201 1202

  return dirty;
1203 1204
}

1205 1206 1207 1208 1209
/**
 * gtk_tree_selection_select_range:
 * @selection: A #GtkTreeSelection.
 * @start_path: The initial node of the range.
 * @end_path: The final node of the range.
1210
 *
1211
 * Selects a range of nodes, determined by @start_path and @end_path inclusive.
1212
 * @selection must be set to #GTK_SELECTION_MULTIPLE mode. 
1213
 **/
1214 1215 1216 1217 1218 1219 1220
void
gtk_tree_selection_select_range (GtkTreeSelection *selection,
				 GtkTreePath      *start_path,
				 GtkTreePath      *end_path)
{
  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
  g_return_if_fail (selection->tree_view != NULL);
1221
  g_return_if_fail (selection->type == GTK_SELECTION_MULTIPLE);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
1222
  g_return_if_fail (selection->tree_view->priv->model != NULL);
1223

1224
  if (gtk_tree_selection_real_modify_range (selection, RANGE_SELECT, start_path, end_path))
Manish Singh's avatar
Manish Singh committed
1225
    g_signal_emit (selection, tree_selection_signals[CHANGED], 0);
1226 1227 1228 1229 1230 1231 1232 1233 1234 1235
}

/**
 * gtk_tree_selection_unselect_range:
 * @selection: A #GtkTreeSelection.
 * @start_path: The initial node of the range.
 * @end_path: The initial node of the range.
 *
 * Unselects a range of nodes, determined by @start_path and @end_path
 * inclusive.
1236 1237
 *
 * Since: 2.2
1238 1239 1240 1241 1242 1243 1244 1245
 **/
void
gtk_tree_selection_unselect_range (GtkTreeSelection *selection,
                                   GtkTreePath      *start_path,
				   GtkTreePath      *end_path)
{
  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
  g_return_if_fail (selection->tree_view != NULL);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
1246
  g_return_if_fail (selection->tree_view->priv->model != NULL);
1247 1248

  if (gtk_tree_selection_real_modify_range (selection, RANGE_UNSELECT, start_path, end_path))
Manish Singh's avatar
Manish Singh committed
1249
    g_signal_emit (selection, tree_selection_signals[CHANGED], 0);
1250
}
1251

1252 1253 1254 1255
gboolean
_gtk_tree_selection_row_is_selectable (GtkTreeSelection *selection,
				       GtkRBNode        *node,
				       GtkTreePath      *path)
1256
{
1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271
  GtkTreeIter iter;
  gboolean sensitive = FALSE;

  if (!gtk_tree_model_get_iter (selection->tree_view->priv->model, &iter, path))
    sensitive = TRUE;

  if (!sensitive && selection->tree_view->priv->row_separator_func)
    {
      /* never allow separators to be selected */
      if ((* selection->tree_view->priv->row_separator_func) (selection->tree_view->priv->model,
							      &iter,
							      selection->tree_view->priv->row_separator_data))
	return FALSE;
    }

1272 1273 1274 1275 1276 1277 1278 1279 1280
  if (selection->user_func)
    return (*selection->user_func) (selection, selection->tree_view->priv->model, path,
				    GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED),
				    selection->user_data);
  else
    return TRUE;
}


1281 1282
/* Called internally by gtktreeview.c It handles actually selecting the tree.
 */
Kristian Rietveld's avatar
Kristian Rietveld committed
1283

1284
/*
Kristian Rietveld's avatar
Kristian Rietveld committed
1285 1286 1287 1288
 * docs about the 'override_browse_mode', we set this flag when we want to
 * unset select the node and override the select browse mode behaviour (that is
 * 'one node should *always* be selected').
 */
1289 1290 1291 1292 1293
void
_gtk_tree_selection_internal_select_node (GtkTreeSelection *selection,
					  GtkRBNode        *node,
					  GtkRBTree        *tree,
					  GtkTreePath      *path,
Kristian Rietveld's avatar
Kristian Rietveld committed
1294
                                          GtkTreeSelectMode mode,
Kristian Rietveld's avatar
Kristian Rietveld committed
1295
					  gboolean          override_browse_mode)
1296 1297
{
  gint flags;
1298
  gint dirty = FALSE;
1299
  GtkTreePath *anchor_path = NULL;
1300

1301 1302
  if (selection->type == GTK_SELECTION_NONE)
    return;
1303

1304 1305 1306
  if (selection->tree_view->priv->anchor)
    anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor);

1307 1308
  if (selection->type == GTK_SELECTION_SINGLE ||
      selection->type == GTK_SELECTION_BROWSE)
1309
    {
Kristian Rietveld's avatar
Kristian Rietveld committed
1310 1311 1312 1313 1314
      /* just unselect */
      if (selection->type == GTK_SELECTION_BROWSE && override_browse_mode)
        {
	  dirty = gtk_tree_selection_real_unselect_all (selection);
	}
1315
      /* Did we try to select the same node again? */
Kristian Rietveld's avatar
Kristian Rietveld committed
1316 1317
      else if (selection->type == GTK_SELECTION_SINGLE &&
	       anchor_path && gtk_tree_path_compare (path, anchor_path) == 0)
1318
	{
Kristian Rietveld's avatar
Kristian Rietveld committed
1319
	  if ((mode & GTK_TREE_SELECT_MODE_TOGGLE) == GTK_TREE_SELECT_MODE_TOGGLE)
1320 1321 1322 1323 1324 1325 1326 1327
	    {
	      dirty = gtk_tree_selection_real_unselect_all (selection);
	    }
	}
      else
	{
	  if (anchor_path)
	    {
1328 1329
	      /* We only want to select the new node if we can unselect the old one,
	       * and we can select the new one. */
1330
	      dirty = _gtk_tree_selection_row_is_selectable (selection, node, path);
1331 1332 1333 1334 1335 1336 1337 1338 1339

	      /* if dirty is FALSE, we weren't able to select the new one, otherwise, we try to
	       * unselect the new one
	       */
	      if (dirty)
		dirty = gtk_tree_selection_real_unselect_all (selection);

	      /* if dirty is TRUE at this point, we successfully unselected the
	       * old one, and can then select the new one */
1340 1341 1342
	      if (dirty)
		{
		  if (selection->tree_view->priv->anchor)
1343 1344 1345 1346 1347
                    {
                      gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
                      selection->tree_view->priv->anchor = NULL;
                    }

1348 1349 1350
		  if (gtk_tree_selection_real_select_node (selection, tree, node, TRUE))
		    {
		      selection->tree_view->priv->anchor =
1351
			gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path);
1352 1353 1354 1355 1356 1357 1358 1359
		    }
		}
	    }
	  else
	    {
	      if (gtk_tree_selection_real_select_node (selection, tree, node, TRUE))
		{
		  dirty = TRUE;
1360 1361 1362
		  if (selection->tree_view->priv->anchor)
		    gtk_tree_row_reference_free (selection->tree_view->priv->anchor);

1363
		  selection->tree_view->priv->anchor =
1364
		    gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path);
1365 1366 1367
		}
	    }
	}
1368
    }
1369
  else if (selection->type == GTK_SELECTION_MULTIPLE)
1370
    {
Kristian Rietveld's avatar
Kristian Rietveld committed
1371 1372
      if ((mode & GTK_TREE_SELECT_MODE_EXTEND) == GTK_TREE_SELECT_MODE_EXTEND
          && (anchor_path == NULL))
1373 1374 1375
	{
	  if (selection->tree_view->priv->anchor)
	    gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
1376

1377
	  selection->tree_view->priv->anchor =
1378
	    gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path);
1379 1380
	  dirty = gtk_tree_selection_real_select_node (selection, tree, node, TRUE);
	}
Kristian Rietveld's avatar
Kristian Rietveld committed
1381
      else if ((mode & (GTK_TREE_SELECT_MODE_EXTEND | GTK_TREE_SELECT_MODE_TOGGLE)) == (GTK_TREE_SELECT_MODE_EXTEND | GTK_TREE_SELECT_MODE_TOGGLE))
1382 1383 1384 1385 1386
	{
	  gtk_tree_selection_select_range (selection,
					   anchor_path,
					   path);
	}
Kristian Rietveld's avatar
Kristian Rietveld committed
1387
      else if ((mode & GTK_TREE_SELECT_MODE_TOGGLE) == GTK_TREE_SELECT_MODE_TOGGLE)
1388 1389 1390 1391
	{
	  flags = node->flags;
	  if (selection->tree_view->priv->anchor)
	    gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
1392

1393
	  selection->tree_view->priv->anchor =
1394
	    gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path);
1395

1396 1397 1398 1399 1400
	  if ((flags & GTK_RBNODE_IS_SELECTED) == GTK_RBNODE_IS_SELECTED)
	    dirty |= gtk_tree_selection_real_select_node (selection, tree, node, FALSE);
	  else
	    dirty |= gtk_tree_selection_real_select_node (selection, tree, node, TRUE);
	}
Kristian Rietveld's avatar
Kristian Rietveld committed
1401
      else if ((mode & GTK_TREE_SELECT_MODE_EXTEND) == GTK_TREE_SELECT_MODE_EXTEND)
1402 1403
	{
	  dirty = gtk_tree_selection_real_unselect_all (selection);
1404 1405
	  dirty |= gtk_tree_selection_real_modify_range (selection,
                                                         RANGE_SELECT,
1406 1407 1408