gtktreeselection.c 39.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/* 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.
 */

#include "gtktreeselection.h"
#include "gtktreeprivate.h"
#include "gtkrbtree.h"
23
#include "gtkmarshalers.h"
24

25 26
static void gtk_tree_selection_init              (GtkTreeSelection      *selection);
static void gtk_tree_selection_class_init        (GtkTreeSelectionClass *class);
27 28

static void gtk_tree_selection_finalize          (GObject               *object);
29 30 31 32 33 34
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);
35

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

42
static GObjectClass *parent_class = NULL;
43
static guint tree_selection_signals [LAST_SIGNAL] = { 0 };
44

Manish Singh's avatar
Manish Singh committed
45
GType
46 47
gtk_tree_selection_get_type (void)
{
Manish Singh's avatar
Manish Singh committed
48
  static GType selection_type = 0;
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64

  if (!selection_type)
    {
      static const GTypeInfo selection_info =
      {
        sizeof (GtkTreeSelectionClass),
	NULL,		/* base_init */
	NULL,		/* base_finalize */
        (GClassInitFunc) gtk_tree_selection_class_init,
	NULL,		/* class_finalize */
	NULL,		/* class_data */
        sizeof (GtkTreeSelection),
	0,              /* n_preallocs */
        (GInstanceInitFunc) gtk_tree_selection_init
      };

Manish Singh's avatar
Manish Singh committed
65 66 67
      selection_type =
	g_type_register_static (G_TYPE_OBJECT, "GtkTreeSelection",
				&selection_info, 0);
68 69 70 71 72 73 74 75
    }

  return selection_type;
}

static void
gtk_tree_selection_class_init (GtkTreeSelectionClass *class)
{
76
  GObjectClass *object_class;
77

78
  object_class = (GObjectClass*) class;
79 80
  parent_class = g_type_class_peek_parent (class);

81
  object_class->finalize = gtk_tree_selection_finalize;
82
  class->changed = NULL;
83

84
  tree_selection_signals[CHANGED] =
Manish Singh's avatar
Manish Singh committed
85 86 87 88 89 90 91
    g_signal_new ("changed",
		  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);
92 93 94 95 96
}

static void
gtk_tree_selection_init (GtkTreeSelection *selection)
{
97
  selection->type = GTK_SELECTION_SINGLE;
98 99
}

100 101 102
static void
gtk_tree_selection_finalize (GObject *object)
{
103 104 105 106 107 108 109 110 111
  GtkTreeSelection *selection = GTK_TREE_SELECTION (object);

  if (selection->destroy)
    {
      GtkDestroyNotify d = selection->destroy;

      selection->destroy = NULL;
      d (selection->user_data);
    }
Tim Janik's avatar
Tim Janik committed
112 113 114

  /* chain parent_class' handler */
  G_OBJECT_CLASS (parent_class)->finalize (object);
115 116
}

117
/**
118
 * _gtk_tree_selection_new:
119
 *
120
 * Creates a new #GtkTreeSelection object.  This function should not be invoked,
121
 * as each #GtkTreeView will create its own #GtkTreeSelection.
122
 *
123 124
 * Return value: A newly created #GtkTreeSelection object.
 **/
125 126
GtkTreeSelection*
_gtk_tree_selection_new (void)
127
{
128
  GtkTreeSelection *selection;
129

Manish Singh's avatar
Manish Singh committed
130
  selection = g_object_new (GTK_TYPE_TREE_SELECTION, NULL);
131 132 133 134

  return selection;
}

135
/**
136
 * _gtk_tree_selection_new_with_tree_view:
137
 * @tree_view: The #GtkTreeView.
138
 *
139
 * Creates a new #GtkTreeSelection object.  This function should not be invoked,
140
 * as each #GtkTreeView will create its own #GtkTreeSelection.
141
 *
142 143
 * Return value: A newly created #GtkTreeSelection object.
 **/
144 145
GtkTreeSelection*
_gtk_tree_selection_new_with_tree_view (GtkTreeView *tree_view)
146
{
147
  GtkTreeSelection *selection;
148 149 150

  g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);

151 152
  selection = _gtk_tree_selection_new ();
  _gtk_tree_selection_set_tree_view (selection, tree_view);
153 154 155 156

  return selection;
}

157
/**
158
 * _gtk_tree_selection_set_tree_view:
159 160
 * @selection: A #GtkTreeSelection.
 * @tree_view: The #GtkTreeView.
161
 *
162 163 164
 * Sets the #GtkTreeView of @selection.  This function should not be invoked, as
 * it is used internally by #GtkTreeView.
 **/
165
void
166 167
_gtk_tree_selection_set_tree_view (GtkTreeSelection *selection,
                                   GtkTreeView      *tree_view)
168 169 170 171 172 173 174 175
{
  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;
}

176
/**
Havoc Pennington's avatar
Havoc Pennington committed
177
 * gtk_tree_selection_set_mode:
178
 * @selection: A #GtkTreeSelection.
179
 * @type: The selection mode
180
 *
181
 * Sets the selection mode of the @selection.  If the previous type was
Matthias Clasen's avatar
Matthias Clasen committed
182
 * #GTK_SELECTION_MULTIPLE, then the anchor is kept selected, if it was
183
 * previously selected.
184
 **/
185
void
186 187
gtk_tree_selection_set_mode (GtkTreeSelection *selection,
			     GtkSelectionMode  type)
188
{
189
  GtkTreeSelectionFunc tmp_func;
190 191 192 193 194
  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));

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

195 196 197 198 199 200 201 202 203
  
  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;
204 205 206

      gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
      selection->tree_view->priv->anchor = NULL;
207 208 209
    }
  else if (type == GTK_SELECTION_SINGLE ||
	   type == GTK_SELECTION_BROWSE)
210 211 212 213
    {
      GtkRBTree *tree = NULL;
      GtkRBNode *node = NULL;
      gint selected = FALSE;
214
      GtkTreePath *anchor_path = NULL;
215 216 217

      if (selection->tree_view->priv->anchor)
	{
218 219 220 221 222 223 224 225
          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);
226

227 228 229
              if (node && GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
                selected = TRUE;
            }
230
	}
231 232

      /* We do this so that we unconditionally unset all rows
233
       */
234 235
      tmp_func = selection->user_func;
      selection->user_func = NULL;
236
      gtk_tree_selection_unselect_all (selection);
237
      selection->user_func = tmp_func;
238

239
      if (node && selected)
240 241 242 243
	_gtk_tree_selection_internal_select_node (selection,
						  node,
						  tree,
						  anchor_path,
Kristian Rietveld's avatar
Kristian Rietveld committed
244
                                                  0,
Kristian Rietveld's avatar
Kristian Rietveld committed
245
						  FALSE);
246 247
      if (anchor_path)
	gtk_tree_path_free (anchor_path);
248
    }
249

250 251 252
  selection->type = type;
}

253 254 255 256 257 258 259 260 261
/**
 * 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
 **/
262
GtkSelectionMode
263 264
gtk_tree_selection_get_mode (GtkTreeSelection *selection)
{
265
  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), GTK_SELECTION_SINGLE);
266 267 268 269

  return selection->type;
}

270 271 272 273 274
/**
 * gtk_tree_selection_set_select_function:
 * @selection: A #GtkTreeSelection.
 * @func: The selection function.
 * @data: The selection function's data.
275
 * @destroy: The destroy function for user data.  May be NULL.
276
 *
277 278
 * 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
279 280
 * 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.
281
 **/
282 283 284
void
gtk_tree_selection_set_select_function (GtkTreeSelection     *selection,
					GtkTreeSelectionFunc  func,
285 286
					gpointer              data,
					GtkDestroyNotify      destroy)
287 288 289 290
{
  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
  g_return_if_fail (func != NULL);

291 292 293 294 295 296 297 298
  if (selection->destroy)
    {
      GtkDestroyNotify d = selection->destroy;

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

299 300
  selection->user_func = func;
  selection->user_data = data;
301
  selection->destroy = destroy;
302 303
}

304 305 306
/**
 * gtk_tree_selection_get_user_data:
 * @selection: A #GtkTreeSelection.
307
 *
308
 * Returns the user data for the selection function.
309
 *
310 311
 * Return value: The user data.
 **/
312 313 314 315 316 317 318 319
gpointer
gtk_tree_selection_get_user_data (GtkTreeSelection *selection)
{
  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), NULL);

  return selection->user_data;
}

320 321 322 323 324 325 326 327 328
/**
 * 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
329 330 331 332 333 334 335
gtk_tree_selection_get_tree_view (GtkTreeSelection *selection)
{
  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), NULL);

  return selection->tree_view;
}

336 337 338
/**
 * gtk_tree_selection_get_selected:
 * @selection: A #GtkTreeSelection.
339
 * @model: A pointer to set to the #GtkTreeModel, or NULL.
Jonathan Blandford's avatar
Jonathan Blandford committed
340
 * @iter: The #GtkTreeIter, or NULL.
341
 *
342
 * Sets @iter to the currently selected node if @selection is set to
343 344 345 346
 * #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.
347
 *
Jonathan Blandford's avatar
Jonathan Blandford committed
348
 * Return value: TRUE, if there is a selected node.
349
 **/
350
gboolean
351 352 353
gtk_tree_selection_get_selected (GtkTreeSelection  *selection,
				 GtkTreeModel     **model,
				 GtkTreeIter       *iter)
354 355 356
{
  GtkRBTree *tree;
  GtkRBNode *node;
357 358
  GtkTreePath *anchor_path;
  gboolean retval;
Kristian Rietveld's avatar
Kristian Rietveld committed
359
  gboolean found_node;
360

361
  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), FALSE);
362
  g_return_val_if_fail (selection->type != GTK_SELECTION_MULTIPLE, FALSE);
363 364
  g_return_val_if_fail (selection->tree_view != NULL, FALSE);
  g_return_val_if_fail (selection->tree_view->priv->model != NULL, FALSE);
365

366 367
  if (model)
    *model = selection->tree_view->priv->model;
368

369
  if (selection->tree_view->priv->anchor == NULL)
370
    return FALSE;
371 372 373 374 375

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

  if (anchor_path == NULL)
    return FALSE;
376

377
  retval = FALSE;
378

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

403
  gtk_tree_path_free (anchor_path);
404

405
  return retval;
406 407
}

408 409 410 411 412 413 414
/**
 * 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
415
 * want to convert the returned list into a list of #GtkTreeRowReference<!-- -->s.
416 417 418 419 420 421 422 423 424
 * To do this, you can use gtk_tree_row_reference_new_proxy().
 *
 * To free the return value, use:
 * <informalexample><programlisting>
 * g_list_foreach (list, gtk_tree_path_free, NULL);
 * g_list_free (list);
 * </programlisting></informalexample>
 *
 * Return value: A #GList containing a #GtkTreePath for each selected row.
425 426
 *
 * Since: 2.2
427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457
 **/
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);
  g_return_val_if_fail (selection->tree_view->priv->model != NULL, NULL);

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

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

  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;

458
	  path = gtk_tree_model_get_path (selection->tree_view->priv->model, &iter);
459 460 461 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 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 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 534 535 536 537 538 539 540 541 542 543 544 545 546 547
	  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))
	list = g_list_append (list, gtk_tree_path_copy (path));

      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);
		      return list;
		    }

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

  gtk_tree_path_free (path);

  return list;
}

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.
548 549
 * 
 * Since: 2.2
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 576 577 578 579 580 581 582 583 584 585 586
 **/
gint
gtk_tree_selection_count_selected_rows (GtkTreeSelection *selection)
{
  gint count = 0;
  GtkRBTree *tree;
  GtkRBNode *node;

  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), 0);
  g_return_val_if_fail (selection->tree_view != NULL, 0);
  g_return_val_if_fail (selection->tree_view->priv->model != 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;
    }

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

  _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;
}

587 588 589 590 591 592 593 594 595
/* gtk_tree_selection_selected_foreach helper */
static void
model_changed (gpointer data)
{
  gboolean *stop = (gboolean *)data;

  *stop = TRUE;
}

596 597 598 599 600
/**
 * 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.
601
 *
602 603 604
 * 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.
605
 **/
606 607 608 609 610 611 612 613
void
gtk_tree_selection_selected_foreach (GtkTreeSelection            *selection,
				     GtkTreeSelectionForeachFunc  func,
				     gpointer                     data)
{
  GtkTreePath *path;
  GtkRBTree *tree;
  GtkRBNode *node;
614
  GtkTreeIter iter;
615

616
  guint inserted_id, deleted_id, reordered_id;
617
  gboolean stop = FALSE, has_next = TRUE, has_parent = TRUE;
618

619 620 621 622 623 624 625 626 627
  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);

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

628 629
  if (selection->type == GTK_SELECTION_SINGLE ||
      selection->type == GTK_SELECTION_BROWSE)
630
    {
631 632 633 634 635 636 637
      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);
	}
638 639 640
      return;
    }

641 642
  tree = selection->tree_view->priv->tree;
  node = selection->tree_view->priv->tree->root;
643
  
644 645 646
  while (node->left != tree->nil)
    node = node->left;

647 648 649 650 651 652 653 654 655 656 657 658 659 660
  /* connect to signals to monitor changes in treemodel */
  inserted_id = g_signal_connect_swapped (selection->tree_view->priv->model,
                                          "row_inserted",
					  G_CALLBACK (model_changed),
				          &stop);
  deleted_id = g_signal_connect_swapped (selection->tree_view->priv->model,
                                         "row_deleted",
					 G_CALLBACK (model_changed),
				         &stop);
  reordered_id = g_signal_connect_swapped (selection->tree_view->priv->model,
                                           "rows_reordered",
					   G_CALLBACK (model_changed),
				           &stop);

661
  /* find the node internally */
662
  path = gtk_tree_path_new_first ();
663 664
  gtk_tree_model_get_iter (selection->tree_view->priv->model,
			   &iter, path);
665 666 667 668

  do
    {
      if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
669
	(* func) (selection->tree_view->priv->model, path, &iter, data);
670 671 672 673

      if (stop)
	goto out;

674 675
      if (node->children)
	{
676 677 678
	  gboolean has_child;
	  GtkTreeIter tmp;

679 680 681 682
	  tree = node->children;
	  node = tree->root;
	  while (node->left != tree->nil)
	    node = node->left;
683 684
	  tmp = iter;
	  has_child = gtk_tree_model_iter_children (selection->tree_view->priv->model, &iter, &tmp);
685
	  gtk_tree_path_append_index (path, 0);
686 687

	  /* we do the sanity check at the bottom of this function */
688
	  if (!has_child)
689
	    goto out;
690 691 692 693 694 695 696 697 698
	}
      else
	{
	  gboolean done = FALSE;
	  do
	    {
	      node = _gtk_rbtree_next (tree, node);
	      if (node != NULL)
		{
699 700 701
		  gboolean has_next;

		  has_next = gtk_tree_model_iter_next (selection->tree_view->priv->model, &iter);
702
		  done = TRUE;
703
		  gtk_tree_path_next (path);
704

705 706 707
		  /* we do the sanity check at the bottom of this function */
		  if (!has_next)
		    goto out;
708 709 710
		}
	      else
		{
711 712 713
		  gboolean has_parent;
		  GtkTreeIter tmp_iter = iter;

714 715 716
		  node = tree->parent_node;
		  tree = tree->parent_tree;
		  if (tree == NULL)
717 718 719
		    {
		      /* we've run out of tree */
		      /* We're done with this function */
720 721

		      goto out;
722
		    }
723

724
		  has_parent = gtk_tree_model_iter_parent (selection->tree_view->priv->model, &iter, &tmp_iter);
725
		  gtk_tree_path_up (path);
726 727

		  /* we do the sanity check at the bottom of this function */
728
		  if (!has_parent)
729
		    goto out;
730 731 732 733 734 735
		}
	    }
	  while (!done);
	}
    }
  while (TRUE);
736 737 738 739 740 741 742 743 744 745 746 747 748

out:
  if (path)
    gtk_tree_path_free (path);

  g_signal_handler_disconnect (selection->tree_view->priv->model,
                               inserted_id);
  g_signal_handler_disconnect (selection->tree_view->priv->model,
                               deleted_id);
  g_signal_handler_disconnect (selection->tree_view->priv->model,
                               reordered_id);

  /* check if we have to spew a scary message */
749
  if (!has_next)
750
    TREE_VIEW_INTERNAL_ASSERT_VOID (has_next);
751
  if (!has_parent)
752 753 754
    TREE_VIEW_INTERNAL_ASSERT_VOID (has_parent);
  if (stop)
    g_warning
755
      ("The model has been modified from within gtk_tree_selection_selected_foreach.\n"
756 757 758
       "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");
759 760
}

761 762 763 764
/**
 * gtk_tree_selection_select_path:
 * @selection: A #GtkTreeSelection.
 * @path: The #GtkTreePath to be selected.
765
 *
766 767
 * Select the row at @path.
 **/
768 769 770 771 772 773
void
gtk_tree_selection_select_path (GtkTreeSelection *selection,
				GtkTreePath      *path)
{
  GtkRBNode *node;
  GtkRBTree *tree;
774
  gboolean ret;
Kristian Rietveld's avatar
Kristian Rietveld committed
775
  GtkTreeSelectMode mode = 0;
776 777 778 779 780

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

781 782 783 784
  ret = _gtk_tree_view_find_node (selection->tree_view,
				  path,
				  &tree,
				  &node);
785

786 787
  if (node == NULL || GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED) ||
      ret == TRUE)
788 789
    return;

790
  if (selection->type == GTK_SELECTION_MULTIPLE)
Kristian Rietveld's avatar
Kristian Rietveld committed
791
    mode = GTK_TREE_SELECT_MODE_TOGGLE;
792 793 794 795 796

  _gtk_tree_selection_internal_select_node (selection,
					    node,
					    tree,
					    path,
Kristian Rietveld's avatar
Kristian Rietveld committed
797
                                            mode,
Kristian Rietveld's avatar
Kristian Rietveld committed
798
					    FALSE);
799 800
}

801 802 803 804
/**
 * gtk_tree_selection_unselect_path:
 * @selection: A #GtkTreeSelection.
 * @path: The #GtkTreePath to be unselected.
805
 *
806 807
 * Unselects the row at @path.
 **/
808 809 810 811 812 813
void
gtk_tree_selection_unselect_path (GtkTreeSelection *selection,
				  GtkTreePath      *path)
{
  GtkRBNode *node;
  GtkRBTree *tree;
814
  gboolean ret;
815 816 817 818 819

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

820 821 822 823
  ret = _gtk_tree_view_find_node (selection->tree_view,
				  path,
				  &tree,
				  &node);
824

825 826
  if (node == NULL || !GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED) ||
      ret == TRUE)
827 828 829 830 831 832
    return;

  _gtk_tree_selection_internal_select_node (selection,
					    node,
					    tree,
					    path,
Kristian Rietveld's avatar
Kristian Rietveld committed
833
                                            GTK_TREE_SELECT_MODE_TOGGLE,
Kristian Rietveld's avatar
Kristian Rietveld committed
834
					    TRUE);
835 836
}

837
/**
838
 * gtk_tree_selection_select_iter:
839
 * @selection: A #GtkTreeSelection.
840
 * @iter: The #GtkTreeIter to be selected.
841
 *
842
 * Selects the specified iterator.
843
 **/
844
void
845 846
gtk_tree_selection_select_iter (GtkTreeSelection *selection,
				GtkTreeIter      *iter)
847 848 849 850 851 852
{
  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);
853
  g_return_if_fail (iter != NULL);
854 855

  path = gtk_tree_model_get_path (selection->tree_view->priv->model,
856
				  iter);
857 858 859 860 861 862 863 864 865

  if (path == NULL)
    return;

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


866
/**
867
 * gtk_tree_selection_unselect_iter:
868
 * @selection: A #GtkTreeSelection.
869
 * @iter: The #GtkTreeIter to be unselected.
870
 *
871
 * Unselects the specified iterator.
872
 **/
873
void
874 875
gtk_tree_selection_unselect_iter (GtkTreeSelection *selection,
				  GtkTreeIter      *iter)
876 877 878 879 880
{
  GtkTreePath *path;

  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
  g_return_if_fail (selection->tree_view != NULL);
881
  g_return_if_fail (selection->tree_view->priv->model != NULL);
882
  g_return_if_fail (iter != NULL);
883 884

  path = gtk_tree_model_get_path (selection->tree_view->priv->model,
885
				  iter);
886 887 888 889

  if (path == NULL)
    return;

Jonathan Blandford's avatar
Jonathan Blandford committed
890
  gtk_tree_selection_unselect_path (selection, path);
891 892 893
  gtk_tree_path_free (path);
}

894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909
/**
 * 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;
910
  gboolean ret;
911 912 913 914 915 916

  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);
  g_return_val_if_fail (selection->tree_view->priv->model != NULL, FALSE);

917 918 919 920
  ret = _gtk_tree_view_find_node (selection->tree_view,
				  path,
				  &tree,
				  &node);
921

922 923
  if ((node == NULL) || !GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED) ||
      ret == TRUE)
924 925 926 927 928 929 930 931 932 933
    return FALSE;

  return TRUE;
}

/**
 * gtk_tree_selection_iter_is_selected:
 * @selection: A #GtkTreeSelection
 * @iter: A valid #GtkTreeIter
 * 
Soren Sandmann's avatar
Soren Sandmann committed
934
 * Returns %TRUE if the row at @iter is currently selected.
935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960
 * 
 * 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;
}


961 962 963 964 965 966 967 968 969
/* Wish I was in python, right now... */
struct _TempTuple {
  GtkTreeSelection *selection;
  gint dirty;
};

static void
select_all_helper (GtkRBTree  *tree,
		   GtkRBNode  *node,
970
		   gpointer    data)
971 972 973 974 975 976 977 978 979 980 981
{
  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))
    {
982
      tuple->dirty = gtk_tree_selection_real_select_node (tuple->selection, tree, node, TRUE) || tuple->dirty;
983 984 985
    }
}

986 987 988 989 990 991

/* 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)
992 993
{
  struct _TempTuple *tuple;
994

995 996
  if (selection->tree_view->priv->tree == NULL)
    return FALSE;
997

998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008
  /* 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)
1009 1010
    {
      g_free (tuple);
1011
      return TRUE;
1012
    }
1013 1014
  g_free (tuple);
  return FALSE;
1015 1016
}

1017 1018 1019
/**
 * gtk_tree_selection_select_all:
 * @selection: A #GtkTreeSelection.
1020
 *
1021 1022
 * Selects all the nodes.  @selection is must be set to #GTK_SELECTION_MULTIPLE
 * mode.
1023
 **/
1024 1025 1026 1027 1028
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);
1029 1030
  if (selection->tree_view->priv->tree == NULL)
    return;
1031
  g_return_if_fail (selection->type == GTK_SELECTION_MULTIPLE);
1032 1033

  if (gtk_tree_selection_real_select_all (selection))
Manish Singh's avatar
Manish Singh committed
1034
    g_signal_emit (selection, tree_selection_signals[CHANGED], 0);
1035 1036 1037 1038 1039
}

static void
unselect_all_helper (GtkRBTree  *tree,
		     GtkRBNode  *node,
1040
		     gpointer    data)
1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051
{
  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))
    {
1052
      tuple->dirty = gtk_tree_selection_real_select_node (tuple->selection, tree, node, FALSE) || tuple->dirty;
1053 1054 1055
    }
}

1056 1057
static gint
gtk_tree_selection_real_unselect_all (GtkTreeSelection *selection)
1058 1059 1060
{
  struct _TempTuple *tuple;

1061 1062
  if (selection->type == GTK_SELECTION_SINGLE ||
      selection->type == GTK_SELECTION_BROWSE)
1063 1064 1065
    {
      GtkRBTree *tree = NULL;
      GtkRBNode *node = NULL;
1066
      GtkTreePath *anchor_path;
1067

1068
      if (selection->tree_view->priv->anchor == NULL)
1069
	return FALSE;
1070

1071 1072 1073 1074
      anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor);

      if (anchor_path == NULL)
        return FALSE;
1075

1076
      _gtk_tree_view_find_node (selection->tree_view,
1077
                                anchor_path,
1078 1079
				&tree,
				&node);
1080 1081

      gtk_tree_path_free (anchor_path);
Havoc Pennington's avatar
Havoc Pennington committed
1082 1083 1084

      if (tree == NULL)
        return FALSE;
1085

1086
      if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
1087
	{
1088 1089 1090 1091 1092 1093
	  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;
	    }
1094 1095
	}
      return FALSE;
1096
    }
1097
  else
1098
    {
1099 1100 1101
      tuple = g_new (struct _TempTuple, 1);
      tuple->selection = selection;
      tuple->dirty = FALSE;
1102

1103 1104 1105 1106 1107
      _gtk_rbtree_traverse (selection->tree_view->priv->tree,
                            selection->tree_view->priv->tree->root,
                            G_PRE_ORDER,
                            unselect_all_helper,
                            tuple);
1108

1109 1110 1111 1112 1113
      if (tuple->dirty)
        {
          g_free (tuple);
          return TRUE;
        }
1114
      g_free (tuple);
1115
      return FALSE;
1116
    }
1117 1118
}

1119 1120 1121
/**
 * gtk_tree_selection_unselect_all:
 * @selection: A #GtkTreeSelection.
1122
 *
1123 1124
 * Unselects all the nodes.
 **/
1125
void
1126
gtk_tree_selection_unselect_all (GtkTreeSelection *selection)
1127 1128 1129
{
  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
  g_return_if_fail (selection->tree_view != NULL);
1130 1131 1132
  if (selection->tree_view->priv->tree == NULL)
    return;
  
1133 1134 1135 1136
  if (selection->tree_view->priv->tree == NULL)
    return;

  if (gtk_tree_selection_real_unselect_all (selection))
Manish Singh's avatar
Manish Singh committed
1137
    g_signal_emit (selection, tree_selection_signals[CHANGED], 0);
1138 1139
}

1140 1141 1142 1143 1144 1145
enum
{
  RANGE_SELECT,
  RANGE_UNSELECT
};

1146
static gint
1147 1148
gtk_tree_selection_real_modify_range (GtkTreeSelection *selection,
                                      gint              mode,
1149 1150 1151 1152 1153
				      GtkTreePath      *start_path,
				      GtkTreePath      *end_path)
{
  GtkRBNode *start_node, *end_node;
  GtkRBTree *start_tree, *end_tree;
1154
  GtkTreePath *anchor_path = NULL;
1155
  gboolean dirty = FALSE;
1156 1157 1158

  switch (gtk_tree_path_compare (start_path, end_path))
    {
1159
    case 1:
1160 1161 1162 1163 1164 1165 1166 1167
      _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);
1168
      anchor_path = start_path;
1169 1170 1171 1172 1173 1174 1175 1176
      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;
1177
      anchor_path = start_path;
1178
      break;
1179
    case -1:
1180 1181 1182 1183 1184 1185 1186 1187
      _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);
1188
      anchor_path = start_path;
1189 1190 1191
      break;
    }

1192 1193
  g_return_val_if_fail (start_node != NULL, FALSE);
  g_return_val_if_fail (end_node != NULL, FALSE);
1194

1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205
  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);
    }

1206 1207
  do
    {
1208
      dirty |= gtk_tree_selection_real_select_node (selection, start_tree, start_node, (mode == RANGE_SELECT)?TRUE:FALSE);
1209 1210

      if (start_node == end_node)
1211
	break;
1212 1213 1214 1215 1216 1217 1218 1219 1220 1221

      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
	{
1222 1223
	  _gtk_rbtree_next_full (start_tree, start_node, &start_tree, &start_node);
	  if (start_tree == NULL)
1224
	    {
1225 1226 1227
	      /* we just ran out of tree.  That means someone passed in bogus values.
	       */
	      return dirty;
1228 1229 1230 1231
	    }
	}
    }
  while (TRUE);
1232 1233

  return dirty;
1234 1235
}

1236 1237 1238 1239 1240
/**
 * gtk_tree_selection_select_range:
 * @selection: A #GtkTreeSelection.
 * @start_path: The initial node of the range.
 * @end_path: The final node of the range.
1241
 *
1242 1243
 * Selects a range of nodes, determined by @start_path and @end_path inclusive.
 **/
1244 1245 1246 1247 1248 1249 1250
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);
1251

1252
  if (gtk_tree_selection_real_modify_range (selection, RANGE_SELECT, start_path, end_path))
Manish Singh's avatar
Manish Singh committed
1253
    g_signal_emit (selection, tree_selection_signals[CHANGED], 0);
1254 1255 1256 1257 1258 1259 1260 1261 1262 1263
}

/**
 * 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.
1264 1265
 *
 * Since: 2.2
1266 1267 1268 1269 1270 1271 1272 1273 1274 1275
 **/
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);

  if (gtk_tree_selection_real_modify_range (selection, RANGE_UNSELECT, start_path, end_path))
Manish Singh's avatar
Manish Singh committed
1276
    g_signal_emit (selection, tree_selection_signals[CHANGED], 0);
1277
}
1278

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

1282
/*
Kristian Rietveld's avatar
Kristian Rietveld committed
1283 1284 1285 1286
 * 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').
 */
1287 1288 1289 1290 1291
void
_gtk_tree_selection_internal_select_node (GtkTreeSelection *selection,
					  GtkRBNode        *node,
					  GtkRBTree        *tree,
					  GtkTreePath      *path,
Kristian Rietveld's avatar
Kristian Rietveld committed
1292
                                          GtkTreeSelectMode mode,
Kristian Rietveld's avatar
Kristian Rietveld committed
1293
					  gboolean          override_browse_mode)