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