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

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
  GtkTreeSelection *selection = GTK_TREE_SELECTION (object);

  if (selection->destroy)
79
    selection->destroy (selection->user_data);
80 81

  /* chain parent_class' handler */
Matthias Clasen's avatar
Matthias Clasen committed
82
  G_OBJECT_CLASS (gtk_tree_selection_parent_class)->finalize (object);
83 84
}

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

Manish Singh's avatar
Manish Singh committed
98
  selection = g_object_new (GTK_TYPE_TREE_SELECTION, NULL);
99 100 101 102

  return selection;
}

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

  g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);

119 120
  selection = _gtk_tree_selection_new ();
  _gtk_tree_selection_set_tree_view (selection, tree_view);
121 122 123 124

  return selection;
}

125
/**
126
 * _gtk_tree_selection_set_tree_view:
127 128
 * @selection: A #GtkTreeSelection.
 * @tree_view: The #GtkTreeView.
129
 *
130 131 132
 * Sets the #GtkTreeView of @selection.  This function should not be invoked, as
 * it is used internally by #GtkTreeView.
 **/
133
void
134 135
_gtk_tree_selection_set_tree_view (GtkTreeSelection *selection,
                                   GtkTreeView      *tree_view)
136 137 138 139 140 141 142 143
{
  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;
}

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

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

163 164 165 166 167 168 169 170 171
  
  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;
172 173 174

      gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
      selection->tree_view->priv->anchor = NULL;
175 176 177
    }
  else if (type == GTK_SELECTION_SINGLE ||
	   type == GTK_SELECTION_BROWSE)
178 179 180 181
    {
      GtkRBTree *tree = NULL;
      GtkRBNode *node = NULL;
      gint selected = FALSE;
182
      GtkTreePath *anchor_path = NULL;
183 184 185

      if (selection->tree_view->priv->anchor)
	{
186 187 188 189 190 191 192 193
          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);
194

195 196 197
              if (node && GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
                selected = TRUE;
            }
198
	}
199 200

      /* We do this so that we unconditionally unset all rows
201
       */
202 203
      tmp_func = selection->user_func;
      selection->user_func = NULL;
204
      gtk_tree_selection_unselect_all (selection);
205
      selection->user_func = tmp_func;
206

207
      if (node && selected)
208 209 210 211
	_gtk_tree_selection_internal_select_node (selection,
						  node,
						  tree,
						  anchor_path,
Kristian Rietveld's avatar
Kristian Rietveld committed
212
                                                  0,
Kristian Rietveld's avatar
Kristian Rietveld committed
213
						  FALSE);
214 215
      if (anchor_path)
	gtk_tree_path_free (anchor_path);
216
    }
217

218 219 220
  selection->type = type;
}

221 222 223 224 225 226 227 228 229
/**
 * 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
 **/
230
GtkSelectionMode
231 232
gtk_tree_selection_get_mode (GtkTreeSelection *selection)
{
233
  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), GTK_SELECTION_SINGLE);
234 235 236 237

  return selection->type;
}

238 239 240
/**
 * gtk_tree_selection_set_select_function:
 * @selection: A #GtkTreeSelection.
241 242 243
 * @func: The selection function. May be %NULL
 * @data: The selection function's data. May be %NULL
 * @destroy: The destroy function for user data.  May be %NULL
244
 *
245 246 247 248 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. 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.
 */
252 253 254
void
gtk_tree_selection_set_select_function (GtkTreeSelection     *selection,
					GtkTreeSelectionFunc  func,
255
					gpointer              data,
256
					GDestroyNotify        destroy)
257 258 259
{
  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));

260
  if (selection->destroy)
261
    selection->destroy (selection->user_data);
262

263 264
  selection->user_func = func;
  selection->user_data = data;
265
  selection->destroy = destroy;
266 267
}

268 269 270 271 272 273 274 275
/**
 * gtk_tree_selection_get_select_function:
 * @selection: A #GtkTreeSelection.
 *
 * Returns the current selection function.
 *
 * Return value: The function.
 *
276
 * Since: 2.14
277 278 279 280 281 282 283 284 285
 **/
GtkTreeSelectionFunc
gtk_tree_selection_get_select_function (GtkTreeSelection *selection)
{
  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), NULL);

  return selection->user_func;
}

286 287 288
/**
 * gtk_tree_selection_get_user_data:
 * @selection: A #GtkTreeSelection.
289
 *
290
 * Returns the user data for the selection function.
291
 *
292 293
 * Return value: The user data.
 **/
294 295 296 297 298 299 300 301
gpointer
gtk_tree_selection_get_user_data (GtkTreeSelection *selection)
{
  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), NULL);

  return selection->user_data;
}

302 303 304 305 306 307 308 309 310
/**
 * 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
311 312 313 314 315 316 317
gtk_tree_selection_get_tree_view (GtkTreeSelection *selection)
{
  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), NULL);

  return selection->tree_view;
}

318 319 320
/**
 * gtk_tree_selection_get_selected:
 * @selection: A #GtkTreeSelection.
321
 * @model: (out) (allow-none) (transfer none): A pointer to set to the #GtkTreeModel, or NULL.
322
 * @iter: (out) (allow-none): The #GtkTreeIter, or NULL.
323
 *
324
 * Sets @iter to the currently selected node if @selection is set to
325 326 327 328
 * #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.
329
 *
Jonathan Blandford's avatar
Jonathan Blandford committed
330
 * Return value: TRUE, if there is a selected node.
331
 **/
332
gboolean
333 334 335
gtk_tree_selection_get_selected (GtkTreeSelection  *selection,
				 GtkTreeModel     **model,
				 GtkTreeIter       *iter)
336 337 338
{
  GtkRBTree *tree;
  GtkRBNode *node;
339 340
  GtkTreePath *anchor_path;
  gboolean retval;
Kristian Rietveld's avatar
Kristian Rietveld committed
341
  gboolean found_node;
342

343
  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), FALSE);
344
  g_return_val_if_fail (selection->type != GTK_SELECTION_MULTIPLE, FALSE);
345
  g_return_val_if_fail (selection->tree_view != NULL, FALSE);
346

347 348 349 350
  /* Clear the iter */
  if (iter)
    memset (iter, 0, sizeof (GtkTreeIter));

351 352
  if (model)
    *model = selection->tree_view->priv->model;
353

354
  if (selection->tree_view->priv->anchor == NULL)
355
    return FALSE;
356 357 358 359 360

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

  if (anchor_path == NULL)
    return FALSE;
361

362
  retval = FALSE;
363

Kristian Rietveld's avatar
Kristian Rietveld committed
364 365 366 367 368 369
  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))
370
    {
Kristian Rietveld's avatar
Kristian Rietveld committed
371 372
      /* we only want to return the anchor if it exists in the rbtree and
       * is selected.
373
       */
Kristian Rietveld's avatar
Kristian Rietveld committed
374 375 376 377 378 379
      if (iter == NULL)
	retval = TRUE;
      else
        retval = gtk_tree_model_get_iter (selection->tree_view->priv->model,
                                          iter,
                                          anchor_path);
380 381 382
    }
  else
    {
Kristian Rietveld's avatar
Kristian Rietveld committed
383 384 385
      /* We don't want to return the anchor if it isn't actually selected.
       */
      retval = FALSE;
386
    }
387

388
  gtk_tree_path_free (anchor_path);
389

390
  return retval;
391 392
}

393 394 395
/**
 * gtk_tree_selection_get_selected_rows:
 * @selection: A #GtkTreeSelection.
396
 * @model: (out) (allow-none) (transfer none): A pointer to set to the #GtkTreeModel, or %NULL.
397 398 399
 *
 * 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
400
 * want to convert the returned list into a list of #GtkTreeRowReference<!-- -->s.
401
 * To do this, you can use gtk_tree_row_reference_new().
402 403
 *
 * To free the return value, use:
Matthias Clasen's avatar
Matthias Clasen committed
404
 * |[
405
 * g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
406
 * g_list_free (list);
Matthias Clasen's avatar
Matthias Clasen committed
407
 * ]|
408
 *
409
 * Return value: (element-type GtkTreePath) (transfer full): A #GList containing a #GtkTreePath for each selected row.
410 411
 *
 * Since: 2.2
412 413 414 415 416 417 418 419 420 421 422 423 424
 **/
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);

425 426 427
  if (model)
    *model = selection->tree_view->priv->model;

428 429 430 431 432 433 434 435 436 437 438 439 440 441
  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;

442
	  path = gtk_tree_model_get_path (selection->tree_view->priv->model, &iter);
443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460
	  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))
461
	list = g_list_prepend (list, gtk_tree_path_copy (path));
462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492

      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);
493 494

		      goto done; 
495 496 497 498 499 500 501 502 503 504 505 506
		    }

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

  gtk_tree_path_free (path);

507 508
 done:
  return g_list_reverse (list);
509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533
}

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.
534 535
 * 
 * Since: 2.2
536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566
 **/
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;
}

567 568 569 570 571 572 573 574 575
/* gtk_tree_selection_selected_foreach helper */
static void
model_changed (gpointer data)
{
  gboolean *stop = (gboolean *)data;

  *stop = TRUE;
}

576 577 578 579 580
/**
 * 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.
581
 *
582 583 584
 * 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.
585
 **/
586 587 588 589 590 591 592 593
void
gtk_tree_selection_selected_foreach (GtkTreeSelection            *selection,
				     GtkTreeSelectionForeachFunc  func,
				     gpointer                     data)
{
  GtkTreePath *path;
  GtkRBTree *tree;
  GtkRBNode *node;
594
  GtkTreeIter iter;
595
  GtkTreeModel *model;
596

597
  gulong inserted_id, deleted_id, reordered_id, changed_id;
598
  gboolean stop = FALSE;
599

600 601 602 603 604 605 606 607
  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;

608 609
  if (selection->type == GTK_SELECTION_SINGLE ||
      selection->type == GTK_SELECTION_BROWSE)
610
    {
611 612 613 614 615 616 617
      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);
	}
618 619 620
      return;
    }

621 622
  tree = selection->tree_view->priv->tree;
  node = selection->tree_view->priv->tree->root;
623
  
624 625 626
  while (node->left != tree->nil)
    node = node->left;

627 628 629
  model = selection->tree_view->priv->model;
  g_object_ref (model);

630
  /* connect to signals to monitor changes in treemodel */
631
  inserted_id = g_signal_connect_swapped (model, "row-inserted",
632 633
					  G_CALLBACK (model_changed),
				          &stop);
634
  deleted_id = g_signal_connect_swapped (model, "row-deleted",
635 636
					 G_CALLBACK (model_changed),
				         &stop);
637
  reordered_id = g_signal_connect_swapped (model, "rows-reordered",
638 639
					   G_CALLBACK (model_changed),
				           &stop);
640 641 642
  changed_id = g_signal_connect_swapped (selection->tree_view, "notify::model",
					 G_CALLBACK (model_changed), 
					 &stop);
643

644
  /* find the node internally */
645
  path = gtk_tree_path_new_first ();
646 647 648 649

  do
    {
      if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
650
        {
651 652
          gtk_tree_model_get_iter (model, &iter, path);
	  (* func) (model, path, &iter, data);
653
        }
654 655 656 657

      if (stop)
	goto out;

658 659 660 661
      if (node->children)
	{
	  tree = node->children;
	  node = tree->root;
662

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

666
	  gtk_tree_path_append_index (path, 0);
667 668 669 670
	}
      else
	{
	  gboolean done = FALSE;
671

672 673 674 675 676 677
	  do
	    {
	      node = _gtk_rbtree_next (tree, node);
	      if (node != NULL)
		{
		  done = TRUE;
678
		  gtk_tree_path_next (path);
679 680 681 682 683
		}
	      else
		{
		  node = tree->parent_node;
		  tree = tree->parent_tree;
684

685
		  if (tree == NULL)
686 687 688
		    {
		      /* we've run out of tree */
		      /* We're done with this function */
689 690

		      goto out;
691
		    }
692

693
		  gtk_tree_path_up (path);
694 695 696 697 698 699
		}
	    }
	  while (!done);
	}
    }
  while (TRUE);
700 701 702 703 704

out:
  if (path)
    gtk_tree_path_free (path);

705 706 707 708 709
  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);
710 711 712

  /* check if we have to spew a scary message */
  if (stop)
713 714 715 716
    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");
717 718
}

719 720 721 722
/**
 * gtk_tree_selection_select_path:
 * @selection: A #GtkTreeSelection.
 * @path: The #GtkTreePath to be selected.
723
 *
724 725
 * Select the row at @path.
 **/
726 727 728 729 730 731
void
gtk_tree_selection_select_path (GtkTreeSelection *selection,
				GtkTreePath      *path)
{
  GtkRBNode *node;
  GtkRBTree *tree;
732
  gboolean ret;
Kristian Rietveld's avatar
Kristian Rietveld committed
733
  GtkTreeSelectMode mode = 0;
734 735 736 737 738

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

739 740 741 742
  ret = _gtk_tree_view_find_node (selection->tree_view,
				  path,
				  &tree,
				  &node);
743

744 745
  if (node == NULL || GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED) ||
      ret == TRUE)
746 747
    return;

748
  if (selection->type == GTK_SELECTION_MULTIPLE)
Kristian Rietveld's avatar
Kristian Rietveld committed
749
    mode = GTK_TREE_SELECT_MODE_TOGGLE;
750 751 752 753 754

  _gtk_tree_selection_internal_select_node (selection,
					    node,
					    tree,
					    path,
Kristian Rietveld's avatar
Kristian Rietveld committed
755
                                            mode,
Kristian Rietveld's avatar
Kristian Rietveld committed
756
					    FALSE);
757 758
}

759 760 761 762
/**
 * gtk_tree_selection_unselect_path:
 * @selection: A #GtkTreeSelection.
 * @path: The #GtkTreePath to be unselected.
763
 *
764 765
 * Unselects the row at @path.
 **/
766 767 768 769 770 771
void
gtk_tree_selection_unselect_path (GtkTreeSelection *selection,
				  GtkTreePath      *path)
{
  GtkRBNode *node;
  GtkRBTree *tree;
772
  gboolean ret;
773 774 775 776 777

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

778 779 780 781
  ret = _gtk_tree_view_find_node (selection->tree_view,
				  path,
				  &tree,
				  &node);
782

783 784
  if (node == NULL || !GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED) ||
      ret == TRUE)
785 786 787 788 789 790
    return;

  _gtk_tree_selection_internal_select_node (selection,
					    node,
					    tree,
					    path,
Kristian Rietveld's avatar
Kristian Rietveld committed
791
                                            GTK_TREE_SELECT_MODE_TOGGLE,
Kristian Rietveld's avatar
Kristian Rietveld committed
792
					    TRUE);
793 794
}

795
/**
796
 * gtk_tree_selection_select_iter:
797
 * @selection: A #GtkTreeSelection.
798
 * @iter: The #GtkTreeIter to be selected.
799
 *
800
 * Selects the specified iterator.
801
 **/
802
void
803 804
gtk_tree_selection_select_iter (GtkTreeSelection *selection,
				GtkTreeIter      *iter)
805 806 807 808 809 810
{
  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);
811
  g_return_if_fail (iter != NULL);
812 813

  path = gtk_tree_model_get_path (selection->tree_view->priv->model,
814
				  iter);
815 816 817 818 819 820 821 822 823

  if (path == NULL)
    return;

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


824
/**
825
 * gtk_tree_selection_unselect_iter:
826
 * @selection: A #GtkTreeSelection.
827
 * @iter: The #GtkTreeIter to be unselected.
828
 *
829
 * Unselects the specified iterator.
830
 **/
831
void
832 833
gtk_tree_selection_unselect_iter (GtkTreeSelection *selection,
				  GtkTreeIter      *iter)
834 835 836 837 838
{
  GtkTreePath *path;

  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
  g_return_if_fail (selection->tree_view != NULL);
839
  g_return_if_fail (selection->tree_view->priv->model != NULL);
840
  g_return_if_fail (iter != NULL);
841 842

  path = gtk_tree_model_get_path (selection->tree_view->priv->model,
843
				  iter);
844 845 846 847

  if (path == NULL)
    return;

848
  gtk_tree_selection_unselect_path (selection, path);
849 850 851
  gtk_tree_path_free (path);
}

852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867
/**
 * 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;
868
  gboolean ret;
869 870 871 872

  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
873 874 875

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

877 878 879 880
  ret = _gtk_tree_view_find_node (selection->tree_view,
				  path,
				  &tree,
				  &node);
881

882 883
  if ((node == NULL) || !GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED) ||
      ret == TRUE)
884 885 886 887 888 889 890 891 892 893
    return FALSE;

  return TRUE;
}

/**
 * gtk_tree_selection_iter_is_selected:
 * @selection: A #GtkTreeSelection
 * @iter: A valid #GtkTreeIter
 * 
894
 * Returns %TRUE if the row at @iter is currently selected.
895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920
 * 
 * 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;
}


921 922 923 924 925 926 927 928 929
/* Wish I was in python, right now... */
struct _TempTuple {
  GtkTreeSelection *selection;
  gint dirty;
};

static void
select_all_helper (GtkRBTree  *tree,
		   GtkRBNode  *node,
930
		   gpointer    data)
931 932 933 934 935 936 937 938 939 940 941
{
  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))
    {
942
      tuple->dirty = gtk_tree_selection_real_select_node (tuple->selection, tree, node, TRUE) || tuple->dirty;
943 944 945
    }
}

946 947 948 949 950 951

/* 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)
952 953
{
  struct _TempTuple *tuple;
954

955 956
  if (selection->tree_view->priv->tree == NULL)
    return FALSE;
957

958 959 960 961 962 963 964 965 966 967 968
  /* 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)
969 970
    {
      g_free (tuple);
971
      return TRUE;
972
    }
973 974
  g_free (tuple);
  return FALSE;
975 976
}

977 978 979
/**
 * gtk_tree_selection_select_all:
 * @selection: A #GtkTreeSelection.
980
 *
981
 * Selects all the nodes. @selection must be set to #GTK_SELECTION_MULTIPLE
982
 * mode.
983
 **/
984 985 986 987 988
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
989 990

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

993
  g_return_if_fail (selection->type == GTK_SELECTION_MULTIPLE);
994 995

  if (gtk_tree_selection_real_select_all (selection))
Manish Singh's avatar
Manish Singh committed
996
    g_signal_emit (selection, tree_selection_signals[CHANGED], 0);
997 998 999 1000 1001
}

static void
unselect_all_helper (GtkRBTree  *tree,
		     GtkRBNode  *node,
1002
		     gpointer    data)
1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013
{
  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))
    {
1014
      tuple->dirty = gtk_tree_selection_real_select_node (tuple->selection, tree, node, FALSE) || tuple->dirty;
1015 1016 1017
    }
}

1018
static gboolean
1019
gtk_tree_selection_real_unselect_all (GtkTreeSelection *selection)
1020 1021 1022
{
  struct _TempTuple *tuple;

1023 1024
  if (selection->type == GTK_SELECTION_SINGLE ||
      selection->type == GTK_SELECTION_BROWSE)
1025 1026 1027
    {
      GtkRBTree *tree = NULL;
      GtkRBNode *node = NULL;
1028
      GtkTreePath *anchor_path;
1029

1030
      if (selection->tree_view->priv->anchor == NULL)
1031
	return FALSE;
1032

1033 1034 1035 1036
      anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor);

      if (anchor_path == NULL)
        return FALSE;
1037

1038
      _gtk_tree_view_find_node (selection->tree_view,
1039
                                anchor_path,
1040 1041
				&tree,
				&node);
1042 1043

      gtk_tree_path_free (anchor_path);
1044 1045 1046

      if (tree == NULL)
        return FALSE;
1047

1048
      if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
1049
	{
1050 1051 1052 1053 1054 1055
	  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;
	    }
1056 1057
	}
      return FALSE;
1058
    }
1059
  else
1060
    {
1061 1062 1063
      tuple = g_new (struct _TempTuple, 1);
      tuple->selection = selection;
      tuple->dirty = FALSE;
1064

1065 1066 1067 1068 1069
      _gtk_rbtree_traverse (selection->tree_view->priv->tree,
                            selection->tree_view->priv->tree->root,
                            G_PRE_ORDER,
                            unselect_all_helper,
                            tuple);
1070

1071 1072 1073 1074 1075
      if (tuple->dirty)
        {
          g_free (tuple);
          return TRUE;
        }
1076
      g_free (tuple);
1077
      return FALSE;
1078
    }
1079 1080
}

1081 1082 1083
/**
 * gtk_tree_selection_unselect_all:
 * @selection: A #GtkTreeSelection.
1084
 *
1085 1086
 * Unselects all the nodes.
 **/
1087
void
1088
gtk_tree_selection_unselect_all (GtkTreeSelection *selection)
1089 1090 1091
{
  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
1092 1093

  if (selection->tree_view->priv->tree == NULL || selection->tree_view->priv->model == NULL)
1094 1095
    return;
  
1096
  if (gtk_tree_selection_real_unselect_all (selection))
Manish Singh's avatar
Manish Singh committed
1097
    g_signal_emit (selection, tree_selection_signals[CHANGED], 0);
1098 1099
}

1100 1101 1102 1103 1104 1105
enum
{
  RANGE_SELECT,
  RANGE_UNSELECT
};

1106
static gint
1107 1108
gtk_tree_selection_real_modify_range (GtkTreeSelection *selection,
                                      gint              mode,
1109 1110 1111 1112 1113
				      GtkTreePath      *start_path,
				      GtkTreePath      *end_path)
{
  GtkRBNode *start_node, *end_node;
  GtkRBTree *start_tree, *end_tree;
1114
  GtkTreePath *anchor_path = NULL;
1115
  gboolean dirty = FALSE;
1116 1117 1118

  switch (gtk_tree_path_compare (start_path, end_path))
    {
1119
    case 1:
1120 1121 1122 1123 1124 1125 1126 1127
      _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);
1128
      anchor_path = start_path;
1129 1130 1131 1132 1133 1134 1135 1136
      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;
1137
      anchor_path = start_path;
1138
      break;
1139
    case -1:
1140 1141 1142 1143 1144 1145 1146 1147
      _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);
1148
      anchor_path = start_path;
1149 1150 1151
      break;
    }

1152 1153
  g_return_val_if_fail (start_node != NULL, FALSE);
  g_return_val_if_fail (end_node != NULL, FALSE);
1154

1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165
  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);
    }

1166 1167
  do
    {
1168
      dirty |= gtk_tree_selection_real_select_node (selection, start_tree, start_node, (mode == RANGE_SELECT)?TRUE:FALSE);
1169 1170

      if (start_node == end_node)
1171
	break;
1172 1173 1174 1175 1176 1177 1178 1179 1180 1181

      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
	{
1182 1183
	  _gtk_rbtree_next_full (start_tree, start_node, &start_tree, &start_node);
	  if (start_tree == NULL)
1184
	    {
1185 1186 1187
	      /* we just ran out of tree.  That means someone passed in bogus values.
	       */
	      return dirty;
1188 1189 1190 1191
	    }
	}
    }
  while (TRUE);
1192 1193

  return dirty;
1194 1195
}

1196 1197 1198 1199 1200
/**
 * gtk_tree_selection_select_range:
 * @selection: A #GtkTreeSelection.
 * @start_path: The initial node of the range.
 * @end_path: The final node of the range.
1201
 *
1202
 * Selects a range of nodes, determined by @start_path and @end_path inclusive.
1203
 * @selection must be set to #GTK_SELECTION_MULTIPLE mode. 
1204
 **/
1205 1206 1207 1208 1209 1210 1211
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);
1212
  g_return_if_fail (selection->type == GTK_SELECTION_MULTIPLE);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
1213
  g_return_if_fail (selection->tree_view->priv->model != NULL);
1214

1215
  if (gtk_tree_selection_real_modify_range (selection, RANGE_SELECT, start_path, end_path))
Manish Singh's avatar
Manish Singh committed
1216
    g_signal_emit (selection, tree_selection_signals[CHANGED], 0);
1217 1218 1219 1220 1221 1222 1223 1224 1225 1226
}

/**
 * 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.
1227 1228
 *
 * Since: 2.2
1229 1230 1231 1232 1233 1234 1235 1236
 **/
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
1237
  g_return_if_fail (selection->tree_view->priv->model != NULL);
1238 1239

  if (gtk_tree_selection_real_modify_range (selection, RANGE_UNSELECT, start_path, end_path))
Manish Singh's avatar
Manish Singh committed
1240
    g_signal_emit (selection, tree_selection_signals[CHANGED], 0);
1241
}
1242

1243 1244 1245 1246
gboolean
_gtk_tree_selection_row_is_selectable (GtkTreeSelection *selection,
				       GtkRBNode        *node,
				       GtkTreePath      *path)
1247
{
1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262
  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;
    }

1263 1264 1265 1266 1267 1268 1269 1270 1271
  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;
}


1272 1273
/* Called internally by gtktreeview.c It handles actually selecting the tree.
 */
Kristian Rietveld's avatar
Kristian Rietveld committed
1274

1275
/*
Kristian Rietveld's avatar
Kristian Rietveld committed
1276 1277 1278 1279
 * 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').
 */
1280 1281 1282 1283 1284
void
_gtk_tree_selection_internal_select_node (GtkTreeSelection *selection,
					  GtkRBNode        *node,
					  GtkRBTree        *tree,
					  GtkTreePath      *path,
Kristian Rietveld's avatar
Kristian Rietveld committed
1285
                                          GtkTreeSelectMode mode,
Kristian Rietveld's avatar
Kristian Rietveld committed
1286
					  gboolean          override_browse_mode)
1287 1288
{
  gint flags;
1289
  gint dirty = FALSE;
1290
  GtkTreePath *anchor_path = NULL;
1291

1292 1293
  if (selection->type == GTK_SELECTION_NONE)
    return;
1294

1295 1296 1297
  if (selection->tree_view->priv->anchor)
    anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor);

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

	      /* 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 */
1331 1332 1333
	      if (dirty)
		{
		  if (selection->tree_view->priv->anchor)
1334 1335 1336 1337 1338
                    {
                      gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
                      selection->tree_view->priv->anchor = NULL;
                    }

1339 1340 1341
		  if (gtk_tree_selection_real_select_node (selection, tree, node, TRUE))
		    {
		      selection->tree_view->priv->anchor =
1342
			gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path);
1343 1344 1345 1346 1347 1348 1349 1350
		    }
		}
	    }
	  else
	    {
	      if (gtk_tree_selection_real_select_node (selection, tree, node, TRUE))
		{
		  dirty = TRUE;
1351 1352 1353
		  if (selection->tree_view->priv->anchor)
		    gtk_tree_row_reference_free (selection->tree_view->priv->anchor);

1354
		  selection->tree_view->priv->anchor =
1355
		    gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path);
1356 1357 1358
		}
	    }
	}
1359
    }
1360
  else if (selection->type == GTK_SELECTION_MULTIPLE)
1361
    {
Kristian Rietveld's avatar
Kristian Rietveld committed
1362 1363
      if ((mode & GTK_TREE_SELECT_MODE_EXTEND) == GTK_TREE_SELECT_MODE_EXTEND
          && (anchor_path == NULL))
1364 1365 1366
	{
	  if (selection->tree_view->priv->anchor)
	    gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
1367

1368
	  selection->tree_view->priv->anchor =
1369
	    gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path);
1370 1371
	  dirty = gtk_tree_selection_real_select_node (selection, tree, node, TRUE);
	}
Kristian Rietveld's avatar
Kristian Rietveld committed
1372
      else if ((mode & (GTK_TREE_SELECT_MODE_EXTEND | GTK_TREE_SELECT_MODE_TOGGLE)) == (GTK_TREE_SELECT_MODE_EXTEND | GTK_TREE_SELECT_MODE_TOGGLE))
1373 1374 1375 1376 1377
	{
	  gtk_tree_selection_select_range (selection,
					   anchor_path,
					   path);
	}
Kristian Rietveld's avatar
Kristian Rietveld committed
1378
      else if ((mode & GTK_TREE_SELECT_MODE_TOGGLE) == GTK_TREE_SELECT_MODE_TOGGLE)
1379 1380 1381 1382
	{
	  flags = node->flags;
	  if (selection->tree_view->priv->anchor)
	    gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
1383

1384
	  selection->tree_view->priv->anchor =
1385
	    gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path);
1386

1387 1388 1389 1390 1391
	  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
1392
      else if ((mode & GTK_TREE_SELECT_MODE_EXTEND) == GTK_TREE_SELECT_MODE_EXTEND)
1393 1394
	{
	  dirty = gtk_tree_selection_real_unselect_all (selection);
1395 1396
	  dirty |= gtk_tree_selection_real_modify_range (selection,
                                                         RANGE_SELECT,
1397 1398 1399
							 anchor_path,
							 path