gtktreeselection.c 43 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 Jardon's avatar
Javier Jardon 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 "gtkprivate.h"
26
#include "gtktypebuiltins.h"
27
#include "a11y/gtktreeviewaccessibleprivate.h"
28

29

30 31 32 33
/**
 * SECTION:gtktreeselection
 * @Short_description: The selection object for GtkTreeView
 * @Title: GtkTreeSelection
William Jon McCann's avatar
William Jon McCann committed
34
 * @See_also: #GtkTreeView, #GtkTreeViewColumn, #GtkTreeModel,
35 36
 *   #GtkTreeSortable, #GtkTreeModelSort, #GtkListStore, #GtkTreeStore,
 *   #GtkCellRenderer, #GtkCellEditable, #GtkCellRendererPixbuf,
37
 *   #GtkCellRendererText, #GtkCellRendererToggle, [GtkTreeView drag-and-drop][gtk3-GtkTreeView-drag-and-drop]
38 39 40 41
 *
 * 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
42
 * cannot exist independently of this widget.  The primary reason the
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
 * #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.
Matthias Clasen's avatar
Matthias Clasen committed
58
 * That is, it may only emit one signal when a range of rows is selected.
59 60 61 62 63
 * 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).
 */

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

73
static void gtk_tree_selection_finalize          (GObject               *object);
74 75 76 77 78 79
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);
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
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
};
95

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

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

105
G_DEFINE_TYPE_WITH_PRIVATE (GtkTreeSelection, gtk_tree_selection, G_TYPE_OBJECT)
106 107 108 109

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

112
  object_class = (GObjectClass*) class;
113

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

119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
  /* 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,
134
                                             GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
135 136 137 138 139 140

  /* 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
		  G_OBJECT_CLASS_TYPE (object_class),
		  G_SIGNAL_RUN_FIRST,
		  G_STRUCT_OFFSET (GtkTreeSelectionClass, changed),
		  NULL, NULL,
156
		  NULL,
Manish Singh's avatar
Manish Singh committed
157
		  G_TYPE_NONE, 0);
158 159 160 161 162
}

static void
gtk_tree_selection_init (GtkTreeSelection *selection)
{
163 164
  selection->priv = gtk_tree_selection_get_instance_private (selection);
  selection->priv->type = GTK_SELECTION_SINGLE;
165 166
}

167 168 169
static void
gtk_tree_selection_finalize (GObject *object)
{
170
  GtkTreeSelection *selection = GTK_TREE_SELECTION (object);
171
  GtkTreeSelectionPrivate *priv = selection->priv;
172

173 174
  if (priv->destroy)
    priv->destroy (priv->user_data);
175 176

  /* chain parent_class' handler */
Matthias Clasen's avatar
Matthias Clasen committed
177
  G_OBJECT_CLASS (gtk_tree_selection_parent_class)->finalize (object);
178 179
}

180 181 182 183 184 185 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
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;
    }
}

218
/**
219
 * _gtk_tree_selection_new:
220
 *
221
 * Creates a new #GtkTreeSelection object.  This function should not be invoked,
222
 * as each #GtkTreeView will create its own #GtkTreeSelection.
223
 *
224
 * Returns: A newly created #GtkTreeSelection object.
225
 **/
226 227
GtkTreeSelection*
_gtk_tree_selection_new (void)
228
{
229
  GtkTreeSelection *selection;
230

Manish Singh's avatar
Manish Singh committed
231
  selection = g_object_new (GTK_TYPE_TREE_SELECTION, NULL);
232 233 234 235

  return selection;
}

236
/**
237
 * _gtk_tree_selection_new_with_tree_view:
238
 * @tree_view: The #GtkTreeView.
239
 *
240
 * Creates a new #GtkTreeSelection object.  This function should not be invoked,
241
 * as each #GtkTreeView will create its own #GtkTreeSelection.
242
 *
243
 * Returns: A newly created #GtkTreeSelection object.
244
 **/
245 246
GtkTreeSelection*
_gtk_tree_selection_new_with_tree_view (GtkTreeView *tree_view)
247
{
248
  GtkTreeSelection *selection;
249 250 251

  g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);

252 253
  selection = _gtk_tree_selection_new ();
  _gtk_tree_selection_set_tree_view (selection, tree_view);
254 255 256 257

  return selection;
}

258
/**
259
 * _gtk_tree_selection_set_tree_view:
260 261
 * @selection: A #GtkTreeSelection.
 * @tree_view: The #GtkTreeView.
262
 *
263 264 265
 * Sets the #GtkTreeView of @selection.  This function should not be invoked, as
 * it is used internally by #GtkTreeView.
 **/
266
void
267 268
_gtk_tree_selection_set_tree_view (GtkTreeSelection *selection,
                                   GtkTreeView      *tree_view)
269
{
270 271
  GtkTreeSelectionPrivate *priv;

272 273 274 275
  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
  if (tree_view != NULL)
    g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));

276 277 278
  priv = selection->priv;

  priv->tree_view = tree_view;
279 280
}

281
/**
Havoc Pennington's avatar
Havoc Pennington committed
282
 * gtk_tree_selection_set_mode:
283
 * @selection: A #GtkTreeSelection.
284
 * @type: The selection mode
285
 *
286
 * Sets the selection mode of the @selection.  If the previous type was
Matthias Clasen's avatar
Matthias Clasen committed
287
 * #GTK_SELECTION_MULTIPLE, then the anchor is kept selected, if it was
288
 * previously selected.
289
 **/
290
void
291 292
gtk_tree_selection_set_mode (GtkTreeSelection *selection,
			     GtkSelectionMode  type)
293
{
294
  GtkTreeSelectionPrivate *priv;
295
  GtkTreeSelectionFunc tmp_func;
296

297 298
  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));

299 300 301
  priv = selection->priv;

  if (priv->type == type)
302 303
    return;

304 305 306 307
  if (type == GTK_SELECTION_NONE)
    {
      /* We do this so that we unconditionally unset all rows
       */
308 309
      tmp_func = priv->user_func;
      priv->user_func = NULL;
310
      gtk_tree_selection_unselect_all (selection);
311
      priv->user_func = tmp_func;
312

313
      _gtk_tree_view_set_anchor_path (priv->tree_view, NULL);
314 315 316
    }
  else if (type == GTK_SELECTION_SINGLE ||
	   type == GTK_SELECTION_BROWSE)
317 318 319 320
    {
      GtkRBTree *tree = NULL;
      GtkRBNode *node = NULL;
      gint selected = FALSE;
321
      GtkTreePath *anchor_path = NULL;
322

323
      anchor_path = _gtk_tree_view_get_anchor_path (priv->tree_view);
324 325

      if (anchor_path)
326
	{
327
	  _gtk_tree_view_find_node (priv->tree_view,
328 329 330 331 332 333
				    anchor_path,
				    &tree,
				    &node);
	  
	  if (node && GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
	    selected = TRUE;
334
	}
335 336

      /* We do this so that we unconditionally unset all rows
337
       */
338 339
      tmp_func = priv->user_func;
      priv->user_func = NULL;
340
      gtk_tree_selection_unselect_all (selection);
341
      priv->user_func = tmp_func;
342

343
      if (node && selected)
344 345 346 347
	_gtk_tree_selection_internal_select_node (selection,
						  node,
						  tree,
						  anchor_path,
Kristian Rietveld's avatar
Kristian Rietveld committed
348
                                                  0,
Kristian Rietveld's avatar
Kristian Rietveld committed
349
						  FALSE);
350 351
      if (anchor_path)
	gtk_tree_path_free (anchor_path);
352
    }
353

354
  priv->type = type;
355 356
  
  g_object_notify_by_pspec (G_OBJECT (selection), properties[PROP_MODE]);
357 358
}

359 360 361 362 363 364 365
/**
 * gtk_tree_selection_get_mode:
 * @selection: a #GtkTreeSelection
 *
 * Gets the selection mode for @selection. See
 * gtk_tree_selection_set_mode().
 *
366
 * Returns: the current selection mode
367
 **/
368
GtkSelectionMode
369 370
gtk_tree_selection_get_mode (GtkTreeSelection *selection)
{
371
  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), GTK_SELECTION_SINGLE);
372

373
  return selection->priv->type;
374 375
}

376 377 378
/**
 * gtk_tree_selection_set_select_function:
 * @selection: A #GtkTreeSelection.
379
 * @func: The selection function. May be %NULL
380
 * @data: The selection function’s data. May be %NULL
381
 * @destroy: The destroy function for user data.  May be %NULL
382
 *
383 384 385 386 387 388 389
 * 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.
 */
390 391 392
void
gtk_tree_selection_set_select_function (GtkTreeSelection     *selection,
					GtkTreeSelectionFunc  func,
393
					gpointer              data,
394
					GDestroyNotify        destroy)
395
{
396 397
  GtkTreeSelectionPrivate *priv;

398 399
  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));

400 401 402 403
  priv = selection->priv;

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

405 406 407
  priv->user_func = func;
  priv->user_data = data;
  priv->destroy = destroy;
408 409
}

410
/**
411
 * gtk_tree_selection_get_select_function: (skip)
412 413 414 415
 * @selection: A #GtkTreeSelection.
 *
 * Returns the current selection function.
 *
416
 * Returns: The function.
417
 *
418
 * Since: 2.14
419 420 421 422 423 424
 **/
GtkTreeSelectionFunc
gtk_tree_selection_get_select_function (GtkTreeSelection *selection)
{
  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), NULL);

425
  return selection->priv->user_func;
426 427
}

428
/**
429
 * gtk_tree_selection_get_user_data: (skip)
430
 * @selection: A #GtkTreeSelection.
431
 *
432
 * Returns the user data for the selection function.
433
 *
434
 * Returns: The user data.
435
 **/
436 437 438 439 440
gpointer
gtk_tree_selection_get_user_data (GtkTreeSelection *selection)
{
  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), NULL);

441
  return selection->priv->user_data;
442 443
}

444 445 446 447 448 449
/**
 * gtk_tree_selection_get_tree_view:
 * @selection: A #GtkTreeSelection
 * 
 * Returns the tree view associated with @selection.
 * 
450
 * Returns: (transfer none): A #GtkTreeView
451 452
 **/
GtkTreeView *
Havoc Pennington's avatar
Havoc Pennington committed
453 454 455 456
gtk_tree_selection_get_tree_view (GtkTreeSelection *selection)
{
  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), NULL);

457
  return selection->priv->tree_view;
Havoc Pennington's avatar
Havoc Pennington committed
458 459
}

460 461 462
/**
 * gtk_tree_selection_get_selected:
 * @selection: A #GtkTreeSelection.
463
 * @model: (out) (allow-none) (transfer none): A pointer to set to the #GtkTreeModel, or NULL.
464
 * @iter: (out) (allow-none): The #GtkTreeIter, or NULL.
465
 *
466
 * Sets @iter to the currently selected node if @selection is set to
467 468 469 470
 * #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.
471
 *
472
 * Returns: TRUE, if there is a selected node.
473
 **/
474
gboolean
475 476 477
gtk_tree_selection_get_selected (GtkTreeSelection  *selection,
				 GtkTreeModel     **model,
				 GtkTreeIter       *iter)
478
{
479
  GtkTreeSelectionPrivate *priv;
480 481
  GtkRBTree *tree;
  GtkRBNode *node;
482
  GtkTreePath *anchor_path;
483
  gboolean retval = FALSE;
Kristian Rietveld's avatar
Kristian Rietveld committed
484
  gboolean found_node;
485

486
  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), FALSE);
487 488 489 490 491

  priv = selection->priv;

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

493 494 495 496
  /* Clear the iter */
  if (iter)
    memset (iter, 0, sizeof (GtkTreeIter));

497
  if (model)
498
    *model = gtk_tree_view_get_model (priv->tree_view);
499

500
  anchor_path = _gtk_tree_view_get_anchor_path (priv->tree_view);
501 502 503

  if (anchor_path == NULL)
    return FALSE;
504

505
  found_node = !_gtk_tree_view_find_node (priv->tree_view,
Kristian Rietveld's avatar
Kristian Rietveld committed
506 507 508 509 510
                                          anchor_path,
                                          &tree,
                                          &node);

  if (found_node && GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
511
    {
Kristian Rietveld's avatar
Kristian Rietveld committed
512 513
      /* we only want to return the anchor if it exists in the rbtree and
       * is selected.
514
       */
Kristian Rietveld's avatar
Kristian Rietveld committed
515 516 517
      if (iter == NULL)
	retval = TRUE;
      else
518
        retval = gtk_tree_model_get_iter (gtk_tree_view_get_model (priv->tree_view),
Kristian Rietveld's avatar
Kristian Rietveld committed
519 520
                                          iter,
                                          anchor_path);
521 522 523
    }
  else
    {
Kristian Rietveld's avatar
Kristian Rietveld committed
524 525 526
      /* We don't want to return the anchor if it isn't actually selected.
       */
      retval = FALSE;
527
    }
528

529
  gtk_tree_path_free (anchor_path);
530

531
  return retval;
532 533
}

534 535 536
/**
 * gtk_tree_selection_get_selected_rows:
 * @selection: A #GtkTreeSelection.
537
 * @model: (out) (allow-none) (transfer none): A pointer to set to the #GtkTreeModel, or %NULL.
538 539 540
 *
 * Creates a list of path of all selected rows. Additionally, if you are
 * planning on modifying the model after calling this function, you may
541
 * want to convert the returned list into a list of #GtkTreeRowReferences.
542
 * To do this, you can use gtk_tree_row_reference_new().
543 544
 *
 * To free the return value, use:
545
 * |[<!-- language="C" -->
546
 * g_list_free_full (list, (GDestroyNotify) gtk_tree_path_free);
Matthias Clasen's avatar
Matthias Clasen committed
547
 * ]|
548
 *
549
 * Returns: (element-type GtkTreePath) (transfer full): A #GList containing a #GtkTreePath for each selected row.
550 551
 *
 * Since: 2.2
552 553 554 555 556
 **/
GList *
gtk_tree_selection_get_selected_rows (GtkTreeSelection   *selection,
                                      GtkTreeModel      **model)
{
557
  GtkTreeSelectionPrivate *priv;
558 559 560 561 562 563
  GList *list = NULL;
  GtkRBTree *tree = NULL;
  GtkRBNode *node = NULL;
  GtkTreePath *path;

  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), NULL);
564 565 566 567

  priv = selection->priv;

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

569
  if (model)
570
    *model = gtk_tree_view_get_model (priv->tree_view);
571

572
  tree = _gtk_tree_view_get_rbtree (priv->tree_view);
573 574

  if (tree == NULL || tree->root == NULL)
575 576
    return NULL;

577
  if (priv->type == GTK_SELECTION_NONE)
578
    return NULL;
579
  else if (priv->type != GTK_SELECTION_MULTIPLE)
580 581 582 583 584
    {
      GtkTreeIter iter;

      if (gtk_tree_selection_get_selected (selection, NULL, &iter))
        {
585
	  path = gtk_tree_model_get_path (gtk_tree_view_get_model (priv->tree_view), &iter);
586 587 588 589 590 591 592 593
	  list = g_list_append (list, path);

	  return list;
	}

      return NULL;
    }

594
  node = _gtk_rbtree_first (tree);
595 596
  path = gtk_tree_path_new_first ();

597
  while (node != NULL)
598 599
    {
      if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
600
	list = g_list_prepend (list, gtk_tree_path_copy (path));
601 602 603 604

      if (node->children)
        {
	  tree = node->children;
605
          node = _gtk_rbtree_first (tree);
606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628

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

		      goto done; 
631 632 633 634 635 636 637 638 639 640 641
		    }

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

  gtk_tree_path_free (path);

642 643
 done:
  return g_list_reverse (list);
644 645 646 647 648 649 650 651 652
}

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

653 654
  g_return_if_fail (node != NULL);

655 656 657 658 659 660 661 662 663 664 665 666 667 668 669
  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.
 *
670
 * Returns: The number of rows selected.
671 672
 * 
 * Since: 2.2
673 674 675 676
 **/
gint
gtk_tree_selection_count_selected_rows (GtkTreeSelection *selection)
{
677
  GtkTreeSelectionPrivate *priv;
678
  gint count = 0;
679
  GtkRBTree *tree;
680 681 682

  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), 0);

683 684 685 686 687
  priv = selection->priv;

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

  tree = _gtk_tree_view_get_rbtree (priv->tree_view);
688 689

  if (tree == NULL || tree->root == NULL)
690 691
    return 0;

692 693
  if (priv->type == GTK_SELECTION_SINGLE ||
      priv->type == GTK_SELECTION_BROWSE)
694 695 696 697 698 699 700
    {
      if (gtk_tree_selection_get_selected (selection, NULL, NULL))
	return 1;
      else
	return 0;
    }

701
  _gtk_rbtree_traverse (tree, tree->root,
702 703 704 705 706 707 708
			G_PRE_ORDER,
			gtk_tree_selection_count_selected_rows_helper,
			&count);

  return count;
}

709 710 711 712 713 714 715 716 717
/* gtk_tree_selection_selected_foreach helper */
static void
model_changed (gpointer data)
{
  gboolean *stop = (gboolean *)data;

  *stop = TRUE;
}

718 719 720
/**
 * gtk_tree_selection_selected_foreach:
 * @selection: A #GtkTreeSelection.
721
 * @func: (scope call): The function to call for each selected node.
722
 * @data: user data to pass to the function.
723
 *
724 725 726
 * 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.
727
 **/
728 729 730 731 732
void
gtk_tree_selection_selected_foreach (GtkTreeSelection            *selection,
				     GtkTreeSelectionForeachFunc  func,
				     gpointer                     data)
{
733
  GtkTreeSelectionPrivate *priv;
734 735 736
  GtkTreePath *path;
  GtkRBTree *tree;
  GtkRBNode *node;
737
  GtkTreeIter iter;
738
  GtkTreeModel *model;
739

740
  gulong inserted_id, deleted_id, reordered_id, changed_id;
741
  gboolean stop = FALSE;
742

743 744
  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));

745 746 747 748 749
  priv = selection->priv;

  g_return_if_fail (priv->tree_view != NULL);

  tree = _gtk_tree_view_get_rbtree (priv->tree_view);
750 751

  if (func == NULL || tree == NULL || tree->root == NULL)
752 753
    return;

754
  model = gtk_tree_view_get_model (priv->tree_view);
755

756 757
  if (priv->type == GTK_SELECTION_SINGLE ||
      priv->type == GTK_SELECTION_BROWSE)
758
    {
759
      path = _gtk_tree_view_get_anchor_path (priv->tree_view);
760 761

      if (path)
762
	{
763 764
	  gtk_tree_model_get_iter (model, &iter, path);
	  (* func) (model, path, &iter, data);
765 766
	  gtk_tree_path_free (path);
	}
767 768 769
      return;
    }

770
  node = _gtk_rbtree_first (tree);
771

772 773
  g_object_ref (model);

774
  /* connect to signals to monitor changes in treemodel */
775
  inserted_id = g_signal_connect_swapped (model, "row-inserted",
776 777
					  G_CALLBACK (model_changed),
				          &stop);
778
  deleted_id = g_signal_connect_swapped (model, "row-deleted",
779 780
					 G_CALLBACK (model_changed),
				         &stop);
781
  reordered_id = g_signal_connect_swapped (model, "rows-reordered",
782 783
					   G_CALLBACK (model_changed),
				           &stop);
784
  changed_id = g_signal_connect_swapped (priv->tree_view, "notify::model",
785 786
					 G_CALLBACK (model_changed), 
					 &stop);
787

788
  /* find the node internally */
789
  path = gtk_tree_path_new_first ();
790

791
  while (node != NULL)
792 793
    {
      if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
794
        {
795 796
          gtk_tree_model_get_iter (model, &iter, path);
	  (* func) (model, path, &iter, data);
797
        }
798 799 800 801

      if (stop)
	goto out;

802 803 804
      if (node->children)
	{
	  tree = node->children;
805
          node = _gtk_rbtree_first (tree);
806

807
	  gtk_tree_path_append_index (path, 0);
808 809 810 811
	}
      else
	{
	  gboolean done = FALSE;
812

813 814 815 816 817 818
	  do
	    {
	      node = _gtk_rbtree_next (tree, node);
	      if (node != NULL)
		{
		  done = TRUE;
819
		  gtk_tree_path_next (path);
820 821 822 823 824
		}
	      else
		{
		  node = tree->parent_node;
		  tree = tree->parent_tree;
825

826
		  if (tree == NULL)
827 828 829
		    {
		      /* we've run out of tree */
		      /* We're done with this function */
830 831

		      goto out;
832
		    }
833

834
		  gtk_tree_path_up (path);
835 836 837 838 839
		}
	    }
	  while (!done);
	}
    }
840 841 842 843 844

out:
  if (path)
    gtk_tree_path_free (path);

845 846 847
  g_signal_handler_disconnect (model, inserted_id);
  g_signal_handler_disconnect (model, deleted_id);
  g_signal_handler_disconnect (model, reordered_id);
848
  g_signal_handler_disconnect (priv->tree_view, changed_id);
849
  g_object_unref (model);
850 851 852

  /* check if we have to spew a scary message */
  if (stop)
853 854 855
    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"
856
	       "gtk_tree_selection_get_selected_rows instead.");
857 858
}

859 860 861 862
/**
 * gtk_tree_selection_select_path:
 * @selection: A #GtkTreeSelection.
 * @path: The #GtkTreePath to be selected.
863
 *
864 865
 * Select the row at @path.
 **/
866 867 868 869
void
gtk_tree_selection_select_path (GtkTreeSelection *selection,
				GtkTreePath      *path)
{
870
  GtkTreeSelectionPrivate *priv;
871 872
  GtkRBNode *node;
  GtkRBTree *tree;
873
  gboolean ret;
Kristian Rietveld's avatar
Kristian Rietveld committed
874
  GtkTreeSelectMode mode = 0;
875 876

  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
877 878 879 880

  priv = selection->priv;

  g_return_if_fail (priv->tree_view != NULL);
881 882
  g_return_if_fail (path != NULL);

883
  ret = _gtk_tree_view_find_node (priv->tree_view,
884 885 886
				  path,
				  &tree,
				  &node);
887

888 889
  if (node == NULL || GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED) ||
      ret == TRUE)
890 891
    return;

892
  if (priv->type == GTK_SELECTION_MULTIPLE)
Kristian Rietveld's avatar
Kristian Rietveld committed
893
    mode = GTK_TREE_SELECT_MODE_TOGGLE;
894 895 896 897 898

  _gtk_tree_selection_internal_select_node (selection,
					    node,
					    tree,
					    path,
Kristian Rietveld's avatar
Kristian Rietveld committed
899
                                            mode,
Kristian Rietveld's avatar
Kristian Rietveld committed
900
					    FALSE);
901 902
}

903 904 905 906
/**
 * gtk_tree_selection_unselect_path:
 * @selection: A #GtkTreeSelection.
 * @path: The #GtkTreePath to be unselected.
907
 *
908 909
 * Unselects the row at @path.
 **/
910 911 912 913
void
gtk_tree_selection_unselect_path (GtkTreeSelection *selection,
				  GtkTreePath      *path)
{
914
  GtkTreeSelectionPrivate *priv;
915 916
  GtkRBNode *node;
  GtkRBTree *tree;
917
  gboolean ret;
918 919

  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
920 921 922 923

  priv = selection->priv;

  g_return_if_fail (priv->tree_view != NULL);
924 925
  g_return_if_fail (path != NULL);

926
  ret = _gtk_tree_view_find_node (priv->tree_view,
927 928 929
				  path,
				  &tree,
				  &node);
930

931 932
  if (node == NULL || !GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED) ||
      ret == TRUE)
933 934 935 936 937 938
    return;

  _gtk_tree_selection_internal_select_node (selection,
					    node,
					    tree,
					    path,
Kristian Rietveld's avatar
Kristian Rietveld committed
939
                                            GTK_TREE_SELECT_MODE_TOGGLE,
Kristian Rietveld's avatar
Kristian Rietveld committed
940
					    TRUE);
941 942
}

943
/**
944
 * gtk_tree_selection_select_iter:
945
 * @selection: A #GtkTreeSelection.
946
 * @iter: The #GtkTreeIter to be selected.
947
 *
948
 * Selects the specified iterator.
949
 **/
950
void
951 952
gtk_tree_selection_select_iter (GtkTreeSelection *selection,
				GtkTreeIter      *iter)
953
{
954
  GtkTreeSelectionPrivate *priv;
955
  GtkTreePath *path;
956
  GtkTreeModel *model;
957 958

  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
959

960 961 962 963 964
  priv = selection->priv;

  g_return_if_fail (priv->tree_view != NULL);

  model = gtk_tree_view_get_model (priv->tree_view);
965
  g_return_if_fail (model != NULL);
966
  g_return_if_fail (iter != NULL);
967

968
  path = gtk_tree_model_get_path (model, iter);
969 970 971 972 973 974 975 976 977

  if (path == NULL)
    return;

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


978
/**
979
 * gtk_tree_selection_unselect_iter:
980
 * @selection: A #GtkTreeSelection.
981
 * @iter: The #GtkTreeIter to be unselected.
982
 *
983
 * Unselects the specified iterator.
984
 **/
985
void
986 987
gtk_tree_selection_unselect_iter (GtkTreeSelection *selection,
				  GtkTreeIter      *iter)
988
{
989
  GtkTreeSelectionPrivate *priv;
990
  GtkTreePath *path;
991
  GtkTreeModel *model;
992 993

  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
994

995 996 997 998 999
  priv = selection->priv;

  g_return_if_fail (priv->tree_view != NULL);

  model = gtk_tree_view_get_model (priv->tree_view);
1000
  g_return_if_fail (model != NULL);
1001
  g_return_if_fail (iter != NULL);
1002

1003
  path = gtk_tree_model_get_path (model, iter);
1004 1005 1006 1007

  if (path == NULL)
    return;

1008
  gtk_tree_selection_unselect_path (selection, path);
1009 1010 1011
  gtk_tree_path_free (path);
}

1012 1013 1014 1015 1016 1017 1018 1019
/**
 * 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
 * 
1020
 * Returns: %TRUE if @path is selected.
1021 1022 1023 1024 1025
 **/
gboolean
gtk_tree_selection_path_is_selected (GtkTreeSelection *selection,
				     GtkTreePath      *path)
{
1026
  GtkTreeSelectionPrivate *priv;
1027 1028
  GtkRBNode *node;
  GtkRBTree *tree;
1029
  gboolean ret;
1030 1031

  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), FALSE);
1032 1033 1034

  priv = selection->priv;

1035
  g_return_val_if_fail (path != NULL, FALSE);
1036
  g_return_val_if_fail (priv->tree_view != NULL, FALSE);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
1037

1038
  if (gtk_tree_view_get_model (priv->tree_view) == NULL)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
1039
    return FALSE;
1040

1041
  ret = _gtk_tree_view_find_node (priv->tree_view,
1042 1043 1044
				  path,
				  &tree,
				  &node);
1045

1046 1047
  if ((node == NULL) || !GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED) ||
      ret == TRUE)
1048 1049 1050 1051 1052 1053 1054 1055 1056 1057
    return FALSE;

  return TRUE;
}

/**
 * gtk_tree_selection_iter_is_selected:
 * @selection: A #GtkTreeSelection
 * @iter: A valid #GtkTreeIter
 * 
1058
 * Returns %TRUE if the row at @iter is currently selected.
1059
 * 
1060
 * Returns: %TRUE, if @iter is selected
1061 1062 1063 1064 1065
 **/
gboolean
gtk_tree_selection_iter_is_selected (GtkTreeSelection *selection,
				     GtkTreeIter      *iter)
{
1066
  GtkTreeSelectionPrivate *priv;
1067
  GtkTreePath *path;
1068
  GtkTreeModel *model;
1069 1070 1071
  gboolean retval;

  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), FALSE);
1072 1073 1074

  priv = selection->priv;

1075
  g_return_val_if_fail (iter != NULL, FALSE);
1076
  g_return_val_if_fail (priv->tree_view != NULL, FALSE);
1077

1078
  model = gtk_tree_view_get_model (priv->tree_view);
1079 1080 1081
  g_return_val_if_fail (model != NULL, FALSE);

  path = gtk_tree_model_get_path (model, iter);
1082 1083 1084 1085 1086 1087 1088 1089 1090 1091
  if (path == NULL)
    return FALSE;

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

  return retval;
}


1092 1093 1094 1095 1096 1097 1098 1099 1100
/* Wish I was in python, right now... */
struct _TempTuple {
  GtkTreeSelection *selection;
  gint dirty;
};

static void
select_all_helper (GtkRBTree  *tree,
		   GtkRBNode  *node,
1101
		   gpointer    data)
1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112
{
  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))
    {
1113
      tuple->dirty = gtk_tree_selection_real_select_node (tuple->selection, tree, node, TRUE) || tuple->dirty;
1114 1115 1116
    }
}

1117 1118 1119 1120 1121 1122

/* 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)
1123
{
1124
  GtkTreeSelectionPrivate *priv = selection->priv;
1125
  struct _TempTuple *tuple;
1126
  GtkRBTree *tree;
1127

1128
  tree = _gtk_tree_view_get_rbtree (priv->tree_view);
1129 1130

  if (tree == NULL)
1131
    return FALSE;
1132

1133 1134 1135 1136 1137
  /* Mark all nodes selected */
  tuple = g_new (struct _TempTuple, 1);
  tuple->selection = selection;
  tuple->dirty = FALSE;

1138
  _gtk_rbtree_traverse (tree, tree->root,
1139 1140 1141 1142
			G_PRE_ORDER,
			select_all_helper,
			tuple);
  if (tuple->dirty)
1143 1144
    {
      g_free (tuple);
1145
      return TRUE;
1146
    }
1147 1148
  g_free (tuple);
  return FALSE;
1149 1150
}

1151 1152 1153
/**
 * gtk_tree_selection_select_all:
 * @selection: A #GtkTreeSelection.
1154
 *
1155
 * Selects all the nodes. @selection must be set to #GTK_SELECTION_MULTIPLE
1156
 * mode.
1157
 **/
1158 1159 1160
void
gtk_tree_selection_select_all (GtkTreeSelection *selection)
{
1161 1162
  GtkTreeSelectionPrivate *priv;

1163
  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
Federico Mena Quintero's avatar
Federico Mena Quintero committed
1164

1165 1166 1167 1168 1169 1170
  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)
1171
    return;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
1172

1173
  g_return_if_fail (priv->type == GTK_SELECTION_MULTIPLE);
1174 1175

  if (gtk_tree_selection_real_select_all (selection))
Manish Singh's avatar
Manish Singh committed
1176
    g_signal_emit (selection, tree_selection_signals[CHANGED], 0);
1177 1178 1179 1180 1181
}

static void
unselect_all_helper (GtkRBTree  *tree,
		     GtkRBNode  *node,
1182
		     gpointer    data)
1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193
{
  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))
    {
1194
      tuple->dirty = gtk_tree_selection_real_select_node (tuple->selection, tree, node, FALSE) || tuple->dirty;
1195 1196 1197
    }
}

1198
static gboolean
1199
gtk_tree_selection_real_unselect_all (GtkTreeSelection *selection)
1200
{
1201
  GtkTreeSelectionPrivate *priv = selection->priv;
1202 1203
  struct _TempTuple *tuple;

1204 1205
  if (priv->type == GTK_SELECTION_SINGLE ||
      priv->type == GTK_SELECTION_BROWSE)
1206 1207 1208
    {
      GtkRBTree *tree = NULL;
      GtkRBNode *node = NULL;
1209
      GtkTreePath *anchor_path;
1210

1211
      anchor_path = _gtk_tree_view_get_anchor_path (priv->tree_view);
1212 1213 1214

      if (anchor_path == NULL)
        return FALSE;
1215

1216
      _gtk_tree_view_find_node (priv->tree_view,
1217
                                anchor_path,
1218 1219
				&tree,
				&node);
1220 1221

      gtk_tree_path_free (anchor_path);
1222 1223 1224

      if (tree == NULL)
        return FALSE;
1225

1226
      if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
1227
	{
1228 1229
	  if (gtk_tree_selection_real_select_node (selection, tree, node, FALSE))
	    {
1230
	      _gtk_tree_view_set_anchor_path (priv->tree_view, NULL);</