gtktreeselection.c 36.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:
Jonathan Blandford's avatar
Jonathan Blandford committed
119
 *
120 121
 * Creates a new #GtkTreeSelection object.  This function should not be invoked,
 * as each #GtkTreeView will create it's own #GtkTreeSelection.
Jonathan Blandford's avatar
Jonathan Blandford committed
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.
Jonathan Blandford's avatar
Jonathan Blandford committed
138
 *
139 140
 * Creates a new #GtkTreeSelection object.  This function should not be invoked,
 * as each #GtkTreeView will create it's own #GtkTreeSelection.
Jonathan Blandford's avatar
Jonathan Blandford committed
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.
Jonathan Blandford's avatar
Jonathan Blandford committed
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
Jonathan Blandford's avatar
Jonathan Blandford committed
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);
Jonathan Blandford's avatar
Jonathan Blandford committed
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 245
						  0,
						  FALSE);
Jonathan Blandford's avatar
Jonathan Blandford committed
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.
Jonathan Blandford's avatar
Jonathan Blandford committed
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.
Jonathan Blandford's avatar
Jonathan Blandford committed
307
 *
308
 * Returns the user data for the selection function.
Jonathan Blandford's avatar
Jonathan Blandford committed
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.
Jonathan Blandford's avatar
Jonathan Blandford committed
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.
Jonathan Blandford's avatar
Jonathan Blandford committed
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;
Jonathan Blandford's avatar
Jonathan Blandford committed
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;
Jonathan Blandford's avatar
Jonathan Blandford committed
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;
Jonathan Blandford's avatar
Jonathan Blandford committed
376

377
  retval = FALSE;
Jonathan Blandford's avatar
Jonathan Blandford committed
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);
Jonathan Blandford's avatar
Jonathan Blandford committed
404

405
  return retval;
406 407
}

408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 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 458 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 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 576 577 578 579 580 581 582
/**
 * 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
 * want to convert the returned list into a list of #GtkTreeRowReferences.
 * 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.
 **/
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;

	  path = gtk_tree_model_get_path (*model, &iter);
	  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.
 **/
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;
}

583 584 585 586 587
/**
 * 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.
Jonathan Blandford's avatar
Jonathan Blandford committed
588
 *
589 590 591
 * 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.
592
 **/
593 594 595 596 597 598 599 600
void
gtk_tree_selection_selected_foreach (GtkTreeSelection            *selection,
				     GtkTreeSelectionForeachFunc  func,
				     gpointer                     data)
{
  GtkTreePath *path;
  GtkRBTree *tree;
  GtkRBNode *node;
601
  GtkTreeIter iter;
602 603 604 605 606 607 608 609 610 611

  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;

612 613
  if (selection->type == GTK_SELECTION_SINGLE ||
      selection->type == GTK_SELECTION_BROWSE)
614
    {
615 616 617 618 619 620 621
      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);
	}
622 623 624
      return;
    }

625 626
  tree = selection->tree_view->priv->tree;
  node = selection->tree_view->priv->tree->root;
627
  
628 629 630 631
  while (node->left != tree->nil)
    node = node->left;

  /* find the node internally */
632
  path = gtk_tree_path_new_first ();
633 634
  gtk_tree_model_get_iter (selection->tree_view->priv->model,
			   &iter, path);
635 636 637 638

  do
    {
      if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
639
	(* func) (selection->tree_view->priv->model, path, &iter, data);
640 641
      if (node->children)
	{
642 643 644
	  gboolean has_child;
	  GtkTreeIter tmp;

645 646 647 648
	  tree = node->children;
	  node = tree->root;
	  while (node->left != tree->nil)
	    node = node->left;
649 650
	  tmp = iter;
	  has_child = gtk_tree_model_iter_children (selection->tree_view->priv->model, &iter, &tmp);
651
	  gtk_tree_path_append_index (path, 0);
652
	  /* Sanity Check! */
653
	  TREE_VIEW_INTERNAL_ASSERT_VOID (has_child);
654 655 656 657 658 659 660 661 662
	}
      else
	{
	  gboolean done = FALSE;
	  do
	    {
	      node = _gtk_rbtree_next (tree, node);
	      if (node != NULL)
		{
663 664 665
		  gboolean has_next;

		  has_next = gtk_tree_model_iter_next (selection->tree_view->priv->model, &iter);
666
		  done = TRUE;
667
		  gtk_tree_path_next (path);
668 669

		  /* Sanity Check! */
670
		  TREE_VIEW_INTERNAL_ASSERT_VOID (has_next);
671 672 673
		}
	      else
		{
674 675 676
		  gboolean has_parent;
		  GtkTreeIter tmp_iter = iter;

677 678 679
		  node = tree->parent_node;
		  tree = tree->parent_tree;
		  if (tree == NULL)
680 681 682 683 684 685
		    {
		      gtk_tree_path_free (path);
		      /* we've run out of tree */
		      /* We're done with this function */
		      return;
		    }
686
		  has_parent = gtk_tree_model_iter_parent (selection->tree_view->priv->model, &iter, &tmp_iter);
687
		  gtk_tree_path_up (path);
688
		  /* Sanity check */
689
		  TREE_VIEW_INTERNAL_ASSERT_VOID (has_parent);
690 691 692 693 694 695 696 697
		}
	    }
	  while (!done);
	}
    }
  while (TRUE);
}

698 699 700 701
/**
 * gtk_tree_selection_select_path:
 * @selection: A #GtkTreeSelection.
 * @path: The #GtkTreePath to be selected.
Jonathan Blandford's avatar
Jonathan Blandford committed
702
 *
703 704
 * Select the row at @path.
 **/
705 706 707 708 709 710 711
void
gtk_tree_selection_select_path (GtkTreeSelection *selection,
				GtkTreePath      *path)
{
  GtkRBNode *node;
  GtkRBTree *tree;
  GdkModifierType state = 0;
712
  gboolean ret;
713 714 715 716 717

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

718 719 720 721
  ret = _gtk_tree_view_find_node (selection->tree_view,
				  path,
				  &tree,
				  &node);
722

723 724
  if (node == NULL || GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED) ||
      ret == TRUE)
725 726
    return;

727
  if (selection->type == GTK_SELECTION_MULTIPLE)
728 729 730 731 732 733
    state = GDK_CONTROL_MASK;

  _gtk_tree_selection_internal_select_node (selection,
					    node,
					    tree,
					    path,
Kristian Rietveld's avatar
Kristian Rietveld committed
734 735
					    state,
					    FALSE);
736 737
}

738 739 740 741
/**
 * gtk_tree_selection_unselect_path:
 * @selection: A #GtkTreeSelection.
 * @path: The #GtkTreePath to be unselected.
Jonathan Blandford's avatar
Jonathan Blandford committed
742
 *
743 744
 * Unselects the row at @path.
 **/
745 746 747 748 749 750
void
gtk_tree_selection_unselect_path (GtkTreeSelection *selection,
				  GtkTreePath      *path)
{
  GtkRBNode *node;
  GtkRBTree *tree;
751
  gboolean ret;
752 753 754 755 756

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

757 758 759 760
  ret = _gtk_tree_view_find_node (selection->tree_view,
				  path,
				  &tree,
				  &node);
761

762 763
  if (node == NULL || !GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED) ||
      ret == TRUE)
764 765 766 767 768 769
    return;

  _gtk_tree_selection_internal_select_node (selection,
					    node,
					    tree,
					    path,
Kristian Rietveld's avatar
Kristian Rietveld committed
770 771
					    GDK_CONTROL_MASK,
					    TRUE);
772 773
}

774
/**
775
 * gtk_tree_selection_select_iter:
776
 * @selection: A #GtkTreeSelection.
777
 * @iter: The #GtkTreeIter to be selected.
Jonathan Blandford's avatar
Jonathan Blandford committed
778
 *
779
 * Selects the specified iterator.
780
 **/
781
void
782 783
gtk_tree_selection_select_iter (GtkTreeSelection *selection,
				GtkTreeIter      *iter)
784 785 786 787 788 789
{
  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);
790
  g_return_if_fail (iter != NULL);
791 792

  path = gtk_tree_model_get_path (selection->tree_view->priv->model,
793
				  iter);
794 795 796 797 798 799 800 801 802

  if (path == NULL)
    return;

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


803
/**
804
 * gtk_tree_selection_unselect_iter:
805
 * @selection: A #GtkTreeSelection.
806
 * @iter: The #GtkTreeIter to be unselected.
Jonathan Blandford's avatar
Jonathan Blandford committed
807
 *
808
 * Unselects the specified iterator.
809
 **/
810
void
811 812
gtk_tree_selection_unselect_iter (GtkTreeSelection *selection,
				  GtkTreeIter      *iter)
813 814 815 816 817
{
  GtkTreePath *path;

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

  path = gtk_tree_model_get_path (selection->tree_view->priv->model,
822
				  iter);
823 824 825 826

  if (path == NULL)
    return;

Jonathan Blandford's avatar
Jonathan Blandford committed
827
  gtk_tree_selection_unselect_path (selection, path);
828 829 830
  gtk_tree_path_free (path);
}

831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846
/**
 * 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;
847
  gboolean ret;
848 849 850 851 852 853

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

854 855 856 857
  ret = _gtk_tree_view_find_node (selection->tree_view,
				  path,
				  &tree,
				  &node);
858

859 860
  if ((node == NULL) || !GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED) ||
      ret == TRUE)
861 862 863 864 865 866 867 868 869 870
    return FALSE;

  return TRUE;
}

/**
 * gtk_tree_selection_iter_is_selected:
 * @selection: A #GtkTreeSelection
 * @iter: A valid #GtkTreeIter
 * 
Soren Sandmann's avatar
Soren Sandmann committed
871
 * Returns %TRUE if the row at @iter is currently selected.
872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897
 * 
 * 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;
}


898 899 900 901 902 903 904 905 906
/* Wish I was in python, right now... */
struct _TempTuple {
  GtkTreeSelection *selection;
  gint dirty;
};

static void
select_all_helper (GtkRBTree  *tree,
		   GtkRBNode  *node,
907
		   gpointer    data)
908 909 910 911 912 913 914 915 916 917 918
{
  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))
    {
919
      tuple->dirty = gtk_tree_selection_real_select_node (tuple->selection, tree, node, TRUE) || tuple->dirty;
920 921 922
    }
}

923 924 925 926 927 928

/* 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)
929 930
{
  struct _TempTuple *tuple;
931

932 933
  if (selection->tree_view->priv->tree == NULL)
    return FALSE;
934

935 936 937 938 939 940 941 942 943 944 945
  /* 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)
946 947
    {
      g_free (tuple);
948
      return TRUE;
949
    }
950 951
  g_free (tuple);
  return FALSE;
952 953
}

954 955 956
/**
 * gtk_tree_selection_select_all:
 * @selection: A #GtkTreeSelection.
Jonathan Blandford's avatar
Jonathan Blandford committed
957
 *
958 959
 * Selects all the nodes.  @selection is must be set to #GTK_SELECTION_MULTIPLE
 * mode.
960
 **/
961 962 963 964 965
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);
966 967
  if (selection->tree_view->priv->tree == NULL)
    return;
968
  g_return_if_fail (selection->type == GTK_SELECTION_MULTIPLE);
969 970

  if (gtk_tree_selection_real_select_all (selection))
Manish Singh's avatar
Manish Singh committed
971
    g_signal_emit (selection, tree_selection_signals[CHANGED], 0);
972 973 974 975 976
}

static void
unselect_all_helper (GtkRBTree  *tree,
		     GtkRBNode  *node,
977
		     gpointer    data)
978 979 980 981 982 983 984 985 986 987 988
{
  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))
    {
989
      tuple->dirty = gtk_tree_selection_real_select_node (tuple->selection, tree, node, FALSE) || tuple->dirty;
990 991 992
    }
}

993 994
static gint
gtk_tree_selection_real_unselect_all (GtkTreeSelection *selection)
995 996 997
{
  struct _TempTuple *tuple;

998 999
  if (selection->type == GTK_SELECTION_SINGLE ||
      selection->type == GTK_SELECTION_BROWSE)
1000 1001 1002
    {
      GtkRBTree *tree = NULL;
      GtkRBNode *node = NULL;
1003
      GtkTreePath *anchor_path;
Jonathan Blandford's avatar
Jonathan Blandford committed
1004

1005
      if (selection->tree_view->priv->anchor == NULL)
1006
	return FALSE;
1007

1008 1009 1010 1011
      anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor);

      if (anchor_path == NULL)
        return FALSE;
Jonathan Blandford's avatar
Jonathan Blandford committed
1012

1013
      _gtk_tree_view_find_node (selection->tree_view,
1014
                                anchor_path,
1015 1016
				&tree,
				&node);
1017 1018

      gtk_tree_path_free (anchor_path);
Havoc Pennington's avatar
Havoc Pennington committed
1019 1020 1021

      if (tree == NULL)
        return FALSE;
Jonathan Blandford's avatar
Jonathan Blandford committed
1022

1023
      if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
1024
	{
1025 1026 1027 1028 1029 1030
	  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;
	    }
1031 1032
	}
      return FALSE;
1033
    }
1034
  else
Jonathan Blandford's avatar
Jonathan Blandford committed
1035
    {
1036 1037 1038
      tuple = g_new (struct _TempTuple, 1);
      tuple->selection = selection;
      tuple->dirty = FALSE;
Jonathan Blandford's avatar
Jonathan Blandford committed
1039

1040 1041 1042 1043 1044
      _gtk_rbtree_traverse (selection->tree_view->priv->tree,
                            selection->tree_view->priv->tree->root,
                            G_PRE_ORDER,
                            unselect_all_helper,
                            tuple);
Jonathan Blandford's avatar
Jonathan Blandford committed
1045

1046 1047 1048 1049 1050
      if (tuple->dirty)
        {
          g_free (tuple);
          return TRUE;
        }
1051
      g_free (tuple);
1052
      return FALSE;
1053
    }
1054 1055
}

1056 1057 1058
/**
 * gtk_tree_selection_unselect_all:
 * @selection: A #GtkTreeSelection.
Jonathan Blandford's avatar
Jonathan Blandford committed
1059
 *
1060 1061
 * Unselects all the nodes.
 **/
1062
void
1063
gtk_tree_selection_unselect_all (GtkTreeSelection *selection)
1064 1065 1066
{
  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
  g_return_if_fail (selection->tree_view != NULL);
1067 1068 1069
  if (selection->tree_view->priv->tree == NULL)
    return;
  
1070 1071 1072 1073
  if (selection