gtktreeselection.c 43.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/* 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
Javier Jardón's avatar
Javier Jardón committed
15
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 17
 */

18
#include "config.h"
19
#include <string.h>
20 21 22
#include "gtktreeselection.h"
#include "gtktreeprivate.h"
#include "gtkrbtree.h"
23
#include "gtkmarshalers.h"
24
#include "gtkintl.h"
25
#include "gtktypebuiltins.h"
26
#include "a11y/gtktreeviewaccessible.h"
27

28

29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
/**
 * SECTION:gtktreeselection
 * @Short_description: The selection object for GtkTreeView
 * @Title: GtkTreeSelection
 * @See_also: #GtkTreeView, #GtkTreeViewColumn, #GtkTreeDnd, #GtkTreeMode,
 *   #GtkTreeSortable, #GtkTreeModelSort, #GtkListStore, #GtkTreeStore,
 *   #GtkCellRenderer, #GtkCellEditable, #GtkCellRendererPixbuf,
 *   #GtkCellRendererText, #GtkCellRendererToggle
 *
 * The #GtkTreeSelection object is a helper object to manage the selection
 * for a #GtkTreeView widget.  The #GtkTreeSelection object is
 * automatically created when a new #GtkTreeView widget is created, and
 * cannot exist independentally of this widget.  The primary reason the
 * #GtkTreeSelection objects exists is for cleanliness of code and API.
 * That is, there is no conceptual reason all these functions could not be
 * methods on the #GtkTreeView widget instead of a separate function.
 *
 * The #GtkTreeSelection object is gotten from a #GtkTreeView by calling
 * gtk_tree_view_get_selection().  It can be manipulated to check the
 * selection status of the tree, as well as select and deselect individual
 * rows.  Selection is done completely view side.  As a result, multiple
 * views of the same model can have completely different selections.
 * Additionally, you cannot change the selection of a row on the model that
 * is not currently displayed by the view without expanding its parents
 * first.
 *
 * One of the important things to remember when monitoring the selection of
 * a view is that the #GtkTreeSelection::changed signal is mostly a hint.
 * That is,it may only emit one signal when a range of rows is selected.
 * Additionally, it may on occasion emit a #GtkTreeSelection::changed signal
 * when nothing has happened (mostly as a result of programmers calling
 * select_row on an already selected row).
 */

63 64 65 66 67 68 69 70
struct _GtkTreeSelectionPrivate
{
  GtkTreeView *tree_view;
  GtkSelectionMode type;
  GtkTreeSelectionFunc user_func;
  gpointer user_data;
  GDestroyNotify destroy;
};
71

72
static void gtk_tree_selection_finalize          (GObject               *object);
73 74 75 76 77 78
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);
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
static void gtk_tree_selection_set_property      (GObject               *object,
                                                  guint                  prop_id,
                                                  const GValue          *value,
                                                  GParamSpec            *pspec);
static void gtk_tree_selection_get_property      (GObject               *object,
                                                  guint                  prop_id,
                                                  GValue                *value,
                                                  GParamSpec            *pspec);

enum
{
  PROP_0,
  PROP_MODE,
  N_PROPERTIES
};
94

95 96
enum
{
97
  CHANGED,
98 99 100
  LAST_SIGNAL
};

101
static GParamSpec *properties[N_PROPERTIES];
102
static guint tree_selection_signals [LAST_SIGNAL] = { 0 };
103

Matthias Clasen's avatar
Matthias Clasen committed
104
G_DEFINE_TYPE (GtkTreeSelection, gtk_tree_selection, G_TYPE_OBJECT)
105 106 107 108

static void
gtk_tree_selection_class_init (GtkTreeSelectionClass *class)
{
109
  GObjectClass *object_class;
110

111
  object_class = (GObjectClass*) class;
112

113
  object_class->finalize = gtk_tree_selection_finalize;
114 115
  object_class->set_property = gtk_tree_selection_set_property;
  object_class->get_property = gtk_tree_selection_get_property;
116
  class->changed = NULL;
117

118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
  /* Properties */
  
  /**
   * GtkTreeSelection:mode:
   *
   * Selection mode.
   * See gtk_tree_selection_set_mode() for more information on this property.
   *
   * Since: 3.2
   */
  properties[PROP_MODE] = g_param_spec_enum ("mode",
                                             P_("Mode"),
                                             P_("Selection mode"),
                                             GTK_TYPE_SELECTION_MODE,
                                             GTK_SELECTION_SINGLE,
                                             G_PARAM_READWRITE |
                                             G_PARAM_STATIC_STRINGS);

  /* Install all properties */
  g_object_class_install_properties (object_class, N_PROPERTIES, properties);
  
  /* Signals */
  
141 142 143 144 145 146 147 148 149
  /**
   * GtkTreeSelection::changed:
   * @treeselection: the object which received the signal.
   *
   * Emitted whenever the selection has (possibly) changed. Please note that
   * this signal is mostly a hint.  It may only be emitted once when a range
   * of rows are selected, and it may occasionally be emitted when nothing
   * has happened.
   */
150
  tree_selection_signals[CHANGED] =
151
    g_signal_new (I_("changed"),
Manish Singh's avatar
Manish Singh committed
152 153 154 155 156 157
		  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);
158 159

  g_type_class_add_private (class, sizeof (GtkTreeSelectionPrivate));
160 161 162 163 164
}

static void
gtk_tree_selection_init (GtkTreeSelection *selection)
{
165 166 167 168 169 170 171 172
  GtkTreeSelectionPrivate *priv;

  selection->priv = G_TYPE_INSTANCE_GET_PRIVATE (selection,
                                                 GTK_TYPE_TREE_SELECTION,
                                                 GtkTreeSelectionPrivate);
  priv = selection->priv;

  priv->type = GTK_SELECTION_SINGLE;
173 174
}

175 176 177
static void
gtk_tree_selection_finalize (GObject *object)
{
178
  GtkTreeSelection *selection = GTK_TREE_SELECTION (object);
179
  GtkTreeSelectionPrivate *priv = selection->priv;
180

181 182
  if (priv->destroy)
    priv->destroy (priv->user_data);
183 184

  /* chain parent_class' handler */
Matthias Clasen's avatar
Matthias Clasen committed
185
  G_OBJECT_CLASS (gtk_tree_selection_parent_class)->finalize (object);
186 187
}

188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
static void
gtk_tree_selection_set_property (GObject *object,
                                 guint prop_id,
                                 const GValue *value,
                                 GParamSpec *pspec)
{
  g_return_if_fail (GTK_IS_TREE_SELECTION (object));

  switch (prop_id)
    {
      case PROP_MODE:
        gtk_tree_selection_set_mode (GTK_TREE_SELECTION (object), g_value_get_enum (value));
        break;
      default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
        break;
    }
}

static void
gtk_tree_selection_get_property (GObject *object,
                                 guint prop_id,
                                 GValue *value,
                                 GParamSpec *pspec)
{
  g_return_if_fail (GTK_IS_TREE_SELECTION (object));

  switch (prop_id)
    {
      case PROP_MODE:
        g_value_set_enum (value, gtk_tree_selection_get_mode (GTK_TREE_SELECTION (object)));
        break;
      default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
        break;
    }
}

226
/**
227
 * _gtk_tree_selection_new:
228
 *
229
 * Creates a new #GtkTreeSelection object.  This function should not be invoked,
230
 * as each #GtkTreeView will create its own #GtkTreeSelection.
231
 *
232 233
 * Return value: A newly created #GtkTreeSelection object.
 **/
234 235
GtkTreeSelection*
_gtk_tree_selection_new (void)
236
{
237
  GtkTreeSelection *selection;
238

Manish Singh's avatar
Manish Singh committed
239
  selection = g_object_new (GTK_TYPE_TREE_SELECTION, NULL);
240 241 242 243

  return selection;
}

244
/**
245
 * _gtk_tree_selection_new_with_tree_view:
246
 * @tree_view: The #GtkTreeView.
247
 *
248
 * Creates a new #GtkTreeSelection object.  This function should not be invoked,
249
 * as each #GtkTreeView will create its own #GtkTreeSelection.
250
 *
251 252
 * Return value: A newly created #GtkTreeSelection object.
 **/
253 254
GtkTreeSelection*
_gtk_tree_selection_new_with_tree_view (GtkTreeView *tree_view)
255
{
256
  GtkTreeSelection *selection;
257 258 259

  g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);

260 261
  selection = _gtk_tree_selection_new ();
  _gtk_tree_selection_set_tree_view (selection, tree_view);
262 263 264 265

  return selection;
}

266
/**
267
 * _gtk_tree_selection_set_tree_view:
268 269
 * @selection: A #GtkTreeSelection.
 * @tree_view: The #GtkTreeView.
270
 *
271 272 273
 * Sets the #GtkTreeView of @selection.  This function should not be invoked, as
 * it is used internally by #GtkTreeView.
 **/
274
void
275 276
_gtk_tree_selection_set_tree_view (GtkTreeSelection *selection,
                                   GtkTreeView      *tree_view)
277
{
278 279
  GtkTreeSelectionPrivate *priv;

280 281 282 283
  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
  if (tree_view != NULL)
    g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));

284 285 286
  priv = selection->priv;

  priv->tree_view = tree_view;
287 288
}

289
/**
Havoc Pennington's avatar
Havoc Pennington committed
290
 * gtk_tree_selection_set_mode:
291
 * @selection: A #GtkTreeSelection.
292
 * @type: The selection mode
293
 *
294
 * Sets the selection mode of the @selection.  If the previous type was
Matthias Clasen's avatar
Matthias Clasen committed
295
 * #GTK_SELECTION_MULTIPLE, then the anchor is kept selected, if it was
296
 * previously selected.
297
 **/
298
void
299 300
gtk_tree_selection_set_mode (GtkTreeSelection *selection,
			     GtkSelectionMode  type)
301
{
302
  GtkTreeSelectionPrivate *priv;
303
  GtkTreeSelectionFunc tmp_func;
304

305 306
  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));

307 308 309
  priv = selection->priv;

  if (priv->type == type)
310 311
    return;

312 313 314 315
  if (type == GTK_SELECTION_NONE)
    {
      /* We do this so that we unconditionally unset all rows
       */
316 317
      tmp_func = priv->user_func;
      priv->user_func = NULL;
318
      gtk_tree_selection_unselect_all (selection);
319
      priv->user_func = tmp_func;
320

321
      _gtk_tree_view_set_anchor_path (priv->tree_view, NULL);
322 323 324
    }
  else if (type == GTK_SELECTION_SINGLE ||
	   type == GTK_SELECTION_BROWSE)
325 326 327 328
    {
      GtkRBTree *tree = NULL;
      GtkRBNode *node = NULL;
      gint selected = FALSE;
329
      GtkTreePath *anchor_path = NULL;
330

331
      anchor_path = _gtk_tree_view_get_anchor_path (priv->tree_view);
332 333

      if (anchor_path)
334
	{
335
	  _gtk_tree_view_find_node (priv->tree_view,
336 337 338 339 340 341
				    anchor_path,
				    &tree,
				    &node);
	  
	  if (node && GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
	    selected = TRUE;
342
	}
343 344

      /* We do this so that we unconditionally unset all rows
345
       */
346 347
      tmp_func = priv->user_func;
      priv->user_func = NULL;
348
      gtk_tree_selection_unselect_all (selection);
349
      priv->user_func = tmp_func;
350

351
      if (node && selected)
352 353 354 355
	_gtk_tree_selection_internal_select_node (selection,
						  node,
						  tree,
						  anchor_path,
Kristian Rietveld's avatar
Kristian Rietveld committed
356
                                                  0,
Kristian Rietveld's avatar
Kristian Rietveld committed
357
						  FALSE);
358 359
      if (anchor_path)
	gtk_tree_path_free (anchor_path);
360
    }
361

362
  priv->type = type;
363 364
  
  g_object_notify_by_pspec (G_OBJECT (selection), properties[PROP_MODE]);
365 366
}

367 368 369 370 371 372 373 374 375
/**
 * 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
 **/
376
GtkSelectionMode
377 378
gtk_tree_selection_get_mode (GtkTreeSelection *selection)
{
379
  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), GTK_SELECTION_SINGLE);
380

381
  return selection->priv->type;
382 383
}

384 385 386
/**
 * gtk_tree_selection_set_select_function:
 * @selection: A #GtkTreeSelection.
387 388 389
 * @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
390
 *
391 392 393 394 395 396 397
 * 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.
 */
398 399 400
void
gtk_tree_selection_set_select_function (GtkTreeSelection     *selection,
					GtkTreeSelectionFunc  func,
401
					gpointer              data,
402
					GDestroyNotify        destroy)
403
{
404 405
  GtkTreeSelectionPrivate *priv;

406 407
  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));

408 409 410 411
  priv = selection->priv;

  if (priv->destroy)
    priv->destroy (priv->user_data);
412

413 414 415
  priv->user_func = func;
  priv->user_data = data;
  priv->destroy = destroy;
416 417
}

418
/**
419
 * gtk_tree_selection_get_select_function: (skip)
420 421 422 423 424 425
 * @selection: A #GtkTreeSelection.
 *
 * Returns the current selection function.
 *
 * Return value: The function.
 *
426
 * Since: 2.14
427 428 429 430 431 432
 **/
GtkTreeSelectionFunc
gtk_tree_selection_get_select_function (GtkTreeSelection *selection)
{
  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), NULL);

433
  return selection->priv->user_func;
434 435
}

436
/**
437
 * gtk_tree_selection_get_user_data: (skip)
438
 * @selection: A #GtkTreeSelection.
439
 *
440
 * Returns the user data for the selection function.
441
 *
442 443
 * Return value: The user data.
 **/
444 445 446 447 448
gpointer
gtk_tree_selection_get_user_data (GtkTreeSelection *selection)
{
  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), NULL);

449
  return selection->priv->user_data;
450 451
}

452 453 454 455 456 457
/**
 * gtk_tree_selection_get_tree_view:
 * @selection: A #GtkTreeSelection
 * 
 * Returns the tree view associated with @selection.
 * 
458
 * Return value: (transfer none): A #GtkTreeView
459 460
 **/
GtkTreeView *
Havoc Pennington's avatar
Havoc Pennington committed
461 462 463 464
gtk_tree_selection_get_tree_view (GtkTreeSelection *selection)
{
  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), NULL);

465
  return selection->priv->tree_view;
Havoc Pennington's avatar
Havoc Pennington committed
466 467
}

468 469 470
/**
 * gtk_tree_selection_get_selected:
 * @selection: A #GtkTreeSelection.
471
 * @model: (out) (allow-none) (transfer none): A pointer to set to the #GtkTreeModel, or NULL.
472
 * @iter: (out) (allow-none): The #GtkTreeIter, or NULL.
473
 *
474
 * Sets @iter to the currently selected node if @selection is set to
475 476 477 478
 * #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.
479
 *
Jonathan Blandford's avatar
Jonathan Blandford committed
480
 * Return value: TRUE, if there is a selected node.
481
 **/
482
gboolean
483 484 485
gtk_tree_selection_get_selected (GtkTreeSelection  *selection,
				 GtkTreeModel     **model,
				 GtkTreeIter       *iter)
486
{
487
  GtkTreeSelectionPrivate *priv;
488 489
  GtkRBTree *tree;
  GtkRBNode *node;
490 491
  GtkTreePath *anchor_path;
  gboolean retval;
Kristian Rietveld's avatar
Kristian Rietveld committed
492
  gboolean found_node;
493

494
  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), FALSE);
495 496 497 498 499

  priv = selection->priv;

  g_return_val_if_fail (priv->type != GTK_SELECTION_MULTIPLE, FALSE);
  g_return_val_if_fail (priv->tree_view != NULL, FALSE);
500

501 502 503 504
  /* Clear the iter */
  if (iter)
    memset (iter, 0, sizeof (GtkTreeIter));

505
  if (model)
506
    *model = gtk_tree_view_get_model (priv->tree_view);
507

508
  anchor_path = _gtk_tree_view_get_anchor_path (priv->tree_view);
509 510 511

  if (anchor_path == NULL)
    return FALSE;
512

513
  retval = FALSE;
514

515
  found_node = !_gtk_tree_view_find_node (priv->tree_view,
Kristian Rietveld's avatar
Kristian Rietveld committed
516 517 518 519 520
                                          anchor_path,
                                          &tree,
                                          &node);

  if (found_node && GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
521
    {
Kristian Rietveld's avatar
Kristian Rietveld committed
522 523
      /* we only want to return the anchor if it exists in the rbtree and
       * is selected.
524
       */
Kristian Rietveld's avatar
Kristian Rietveld committed
525 526 527
      if (iter == NULL)
	retval = TRUE;
      else
528
        retval = gtk_tree_model_get_iter (gtk_tree_view_get_model (priv->tree_view),
Kristian Rietveld's avatar
Kristian Rietveld committed
529 530
                                          iter,
                                          anchor_path);
531 532 533
    }
  else
    {
Kristian Rietveld's avatar
Kristian Rietveld committed
534 535 536
      /* We don't want to return the anchor if it isn't actually selected.
       */
      retval = FALSE;
537
    }
538

539
  gtk_tree_path_free (anchor_path);
540

541
  return retval;
542 543
}

544 545 546
/**
 * gtk_tree_selection_get_selected_rows:
 * @selection: A #GtkTreeSelection.
547
 * @model: (out) (allow-none) (transfer none): A pointer to set to the #GtkTreeModel, or %NULL.
548 549 550
 *
 * 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
551
 * want to convert the returned list into a list of #GtkTreeRowReference<!-- -->s.
552
 * To do this, you can use gtk_tree_row_reference_new().
553 554
 *
 * To free the return value, use:
Matthias Clasen's avatar
Matthias Clasen committed
555
 * |[
556
 * g_list_free_full (list, (GDestroyNotify) gtk_tree_path_free);
Matthias Clasen's avatar
Matthias Clasen committed
557
 * ]|
558
 *
559
 * Return value: (element-type GtkTreePath) (transfer full): A #GList containing a #GtkTreePath for each selected row.
560 561
 *
 * Since: 2.2
562 563 564 565 566
 **/
GList *
gtk_tree_selection_get_selected_rows (GtkTreeSelection   *selection,
                                      GtkTreeModel      **model)
{
567
  GtkTreeSelectionPrivate *priv;
568 569 570 571 572 573
  GList *list = NULL;
  GtkRBTree *tree = NULL;
  GtkRBNode *node = NULL;
  GtkTreePath *path;

  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), NULL);
574 575 576 577

  priv = selection->priv;

  g_return_val_if_fail (priv->tree_view != NULL, NULL);
578

579
  if (model)
580
    *model = gtk_tree_view_get_model (priv->tree_view);
581

582
  tree = _gtk_tree_view_get_rbtree (priv->tree_view);
583 584

  if (tree == NULL || tree->root == NULL)
585 586
    return NULL;

587
  if (priv->type == GTK_SELECTION_NONE)
588
    return NULL;
589
  else if (priv->type != GTK_SELECTION_MULTIPLE)
590 591 592 593 594 595 596
    {
      GtkTreeIter iter;

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

597
	  path = gtk_tree_model_get_path (gtk_tree_view_get_model (priv->tree_view), &iter);
598 599 600 601 602 603 604 605
	  list = g_list_append (list, path);

	  return list;
	}

      return NULL;
    }

606
  node = _gtk_rbtree_first (tree);
607 608 609 610 611
  path = gtk_tree_path_new_first ();

  do
    {
      if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
612
	list = g_list_prepend (list, gtk_tree_path_copy (path));
613 614 615 616

      if (node->children)
        {
	  tree = node->children;
617
          node = _gtk_rbtree_first (tree);
618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640

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

		      goto done; 
643 644 645 646 647 648 649 650 651 652 653 654
		    }

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

  gtk_tree_path_free (path);

655 656
 done:
  return g_list_reverse (list);
657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681
}

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.
682 683
 * 
 * Since: 2.2
684 685 686 687
 **/
gint
gtk_tree_selection_count_selected_rows (GtkTreeSelection *selection)
{
688
  GtkTreeSelectionPrivate *priv;
689
  gint count = 0;
690
  GtkRBTree *tree;
691 692 693

  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), 0);

694 695 696 697 698
  priv = selection->priv;

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

  tree = _gtk_tree_view_get_rbtree (priv->tree_view);
699 700

  if (tree == NULL || tree->root == NULL)
701 702
    return 0;

703 704
  if (priv->type == GTK_SELECTION_SINGLE ||
      priv->type == GTK_SELECTION_BROWSE)
705 706 707 708 709 710 711
    {
      if (gtk_tree_selection_get_selected (selection, NULL, NULL))
	return 1;
      else
	return 0;
    }

712
  _gtk_rbtree_traverse (tree, tree->root,
713 714 715 716 717 718 719
			G_PRE_ORDER,
			gtk_tree_selection_count_selected_rows_helper,
			&count);

  return count;
}

720 721 722 723 724 725 726 727 728
/* gtk_tree_selection_selected_foreach helper */
static void
model_changed (gpointer data)
{
  gboolean *stop = (gboolean *)data;

  *stop = TRUE;
}

729 730 731
/**
 * gtk_tree_selection_selected_foreach:
 * @selection: A #GtkTreeSelection.
732
 * @func: (scope call): The function to call for each selected node.
733
 * @data: user data to pass to the function.
734
 *
735 736 737
 * 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.
738
 **/
739 740 741 742 743
void
gtk_tree_selection_selected_foreach (GtkTreeSelection            *selection,
				     GtkTreeSelectionForeachFunc  func,
				     gpointer                     data)
{
744
  GtkTreeSelectionPrivate *priv;
745 746 747
  GtkTreePath *path;
  GtkRBTree *tree;
  GtkRBNode *node;
748
  GtkTreeIter iter;
749
  GtkTreeModel *model;
750

751
  gulong inserted_id, deleted_id, reordered_id, changed_id;
752
  gboolean stop = FALSE;
753

754 755
  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));

756 757 758 759 760
  priv = selection->priv;

  g_return_if_fail (priv->tree_view != NULL);

  tree = _gtk_tree_view_get_rbtree (priv->tree_view);
761 762

  if (func == NULL || tree == NULL || tree->root == NULL)
763 764
    return;

765
  model = gtk_tree_view_get_model (priv->tree_view);
766

767 768
  if (priv->type == GTK_SELECTION_SINGLE ||
      priv->type == GTK_SELECTION_BROWSE)
769
    {
770
      path = _gtk_tree_view_get_anchor_path (priv->tree_view);
771 772

      if (path)
773
	{
774 775
	  gtk_tree_model_get_iter (model, &iter, path);
	  (* func) (model, path, &iter, data);
776 777
	  gtk_tree_path_free (path);
	}
778 779 780
      return;
    }

781
  node = _gtk_rbtree_first (tree);
782

783 784
  g_object_ref (model);

785
  /* connect to signals to monitor changes in treemodel */
786
  inserted_id = g_signal_connect_swapped (model, "row-inserted",
787 788
					  G_CALLBACK (model_changed),
				          &stop);
789
  deleted_id = g_signal_connect_swapped (model, "row-deleted",
790 791
					 G_CALLBACK (model_changed),
				         &stop);
792
  reordered_id = g_signal_connect_swapped (model, "rows-reordered",
793 794
					   G_CALLBACK (model_changed),
				           &stop);
795
  changed_id = g_signal_connect_swapped (priv->tree_view, "notify::model",
796 797
					 G_CALLBACK (model_changed), 
					 &stop);
798

799
  /* find the node internally */
800
  path = gtk_tree_path_new_first ();
801 802 803 804

  do
    {
      if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
805
        {
806 807
          gtk_tree_model_get_iter (model, &iter, path);
	  (* func) (model, path, &iter, data);
808
        }
809 810 811 812

      if (stop)
	goto out;

813 814 815
      if (node->children)
	{
	  tree = node->children;
816
          node = _gtk_rbtree_first (tree);
817

818
	  gtk_tree_path_append_index (path, 0);
819 820 821 822
	}
      else
	{
	  gboolean done = FALSE;
823

824 825 826 827 828 829
	  do
	    {
	      node = _gtk_rbtree_next (tree, node);
	      if (node != NULL)
		{
		  done = TRUE;
830
		  gtk_tree_path_next (path);
831 832 833 834 835
		}
	      else
		{
		  node = tree->parent_node;
		  tree = tree->parent_tree;
836

837
		  if (tree == NULL)
838 839 840
		    {
		      /* we've run out of tree */
		      /* We're done with this function */
841 842

		      goto out;
843
		    }
844

845
		  gtk_tree_path_up (path);
846 847 848 849 850 851
		}
	    }
	  while (!done);
	}
    }
  while (TRUE);
852 853 854 855 856

out:
  if (path)
    gtk_tree_path_free (path);

857 858 859
  g_signal_handler_disconnect (model, inserted_id);
  g_signal_handler_disconnect (model, deleted_id);
  g_signal_handler_disconnect (model, reordered_id);
860
  g_signal_handler_disconnect (priv->tree_view, changed_id);
861
  g_object_unref (model);
862 863 864

  /* check if we have to spew a scary message */
  if (stop)
865 866 867 868
    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");
869 870
}

871 872 873 874
/**
 * gtk_tree_selection_select_path:
 * @selection: A #GtkTreeSelection.
 * @path: The #GtkTreePath to be selected.
875
 *
876 877
 * Select the row at @path.
 **/
878 879 880 881
void
gtk_tree_selection_select_path (GtkTreeSelection *selection,
				GtkTreePath      *path)
{
882
  GtkTreeSelectionPrivate *priv;
883 884
  GtkRBNode *node;
  GtkRBTree *tree;
885
  gboolean ret;
Kristian Rietveld's avatar
Kristian Rietveld committed
886
  GtkTreeSelectMode mode = 0;
887 888

  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
889 890 891 892

  priv = selection->priv;

  g_return_if_fail (priv->tree_view != NULL);
893 894
  g_return_if_fail (path != NULL);

895
  ret = _gtk_tree_view_find_node (priv->tree_view,
896 897 898
				  path,
				  &tree,
				  &node);
899

900 901
  if (node == NULL || GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED) ||
      ret == TRUE)
902 903
    return;

904
  if (priv->type == GTK_SELECTION_MULTIPLE)
Kristian Rietveld's avatar
Kristian Rietveld committed
905
    mode = GTK_TREE_SELECT_MODE_TOGGLE;
906 907 908 909 910

  _gtk_tree_selection_internal_select_node (selection,
					    node,
					    tree,
					    path,
Kristian Rietveld's avatar
Kristian Rietveld committed
911
                                            mode,
Kristian Rietveld's avatar
Kristian Rietveld committed
912
					    FALSE);
913 914
}

915 916 917 918
/**
 * gtk_tree_selection_unselect_path:
 * @selection: A #GtkTreeSelection.
 * @path: The #GtkTreePath to be unselected.
919
 *
920 921
 * Unselects the row at @path.
 **/
922 923 924 925
void
gtk_tree_selection_unselect_path (GtkTreeSelection *selection,
				  GtkTreePath      *path)
{
926
  GtkTreeSelectionPrivate *priv;
927 928
  GtkRBNode *node;
  GtkRBTree *tree;
929
  gboolean ret;
930 931

  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
932 933 934 935

  priv = selection->priv;

  g_return_if_fail (priv->tree_view != NULL);
936 937
  g_return_if_fail (path != NULL);

938
  ret = _gtk_tree_view_find_node (priv->tree_view,
939 940 941
				  path,
				  &tree,
				  &node);
942

943 944
  if (node == NULL || !GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED) ||
      ret == TRUE)
945 946 947 948 949 950
    return;

  _gtk_tree_selection_internal_select_node (selection,
					    node,
					    tree,
					    path,
Kristian Rietveld's avatar
Kristian Rietveld committed
951
                                            GTK_TREE_SELECT_MODE_TOGGLE,
Kristian Rietveld's avatar
Kristian Rietveld committed
952
					    TRUE);
953 954
}

955
/**
956
 * gtk_tree_selection_select_iter:
957
 * @selection: A #GtkTreeSelection.
958
 * @iter: The #GtkTreeIter to be selected.
959
 *
960
 * Selects the specified iterator.
961
 **/
962
void
963 964
gtk_tree_selection_select_iter (GtkTreeSelection *selection,
				GtkTreeIter      *iter)
965
{
966
  GtkTreeSelectionPrivate *priv;
967
  GtkTreePath *path;
968
  GtkTreeModel *model;
969 970

  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
971

972 973 974 975 976
  priv = selection->priv;

  g_return_if_fail (priv->tree_view != NULL);

  model = gtk_tree_view_get_model (priv->tree_view);
977
  g_return_if_fail (model != NULL);
978
  g_return_if_fail (iter != NULL);
979

980
  path = gtk_tree_model_get_path (model, iter);
981 982 983 984 985 986 987 988 989

  if (path == NULL)
    return;

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


990
/**
991
 * gtk_tree_selection_unselect_iter:
992
 * @selection: A #GtkTreeSelection.
993
 * @iter: The #GtkTreeIter to be unselected.
994
 *
995
 * Unselects the specified iterator.
996
 **/
997
void
998 999
gtk_tree_selection_unselect_iter (GtkTreeSelection *selection,
				  GtkTreeIter      *iter)
1000
{
1001
  GtkTreeSelectionPrivate *priv;
1002
  GtkTreePath *path;
1003
  GtkTreeModel *model;
1004 1005

  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1006

1007 1008 1009 1010 1011
  priv = selection->priv;

  g_return_if_fail (priv->tree_view != NULL);

  model = gtk_tree_view_get_model (priv->tree_view);
1012
  g_return_if_fail (model != NULL);
1013
  g_return_if_fail (iter != NULL);
1014

1015
  path = gtk_tree_model_get_path (model, iter);
1016 1017 1018 1019

  if (path == NULL)
    return;

1020
  gtk_tree_selection_unselect_path (selection, path);
1021 1022 1023
  gtk_tree_path_free (path);
}

1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037
/**
 * 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)
{
1038
  GtkTreeSelectionPrivate *priv;
1039 1040
  GtkRBNode *node;
  GtkRBTree *tree;
1041
  gboolean ret;
1042 1043

  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), FALSE);
1044 1045 1046

  priv = selection->priv;

1047
  g_return_val_if_fail (path != NULL, FALSE);
1048
  g_return_val_if_fail (priv->tree_view != NULL, FALSE);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
1049

1050
  if (gtk_tree_view_get_model (priv->tree_view) == NULL)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
1051
    return FALSE;
1052

1053
  ret = _gtk_tree_view_find_node (priv->tree_view,
1054 1055 1056
				  path,
				  &tree,
				  &node);
1057

1058 1059
  if ((node == NULL) || !GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED) ||
      ret == TRUE)
1060 1061 1062 1063 1064 1065 1066 1067 1068 1069
    return FALSE;

  return TRUE;
}

/**
 * gtk_tree_selection_iter_is_selected:
 * @selection: A #GtkTreeSelection
 * @iter: A valid #GtkTreeIter
 * 
1070
 * Returns %TRUE if the row at @iter is currently selected.
1071 1072 1073 1074 1075 1076 1077
 * 
 * Return value: %TRUE, if @iter is selected
 **/
gboolean
gtk_tree_selection_iter_is_selected (GtkTreeSelection *selection,
				     GtkTreeIter      *iter)
{
1078
  GtkTreeSelectionPrivate *priv;
1079
  GtkTreePath *path;
1080
  GtkTreeModel *model;
1081 1082 1083
  gboolean retval;

  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), FALSE);
1084 1085 1086

  priv = selection->priv;

1087
  g_return_val_if_fail (iter != NULL, FALSE);
1088
  g_return_val_if_fail (priv->tree_view != NULL, FALSE);
1089

1090
  model = gtk_tree_view_get_model (priv->tree_view);
1091 1092 1093
  g_return_val_if_fail (model != NULL, FALSE);

  path = gtk_tree_model_get_path (model, iter);
1094 1095 1096 1097 1098 1099 1100 1101 1102 1103
  if (path == NULL)
    return FALSE;

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

  return retval;
}


1104 1105 1106 1107 1108 1109 1110 1111 1112
/* Wish I was in python, right now... */
struct _TempTuple {
  GtkTreeSelection *selection;
  gint dirty;
};

static void
select_all_helper (GtkRBTree  *tree,
		   GtkRBNode  *node,
1113
		   gpointer    data)
1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124
{
  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))
    {
1125
      tuple->dirty = gtk_tree_selection_real_select_node (tuple->selection, tree, node, TRUE) || tuple->dirty;
1126 1127 1128
    }
}

1129 1130 1131 1132 1133 1134

/* 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)
1135
{
1136
  GtkTreeSelectionPrivate *priv = selection->priv;
1137
  struct _TempTuple *tuple;
1138
  GtkRBTree *tree;
1139

1140
  tree = _gtk_tree_view_get_rbtree (priv->tree_view);
1141 1142

  if (tree == NULL)
1143
    return FALSE;
1144

1145 1146 1147 1148 1149
  /* Mark all nodes selected */
  tuple = g_new (struct _TempTuple, 1);
  tuple->selection = selection;
  tuple->dirty = FALSE;

1150
  _gtk_rbtree_traverse (tree, tree->root,
1151 1152 1153 1154
			G_PRE_ORDER,
			select_all_helper,
			tuple);
  if (tuple->dirty)
1155 1156
    {
      g_free (tuple);
1157
      return TRUE;
1158
    }
1159 1160
  g_free (tuple);
  return FALSE;
1161 1162
}

1163 1164 1165
/**
 * gtk_tree_selection_select_all:
 * @selection: A #GtkTreeSelection.
1166
 *
1167
 * Selects all the nodes. @selection must be set to #GTK_SELECTION_MULTIPLE
1168
 * mode.
1169
 **/
1170 1171 1172
void
gtk_tree_selection_select_all (GtkTreeSelection *selection)
{
1173 1174
  GtkTreeSelectionPrivate *priv;

1175
  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
Federico Mena Quintero's avatar
Federico Mena Quintero committed
1176

1177 1178 1179 1180 1181 1182
  priv = selection->priv;

  g_return_if_fail (priv->tree_view != NULL);

  if (_gtk_tree_view_get_rbtree (priv->tree_view) == NULL ||
      gtk_tree_view_get_model (priv->tree_view) == NULL)
1183
    return;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
1184

1185
  g_return_if_fail (priv->type == GTK_SELECTION_MULTIPLE);
1186 1187

  if (gtk_tree_selection_real_select_all (selection))
Manish Singh's avatar
Manish Singh committed
1188
    g_signal_emit (selection, tree_selection_signals[CHANGED], 0);
1189 1190 1191 1192 1193
}

static void
unselect_all_helper (GtkRBTree  *tree,
		     GtkRBNode  *node,
1194
		     gpointer    data)
1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205
{
  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))
    {
1206
      tuple->dirty = gtk_tree_selection_real_select_node (tuple->selection, tree, node, FALSE) || tuple->dirty;
1207 1208 1209
    }
}

1210
static gboolean
1211
gtk_tree_selection_real_unselect_all (GtkTreeSelection *selection)
1212
{
1213
  GtkTreeSelectionPrivate *priv = selection->priv;
1214 1215
  struct _TempTuple *tuple;

1216 1217
  if (priv->type == GTK_SELECTION_SINGLE ||
      priv->type == GTK_SELECTION_BROWSE)
1218 1219 1220
    {
      GtkRBTree *tree = NULL;
      GtkRBNode *node = NULL;
1221
      GtkTreePath *anchor_path;
1222

1223
      anchor_path = _gtk_tree_view_get_anchor_path (priv->tree_view);
1224 1225 1226

      if (anchor_path == NULL)
        return FALSE;
1227

1228
      _gtk_tree_view_find_node (priv->tree_view,
1229
                                anchor_path,
1230 1231
				&tree,
				&node);
1232 1233

      gtk_tree_path_free (anchor_path);
1234 1235 1236

      if (tree == NULL)
        return FALSE;
1237

1238
      if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
1239
	{
1240 1241
	  if (gtk_tree_selection_real_select_node (selection, tree, node, FALSE))
	    {
1242
	      _gtk_tree_view_set_anchor_path (priv->tree_view, NULL);
1243 1244
	      return TRUE;
	    }
1245 1246
	}
      return FALSE;
1247
    }
1248
  else
1249
    {
1250 1251
      GtkRBTree *tree;

1252 1253 1254
      tuple = g_new (struct _TempTuple, 1);
      tuple->selection = selection;
      tuple->dirty = FALSE;
1255

1256
      tree = _gtk_tree_view_get_rbtree (priv->tree_view);
1257
      _gtk_rbtree_traverse (tree, tree->root,
1258 1259 1260
                            G_PRE_ORDER,
                            unselect_all_helper,
                            tuple);
1261

1262 1263 1264 1265 1266
      if (tuple->dirty)
        {
          g_free (tuple);
          return TRUE;
        }
1267
      g_free (tuple);
1268
      return FALSE;
1269
    }
1270 1271
}

1272 1273 1274
/**
 * gtk_tree_selection_unselect_all:
 * @selection: A #GtkTreeSelection.
1275
 *
1276 1277
 * Unselects all the nodes.
 **/
1278
void
1279
gtk_tree_selection_unselect_all (GtkTreeSelection *selection)
1280
{
1281 1282
  GtkTreeSelectionPrivate *priv;

1283
  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
Federico Mena Quintero's avatar
Federico Mena Quintero committed
1284

1285 1286 1287 1288 1289 1290
  priv = selection->priv;

  g_return_if_fail (priv->tree_view != NULL);

  if (_gtk_tree_view_get_rbtree (priv->tree_view) == NULL ||
      gtk_tree_view_get_model (priv->tree_view) == NULL)
1291 1292
    return;
  
1293
  if (gtk_tree_selection_real_unselect_all (selection))
Manish Singh's avatar
Manish Singh committed