gtktreeselection.c 43.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/* gtktreeselection.h
 * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

20
#include "config.h"
Matthias Clasen's avatar
Matthias Clasen committed
21
#include <string.h>
22 23 24
#include "gtktreeselection.h"
#include "gtktreeprivate.h"
#include "gtkrbtree.h"
25
#include "gtkmarshalers.h"
Matthias Clasen's avatar
Matthias Clasen committed
26
#include "gtkintl.h"
27
#include "gtktypebuiltins.h"
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 63
/**
 * 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).
 */

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

Matthias Clasen's avatar
Matthias Clasen committed
105
G_DEFINE_TYPE (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 134 135 136 137 138 139 140 141
  /* 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 */
  
142 143 144 145 146 147 148 149 150
  /**
   * 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.
   */
151
  tree_selection_signals[CHANGED] =
Matthias Clasen's avatar
Matthias Clasen committed
152
    g_signal_new (I_("changed"),
Manish Singh's avatar
Manish Singh committed
153 154 155 156 157 158
		  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);
159 160

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

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

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

  priv->type = GTK_SELECTION_SINGLE;
174 175
}

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

182 183
  if (priv->destroy)
    priv->destroy (priv->user_data);
Tim Janik's avatar
Tim Janik committed
184 185

  /* chain parent_class' handler */
Matthias Clasen's avatar
Matthias Clasen committed
186
  G_OBJECT_CLASS (gtk_tree_selection_parent_class)->finalize (object);
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 226
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;
    }
}

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

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

  return selection;
}

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

  g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);

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

  return selection;
}

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

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

285 286 287
  priv = selection->priv;

  priv->tree_view = tree_view;
288 289
}

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

306 307
  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));

308 309 310
  priv = selection->priv;

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

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

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

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

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

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

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

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

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

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

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

407 408
  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));

409 410 411 412
  priv = selection->priv;

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

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

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

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

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

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

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

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

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

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

  priv = selection->priv;

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

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

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

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

  if (anchor_path == NULL)
    return FALSE;
513

514
  retval = FALSE;
515

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

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

540
  gtk_tree_path_free (anchor_path);
541

542
  return retval;
543 544
}

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

  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), NULL);
576 577 578 579

  priv = selection->priv;

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

581
  if (model)
582
    *model = gtk_tree_view_get_model (priv->tree_view);
583

584
  tree = _gtk_tree_view_get_rbtree (priv->tree_view);
585 586

  if (tree == NULL || tree->root == NULL)
587 588
    return NULL;

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

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

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

	  return list;
	}

      return NULL;
    }

608
  node = tree->root;
609 610 611 612 613 614 615 616

  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))
617
	list = g_list_prepend (list, gtk_tree_path_copy (path));
618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648

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

		      goto done; 
651 652 653 654 655 656 657 658 659 660 661 662
		    }

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

  gtk_tree_path_free (path);

663 664
 done:
  return g_list_reverse (list);
665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689
}

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.
690 691
 * 
 * Since: 2.2
692 693 694 695
 **/
gint
gtk_tree_selection_count_selected_rows (GtkTreeSelection *selection)
{
696
  GtkTreeSelectionPrivate *priv;
697
  gint count = 0;
698
  GtkRBTree *tree;
699 700 701

  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), 0);

702 703 704 705 706
  priv = selection->priv;

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

  tree = _gtk_tree_view_get_rbtree (priv->tree_view);
707 708

  if (tree == NULL || tree->root == NULL)
709 710
    return 0;

711 712
  if (priv->type == GTK_SELECTION_SINGLE ||
      priv->type == GTK_SELECTION_BROWSE)
713 714 715 716 717 718 719
    {
      if (gtk_tree_selection_get_selected (selection, NULL, NULL))
	return 1;
      else
	return 0;
    }

720
  _gtk_rbtree_traverse (tree, tree->root,
721 722 723 724 725 726 727
			G_PRE_ORDER,
			gtk_tree_selection_count_selected_rows_helper,
			&count);

  return count;
}

728 729 730 731 732 733 734 735 736
/* gtk_tree_selection_selected_foreach helper */
static void
model_changed (gpointer data)
{
  gboolean *stop = (gboolean *)data;

  *stop = TRUE;
}

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

759
  gulong inserted_id, deleted_id, reordered_id, changed_id;
760
  gboolean stop = FALSE;
761

762 763
  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));

764 765 766 767 768
  priv = selection->priv;

  g_return_if_fail (priv->tree_view != NULL);

  tree = _gtk_tree_view_get_rbtree (priv->tree_view);
769 770

  if (func == NULL || tree == NULL || tree->root == NULL)
771 772
    return;

773
  model = gtk_tree_view_get_model (priv->tree_view);
774

775 776
  if (priv->type == GTK_SELECTION_SINGLE ||
      priv->type == GTK_SELECTION_BROWSE)
777
    {
778
      path = _gtk_tree_view_get_anchor_path (priv->tree_view);
779 780

      if (path)
781
	{
782 783
	  gtk_tree_model_get_iter (model, &iter, path);
	  (* func) (model, path, &iter, data);
784 785
	  gtk_tree_path_free (path);
	}
786 787 788
      return;
    }

789
  node = tree->root;
790
  
791 792 793
  while (node->left != tree->nil)
    node = node->left;

794 795
  g_object_ref (model);

796
  /* connect to signals to monitor changes in treemodel */
797
  inserted_id = g_signal_connect_swapped (model, "row-inserted",
798 799
					  G_CALLBACK (model_changed),
				          &stop);
800
  deleted_id = g_signal_connect_swapped (model, "row-deleted",
801 802
					 G_CALLBACK (model_changed),
				         &stop);
803
  reordered_id = g_signal_connect_swapped (model, "rows-reordered",
804 805
					   G_CALLBACK (model_changed),
				           &stop);
806
  changed_id = g_signal_connect_swapped (priv->tree_view, "notify::model",
807 808
					 G_CALLBACK (model_changed), 
					 &stop);
809

810
  /* find the node internally */
811
  path = gtk_tree_path_new_first ();
812 813 814 815

  do
    {
      if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
816
        {
817 818
          gtk_tree_model_get_iter (model, &iter, path);
	  (* func) (model, path, &iter, data);
819
        }
820 821 822 823

      if (stop)
	goto out;

824 825 826 827
      if (node->children)
	{
	  tree = node->children;
	  node = tree->root;
828

829 830
	  while (node->left != tree->nil)
	    node = node->left;
831

832
	  gtk_tree_path_append_index (path, 0);
833 834 835 836
	}
      else
	{
	  gboolean done = FALSE;
837

838 839 840 841 842 843
	  do
	    {
	      node = _gtk_rbtree_next (tree, node);
	      if (node != NULL)
		{
		  done = TRUE;
844
		  gtk_tree_path_next (path);
845 846 847 848 849
		}
	      else
		{
		  node = tree->parent_node;
		  tree = tree->parent_tree;
850

851
		  if (tree == NULL)
852 853 854
		    {
		      /* we've run out of tree */
		      /* We're done with this function */
855 856

		      goto out;
857
		    }
858

859
		  gtk_tree_path_up (path);
860 861 862 863 864 865
		}
	    }
	  while (!done);
	}
    }
  while (TRUE);
866 867 868 869 870

out:
  if (path)
    gtk_tree_path_free (path);

871 872 873
  g_signal_handler_disconnect (model, inserted_id);
  g_signal_handler_disconnect (model, deleted_id);
  g_signal_handler_disconnect (model, reordered_id);
874
  g_signal_handler_disconnect (priv->tree_view, changed_id);
875
  g_object_unref (model);
876 877 878

  /* check if we have to spew a scary message */
  if (stop)
879 880 881 882
    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");
883 884
}

885 886 887 888
/**
 * gtk_tree_selection_select_path:
 * @selection: A #GtkTreeSelection.
 * @path: The #GtkTreePath to be selected.
889
 *
890 891
 * Select the row at @path.
 **/
892 893 894 895
void
gtk_tree_selection_select_path (GtkTreeSelection *selection,
				GtkTreePath      *path)
{
896
  GtkTreeSelectionPrivate *priv;
897 898
  GtkRBNode *node;
  GtkRBTree *tree;
899
  gboolean ret;
Kristian Rietveld's avatar
Kristian Rietveld committed
900
  GtkTreeSelectMode mode = 0;
901 902

  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
903 904 905 906

  priv = selection->priv;

  g_return_if_fail (priv->tree_view != NULL);
907 908
  g_return_if_fail (path != NULL);

909
  ret = _gtk_tree_view_find_node (priv->tree_view,
910 911 912
				  path,
				  &tree,
				  &node);
913

914 915
  if (node == NULL || GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED) ||
      ret == TRUE)
916 917
    return;

918
  if (priv->type == GTK_SELECTION_MULTIPLE)
Kristian Rietveld's avatar
Kristian Rietveld committed
919
    mode = GTK_TREE_SELECT_MODE_TOGGLE;
920 921 922 923 924

  _gtk_tree_selection_internal_select_node (selection,
					    node,
					    tree,
					    path,
Kristian Rietveld's avatar
Kristian Rietveld committed
925
                                            mode,
Kristian Rietveld's avatar
Kristian Rietveld committed
926
					    FALSE);
927 928
}

929 930 931 932
/**
 * gtk_tree_selection_unselect_path:
 * @selection: A #GtkTreeSelection.
 * @path: The #GtkTreePath to be unselected.
933
 *
934 935
 * Unselects the row at @path.
 **/
936 937 938 939
void
gtk_tree_selection_unselect_path (GtkTreeSelection *selection,
				  GtkTreePath      *path)
{
940
  GtkTreeSelectionPrivate *priv;
941 942
  GtkRBNode *node;
  GtkRBTree *tree;
943
  gboolean ret;
944 945

  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
946 947 948 949

  priv = selection->priv;

  g_return_if_fail (priv->tree_view != NULL);
950 951
  g_return_if_fail (path != NULL);

952
  ret = _gtk_tree_view_find_node (priv->tree_view,
953 954 955
				  path,
				  &tree,
				  &node);
956

957 958
  if (node == NULL || !GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED) ||
      ret == TRUE)
959 960 961 962 963 964
    return;

  _gtk_tree_selection_internal_select_node (selection,
					    node,
					    tree,
					    path,
Kristian Rietveld's avatar
Kristian Rietveld committed
965
                                            GTK_TREE_SELECT_MODE_TOGGLE,
Kristian Rietveld's avatar
Kristian Rietveld committed
966
					    TRUE);
967 968
}

969
/**
970
 * gtk_tree_selection_select_iter:
971
 * @selection: A #GtkTreeSelection.
972
 * @iter: The #GtkTreeIter to be selected.
973
 *
974
 * Selects the specified iterator.
975
 **/
976
void
977 978
gtk_tree_selection_select_iter (GtkTreeSelection *selection,
				GtkTreeIter      *iter)
979
{
980
  GtkTreeSelectionPrivate *priv;
981
  GtkTreePath *path;
982
  GtkTreeModel *model;
983 984

  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
985

986 987 988 989 990
  priv = selection->priv;

  g_return_if_fail (priv->tree_view != NULL);

  model = gtk_tree_view_get_model (priv->tree_view);
991
  g_return_if_fail (model != NULL);
992
  g_return_if_fail (iter != NULL);
993

994
  path = gtk_tree_model_get_path (model, iter);
995 996 997 998 999 1000 1001 1002 1003

  if (path == NULL)
    return;

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


1004
/**
1005
 * gtk_tree_selection_unselect_iter:
1006
 * @selection: A #GtkTreeSelection.
1007
 * @iter: The #GtkTreeIter to be unselected.
1008
 *
1009
 * Unselects the specified iterator.
1010
 **/
1011
void
1012 1013
gtk_tree_selection_unselect_iter (GtkTreeSelection *selection,
				  GtkTreeIter      *iter)
1014
{
1015
  GtkTreeSelectionPrivate *priv;
1016
  GtkTreePath *path;
1017
  GtkTreeModel *model;
1018 1019

  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1020

1021 1022 1023 1024 1025
  priv = selection->priv;

  g_return_if_fail (priv->tree_view != NULL);

  model = gtk_tree_view_get_model (priv->tree_view);
1026
  g_return_if_fail (model != NULL);
1027
  g_return_if_fail (iter != NULL);
1028

1029
  path = gtk_tree_model_get_path (model, iter);
1030 1031 1032 1033

  if (path == NULL)
    return;

Jonathan Blandford's avatar
Jonathan Blandford committed
1034
  gtk_tree_selection_unselect_path (selection, path);
1035 1036 1037
  gtk_tree_path_free (path);
}

1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051
/**
 * 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)
{
1052
  GtkTreeSelectionPrivate *priv;
1053 1054
  GtkRBNode *node;
  GtkRBTree *tree;
1055
  gboolean ret;
1056 1057

  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), FALSE);
1058 1059 1060

  priv = selection->priv;

1061
  g_return_val_if_fail (path != NULL, FALSE);
1062
  g_return_val_if_fail (priv->tree_view != NULL, FALSE);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
1063

1064
  if (gtk_tree_view_get_model (priv->tree_view) == NULL)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
1065
    return FALSE;
1066

1067
  ret = _gtk_tree_view_find_node (priv->tree_view,
1068 1069 1070
				  path,
				  &tree,
				  &node);
1071

1072 1073
  if ((node == NULL) || !GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED) ||
      ret == TRUE)
1074 1075 1076 1077 1078 1079 1080 1081 1082 1083
    return FALSE;

  return TRUE;
}

/**
 * gtk_tree_selection_iter_is_selected:
 * @selection: A #GtkTreeSelection
 * @iter: A valid #GtkTreeIter
 * 
Soren Sandmann's avatar
Soren Sandmann committed
1084
 * Returns %TRUE if the row at @iter is currently selected.
1085 1086 1087 1088 1089 1090 1091
 * 
 * Return value: %TRUE, if @iter is selected
 **/
gboolean
gtk_tree_selection_iter_is_selected (GtkTreeSelection *selection,
				     GtkTreeIter      *iter)
{
1092
  GtkTreeSelectionPrivate *priv;
1093
  GtkTreePath *path;
1094
  GtkTreeModel *model;
1095 1096 1097
  gboolean retval;

  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), FALSE);
1098 1099 1100

  priv = selection->priv;

1101
  g_return_val_if_fail (iter != NULL, FALSE);
1102
  g_return_val_if_fail (priv->tree_view != NULL, FALSE);
1103

1104
  model = gtk_tree_view_get_model (priv->tree_view);
1105 1106 1107
  g_return_val_if_fail (model != NULL, FALSE);

  path = gtk_tree_model_get_path (model, iter);
1108 1109 1110 1111 1112 1113 1114 1115 1116 1117
  if (path == NULL)
    return FALSE;

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

  return retval;
}


1118 1119 1120 1121 1122 1123 1124 1125 1126
/* Wish I was in python, right now... */
struct _TempTuple {
  GtkTreeSelection *selection;
  gint dirty;
};

static void
select_all_helper (GtkRBTree  *tree,
		   GtkRBNode  *node,
1127
		   gpointer    data)
1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138
{
  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))
    {
1139
      tuple->dirty = gtk_tree_selection_real_select_node (tuple->selection, tree, node, TRUE) || tuple->dirty;
1140 1141 1142
    }
}

1143 1144 1145 1146 1147 1148

/* 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)
1149
{
1150