1 | /* gtktreeselection.h |
2 | * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com> |
3 | * |
4 | * This library is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Library General Public |
6 | * License as published by the Free Software Foundation; either |
7 | * version 2 of the License, or (at your option) any later version. |
8 | * |
9 | * This library is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | * Library General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU Library General Public |
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 | #include "gtktreeselection.h" |
21 | #include "gtktreeprivate.h" |
22 | #include "gtktreerbtreeprivate.h" |
23 | #include "gtkmarshalers.h" |
24 | #include "gtkintl.h" |
25 | #include "gtkprivate.h" |
26 | #include "gtktypebuiltins.h" |
27 | |
28 | |
29 | /** |
30 | * GtkTreeSelection: |
31 | * |
32 | * The selection object for GtkTreeView |
33 | * |
34 | * The `GtkTreeSelection` object is a helper object to manage the selection |
35 | * for a `GtkTreeView` widget. The `GtkTreeSelection` object is |
36 | * automatically created when a new `GtkTreeView` widget is created, and |
37 | * cannot exist independently of this widget. The primary reason the |
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 |
40 | * methods on the `GtkTreeView` widget instead of a separate function. |
41 | * |
42 | * The `GtkTreeSelection` object is gotten from a `GtkTreeView` by calling |
43 | * gtk_tree_view_get_selection(). It can be manipulated to check the |
44 | * selection status of the tree, as well as select and deselect individual |
45 | * rows. Selection is done completely view side. As a result, multiple |
46 | * views of the same model can have completely different selections. |
47 | * Additionally, you cannot change the selection of a row on the model that |
48 | * is not currently displayed by the view without expanding its parents |
49 | * first. |
50 | * |
51 | * One of the important things to remember when monitoring the selection of |
52 | * a view is that the `GtkTreeSelection`::changed signal is mostly a hint. |
53 | * That is, it may only emit one signal when a range of rows is selected. |
54 | * Additionally, it may on occasion emit a `GtkTreeSelection`::changed signal |
55 | * when nothing has happened (mostly as a result of programmers calling |
56 | * select_row on an already selected row). |
57 | */ |
58 | |
59 | typedef struct _GtkTreeSelectionClass GtkTreeSelectionClass; |
60 | |
61 | struct _GtkTreeSelection |
62 | { |
63 | GObject parent; |
64 | |
65 | GtkTreeView *tree_view; |
66 | GtkSelectionMode type; |
67 | GtkTreeSelectionFunc user_func; |
68 | gpointer user_data; |
69 | GDestroyNotify destroy; |
70 | }; |
71 | |
72 | struct _GtkTreeSelectionClass |
73 | { |
74 | GObjectClass parent_class; |
75 | |
76 | void (* changed) (GtkTreeSelection *selection); |
77 | }; |
78 | |
79 | static void gtk_tree_selection_finalize (GObject *object); |
80 | static int gtk_tree_selection_real_select_all (GtkTreeSelection *selection); |
81 | static int gtk_tree_selection_real_unselect_all (GtkTreeSelection *selection); |
82 | static int gtk_tree_selection_real_select_node (GtkTreeSelection *selection, |
83 | GtkTreeRBTree *tree, |
84 | GtkTreeRBNode *node, |
85 | gboolean select); |
86 | static void gtk_tree_selection_set_property (GObject *object, |
87 | guint prop_id, |
88 | const GValue *value, |
89 | GParamSpec *pspec); |
90 | static void gtk_tree_selection_get_property (GObject *object, |
91 | guint prop_id, |
92 | GValue *value, |
93 | GParamSpec *pspec); |
94 | |
95 | enum |
96 | { |
97 | PROP_0, |
98 | PROP_MODE, |
99 | N_PROPERTIES |
100 | }; |
101 | |
102 | enum |
103 | { |
104 | CHANGED, |
105 | LAST_SIGNAL |
106 | }; |
107 | |
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 | static void |
114 | gtk_tree_selection_class_init (GtkTreeSelectionClass *class) |
115 | { |
116 | GObjectClass *object_class; |
117 | |
118 | object_class = (GObjectClass*) class; |
119 | |
120 | object_class->finalize = gtk_tree_selection_finalize; |
121 | object_class->set_property = gtk_tree_selection_set_property; |
122 | object_class->get_property = gtk_tree_selection_get_property; |
123 | class->changed = NULL; |
124 | |
125 | /* Properties */ |
126 | |
127 | /** |
128 | * GtkTreeSelection:mode: |
129 | * |
130 | * Selection mode. |
131 | * See gtk_tree_selection_set_mode() for more information on this property. |
132 | */ |
133 | properties[PROP_MODE] = g_param_spec_enum (name: "mode" , |
134 | P_("Mode" ), |
135 | P_("Selection mode" ), |
136 | enum_type: GTK_TYPE_SELECTION_MODE, |
137 | default_value: GTK_SELECTION_SINGLE, |
138 | GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); |
139 | |
140 | /* Install all properties */ |
141 | g_object_class_install_properties (oclass: object_class, n_pspecs: N_PROPERTIES, pspecs: properties); |
142 | |
143 | /* Signals */ |
144 | |
145 | /** |
146 | * GtkTreeSelection::changed: |
147 | * @treeselection: the object which received the signal. |
148 | * |
149 | * Emitted whenever the selection has (possibly) changed. Please note that |
150 | * this signal is mostly a hint. It may only be emitted once when a range |
151 | * of rows are selected, and it may occasionally be emitted when nothing |
152 | * has happened. |
153 | */ |
154 | tree_selection_signals[CHANGED] = |
155 | g_signal_new (I_("changed" ), |
156 | G_OBJECT_CLASS_TYPE (object_class), |
157 | signal_flags: G_SIGNAL_RUN_FIRST, |
158 | G_STRUCT_OFFSET (GtkTreeSelectionClass, changed), |
159 | NULL, NULL, |
160 | NULL, |
161 | G_TYPE_NONE, n_params: 0); |
162 | } |
163 | |
164 | static void |
165 | gtk_tree_selection_init (GtkTreeSelection *selection) |
166 | { |
167 | selection->type = GTK_SELECTION_SINGLE; |
168 | } |
169 | |
170 | static void |
171 | gtk_tree_selection_finalize (GObject *object) |
172 | { |
173 | GtkTreeSelection *selection = GTK_TREE_SELECTION (object); |
174 | |
175 | if (selection->destroy) |
176 | selection->destroy (selection->user_data); |
177 | |
178 | /* chain parent_class' handler */ |
179 | G_OBJECT_CLASS (gtk_tree_selection_parent_class)->finalize (object); |
180 | } |
181 | |
182 | static void |
183 | gtk_tree_selection_set_property (GObject *object, |
184 | guint prop_id, |
185 | const GValue *value, |
186 | GParamSpec *pspec) |
187 | { |
188 | g_return_if_fail (GTK_IS_TREE_SELECTION (object)); |
189 | |
190 | switch (prop_id) |
191 | { |
192 | case PROP_MODE: |
193 | gtk_tree_selection_set_mode (GTK_TREE_SELECTION (object), type: g_value_get_enum (value)); |
194 | break; |
195 | default: |
196 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
197 | break; |
198 | } |
199 | } |
200 | |
201 | static void |
202 | gtk_tree_selection_get_property (GObject *object, |
203 | guint prop_id, |
204 | GValue *value, |
205 | GParamSpec *pspec) |
206 | { |
207 | g_return_if_fail (GTK_IS_TREE_SELECTION (object)); |
208 | |
209 | switch (prop_id) |
210 | { |
211 | case PROP_MODE: |
212 | g_value_set_enum (value, v_enum: gtk_tree_selection_get_mode (GTK_TREE_SELECTION (object))); |
213 | break; |
214 | default: |
215 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
216 | break; |
217 | } |
218 | } |
219 | |
220 | /** |
221 | * _gtk_tree_selection_new: |
222 | * |
223 | * Creates a new `GtkTreeSelection` object. This function should not be invoked, |
224 | * as each `GtkTreeView` will create its own `GtkTreeSelection`. |
225 | * |
226 | * Returns: A newly created `GtkTreeSelection` object. |
227 | **/ |
228 | GtkTreeSelection* |
229 | _gtk_tree_selection_new (void) |
230 | { |
231 | GtkTreeSelection *selection; |
232 | |
233 | selection = g_object_new (GTK_TYPE_TREE_SELECTION, NULL); |
234 | |
235 | return selection; |
236 | } |
237 | |
238 | /** |
239 | * _gtk_tree_selection_new_with_tree_view: |
240 | * @tree_view: The `GtkTreeView`. |
241 | * |
242 | * Creates a new `GtkTreeSelection` object. This function should not be invoked, |
243 | * as each `GtkTreeView` will create its own `GtkTreeSelection`. |
244 | * |
245 | * Returns: A newly created `GtkTreeSelection` object. |
246 | **/ |
247 | GtkTreeSelection* |
248 | _gtk_tree_selection_new_with_tree_view (GtkTreeView *tree_view) |
249 | { |
250 | GtkTreeSelection *selection; |
251 | |
252 | g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL); |
253 | |
254 | selection = _gtk_tree_selection_new (); |
255 | _gtk_tree_selection_set_tree_view (selection, tree_view); |
256 | |
257 | return selection; |
258 | } |
259 | |
260 | /** |
261 | * _gtk_tree_selection_set_tree_view: |
262 | * @selection: A `GtkTreeSelection`. |
263 | * @tree_view: The `GtkTreeView`. |
264 | * |
265 | * Sets the `GtkTreeView` of @selection. This function should not be invoked, as |
266 | * it is used internally by `GtkTreeView`. |
267 | **/ |
268 | void |
269 | _gtk_tree_selection_set_tree_view (GtkTreeSelection *selection, |
270 | GtkTreeView *tree_view) |
271 | { |
272 | |
273 | g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); |
274 | if (tree_view != NULL) |
275 | g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); |
276 | |
277 | selection->tree_view = tree_view; |
278 | } |
279 | |
280 | /** |
281 | * gtk_tree_selection_set_mode: |
282 | * @selection: A `GtkTreeSelection`. |
283 | * @type: The selection mode |
284 | * |
285 | * Sets the selection mode of the @selection. If the previous type was |
286 | * %GTK_SELECTION_MULTIPLE, then the anchor is kept selected, if it was |
287 | * previously selected. |
288 | **/ |
289 | void |
290 | gtk_tree_selection_set_mode (GtkTreeSelection *selection, |
291 | GtkSelectionMode type) |
292 | { |
293 | GtkTreeSelectionFunc tmp_func; |
294 | |
295 | g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); |
296 | |
297 | if (selection->type == type) |
298 | return; |
299 | |
300 | if (type == GTK_SELECTION_NONE) |
301 | { |
302 | /* We do this so that we unconditionally unset all rows |
303 | */ |
304 | tmp_func = selection->user_func; |
305 | selection->user_func = NULL; |
306 | gtk_tree_selection_unselect_all (selection); |
307 | selection->user_func = tmp_func; |
308 | |
309 | _gtk_tree_view_set_anchor_path (tree_view: selection->tree_view, NULL); |
310 | } |
311 | else if (type == GTK_SELECTION_SINGLE || |
312 | type == GTK_SELECTION_BROWSE) |
313 | { |
314 | GtkTreeRBTree *tree = NULL; |
315 | GtkTreeRBNode *node = NULL; |
316 | int selected = FALSE; |
317 | GtkTreePath *anchor_path = NULL; |
318 | |
319 | anchor_path = _gtk_tree_view_get_anchor_path (tree_view: selection->tree_view); |
320 | |
321 | if (anchor_path) |
322 | { |
323 | _gtk_tree_view_find_node (tree_view: selection->tree_view, |
324 | path: anchor_path, |
325 | tree: &tree, |
326 | node: &node); |
327 | |
328 | if (node && GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED)) |
329 | selected = TRUE; |
330 | } |
331 | |
332 | /* We do this so that we unconditionally unset all rows |
333 | */ |
334 | tmp_func = selection->user_func; |
335 | selection->user_func = NULL; |
336 | gtk_tree_selection_unselect_all (selection); |
337 | selection->user_func = tmp_func; |
338 | |
339 | if (node && selected) |
340 | _gtk_tree_selection_internal_select_node (selection, |
341 | node, |
342 | tree, |
343 | path: anchor_path, |
344 | mode: 0, |
345 | FALSE); |
346 | if (anchor_path) |
347 | gtk_tree_path_free (path: anchor_path); |
348 | } |
349 | |
350 | selection->type = type; |
351 | |
352 | g_object_notify_by_pspec (G_OBJECT (selection), pspec: properties[PROP_MODE]); |
353 | } |
354 | |
355 | /** |
356 | * gtk_tree_selection_get_mode: |
357 | * @selection: a `GtkTreeSelection` |
358 | * |
359 | * Gets the selection mode for @selection. See |
360 | * gtk_tree_selection_set_mode(). |
361 | * |
362 | * Returns: the current selection mode |
363 | **/ |
364 | GtkSelectionMode |
365 | gtk_tree_selection_get_mode (GtkTreeSelection *selection) |
366 | { |
367 | g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), GTK_SELECTION_SINGLE); |
368 | |
369 | return selection->type; |
370 | } |
371 | |
372 | /** |
373 | * gtk_tree_selection_set_select_function: |
374 | * @selection: A `GtkTreeSelection`. |
375 | * @func: (nullable): The selection function. May be %NULL |
376 | * @data: The selection function’s data. May be %NULL |
377 | * @destroy: The destroy function for user data. May be %NULL |
378 | * |
379 | * Sets the selection function. |
380 | * |
381 | * If set, this function is called before any node is selected or unselected, |
382 | * giving some control over which nodes are selected. The select function |
383 | * should return %TRUE if the state of the node may be toggled, and %FALSE |
384 | * if the state of the node should be left unchanged. |
385 | */ |
386 | void |
387 | gtk_tree_selection_set_select_function (GtkTreeSelection *selection, |
388 | GtkTreeSelectionFunc func, |
389 | gpointer data, |
390 | GDestroyNotify destroy) |
391 | { |
392 | |
393 | g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); |
394 | |
395 | if (selection->destroy) |
396 | selection->destroy (selection->user_data); |
397 | |
398 | selection->user_func = func; |
399 | selection->user_data = data; |
400 | selection->destroy = destroy; |
401 | } |
402 | |
403 | /** |
404 | * gtk_tree_selection_get_select_function: (skip) |
405 | * @selection: A `GtkTreeSelection`. |
406 | * |
407 | * Returns the current selection function. |
408 | * |
409 | * Returns: The function. |
410 | **/ |
411 | GtkTreeSelectionFunc |
412 | gtk_tree_selection_get_select_function (GtkTreeSelection *selection) |
413 | { |
414 | g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), NULL); |
415 | |
416 | return selection->user_func; |
417 | } |
418 | |
419 | /** |
420 | * gtk_tree_selection_get_user_data: (skip) |
421 | * @selection: A `GtkTreeSelection`. |
422 | * |
423 | * Returns the user data for the selection function. |
424 | * |
425 | * Returns: The user data. |
426 | **/ |
427 | gpointer |
428 | gtk_tree_selection_get_user_data (GtkTreeSelection *selection) |
429 | { |
430 | g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), NULL); |
431 | |
432 | return selection->user_data; |
433 | } |
434 | |
435 | /** |
436 | * gtk_tree_selection_get_tree_view: |
437 | * @selection: A `GtkTreeSelection` |
438 | * |
439 | * Returns the tree view associated with @selection. |
440 | * |
441 | * Returns: (transfer none): A `GtkTreeView` |
442 | **/ |
443 | GtkTreeView * |
444 | gtk_tree_selection_get_tree_view (GtkTreeSelection *selection) |
445 | { |
446 | g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), NULL); |
447 | |
448 | return selection->tree_view; |
449 | } |
450 | |
451 | /** |
452 | * gtk_tree_selection_get_selected: |
453 | * @selection: A `GtkTreeSelection`. |
454 | * @model: (out) (optional) (transfer none): A pointer to set to the `GtkTreeModel` |
455 | * @iter: (out) (optional): The `GtkTreeIter` |
456 | * |
457 | * Sets @iter to the currently selected node if @selection is set to |
458 | * %GTK_SELECTION_SINGLE or %GTK_SELECTION_BROWSE. @iter may be NULL if you |
459 | * just want to test if @selection has any selected nodes. @model is filled |
460 | * with the current model as a convenience. This function will not work if you |
461 | * use @selection is %GTK_SELECTION_MULTIPLE. |
462 | * |
463 | * Returns: TRUE, if there is a selected node. |
464 | **/ |
465 | gboolean |
466 | gtk_tree_selection_get_selected (GtkTreeSelection *selection, |
467 | GtkTreeModel **model, |
468 | GtkTreeIter *iter) |
469 | { |
470 | GtkTreeRBTree *tree; |
471 | GtkTreeRBNode *node; |
472 | GtkTreePath *anchor_path; |
473 | gboolean retval = FALSE; |
474 | gboolean found_node; |
475 | |
476 | g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), FALSE); |
477 | g_return_val_if_fail (selection->type != GTK_SELECTION_MULTIPLE, FALSE); |
478 | g_return_val_if_fail (selection->tree_view != NULL, FALSE); |
479 | |
480 | /* Clear the iter */ |
481 | if (iter) |
482 | memset (s: iter, c: 0, n: sizeof (GtkTreeIter)); |
483 | |
484 | if (model) |
485 | *model = gtk_tree_view_get_model (tree_view: selection->tree_view); |
486 | |
487 | anchor_path = _gtk_tree_view_get_anchor_path (tree_view: selection->tree_view); |
488 | |
489 | if (anchor_path == NULL) |
490 | return FALSE; |
491 | |
492 | found_node = !_gtk_tree_view_find_node (tree_view: selection->tree_view, |
493 | path: anchor_path, |
494 | tree: &tree, |
495 | node: &node); |
496 | |
497 | if (found_node && GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED)) |
498 | { |
499 | /* we only want to return the anchor if it exists in the rbtree and |
500 | * is selected. |
501 | */ |
502 | if (iter == NULL) |
503 | retval = TRUE; |
504 | else |
505 | retval = gtk_tree_model_get_iter (tree_model: gtk_tree_view_get_model (tree_view: selection->tree_view), |
506 | iter, |
507 | path: anchor_path); |
508 | } |
509 | else |
510 | { |
511 | /* We don't want to return the anchor if it isn't actually selected. |
512 | */ |
513 | retval = FALSE; |
514 | } |
515 | |
516 | gtk_tree_path_free (path: anchor_path); |
517 | |
518 | return retval; |
519 | } |
520 | |
521 | /** |
522 | * gtk_tree_selection_get_selected_rows: |
523 | * @selection: A `GtkTreeSelection`. |
524 | * @model: (out) (optional) (transfer none): A pointer to set to the `GtkTreeModel` |
525 | * |
526 | * Creates a list of path of all selected rows. Additionally, if you are |
527 | * planning on modifying the model after calling this function, you may |
528 | * want to convert the returned list into a list of `GtkTreeRowReference`s. |
529 | * To do this, you can use gtk_tree_row_reference_new(). |
530 | * |
531 | * To free the return value, use: |
532 | * |[<!-- language="C" --> |
533 | * g_list_free_full (list, (GDestroyNotify) gtk_tree_path_free); |
534 | * ]| |
535 | * |
536 | * Returns: (element-type GtkTreePath) (transfer full): A `GList` containing a `GtkTreePath` for each selected row. |
537 | **/ |
538 | GList * |
539 | gtk_tree_selection_get_selected_rows (GtkTreeSelection *selection, |
540 | GtkTreeModel **model) |
541 | { |
542 | GList *list = NULL; |
543 | GtkTreeRBTree *tree = NULL; |
544 | GtkTreeRBNode *node = NULL; |
545 | GtkTreePath *path; |
546 | |
547 | g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), NULL); |
548 | g_return_val_if_fail (selection->tree_view != NULL, NULL); |
549 | |
550 | if (model) |
551 | *model = gtk_tree_view_get_model (tree_view: selection->tree_view); |
552 | |
553 | tree = _gtk_tree_view_get_rbtree (tree_view: selection->tree_view); |
554 | |
555 | if (tree == NULL || tree->root == NULL) |
556 | return NULL; |
557 | |
558 | if (selection->type == GTK_SELECTION_NONE) |
559 | return NULL; |
560 | else if (selection->type != GTK_SELECTION_MULTIPLE) |
561 | { |
562 | GtkTreeIter iter; |
563 | |
564 | if (gtk_tree_selection_get_selected (selection, NULL, iter: &iter)) |
565 | { |
566 | path = gtk_tree_model_get_path (tree_model: gtk_tree_view_get_model (tree_view: selection->tree_view), iter: &iter); |
567 | list = g_list_append (list, data: path); |
568 | |
569 | return list; |
570 | } |
571 | |
572 | return NULL; |
573 | } |
574 | |
575 | node = gtk_tree_rbtree_first (tree); |
576 | path = gtk_tree_path_new_first (); |
577 | |
578 | while (node != NULL) |
579 | { |
580 | if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED)) |
581 | list = g_list_prepend (list, data: gtk_tree_path_copy (path)); |
582 | |
583 | if (node->children) |
584 | { |
585 | tree = node->children; |
586 | node = gtk_tree_rbtree_first (tree); |
587 | |
588 | gtk_tree_path_append_index (path, index_: 0); |
589 | } |
590 | else |
591 | { |
592 | gboolean done = FALSE; |
593 | |
594 | do |
595 | { |
596 | node = gtk_tree_rbtree_next (tree, node); |
597 | if (node != NULL) |
598 | { |
599 | done = TRUE; |
600 | gtk_tree_path_next (path); |
601 | } |
602 | else |
603 | { |
604 | node = tree->parent_node; |
605 | tree = tree->parent_tree; |
606 | |
607 | if (!tree) |
608 | { |
609 | gtk_tree_path_free (path); |
610 | |
611 | goto done; |
612 | } |
613 | |
614 | gtk_tree_path_up (path); |
615 | } |
616 | } |
617 | while (!done); |
618 | } |
619 | } |
620 | |
621 | gtk_tree_path_free (path); |
622 | |
623 | done: |
624 | return g_list_reverse (list); |
625 | } |
626 | |
627 | static void |
628 | gtk_tree_selection_count_selected_rows_helper (GtkTreeRBTree *tree, |
629 | GtkTreeRBNode *node, |
630 | gpointer data) |
631 | { |
632 | int *count = (int *)data; |
633 | |
634 | g_return_if_fail (node != NULL); |
635 | |
636 | if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED)) |
637 | (*count)++; |
638 | |
639 | if (node->children) |
640 | gtk_tree_rbtree_traverse (tree: node->children, node: node->children->root, |
641 | order: G_PRE_ORDER, |
642 | func: gtk_tree_selection_count_selected_rows_helper, data); |
643 | } |
644 | |
645 | /** |
646 | * gtk_tree_selection_count_selected_rows: |
647 | * @selection: A `GtkTreeSelection`. |
648 | * |
649 | * Returns the number of rows that have been selected in @tree. |
650 | * |
651 | * Returns: The number of rows selected. |
652 | **/ |
653 | int |
654 | gtk_tree_selection_count_selected_rows (GtkTreeSelection *selection) |
655 | { |
656 | int count = 0; |
657 | GtkTreeRBTree *tree; |
658 | |
659 | g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), 0); |
660 | g_return_val_if_fail (selection->tree_view != NULL, 0); |
661 | |
662 | tree = _gtk_tree_view_get_rbtree (tree_view: selection->tree_view); |
663 | |
664 | if (tree == NULL || tree->root == NULL) |
665 | return 0; |
666 | |
667 | if (selection->type == GTK_SELECTION_SINGLE || |
668 | selection->type == GTK_SELECTION_BROWSE) |
669 | { |
670 | if (gtk_tree_selection_get_selected (selection, NULL, NULL)) |
671 | return 1; |
672 | else |
673 | return 0; |
674 | } |
675 | |
676 | gtk_tree_rbtree_traverse (tree, node: tree->root, |
677 | order: G_PRE_ORDER, |
678 | func: gtk_tree_selection_count_selected_rows_helper, |
679 | data: &count); |
680 | |
681 | return count; |
682 | } |
683 | |
684 | /* gtk_tree_selection_selected_foreach helper */ |
685 | static void |
686 | model_changed (gpointer data) |
687 | { |
688 | gboolean *stop = (gboolean *)data; |
689 | |
690 | *stop = TRUE; |
691 | } |
692 | |
693 | /** |
694 | * gtk_tree_selection_selected_foreach: |
695 | * @selection: A `GtkTreeSelection`. |
696 | * @func: (scope call): The function to call for each selected node. |
697 | * @data: user data to pass to the function. |
698 | * |
699 | * Calls a function for each selected node. Note that you cannot modify |
700 | * the tree or selection from within this function. As a result, |
701 | * gtk_tree_selection_get_selected_rows() might be more useful. |
702 | **/ |
703 | void |
704 | gtk_tree_selection_selected_foreach (GtkTreeSelection *selection, |
705 | GtkTreeSelectionForeachFunc func, |
706 | gpointer data) |
707 | { |
708 | GtkTreePath *path; |
709 | GtkTreeRBTree *tree; |
710 | GtkTreeRBNode *node; |
711 | GtkTreeIter iter; |
712 | GtkTreeModel *model; |
713 | |
714 | gulong inserted_id, deleted_id, reordered_id, changed_id; |
715 | gboolean stop = FALSE; |
716 | |
717 | g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); |
718 | g_return_if_fail (selection->tree_view != NULL); |
719 | |
720 | tree = _gtk_tree_view_get_rbtree (tree_view: selection->tree_view); |
721 | |
722 | if (func == NULL || tree == NULL || tree->root == NULL) |
723 | return; |
724 | |
725 | model = gtk_tree_view_get_model (tree_view: selection->tree_view); |
726 | |
727 | if (selection->type == GTK_SELECTION_SINGLE || |
728 | selection->type == GTK_SELECTION_BROWSE) |
729 | { |
730 | path = _gtk_tree_view_get_anchor_path (tree_view: selection->tree_view); |
731 | |
732 | if (path) |
733 | { |
734 | gtk_tree_model_get_iter (tree_model: model, iter: &iter, path); |
735 | (* func) (model, path, &iter, data); |
736 | gtk_tree_path_free (path); |
737 | } |
738 | return; |
739 | } |
740 | |
741 | node = gtk_tree_rbtree_first (tree); |
742 | |
743 | g_object_ref (model); |
744 | |
745 | /* connect to signals to monitor changes in treemodel */ |
746 | inserted_id = g_signal_connect_swapped (model, "row-inserted" , |
747 | G_CALLBACK (model_changed), |
748 | &stop); |
749 | deleted_id = g_signal_connect_swapped (model, "row-deleted" , |
750 | G_CALLBACK (model_changed), |
751 | &stop); |
752 | reordered_id = g_signal_connect_swapped (model, "rows-reordered" , |
753 | G_CALLBACK (model_changed), |
754 | &stop); |
755 | changed_id = g_signal_connect_swapped (selection->tree_view, "notify::model" , |
756 | G_CALLBACK (model_changed), |
757 | &stop); |
758 | |
759 | /* find the node internally */ |
760 | path = gtk_tree_path_new_first (); |
761 | |
762 | while (node != NULL) |
763 | { |
764 | if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED)) |
765 | { |
766 | gtk_tree_model_get_iter (tree_model: model, iter: &iter, path); |
767 | (* func) (model, path, &iter, data); |
768 | } |
769 | |
770 | if (stop) |
771 | goto out; |
772 | |
773 | if (node->children) |
774 | { |
775 | tree = node->children; |
776 | node = gtk_tree_rbtree_first (tree); |
777 | |
778 | gtk_tree_path_append_index (path, index_: 0); |
779 | } |
780 | else |
781 | { |
782 | gboolean done = FALSE; |
783 | |
784 | do |
785 | { |
786 | node = gtk_tree_rbtree_next (tree, node); |
787 | if (node != NULL) |
788 | { |
789 | done = TRUE; |
790 | gtk_tree_path_next (path); |
791 | } |
792 | else |
793 | { |
794 | node = tree->parent_node; |
795 | tree = tree->parent_tree; |
796 | |
797 | if (tree == NULL) |
798 | { |
799 | /* we've run out of tree */ |
800 | /* We're done with this function */ |
801 | |
802 | goto out; |
803 | } |
804 | |
805 | gtk_tree_path_up (path); |
806 | } |
807 | } |
808 | while (!done); |
809 | } |
810 | } |
811 | |
812 | out: |
813 | if (path) |
814 | gtk_tree_path_free (path); |
815 | |
816 | g_signal_handler_disconnect (instance: model, handler_id: inserted_id); |
817 | g_signal_handler_disconnect (instance: model, handler_id: deleted_id); |
818 | g_signal_handler_disconnect (instance: model, handler_id: reordered_id); |
819 | g_signal_handler_disconnect (instance: selection->tree_view, handler_id: changed_id); |
820 | g_object_unref (object: model); |
821 | |
822 | /* check if we have to spew a scary message */ |
823 | if (stop) |
824 | g_warning ("The model has been modified from within gtk_tree_selection_selected_foreach.\n" |
825 | "This function is for observing the selections of the tree only. If\n" |
826 | "you are trying to get all selected items from the tree, try using\n" |
827 | "gtk_tree_selection_get_selected_rows instead." ); |
828 | } |
829 | |
830 | /** |
831 | * gtk_tree_selection_select_path: |
832 | * @selection: A `GtkTreeSelection`. |
833 | * @path: The `GtkTreePath` to be selected. |
834 | * |
835 | * Select the row at @path. |
836 | **/ |
837 | void |
838 | gtk_tree_selection_select_path (GtkTreeSelection *selection, |
839 | GtkTreePath *path) |
840 | { |
841 | GtkTreeRBNode *node; |
842 | GtkTreeRBTree *tree; |
843 | gboolean ret; |
844 | GtkTreeSelectMode mode = 0; |
845 | |
846 | g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); |
847 | g_return_if_fail (selection->tree_view != NULL); |
848 | g_return_if_fail (path != NULL); |
849 | |
850 | ret = _gtk_tree_view_find_node (tree_view: selection->tree_view, |
851 | path, |
852 | tree: &tree, |
853 | node: &node); |
854 | |
855 | if (node == NULL || GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED) || |
856 | ret == TRUE) |
857 | return; |
858 | |
859 | if (selection->type == GTK_SELECTION_MULTIPLE) |
860 | mode = GTK_TREE_SELECT_MODE_TOGGLE; |
861 | |
862 | _gtk_tree_selection_internal_select_node (selection, |
863 | node, |
864 | tree, |
865 | path, |
866 | mode, |
867 | FALSE); |
868 | } |
869 | |
870 | /** |
871 | * gtk_tree_selection_unselect_path: |
872 | * @selection: A `GtkTreeSelection`. |
873 | * @path: The `GtkTreePath` to be unselected. |
874 | * |
875 | * Unselects the row at @path. |
876 | **/ |
877 | void |
878 | gtk_tree_selection_unselect_path (GtkTreeSelection *selection, |
879 | GtkTreePath *path) |
880 | { |
881 | GtkTreeRBNode *node; |
882 | GtkTreeRBTree *tree; |
883 | gboolean ret; |
884 | |
885 | g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); |
886 | g_return_if_fail (selection->tree_view != NULL); |
887 | g_return_if_fail (path != NULL); |
888 | |
889 | ret = _gtk_tree_view_find_node (tree_view: selection->tree_view, |
890 | path, |
891 | tree: &tree, |
892 | node: &node); |
893 | |
894 | if (node == NULL || !GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED) || |
895 | ret == TRUE) |
896 | return; |
897 | |
898 | _gtk_tree_selection_internal_select_node (selection, |
899 | node, |
900 | tree, |
901 | path, |
902 | mode: GTK_TREE_SELECT_MODE_TOGGLE, |
903 | TRUE); |
904 | } |
905 | |
906 | /** |
907 | * gtk_tree_selection_select_iter: |
908 | * @selection: A `GtkTreeSelection`. |
909 | * @iter: The `GtkTreeIter` to be selected. |
910 | * |
911 | * Selects the specified iterator. |
912 | **/ |
913 | void |
914 | gtk_tree_selection_select_iter (GtkTreeSelection *selection, |
915 | GtkTreeIter *iter) |
916 | { |
917 | GtkTreePath *path; |
918 | GtkTreeModel *model; |
919 | |
920 | g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); |
921 | g_return_if_fail (selection->tree_view != NULL); |
922 | |
923 | model = gtk_tree_view_get_model (tree_view: selection->tree_view); |
924 | g_return_if_fail (model != NULL); |
925 | g_return_if_fail (iter != NULL); |
926 | |
927 | path = gtk_tree_model_get_path (tree_model: model, iter); |
928 | |
929 | if (path == NULL) |
930 | return; |
931 | |
932 | gtk_tree_selection_select_path (selection, path); |
933 | gtk_tree_path_free (path); |
934 | } |
935 | |
936 | |
937 | /** |
938 | * gtk_tree_selection_unselect_iter: |
939 | * @selection: A `GtkTreeSelection`. |
940 | * @iter: The `GtkTreeIter` to be unselected. |
941 | * |
942 | * Unselects the specified iterator. |
943 | **/ |
944 | void |
945 | gtk_tree_selection_unselect_iter (GtkTreeSelection *selection, |
946 | GtkTreeIter *iter) |
947 | { |
948 | GtkTreePath *path; |
949 | GtkTreeModel *model; |
950 | |
951 | g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); |
952 | g_return_if_fail (selection->tree_view != NULL); |
953 | |
954 | model = gtk_tree_view_get_model (tree_view: selection->tree_view); |
955 | g_return_if_fail (model != NULL); |
956 | g_return_if_fail (iter != NULL); |
957 | |
958 | path = gtk_tree_model_get_path (tree_model: model, iter); |
959 | |
960 | if (path == NULL) |
961 | return; |
962 | |
963 | gtk_tree_selection_unselect_path (selection, path); |
964 | gtk_tree_path_free (path); |
965 | } |
966 | |
967 | /** |
968 | * gtk_tree_selection_path_is_selected: |
969 | * @selection: A `GtkTreeSelection`. |
970 | * @path: A `GtkTreePath` to check selection on. |
971 | * |
972 | * Returns %TRUE if the row pointed to by @path is currently selected. If @path |
973 | * does not point to a valid location, %FALSE is returned |
974 | * |
975 | * Returns: %TRUE if @path is selected. |
976 | **/ |
977 | gboolean |
978 | gtk_tree_selection_path_is_selected (GtkTreeSelection *selection, |
979 | GtkTreePath *path) |
980 | { |
981 | GtkTreeRBNode *node; |
982 | GtkTreeRBTree *tree; |
983 | gboolean ret; |
984 | |
985 | g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), FALSE); |
986 | g_return_val_if_fail (path != NULL, FALSE); |
987 | g_return_val_if_fail (selection->tree_view != NULL, FALSE); |
988 | |
989 | if (gtk_tree_view_get_model (tree_view: selection->tree_view) == NULL) |
990 | return FALSE; |
991 | |
992 | ret = _gtk_tree_view_find_node (tree_view: selection->tree_view, |
993 | path, |
994 | tree: &tree, |
995 | node: &node); |
996 | |
997 | if ((node == NULL) || !GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED) || |
998 | ret == TRUE) |
999 | return FALSE; |
1000 | |
1001 | return TRUE; |
1002 | } |
1003 | |
1004 | /** |
1005 | * gtk_tree_selection_iter_is_selected: |
1006 | * @selection: A `GtkTreeSelection` |
1007 | * @iter: A valid `GtkTreeIter` |
1008 | * |
1009 | * Returns %TRUE if the row at @iter is currently selected. |
1010 | * |
1011 | * Returns: %TRUE, if @iter is selected |
1012 | **/ |
1013 | gboolean |
1014 | gtk_tree_selection_iter_is_selected (GtkTreeSelection *selection, |
1015 | GtkTreeIter *iter) |
1016 | { |
1017 | GtkTreePath *path; |
1018 | GtkTreeModel *model; |
1019 | gboolean retval; |
1020 | |
1021 | g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), FALSE); |
1022 | g_return_val_if_fail (iter != NULL, FALSE); |
1023 | g_return_val_if_fail (selection->tree_view != NULL, FALSE); |
1024 | |
1025 | model = gtk_tree_view_get_model (tree_view: selection->tree_view); |
1026 | g_return_val_if_fail (model != NULL, FALSE); |
1027 | |
1028 | path = gtk_tree_model_get_path (tree_model: model, iter); |
1029 | if (path == NULL) |
1030 | return FALSE; |
1031 | |
1032 | retval = gtk_tree_selection_path_is_selected (selection, path); |
1033 | gtk_tree_path_free (path); |
1034 | |
1035 | return retval; |
1036 | } |
1037 | |
1038 | |
1039 | /* Wish I was in python, right now... */ |
1040 | struct _TempTuple { |
1041 | GtkTreeSelection *selection; |
1042 | int dirty; |
1043 | }; |
1044 | |
1045 | static void |
1046 | select_all_helper (GtkTreeRBTree *tree, |
1047 | GtkTreeRBNode *node, |
1048 | gpointer data) |
1049 | { |
1050 | struct _TempTuple *tuple = data; |
1051 | |
1052 | if (node->children) |
1053 | gtk_tree_rbtree_traverse (tree: node->children, |
1054 | node: node->children->root, |
1055 | order: G_PRE_ORDER, |
1056 | func: select_all_helper, |
1057 | data); |
1058 | if (!GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED)) |
1059 | { |
1060 | tuple->dirty = gtk_tree_selection_real_select_node (selection: tuple->selection, tree, node, TRUE) || tuple->dirty; |
1061 | } |
1062 | } |
1063 | |
1064 | |
1065 | /* We have a real_{un,}select_all function that doesn't emit the signal, so we |
1066 | * can use it in other places without fear of the signal being emitted. |
1067 | */ |
1068 | static int |
1069 | gtk_tree_selection_real_select_all (GtkTreeSelection *selection) |
1070 | { |
1071 | struct _TempTuple *tuple; |
1072 | GtkTreeRBTree *tree; |
1073 | |
1074 | tree = _gtk_tree_view_get_rbtree (tree_view: selection->tree_view); |
1075 | |
1076 | if (tree == NULL) |
1077 | return FALSE; |
1078 | |
1079 | /* Mark all nodes selected */ |
1080 | tuple = g_new (struct _TempTuple, 1); |
1081 | tuple->selection = selection; |
1082 | tuple->dirty = FALSE; |
1083 | |
1084 | gtk_tree_rbtree_traverse (tree, node: tree->root, |
1085 | order: G_PRE_ORDER, |
1086 | func: select_all_helper, |
1087 | data: tuple); |
1088 | if (tuple->dirty) |
1089 | { |
1090 | g_free (mem: tuple); |
1091 | return TRUE; |
1092 | } |
1093 | g_free (mem: tuple); |
1094 | return FALSE; |
1095 | } |
1096 | |
1097 | /** |
1098 | * gtk_tree_selection_select_all: |
1099 | * @selection: A `GtkTreeSelection`. |
1100 | * |
1101 | * Selects all the nodes. @selection must be set to %GTK_SELECTION_MULTIPLE |
1102 | * mode. |
1103 | **/ |
1104 | void |
1105 | gtk_tree_selection_select_all (GtkTreeSelection *selection) |
1106 | { |
1107 | |
1108 | g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); |
1109 | g_return_if_fail (selection->tree_view != NULL); |
1110 | |
1111 | if (_gtk_tree_view_get_rbtree (tree_view: selection->tree_view) == NULL || |
1112 | gtk_tree_view_get_model (tree_view: selection->tree_view) == NULL) |
1113 | return; |
1114 | |
1115 | g_return_if_fail (selection->type == GTK_SELECTION_MULTIPLE); |
1116 | |
1117 | if (gtk_tree_selection_real_select_all (selection)) |
1118 | g_signal_emit (instance: selection, signal_id: tree_selection_signals[CHANGED], detail: 0); |
1119 | } |
1120 | |
1121 | static void |
1122 | unselect_all_helper (GtkTreeRBTree *tree, |
1123 | GtkTreeRBNode *node, |
1124 | gpointer data) |
1125 | { |
1126 | struct _TempTuple *tuple = data; |
1127 | |
1128 | if (node->children) |
1129 | gtk_tree_rbtree_traverse (tree: node->children, |
1130 | node: node->children->root, |
1131 | order: G_PRE_ORDER, |
1132 | func: unselect_all_helper, |
1133 | data); |
1134 | if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED)) |
1135 | { |
1136 | tuple->dirty = gtk_tree_selection_real_select_node (selection: tuple->selection, tree, node, FALSE) || tuple->dirty; |
1137 | } |
1138 | } |
1139 | |
1140 | static gboolean |
1141 | gtk_tree_selection_real_unselect_all (GtkTreeSelection *selection) |
1142 | { |
1143 | struct _TempTuple *tuple; |
1144 | |
1145 | if (selection->type == GTK_SELECTION_SINGLE || |
1146 | selection->type == GTK_SELECTION_BROWSE) |
1147 | { |
1148 | GtkTreeRBTree *tree = NULL; |
1149 | GtkTreeRBNode *node = NULL; |
1150 | GtkTreePath *anchor_path; |
1151 | |
1152 | anchor_path = _gtk_tree_view_get_anchor_path (tree_view: selection->tree_view); |
1153 | |
1154 | if (anchor_path == NULL) |
1155 | return FALSE; |
1156 | |
1157 | _gtk_tree_view_find_node (tree_view: selection->tree_view, |
1158 | path: anchor_path, |
1159 | tree: &tree, |
1160 | node: &node); |
1161 | |
1162 | gtk_tree_path_free (path: anchor_path); |
1163 | |
1164 | if (tree == NULL) |
1165 | return FALSE; |
1166 | |
1167 | if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED)) |
1168 | { |
1169 | if (gtk_tree_selection_real_select_node (selection, tree, node, FALSE)) |
1170 | { |
1171 | _gtk_tree_view_set_anchor_path (tree_view: selection->tree_view, NULL); |
1172 | return TRUE; |
1173 | } |
1174 | } |
1175 | return FALSE; |
1176 | } |
1177 | else |
1178 | { |
1179 | GtkTreeRBTree *tree; |
1180 | |
1181 | tuple = g_new (struct _TempTuple, 1); |
1182 | tuple->selection = selection; |
1183 | tuple->dirty = FALSE; |
1184 | |
1185 | tree = _gtk_tree_view_get_rbtree (tree_view: selection->tree_view); |
1186 | gtk_tree_rbtree_traverse (tree, node: tree->root, |
1187 | order: G_PRE_ORDER, |
1188 | func: unselect_all_helper, |
1189 | data: tuple); |
1190 | |
1191 | if (tuple->dirty) |
1192 | { |
1193 | g_free (mem: tuple); |
1194 | return TRUE; |
1195 | } |
1196 | g_free (mem: tuple); |
1197 | return FALSE; |
1198 | } |
1199 | } |
1200 | |
1201 | /** |
1202 | * gtk_tree_selection_unselect_all: |
1203 | * @selection: A `GtkTreeSelection`. |
1204 | * |
1205 | * Unselects all the nodes. |
1206 | **/ |
1207 | void |
1208 | gtk_tree_selection_unselect_all (GtkTreeSelection *selection) |
1209 | { |
1210 | |
1211 | g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); |
1212 | g_return_if_fail (selection->tree_view != NULL); |
1213 | |
1214 | if (_gtk_tree_view_get_rbtree (tree_view: selection->tree_view) == NULL || |
1215 | gtk_tree_view_get_model (tree_view: selection->tree_view) == NULL) |
1216 | return; |
1217 | |
1218 | if (gtk_tree_selection_real_unselect_all (selection)) |
1219 | g_signal_emit (instance: selection, signal_id: tree_selection_signals[CHANGED], detail: 0); |
1220 | } |
1221 | |
1222 | enum |
1223 | { |
1224 | RANGE_SELECT, |
1225 | RANGE_UNSELECT |
1226 | }; |
1227 | |
1228 | static int |
1229 | gtk_tree_selection_real_modify_range (GtkTreeSelection *selection, |
1230 | int mode, |
1231 | GtkTreePath *start_path, |
1232 | GtkTreePath *end_path) |
1233 | { |
1234 | GtkTreeRBNode *start_node = NULL, *end_node = NULL; |
1235 | GtkTreeRBTree *start_tree, *end_tree; |
1236 | GtkTreePath *anchor_path = NULL; |
1237 | gboolean dirty = FALSE; |
1238 | |
1239 | switch (gtk_tree_path_compare (a: start_path, b: end_path)) |
1240 | { |
1241 | case 1: |
1242 | _gtk_tree_view_find_node (tree_view: selection->tree_view, |
1243 | path: end_path, |
1244 | tree: &start_tree, |
1245 | node: &start_node); |
1246 | _gtk_tree_view_find_node (tree_view: selection->tree_view, |
1247 | path: start_path, |
1248 | tree: &end_tree, |
1249 | node: &end_node); |
1250 | anchor_path = start_path; |
1251 | break; |
1252 | case 0: |
1253 | _gtk_tree_view_find_node (tree_view: selection->tree_view, |
1254 | path: start_path, |
1255 | tree: &start_tree, |
1256 | node: &start_node); |
1257 | end_tree = start_tree; |
1258 | end_node = start_node; |
1259 | anchor_path = start_path; |
1260 | break; |
1261 | case -1: |
1262 | _gtk_tree_view_find_node (tree_view: selection->tree_view, |
1263 | path: start_path, |
1264 | tree: &start_tree, |
1265 | node: &start_node); |
1266 | _gtk_tree_view_find_node (tree_view: selection->tree_view, |
1267 | path: end_path, |
1268 | tree: &end_tree, |
1269 | node: &end_node); |
1270 | anchor_path = start_path; |
1271 | break; |
1272 | default: |
1273 | g_assert_not_reached (); |
1274 | break; |
1275 | } |
1276 | |
1277 | /* Invalid start or end node? */ |
1278 | if (start_node == NULL || end_node == NULL) |
1279 | return dirty; |
1280 | |
1281 | if (anchor_path) |
1282 | _gtk_tree_view_set_anchor_path (tree_view: selection->tree_view, anchor_path); |
1283 | |
1284 | do |
1285 | { |
1286 | dirty |= gtk_tree_selection_real_select_node (selection, tree: start_tree, node: start_node, select: (mode == RANGE_SELECT)?TRUE:FALSE); |
1287 | |
1288 | if (start_node == end_node) |
1289 | break; |
1290 | |
1291 | if (start_node->children) |
1292 | { |
1293 | start_tree = start_node->children; |
1294 | start_node = gtk_tree_rbtree_first (tree: start_tree); |
1295 | } |
1296 | else |
1297 | { |
1298 | gtk_tree_rbtree_next_full (tree: start_tree, node: start_node, new_tree: &start_tree, new_node: &start_node); |
1299 | if (start_tree == NULL) |
1300 | { |
1301 | /* we just ran out of tree. That means someone passed in bogus values. |
1302 | */ |
1303 | return dirty; |
1304 | } |
1305 | } |
1306 | } |
1307 | while (TRUE); |
1308 | |
1309 | return dirty; |
1310 | } |
1311 | |
1312 | /** |
1313 | * gtk_tree_selection_select_range: |
1314 | * @selection: A `GtkTreeSelection`. |
1315 | * @start_path: The initial node of the range. |
1316 | * @end_path: The final node of the range. |
1317 | * |
1318 | * Selects a range of nodes, determined by @start_path and @end_path inclusive. |
1319 | * @selection must be set to %GTK_SELECTION_MULTIPLE mode. |
1320 | **/ |
1321 | void |
1322 | gtk_tree_selection_select_range (GtkTreeSelection *selection, |
1323 | GtkTreePath *start_path, |
1324 | GtkTreePath *end_path) |
1325 | { |
1326 | |
1327 | g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); |
1328 | g_return_if_fail (selection->tree_view != NULL); |
1329 | g_return_if_fail (selection->type == GTK_SELECTION_MULTIPLE); |
1330 | g_return_if_fail (gtk_tree_view_get_model (selection->tree_view) != NULL); |
1331 | |
1332 | if (gtk_tree_selection_real_modify_range (selection, mode: RANGE_SELECT, start_path, end_path)) |
1333 | g_signal_emit (instance: selection, signal_id: tree_selection_signals[CHANGED], detail: 0); |
1334 | } |
1335 | |
1336 | /** |
1337 | * gtk_tree_selection_unselect_range: |
1338 | * @selection: A `GtkTreeSelection`. |
1339 | * @start_path: The initial node of the range. |
1340 | * @end_path: The initial node of the range. |
1341 | * |
1342 | * Unselects a range of nodes, determined by @start_path and @end_path |
1343 | * inclusive. |
1344 | **/ |
1345 | void |
1346 | gtk_tree_selection_unselect_range (GtkTreeSelection *selection, |
1347 | GtkTreePath *start_path, |
1348 | GtkTreePath *end_path) |
1349 | { |
1350 | |
1351 | g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); |
1352 | g_return_if_fail (selection->tree_view != NULL); |
1353 | g_return_if_fail (gtk_tree_view_get_model (selection->tree_view) != NULL); |
1354 | |
1355 | if (gtk_tree_selection_real_modify_range (selection, mode: RANGE_UNSELECT, start_path, end_path)) |
1356 | g_signal_emit (instance: selection, signal_id: tree_selection_signals[CHANGED], detail: 0); |
1357 | } |
1358 | |
1359 | gboolean |
1360 | _gtk_tree_selection_row_is_selectable (GtkTreeSelection *selection, |
1361 | GtkTreeRBNode *node, |
1362 | GtkTreePath *path) |
1363 | { |
1364 | GtkTreeIter iter; |
1365 | GtkTreeModel *model; |
1366 | GtkTreeViewRowSeparatorFunc separator_func; |
1367 | gpointer separator_data; |
1368 | gboolean sensitive = FALSE; |
1369 | |
1370 | model = gtk_tree_view_get_model (tree_view: selection->tree_view); |
1371 | |
1372 | _gtk_tree_view_get_row_separator_func (tree_view: selection->tree_view, |
1373 | func: &separator_func, data: &separator_data); |
1374 | |
1375 | if (!gtk_tree_model_get_iter (tree_model: model, iter: &iter, path)) |
1376 | sensitive = TRUE; |
1377 | |
1378 | if (!sensitive && separator_func) |
1379 | { |
1380 | /* never allow separators to be selected */ |
1381 | if ((* separator_func) (model, &iter, separator_data)) |
1382 | return FALSE; |
1383 | } |
1384 | |
1385 | if (selection->user_func) |
1386 | return (*selection->user_func) (selection, model, path, |
1387 | GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED), |
1388 | selection->user_data); |
1389 | else |
1390 | return TRUE; |
1391 | } |
1392 | |
1393 | |
1394 | /* Called internally by gtktreeview.c It handles actually selecting the tree. |
1395 | */ |
1396 | |
1397 | /* |
1398 | * docs about the “override_browse_mode”, we set this flag when we want to |
1399 | * unset select the node and override the select browse mode behaviour (that is |
1400 | * 'one node should *always* be selected'). |
1401 | */ |
1402 | void |
1403 | _gtk_tree_selection_internal_select_node (GtkTreeSelection *selection, |
1404 | GtkTreeRBNode *node, |
1405 | GtkTreeRBTree *tree, |
1406 | GtkTreePath *path, |
1407 | GtkTreeSelectMode mode, |
1408 | gboolean override_browse_mode) |
1409 | { |
1410 | int flags; |
1411 | int dirty = FALSE; |
1412 | GtkTreePath *anchor_path = NULL; |
1413 | |
1414 | if (selection->type == GTK_SELECTION_NONE) |
1415 | return; |
1416 | |
1417 | anchor_path = _gtk_tree_view_get_anchor_path (tree_view: selection->tree_view); |
1418 | |
1419 | if (selection->type == GTK_SELECTION_SINGLE || |
1420 | selection->type == GTK_SELECTION_BROWSE) |
1421 | { |
1422 | /* just unselect */ |
1423 | if (selection->type == GTK_SELECTION_BROWSE && override_browse_mode) |
1424 | { |
1425 | dirty = gtk_tree_selection_real_unselect_all (selection); |
1426 | } |
1427 | /* Did we try to select the same node again? */ |
1428 | else if (selection->type == GTK_SELECTION_SINGLE && |
1429 | anchor_path && gtk_tree_path_compare (a: path, b: anchor_path) == 0) |
1430 | { |
1431 | if ((mode & GTK_TREE_SELECT_MODE_TOGGLE) == GTK_TREE_SELECT_MODE_TOGGLE) |
1432 | { |
1433 | dirty = gtk_tree_selection_real_unselect_all (selection); |
1434 | } |
1435 | } |
1436 | else |
1437 | { |
1438 | if (anchor_path) |
1439 | { |
1440 | /* We only want to select the new node if we can unselect the old one, |
1441 | * and we can select the new one. */ |
1442 | dirty = _gtk_tree_selection_row_is_selectable (selection, node, path); |
1443 | |
1444 | /* if dirty is FALSE, we weren't able to select the new one, otherwise, we try to |
1445 | * unselect the new one |
1446 | */ |
1447 | if (dirty) |
1448 | dirty = gtk_tree_selection_real_unselect_all (selection); |
1449 | |
1450 | /* if dirty is TRUE at this point, we successfully unselected the |
1451 | * old one, and can then select the new one */ |
1452 | if (dirty) |
1453 | { |
1454 | |
1455 | _gtk_tree_view_set_anchor_path (tree_view: selection->tree_view, NULL); |
1456 | |
1457 | if (gtk_tree_selection_real_select_node (selection, tree, node, TRUE)) |
1458 | _gtk_tree_view_set_anchor_path (tree_view: selection->tree_view, anchor_path: path); |
1459 | } |
1460 | } |
1461 | else |
1462 | { |
1463 | if (gtk_tree_selection_real_select_node (selection, tree, node, TRUE)) |
1464 | { |
1465 | dirty = TRUE; |
1466 | |
1467 | _gtk_tree_view_set_anchor_path (tree_view: selection->tree_view, anchor_path: path); |
1468 | } |
1469 | } |
1470 | } |
1471 | } |
1472 | else if (selection->type == GTK_SELECTION_MULTIPLE) |
1473 | { |
1474 | if ((mode & GTK_TREE_SELECT_MODE_EXTEND) == GTK_TREE_SELECT_MODE_EXTEND |
1475 | && (anchor_path == NULL)) |
1476 | { |
1477 | _gtk_tree_view_set_anchor_path (tree_view: selection->tree_view, anchor_path: path); |
1478 | |
1479 | dirty = gtk_tree_selection_real_select_node (selection, tree, node, TRUE); |
1480 | } |
1481 | else if ((mode & (GTK_TREE_SELECT_MODE_EXTEND | GTK_TREE_SELECT_MODE_TOGGLE)) == (GTK_TREE_SELECT_MODE_EXTEND | GTK_TREE_SELECT_MODE_TOGGLE)) |
1482 | { |
1483 | gtk_tree_selection_select_range (selection, |
1484 | start_path: anchor_path, |
1485 | end_path: path); |
1486 | } |
1487 | else if ((mode & GTK_TREE_SELECT_MODE_TOGGLE) == GTK_TREE_SELECT_MODE_TOGGLE) |
1488 | { |
1489 | flags = node->flags; |
1490 | |
1491 | _gtk_tree_view_set_anchor_path (tree_view: selection->tree_view, anchor_path: path); |
1492 | |
1493 | if ((flags & GTK_TREE_RBNODE_IS_SELECTED) == GTK_TREE_RBNODE_IS_SELECTED) |
1494 | dirty |= gtk_tree_selection_real_select_node (selection, tree, node, FALSE); |
1495 | else |
1496 | dirty |= gtk_tree_selection_real_select_node (selection, tree, node, TRUE); |
1497 | } |
1498 | else if ((mode & GTK_TREE_SELECT_MODE_EXTEND) == GTK_TREE_SELECT_MODE_EXTEND) |
1499 | { |
1500 | dirty = gtk_tree_selection_real_unselect_all (selection); |
1501 | dirty |= gtk_tree_selection_real_modify_range (selection, |
1502 | mode: RANGE_SELECT, |
1503 | start_path: anchor_path, |
1504 | end_path: path); |
1505 | } |
1506 | else |
1507 | { |
1508 | dirty = gtk_tree_selection_real_unselect_all (selection); |
1509 | |
1510 | _gtk_tree_view_set_anchor_path (tree_view: selection->tree_view, anchor_path: path); |
1511 | |
1512 | dirty |= gtk_tree_selection_real_select_node (selection, tree, node, TRUE); |
1513 | } |
1514 | } |
1515 | |
1516 | if (anchor_path) |
1517 | gtk_tree_path_free (path: anchor_path); |
1518 | |
1519 | if (dirty) |
1520 | g_signal_emit (instance: selection, signal_id: tree_selection_signals[CHANGED], detail: 0); |
1521 | } |
1522 | |
1523 | |
1524 | void |
1525 | _gtk_tree_selection_emit_changed (GtkTreeSelection *selection) |
1526 | { |
1527 | g_signal_emit (instance: selection, signal_id: tree_selection_signals[CHANGED], detail: 0); |
1528 | } |
1529 | |
1530 | /* NOTE: Any {un,}selection ever done _MUST_ be done through this function! |
1531 | */ |
1532 | |
1533 | static int |
1534 | gtk_tree_selection_real_select_node (GtkTreeSelection *selection, |
1535 | GtkTreeRBTree *tree, |
1536 | GtkTreeRBNode *node, |
1537 | gboolean select) |
1538 | { |
1539 | gboolean toggle = FALSE; |
1540 | GtkTreePath *path = NULL; |
1541 | |
1542 | g_return_val_if_fail (node != NULL, FALSE); |
1543 | |
1544 | select = !! select; |
1545 | |
1546 | if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED) != select) |
1547 | { |
1548 | path = _gtk_tree_path_new_from_rbtree (tree, node); |
1549 | toggle = _gtk_tree_selection_row_is_selectable (selection, node, path); |
1550 | gtk_tree_path_free (path); |
1551 | } |
1552 | |
1553 | if (toggle) |
1554 | { |
1555 | if (!GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED)) |
1556 | { |
1557 | GTK_TREE_RBNODE_SET_FLAG (node, GTK_TREE_RBNODE_IS_SELECTED); |
1558 | } |
1559 | else |
1560 | { |
1561 | GTK_TREE_RBNODE_UNSET_FLAG (node, GTK_TREE_RBNODE_IS_SELECTED); |
1562 | } |
1563 | |
1564 | gtk_widget_queue_draw (GTK_WIDGET (selection->tree_view)); |
1565 | |
1566 | return TRUE; |
1567 | } |
1568 | |
1569 | return FALSE; |
1570 | } |
1571 | |