gtktreeselection.c 42.6 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"
Matthias Clasen's avatar
Matthias Clasen committed
19
#include <string.h>
20
21
#include "gtktreeselection.h"
#include "gtktreeprivate.h"
22
#include "gtktreerbtreeprivate.h"
23
#include "gtkmarshalers.h"
Matthias Clasen's avatar
Matthias Clasen committed
24
#include "gtkintl.h"
25
#include "gtkprivate.h"
26
#include "gtktypebuiltins.h"
27

28

29
/**
Matthias Clasen's avatar
Matthias Clasen committed
30
31
32
 * GtkTreeSelection:
 *
 * The selection object for GtkTreeView
33
 *
Matthias Clasen's avatar
Matthias Clasen committed
34
35
36
 * 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
37
 * cannot exist independently of this widget.  The primary reason the
Matthias Clasen's avatar
Matthias Clasen committed
38
 * `GtkTreeSelection` objects exists is for cleanliness of code and API.
39
 * That is, there is no conceptual reason all these functions could not be
Matthias Clasen's avatar
Matthias Clasen committed
40
 * methods on the `GtkTreeView` widget instead of a separate function.
41
 *
Matthias Clasen's avatar
Matthias Clasen committed
42
 * The `GtkTreeSelection` object is gotten from a `GtkTreeView` by calling
43
44
45
46
47
48
49
50
51
 * 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
Matthias Clasen's avatar
Matthias Clasen committed
52
 * a view is that the `GtkTreeSelection`::changed signal is mostly a hint.
Matthias Clasen's avatar
Matthias Clasen committed
53
 * That is, it may only emit one signal when a range of rows is selected.
Matthias Clasen's avatar
Matthias Clasen committed
54
 * Additionally, it may on occasion emit a `GtkTreeSelection`::changed signal
55
56
57
58
 * when nothing has happened (mostly as a result of programmers calling
 * select_row on an already selected row).
 */

Matthias Clasen's avatar
Matthias Clasen committed
59
60
61
62
63
64
typedef struct _GtkTreeSelectionClass   GtkTreeSelectionClass;

struct _GtkTreeSelection
{
  GObject parent;

65
66
67
68
69
  GtkTreeView *tree_view;
  GtkSelectionMode type;
  GtkTreeSelectionFunc user_func;
  gpointer user_data;
  GDestroyNotify destroy;
Matthias Clasen's avatar
Matthias Clasen committed
70
71
72
73
74
75
76
77
78
};

struct _GtkTreeSelectionClass
{
  GObjectClass parent_class;

  void (* changed) (GtkTreeSelection *selection);
};

79
static void gtk_tree_selection_finalize          (GObject               *object);
Benjamin Otte's avatar
Benjamin Otte committed
80
81
82
static int  gtk_tree_selection_real_select_all   (GtkTreeSelection      *selection);
static int  gtk_tree_selection_real_unselect_all (GtkTreeSelection      *selection);
static int  gtk_tree_selection_real_select_node  (GtkTreeSelection      *selection,
83
84
						  GtkTreeRBTree         *tree,
						  GtkTreeRBNode         *node,
85
						  gboolean               select);
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
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
};
101

102
103
enum
{
104
  CHANGED,
105
106
107
  LAST_SIGNAL
};

108
static GParamSpec *properties[N_PROPERTIES];
109
static guint tree_selection_signals [LAST_SIGNAL] = { 0 };
110

111
G_DEFINE_TYPE(GtkTreeSelection, gtk_tree_selection, G_TYPE_OBJECT)
112
113
114
115

static void
gtk_tree_selection_class_init (GtkTreeSelectionClass *class)
{
116
  GObjectClass *object_class;
117

118
  object_class = (GObjectClass*) class;
119

120
  object_class->finalize = gtk_tree_selection_finalize;
121
122
  object_class->set_property = gtk_tree_selection_set_property;
  object_class->get_property = gtk_tree_selection_get_property;
123
  class->changed = NULL;
124

125
126
127
128
129
130
131
132
  /* Properties */
  
  /**
   * GtkTreeSelection:mode:
   *
   * Selection mode.
   * See gtk_tree_selection_set_mode() for more information on this property.
   */
133
  properties[PROP_MODE] = g_param_spec_enum ("mode", NULL, NULL,
134
135
                                             GTK_TYPE_SELECTION_MODE,
                                             GTK_SELECTION_SINGLE,
136
                                             GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
137
138
139
140
141
142

  /* Install all properties */
  g_object_class_install_properties (object_class, N_PROPERTIES, properties);
  
  /* Signals */
  
143
144
145
146
147
148
149
150
151
  /**
   * 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.
   */
152
  tree_selection_signals[CHANGED] =
Matthias Clasen's avatar
Matthias Clasen committed
153
    g_signal_new (I_("changed"),
Manish Singh's avatar
Manish Singh committed
154
155
156
157
		  G_OBJECT_CLASS_TYPE (object_class),
		  G_SIGNAL_RUN_FIRST,
		  G_STRUCT_OFFSET (GtkTreeSelectionClass, changed),
		  NULL, NULL,
158
		  NULL,
Manish Singh's avatar
Manish Singh committed
159
		  G_TYPE_NONE, 0);
160
161
162
163
164
}

static void
gtk_tree_selection_init (GtkTreeSelection *selection)
{
165
  selection->type = GTK_SELECTION_SINGLE;
166
167
}

168
169
170
static void
gtk_tree_selection_finalize (GObject *object)
{
171
172
  GtkTreeSelection *selection = GTK_TREE_SELECTION (object);

173
174
  if (selection->destroy)
    selection->destroy (selection->user_data);
Tim Janik's avatar
Tim Janik committed
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:
Jonathan Blandford's avatar
Jonathan Blandford committed
220
 *
Matthias Clasen's avatar
Matthias Clasen committed
221
222
 * Creates a new `GtkTreeSelection` object.  This function should not be invoked,
 * as each `GtkTreeView` will create its own `GtkTreeSelection`.
Jonathan Blandford's avatar
Jonathan Blandford committed
223
 *
Matthias Clasen's avatar
Matthias Clasen committed
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:
Matthias Clasen's avatar
Matthias Clasen committed
238
 * @tree_view: The `GtkTreeView`.
Jonathan Blandford's avatar
Jonathan Blandford committed
239
 *
Matthias Clasen's avatar
Matthias Clasen committed
240
241
 * Creates a new `GtkTreeSelection` object.  This function should not be invoked,
 * as each `GtkTreeView` will create its own `GtkTreeSelection`.
Jonathan Blandford's avatar
Jonathan Blandford committed
242
 *
Matthias Clasen's avatar
Matthias Clasen committed
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:
Matthias Clasen's avatar
Matthias Clasen committed
260
261
 * @selection: A `GtkTreeSelection`.
 * @tree_view: The `GtkTreeView`.
Jonathan Blandford's avatar
Jonathan Blandford committed
262
 *
Matthias Clasen's avatar
Matthias Clasen committed
263
264
 * Sets the `GtkTreeView` of @selection.  This function should not be invoked, as
 * it is used internally by `GtkTreeView`.
265
 **/
266
void
267
268
_gtk_tree_selection_set_tree_view (GtkTreeSelection *selection,
                                   GtkTreeView      *tree_view)
269
{
270

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

275
  selection->tree_view = tree_view;
276
277
}

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

293
294
  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));

295
  if (selection->type == type)
296
297
    return;

298
299
300
301
  if (type == GTK_SELECTION_NONE)
    {
      /* We do this so that we unconditionally unset all rows
       */
302
303
      tmp_func = selection->user_func;
      selection->user_func = NULL;
304
      gtk_tree_selection_unselect_all (selection);
305
      selection->user_func = tmp_func;
306

307
      _gtk_tree_view_set_anchor_path (selection->tree_view, NULL);
308
309
310
    }
  else if (type == GTK_SELECTION_SINGLE ||
	   type == GTK_SELECTION_BROWSE)
311
    {
312
313
      GtkTreeRBTree *tree = NULL;
      GtkTreeRBNode *node = NULL;
Benjamin Otte's avatar
Benjamin Otte committed
314
      int selected = FALSE;
315
      GtkTreePath *anchor_path = NULL;
316

317
      anchor_path = _gtk_tree_view_get_anchor_path (selection->tree_view);
318
319

      if (anchor_path)
320
	{
321
	  _gtk_tree_view_find_node (selection->tree_view,
322
323
324
325
				    anchor_path,
				    &tree,
				    &node);
	  
326
	  if (node && GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED))
327
	    selected = TRUE;
328
	}
329
330

      /* We do this so that we unconditionally unset all rows
331
       */
332
333
      tmp_func = selection->user_func;
      selection->user_func = NULL;
334
      gtk_tree_selection_unselect_all (selection);
335
      selection->user_func = tmp_func;
336

337
      if (node && selected)
338
339
340
341
	_gtk_tree_selection_internal_select_node (selection,
						  node,
						  tree,
						  anchor_path,
Kristian Rietveld's avatar
Kristian Rietveld committed
342
                                                  0,
Kristian Rietveld's avatar
Kristian Rietveld committed
343
						  FALSE);
Jonathan Blandford's avatar
Jonathan Blandford committed
344
345
      if (anchor_path)
	gtk_tree_path_free (anchor_path);
346
    }
347

348
  selection->type = type;
349
350
  
  g_object_notify_by_pspec (G_OBJECT (selection), properties[PROP_MODE]);
351
352
}

353
354
/**
 * gtk_tree_selection_get_mode:
Matthias Clasen's avatar
Matthias Clasen committed
355
 * @selection: a `GtkTreeSelection`
356
357
358
359
 *
 * Gets the selection mode for @selection. See
 * gtk_tree_selection_set_mode().
 *
360
 * Returns: the current selection mode
361
 **/
362
GtkSelectionMode
363
364
gtk_tree_selection_get_mode (GtkTreeSelection *selection)
{
365
  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), GTK_SELECTION_SINGLE);
366

367
  return selection->type;
368
369
}

370
371
/**
 * gtk_tree_selection_set_select_function:
Matthias Clasen's avatar
Matthias Clasen committed
372
 * @selection: A `GtkTreeSelection`.
373
 * @func: (nullable): The selection function. May be %NULL
374
 * @data: The selection function’s data. May be %NULL
375
 * @destroy: The destroy function for user data.  May be %NULL
Jonathan Blandford's avatar
Jonathan Blandford committed
376
 *
377
378
379
380
381
382
383
 * 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.
 */
384
385
386
void
gtk_tree_selection_set_select_function (GtkTreeSelection     *selection,
					GtkTreeSelectionFunc  func,
387
					gpointer              data,
Michael Natterer's avatar
Michael Natterer committed
388
					GDestroyNotify        destroy)
389
{
390

391
392
  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));

393
394
  if (selection->destroy)
    selection->destroy (selection->user_data);
395

396
397
398
  selection->user_func = func;
  selection->user_data = data;
  selection->destroy = destroy;
399
400
}

401
/**
402
 * gtk_tree_selection_get_select_function: (skip)
Matthias Clasen's avatar
Matthias Clasen committed
403
 * @selection: A `GtkTreeSelection`.
404
405
406
 *
 * Returns the current selection function.
 *
407
 * Returns: The function.
408
409
410
411
412
413
 **/
GtkTreeSelectionFunc
gtk_tree_selection_get_select_function (GtkTreeSelection *selection)
{
  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), NULL);

414
  return selection->user_func;
415
416
}

417
/**
418
 * gtk_tree_selection_get_user_data: (skip)
Matthias Clasen's avatar
Matthias Clasen committed
419
 * @selection: A `GtkTreeSelection`.
Jonathan Blandford's avatar
Jonathan Blandford committed
420
 *
421
 * Returns the user data for the selection function.
Jonathan Blandford's avatar
Jonathan Blandford committed
422
 *
423
 * Returns: The user data.
424
 **/
425
426
427
428
429
gpointer
gtk_tree_selection_get_user_data (GtkTreeSelection *selection)
{
  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), NULL);

430
  return selection->user_data;
431
432
}

433
434
/**
 * gtk_tree_selection_get_tree_view:
Matthias Clasen's avatar
Matthias Clasen committed
435
 * @selection: A `GtkTreeSelection`
436
437
438
 * 
 * Returns the tree view associated with @selection.
 * 
Matthias Clasen's avatar
Matthias Clasen committed
439
 * Returns: (transfer none): A `GtkTreeView`
440
441
 **/
GtkTreeView *
Havoc Pennington's avatar
Havoc Pennington committed
442
443
444
445
gtk_tree_selection_get_tree_view (GtkTreeSelection *selection)
{
  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), NULL);

446
  return selection->tree_view;
Havoc Pennington's avatar
Havoc Pennington committed
447
448
}

449
450
/**
 * gtk_tree_selection_get_selected:
Matthias Clasen's avatar
Matthias Clasen committed
451
452
453
 * @selection: A `GtkTreeSelection`.
 * @model: (out) (optional) (transfer none): A pointer to set to the `GtkTreeModel`
 * @iter: (out) (optional): The `GtkTreeIter`
Jonathan Blandford's avatar
Jonathan Blandford committed
454
 *
455
 * Sets @iter to the currently selected node if @selection is set to
Matthias Clasen's avatar
Matthias Clasen committed
456
 * %GTK_SELECTION_SINGLE or %GTK_SELECTION_BROWSE.  @iter may be NULL if you
457
458
 * 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
Matthias Clasen's avatar
Matthias Clasen committed
459
 * use @selection is %GTK_SELECTION_MULTIPLE.
Jonathan Blandford's avatar
Jonathan Blandford committed
460
 *
461
 * Returns: TRUE, if there is a selected node.
462
 **/
463
gboolean
464
465
466
gtk_tree_selection_get_selected (GtkTreeSelection  *selection,
				 GtkTreeModel     **model,
				 GtkTreeIter       *iter)
467
{
468
469
  GtkTreeRBTree *tree;
  GtkTreeRBNode *node;
470
  GtkTreePath *anchor_path;
471
  gboolean retval = FALSE;
Kristian Rietveld's avatar
Kristian Rietveld committed
472
  gboolean found_node;
Jonathan Blandford's avatar
Jonathan Blandford committed
473

474
  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), FALSE);
475
476
  g_return_val_if_fail (selection->type != GTK_SELECTION_MULTIPLE, FALSE);
  g_return_val_if_fail (selection->tree_view != NULL, FALSE);
477

478
479
480
481
  /* Clear the iter */
  if (iter)
    memset (iter, 0, sizeof (GtkTreeIter));

482
  if (model)
483
    *model = gtk_tree_view_get_model (selection->tree_view);
484

485
  anchor_path = _gtk_tree_view_get_anchor_path (selection->tree_view);
486
487
488

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

490
  found_node = !_gtk_tree_view_find_node (selection->tree_view,
Kristian Rietveld's avatar
Kristian Rietveld committed
491
492
493
494
                                          anchor_path,
                                          &tree,
                                          &node);

495
  if (found_node && GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED))
496
    {
Kristian Rietveld's avatar
Kristian Rietveld committed
497
498
      /* we only want to return the anchor if it exists in the rbtree and
       * is selected.
499
       */
Kristian Rietveld's avatar
Kristian Rietveld committed
500
501
502
      if (iter == NULL)
	retval = TRUE;
      else
503
        retval = gtk_tree_model_get_iter (gtk_tree_view_get_model (selection->tree_view),
Kristian Rietveld's avatar
Kristian Rietveld committed
504
505
                                          iter,
                                          anchor_path);
506
507
508
    }
  else
    {
Kristian Rietveld's avatar
Kristian Rietveld committed
509
510
511
      /* We don't want to return the anchor if it isn't actually selected.
       */
      retval = FALSE;
512
    }
513

514
  gtk_tree_path_free (anchor_path);
Jonathan Blandford's avatar
Jonathan Blandford committed
515

516
  return retval;
517
518
}

519
520
/**
 * gtk_tree_selection_get_selected_rows:
Matthias Clasen's avatar
Matthias Clasen committed
521
522
 * @selection: A `GtkTreeSelection`.
 * @model: (out) (optional) (transfer none): A pointer to set to the `GtkTreeModel`
523
524
525
 *
 * 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
526
 * want to convert the returned list into a list of `GtkTreeRowReference`s.
527
 * To do this, you can use gtk_tree_row_reference_new().
528
529
 *
 * To free the return value, use:
530
 * |[<!-- language="C" -->
531
 * g_list_free_full (list, (GDestroyNotify) gtk_tree_path_free);
Matthias Clasen's avatar
Matthias Clasen committed
532
 * ]|
533
 *
Matthias Clasen's avatar
Matthias Clasen committed
534
 * Returns: (element-type GtkTreePath) (transfer full): A `GList` containing a `GtkTreePath` for each selected row.
535
536
537
538
539
540
 **/
GList *
gtk_tree_selection_get_selected_rows (GtkTreeSelection   *selection,
                                      GtkTreeModel      **model)
{
  GList *list = NULL;
541
542
  GtkTreeRBTree *tree = NULL;
  GtkTreeRBNode *node = NULL;
543
544
545
  GtkTreePath *path;

  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), NULL);
546
  g_return_val_if_fail (selection->tree_view != NULL, NULL);
547

548
  if (model)
549
    *model = gtk_tree_view_get_model (selection->tree_view);
550

551
  tree = _gtk_tree_view_get_rbtree (selection->tree_view);
552
553

  if (tree == NULL || tree->root == NULL)
554
555
    return NULL;

556
  if (selection->type == GTK_SELECTION_NONE)
557
    return NULL;
558
  else if (selection->type != GTK_SELECTION_MULTIPLE)
559
560
561
562
563
    {
      GtkTreeIter iter;

      if (gtk_tree_selection_get_selected (selection, NULL, &iter))
        {
564
	  path = gtk_tree_model_get_path (gtk_tree_view_get_model (selection->tree_view), &iter);
565
566
567
568
569
570
571
572
	  list = g_list_append (list, path);

	  return list;
	}

      return NULL;
    }

573
  node = gtk_tree_rbtree_first (tree);
574
575
  path = gtk_tree_path_new_first ();

576
  while (node != NULL)
577
    {
578
      if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED))
579
	list = g_list_prepend (list, gtk_tree_path_copy (path));
580
581
582
583

      if (node->children)
        {
	  tree = node->children;
584
          node = gtk_tree_rbtree_first (tree);
585
586
587
588
589
590
591
592
593

	  gtk_tree_path_append_index (path, 0);
	}
      else
        {
	  gboolean done = FALSE;

	  do
	    {
594
	      node = gtk_tree_rbtree_next (tree, node);
595
596
597
598
599
600
601
602
603
604
605
606
607
	      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);
608
609

		      goto done; 
610
611
612
613
614
615
616
617
618
619
620
		    }

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

  gtk_tree_path_free (path);

621
622
 done:
  return g_list_reverse (list);
623
624
625
}

static void
626
627
628
gtk_tree_selection_count_selected_rows_helper (GtkTreeRBTree *tree,
                                               GtkTreeRBNode *node,
                                               gpointer       data)
629
{
Benjamin Otte's avatar
Benjamin Otte committed
630
  int *count = (int *)data;
631

632
633
  g_return_if_fail (node != NULL);

634
  if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED))
635
636
637
    (*count)++;

  if (node->children)
638
639
640
    gtk_tree_rbtree_traverse (node->children, node->children->root,
                              G_PRE_ORDER,
                              gtk_tree_selection_count_selected_rows_helper, data);
641
642
643
644
}

/**
 * gtk_tree_selection_count_selected_rows:
Matthias Clasen's avatar
Matthias Clasen committed
645
 * @selection: A `GtkTreeSelection`.
646
647
648
 *
 * Returns the number of rows that have been selected in @tree.
 *
649
 * Returns: The number of rows selected.
650
 **/
Benjamin Otte's avatar
Benjamin Otte committed
651
int
652
653
gtk_tree_selection_count_selected_rows (GtkTreeSelection *selection)
{
Benjamin Otte's avatar
Benjamin Otte committed
654
  int count = 0;
655
  GtkTreeRBTree *tree;
656
657

  g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), 0);
658
  g_return_val_if_fail (selection->tree_view != NULL, 0);
659

660
  tree = _gtk_tree_view_get_rbtree (selection->tree_view);
661
662

  if (tree == NULL || tree->root == NULL)
663
664
    return 0;

665
666
  if (selection->type == GTK_SELECTION_SINGLE ||
      selection->type == GTK_SELECTION_BROWSE)
667
668
    {
      if (gtk_tree_selection_get_selected (selection, NULL, NULL))
669
        return 1;
670
      else
671
        return 0;
672
673
    }

674
675
676
677
  gtk_tree_rbtree_traverse (tree, tree->root,
                            G_PRE_ORDER,
                            gtk_tree_selection_count_selected_rows_helper,
                            &count);
678
679
680
681

  return count;
}

682
683
684
685
686
687
688
689
690
/* gtk_tree_selection_selected_foreach helper */
static void
model_changed (gpointer data)
{
  gboolean *stop = (gboolean *)data;

  *stop = TRUE;
}

691
692
/**
 * gtk_tree_selection_selected_foreach:
Matthias Clasen's avatar
Matthias Clasen committed
693
 * @selection: A `GtkTreeSelection`.
694
 * @func: (scope call): The function to call for each selected node.
695
 * @data: user data to pass to the function.
Jonathan Blandford's avatar
Jonathan Blandford committed
696
 *
697
698
699
 * 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.
700
 **/
701
702
703
704
705
706
void
gtk_tree_selection_selected_foreach (GtkTreeSelection            *selection,
				     GtkTreeSelectionForeachFunc  func,
				     gpointer                     data)
{
  GtkTreePath *path;
707
708
  GtkTreeRBTree *tree;
  GtkTreeRBNode *node;
709
  GtkTreeIter iter;
710
  GtkTreeModel *model;
711

712
  gulong inserted_id, deleted_id, reordered_id, changed_id;
713
  gboolean stop = FALSE;
714

715
  g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
716
  g_return_if_fail (selection->tree_view != NULL);
717

718
  tree = _gtk_tree_view_get_rbtree (selection->tree_view);
719
720

  if (func == NULL || tree == NULL || tree->root == NULL)
721
722
    return;

723
  model = gtk_tree_view_get_model (selection->tree_view);
724

725
726
  if (selection->type == GTK_SELECTION_SINGLE ||
      selection->type == GTK_SELECTION_BROWSE)
727
    {
728
      path = _gtk_tree_view_get_anchor_path (selection->tree_view);
729
730

      if (path)
731
	{
732
733
	  gtk_tree_model_get_iter (model, &iter, path);
	  (* func) (model, path, &iter, data);
734
735
	  gtk_tree_path_free (path);
	}
736
737
738
      return;
    }

739
  node = gtk_tree_rbtree_first (tree);
740

741
742
  g_object_ref (model);

743
  /* connect to signals to monitor changes in treemodel */
744
  inserted_id = g_signal_connect_swapped (model, "row-inserted",
745
746
					  G_CALLBACK (model_changed),
				          &stop);
747
  deleted_id = g_signal_connect_swapped (model, "row-deleted",
748
749
					 G_CALLBACK (model_changed),
				         &stop);
750
  reordered_id = g_signal_connect_swapped (model, "rows-reordered",
751
752
					   G_CALLBACK (model_changed),
				           &stop);
753
  changed_id = g_signal_connect_swapped (selection->tree_view, "notify::model",
754
755
					 G_CALLBACK (model_changed), 
					 &stop);
756

757
  /* find the node internally */
758
  path = gtk_tree_path_new_first ();
759

760
  while (node != NULL)
761
    {
762
      if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED))
763
        {
764
765
          gtk_tree_model_get_iter (model, &iter, path);
	  (* func) (model, path, &iter, data);
766
        }
767
768
769
770

      if (stop)
	goto out;

771
772
773
      if (node->children)
	{
	  tree = node->children;
774
          node = gtk_tree_rbtree_first (tree);
775

776
	  gtk_tree_path_append_index (path, 0);
777
778
779
780
	}
      else
	{
	  gboolean done = FALSE;
781

782
783
	  do
	    {
784
	      node = gtk_tree_rbtree_next (tree, node);
785
786
787
	      if (node != NULL)
		{
		  done = TRUE;
788
		  gtk_tree_path_next (path);
789
790
791
792
793
		}
	      else
		{
		  node = tree->parent_node;
		  tree = tree->parent_tree;
794

795
		  if (tree == NULL)
796
797
798
		    {
		      /* we've run out of tree */
		      /* We're done with this function */
799
800

		      goto out;
801
		    }
802

803
		  gtk_tree_path_up (path);
804
805
806
807
808
		}
	    }
	  while (!done);
	}
    }
809
810
811
812
813

out:
  if (path)
    gtk_tree_path_free (path);

814
815
816
  g_signal_handler_disconnect (model, inserted_id);
  g_signal_handler_disconnect (model, deleted_id);
  g_signal_handler_disconnect (model, reordered_id);
817
  g_signal_handler_disconnect (selection->tree_view, changed_id);
818
  g_object_unref (model);
819
820
821

  /* check if we have to spew a scary message */
  if (stop)
822
823
824
    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"
825
	       "gtk_tree_selection_get_selected_rows instead.");
826
827
}

828
829
/**
 * gtk_tree_selection_select_path:
Matthias Clasen's avatar
Matthias Clasen committed
830
831
 * @selection: A `GtkTreeSelection`.
 * @path: The `GtkTreePath` to be selected.
Jonathan Blandford's avatar
Jonathan Blandford committed
832
 *
833
834
 * Select the row at @path.
 **/
835
836
void
gtk_tree_selection_select_path (GtkTreeSelection *selection,
837
                                GtkTreePath      *path)
838
{
839
840
  GtkTreeRBNode *node;
  GtkTreeRBTree *tree;
841
  gboolean ret;
Kristian Rietveld's avatar
Kristian Rietveld committed
842
  GtkTreeSelectMode mode = 0;
843
844

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

848
  ret = _gtk_tree_view_find_node (selection->tree_view,
849
850
851
				  path,
				  &tree,
				  &node);
852

853
  if (node == NULL || GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED) ||
854
      ret == TRUE)
855
856
    return;

857
  if (selection->type == GTK_SELECTION_MULTIPLE)
Kristian Rietveld's avatar
Kristian Rietveld committed
858
    mode = GTK_TREE_SELECT_MODE_TOGGLE;
859
860
861
862
863

  _gtk_tree_selection_internal_select_node (selection,
					    node,
					    tree,
					    path,
Kristian Rietveld's avatar
Kristian Rietveld committed
864
                                            mode,
Kristian Rietveld's avatar
Kristian Rietveld committed
865
					    FALSE);
866
867
}

868
869
/**
 * gtk_tree_selection_unselect_path:
Matthias Clasen's avatar
Matthias Clasen committed
870
871
 * @selection: A `GtkTreeSelection`.
 * @path: The `GtkTreePath` to be unselected.
Jonathan Blandford's avatar
Jonathan Blandford committed
872
 *
873
874
 * Unselects the row at @path.
 **/
875
876
877
878
void
gtk_tree_selection_unselect_path (GtkTreeSelection *selection,
				  GtkTreePath      *path)
{
879
880
  GtkTreeRBNode *node;
  GtkTreeRBTree *tree;
881
  gboolean ret;
882
883

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

887
  ret = _gtk_tree_view_find_node (selection->tree_view,
888
889
890
				  path,
				  &tree,
				  &node);
891

892
  if (node == NULL || !GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED) ||
893
      ret == TRUE)
894
895
896
897
898
899
    return;

  _gtk_tree_selection_internal_select_node (selection,
					    node,
					    tree,
					    path,
Kristian Rietveld's avatar
Kristian Rietveld committed
900
                                            GTK_TREE_SELECT_MODE_TOGGLE,
Kristian Rietveld's avatar
Kristian Rietveld committed
901
					    TRUE);
902
903
}

904
/**
905
 * gtk_tree_selection_select_iter:
Matthias Clasen's avatar
Matthias Clasen committed
906
907
 * @selection: A `GtkTreeSelection`.
 * @iter: The `GtkTreeIter` to be selected.
Jonathan Blandford's avatar
Jonathan Blandford committed
908
 *
909
 * Selects the specified iterator.
910
 **/
911
void
912
913
gtk_tree_selection_select_iter (GtkTreeSelection *selection,
				GtkTreeIter      *iter)
914
915
{
  GtkTreePath *path;
916
  GtkTreeModel *model;
917
918

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

921
  model = gtk_tree_view_get_model (selection->tree_view);
922
  g_return_if_fail (model != NULL);
923
  g_return_if_fail (iter != NULL);
924

925
  path = gtk_tree_model_get_path (model, iter);
926
927
928
929
930
931
932
933
934

  if (path == NULL)
    return;

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


935
/**
936
 * gtk_tree_selection_unselect_iter:
Matthias Clasen's avatar
Matthias Clasen committed
937
938
 * @selection: A `GtkTreeSelection`.
 * @iter: The `GtkTreeIter` to be unselected.
Jonathan Blandford's avatar
Jonathan Blandford committed
939
 *
940
 * Unselects the specified iterator.
941
 **/
942
void