pspp-sheet-view: Reduce time and memory cost to O(1) in number of rows.
[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 #include "libpspp/range-set.h"
46
47 static void pspp_sheet_selection_finalize          (GObject               *object);
48 static gint pspp_sheet_selection_real_select_all   (PsppSheetSelection      *selection);
49 static gint pspp_sheet_selection_real_unselect_all (PsppSheetSelection      *selection);
50 static gint pspp_sheet_selection_real_select_node  (PsppSheetSelection      *selection,
51                                                   int node,
52                                                   gboolean               select);
53
54 enum
55 {
56   CHANGED,
57   LAST_SIGNAL
58 };
59
60 static guint tree_selection_signals [LAST_SIGNAL] = { 0 };
61
62 G_DEFINE_TYPE (PsppSheetSelection, pspp_sheet_selection, G_TYPE_OBJECT)
63
64 static void
65 pspp_sheet_selection_class_init (PsppSheetSelectionClass *class)
66 {
67   GObjectClass *object_class;
68
69   object_class = (GObjectClass*) class;
70
71   object_class->finalize = pspp_sheet_selection_finalize;
72   class->changed = NULL;
73
74   tree_selection_signals[CHANGED] =
75     g_signal_new ("changed",
76                   G_OBJECT_CLASS_TYPE (object_class),
77                   G_SIGNAL_RUN_FIRST,
78                   G_STRUCT_OFFSET (PsppSheetSelectionClass, changed),
79                   NULL, NULL,
80                   g_cclosure_marshal_VOID__VOID,
81                   G_TYPE_NONE, 0);
82 }
83
84 static void
85 pspp_sheet_selection_init (PsppSheetSelection *selection)
86 {
87   selection->type = GTK_SELECTION_SINGLE;
88 }
89
90 static void
91 pspp_sheet_selection_finalize (GObject *object)
92 {
93   G_OBJECT_CLASS (pspp_sheet_selection_parent_class)->finalize (object);
94 }
95
96 /**
97  * _pspp_sheet_selection_new:
98  *
99  * Creates a new #PsppSheetSelection object.  This function should not be invoked,
100  * as each #PsppSheetView will create its own #PsppSheetSelection.
101  *
102  * Return value: A newly created #PsppSheetSelection object.
103  **/
104 PsppSheetSelection*
105 _pspp_sheet_selection_new (void)
106 {
107   PsppSheetSelection *selection;
108
109   selection = g_object_new (PSPP_TYPE_SHEET_SELECTION, NULL);
110
111   return selection;
112 }
113
114 /**
115  * _pspp_sheet_selection_new_with_tree_view:
116  * @tree_view: The #PsppSheetView.
117  *
118  * Creates a new #PsppSheetSelection object.  This function should not be invoked,
119  * as each #PsppSheetView will create its own #PsppSheetSelection.
120  *
121  * Return value: A newly created #PsppSheetSelection object.
122  **/
123 PsppSheetSelection*
124 _pspp_sheet_selection_new_with_tree_view (PsppSheetView *tree_view)
125 {
126   PsppSheetSelection *selection;
127
128   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
129
130   selection = _pspp_sheet_selection_new ();
131   _pspp_sheet_selection_set_tree_view (selection, tree_view);
132
133   return selection;
134 }
135
136 /**
137  * _pspp_sheet_selection_set_tree_view:
138  * @selection: A #PsppSheetSelection.
139  * @tree_view: The #PsppSheetView.
140  *
141  * Sets the #PsppSheetView of @selection.  This function should not be invoked, as
142  * it is used internally by #PsppSheetView.
143  **/
144 void
145 _pspp_sheet_selection_set_tree_view (PsppSheetSelection *selection,
146                                    PsppSheetView      *tree_view)
147 {
148   g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
149   if (tree_view != NULL)
150     g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
151
152   selection->tree_view = tree_view;
153 }
154
155 /**
156  * pspp_sheet_selection_set_mode:
157  * @selection: A #PsppSheetSelection.
158  * @type: The selection mode
159  *
160  * Sets the selection mode of the @selection.  If the previous type was
161  * #GTK_SELECTION_MULTIPLE, then the anchor is kept selected, if it was
162  * previously selected.
163  **/
164 void
165 pspp_sheet_selection_set_mode (PsppSheetSelection *selection,
166                              GtkSelectionMode  type)
167 {
168   g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
169
170   if (selection->type == type)
171     return;
172
173   
174   if (type == GTK_SELECTION_NONE)
175     {
176       pspp_sheet_selection_unselect_all (selection);
177
178       gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
179       selection->tree_view->priv->anchor = NULL;
180     }
181   else if (type == GTK_SELECTION_SINGLE ||
182            type == GTK_SELECTION_BROWSE)
183     {
184       int node = -1;
185       gint selected = FALSE;
186       GtkTreePath *anchor_path = NULL;
187
188       if (selection->tree_view->priv->anchor)
189         {
190           anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor);
191
192           if (anchor_path)
193             {
194               _pspp_sheet_view_find_node (selection->tree_view,
195                                         anchor_path,
196                                         &node);
197
198               if (node >= 0 && pspp_sheet_view_node_is_selected (selection->tree_view, node))
199                 selected = TRUE;
200             }
201         }
202
203       /* We do this so that we unconditionally unset all rows
204        */
205       pspp_sheet_selection_unselect_all (selection);
206
207       if (node >= 0 && selected)
208         _pspp_sheet_selection_internal_select_node (selection,
209                                                   node,
210                                                   anchor_path,
211                                                   0,
212                                                   FALSE);
213       if (anchor_path)
214         gtk_tree_path_free (anchor_path);
215     }
216
217   selection->type = type;
218 }
219
220 /**
221  * pspp_sheet_selection_get_mode:
222  * @selection: a #PsppSheetSelection
223  *
224  * Gets the selection mode for @selection. See
225  * pspp_sheet_selection_set_mode().
226  *
227  * Return value: the current selection mode
228  **/
229 GtkSelectionMode
230 pspp_sheet_selection_get_mode (PsppSheetSelection *selection)
231 {
232   g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), GTK_SELECTION_SINGLE);
233
234   return selection->type;
235 }
236
237 /**
238  * pspp_sheet_selection_get_tree_view:
239  * @selection: A #PsppSheetSelection
240  * 
241  * Returns the tree view associated with @selection.
242  * 
243  * Return value: A #PsppSheetView
244  **/
245 PsppSheetView *
246 pspp_sheet_selection_get_tree_view (PsppSheetSelection *selection)
247 {
248   g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), NULL);
249
250   return selection->tree_view;
251 }
252
253 /**
254  * pspp_sheet_selection_get_selected:
255  * @selection: A #PsppSheetSelection.
256  * @model: (out) (allow-none): A pointer to set to the #GtkTreeModel, or NULL.
257  * @iter: (allow-none): The #GtkTreeIter, or NULL.
258  *
259  * Sets @iter to the currently selected node if @selection is set to
260  * #GTK_SELECTION_SINGLE or #GTK_SELECTION_BROWSE.  @iter may be NULL if you
261  * just want to test if @selection has any selected nodes.  @model is filled
262  * with the current model as a convenience.  This function will not work if you
263  * use @selection is #GTK_SELECTION_MULTIPLE.
264  *
265  * Return value: TRUE, if there is a selected node.
266  **/
267 gboolean
268 pspp_sheet_selection_get_selected (PsppSheetSelection  *selection,
269                                  GtkTreeModel     **model,
270                                  GtkTreeIter       *iter)
271 {
272   int node;
273   GtkTreePath *anchor_path;
274   gboolean retval;
275
276   g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), FALSE);
277   g_return_val_if_fail (selection->type != GTK_SELECTION_MULTIPLE, FALSE);
278   g_return_val_if_fail (selection->tree_view != NULL, FALSE);
279
280   /* Clear the iter */
281   if (iter)
282     memset (iter, 0, sizeof (GtkTreeIter));
283
284   if (model)
285     *model = selection->tree_view->priv->model;
286
287   if (selection->tree_view->priv->anchor == NULL)
288     return FALSE;
289
290   anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor);
291
292   if (anchor_path == NULL)
293     return FALSE;
294
295   retval = FALSE;
296
297   _pspp_sheet_view_find_node (selection->tree_view,
298                               anchor_path,
299                               &node);
300
301   if (pspp_sheet_view_node_is_selected (selection->tree_view, node))
302     {
303       /* we only want to return the anchor if it exists in the rbtree and
304        * is selected.
305        */
306       if (iter == NULL)
307         retval = TRUE;
308       else
309         retval = gtk_tree_model_get_iter (selection->tree_view->priv->model,
310                                           iter,
311                                           anchor_path);
312     }
313   else
314     {
315       /* We don't want to return the anchor if it isn't actually selected.
316        */
317       retval = FALSE;
318     }
319
320   gtk_tree_path_free (anchor_path);
321
322   return retval;
323 }
324
325 /**
326  * pspp_sheet_selection_get_selected_rows:
327  * @selection: A #PsppSheetSelection.
328  * @model: (allow-none): A pointer to set to the #GtkTreeModel, or NULL.
329  *
330  * Creates a list of path of all selected rows. Additionally, if you are
331  * planning on modifying the model after calling this function, you may
332  * want to convert the returned list into a list of #GtkTreeRowReference<!-- -->s.
333  * To do this, you can use gtk_tree_row_reference_new().
334  *
335  * To free the return value, use:
336  * |[
337  * g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
338  * g_list_free (list);
339  * ]|
340  *
341  * Return value: (element-type GtkTreePath) (transfer full): A #GList containing a #GtkTreePath for each selected row.
342  *
343  * Since: 2.2
344  **/
345 GList *
346 pspp_sheet_selection_get_selected_rows (PsppSheetSelection   *selection,
347                                       GtkTreeModel      **model)
348 {
349   const struct range_tower_node *node;
350   unsigned long int start;
351   GList *list = NULL;
352
353   g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), NULL);
354   g_return_val_if_fail (selection->tree_view != NULL, NULL);
355
356   if (model)
357     *model = selection->tree_view->priv->model;
358
359   if (selection->tree_view->priv->row_count == 0)
360     return NULL;
361
362   if (selection->type == GTK_SELECTION_NONE)
363     return NULL;
364   else if (selection->type != GTK_SELECTION_MULTIPLE)
365     {
366       GtkTreeIter iter;
367
368       if (pspp_sheet_selection_get_selected (selection, NULL, &iter))
369         {
370           GtkTreePath *path;
371
372           path = gtk_tree_model_get_path (selection->tree_view->priv->model, &iter);
373           list = g_list_append (list, path);
374
375           return list;
376         }
377
378       return NULL;
379     }
380
381   RANGE_TOWER_FOR_EACH (node, start, selection->tree_view->priv->selected)
382     {
383       unsigned long int width = range_tower_node_get_width (node);
384       unsigned long int index;
385
386       for (index = start; index < start + width; index++)
387         list = g_list_prepend (list, gtk_tree_path_new_from_indices (index, -1));
388     }
389
390   return g_list_reverse (list);
391 }
392
393 /**
394  * pspp_sheet_selection_count_selected_rows:
395  * @selection: A #PsppSheetSelection.
396  *
397  * Returns the number of rows that have been selected in @tree.
398  *
399  * Return value: The number of rows selected.
400  * 
401  * Since: 2.2
402  **/
403 gint
404 pspp_sheet_selection_count_selected_rows (PsppSheetSelection *selection)
405 {
406   const struct range_tower_node *node;
407   unsigned long int start;
408   gint count = 0;
409
410   g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), 0);
411   g_return_val_if_fail (selection->tree_view != NULL, 0);
412
413   if (selection->tree_view->priv->row_count == 0)
414     return 0;
415
416   if (selection->type == GTK_SELECTION_SINGLE ||
417       selection->type == GTK_SELECTION_BROWSE)
418     {
419       if (pspp_sheet_selection_get_selected (selection, NULL, NULL))
420         return 1;
421       else
422         return 0;
423     }
424
425   count = 0;
426   RANGE_TOWER_FOR_EACH (node, start, selection->tree_view->priv->selected)
427     count += range_tower_node_get_width (node);
428
429   return count;
430 }
431
432 /* pspp_sheet_selection_selected_foreach helper */
433 static void
434 model_changed (gpointer data)
435 {
436   gboolean *stop = (gboolean *)data;
437
438   *stop = TRUE;
439 }
440
441 /**
442  * pspp_sheet_selection_selected_foreach:
443  * @selection: A #PsppSheetSelection.
444  * @func: The function to call for each selected node.
445  * @data: user data to pass to the function.
446  *
447  * Calls a function for each selected node. Note that you cannot modify
448  * the tree or selection from within this function. As a result,
449  * pspp_sheet_selection_get_selected_rows() might be more useful.
450  **/
451 void
452 pspp_sheet_selection_selected_foreach (PsppSheetSelection            *selection,
453                                      PsppSheetSelectionForeachFunc  func,
454                                      gpointer                     data)
455 {
456   const struct range_tower_node *node;
457   unsigned long int start;
458   GtkTreePath *path;
459   GtkTreeIter iter;
460   GtkTreeModel *model;
461
462   gulong inserted_id, deleted_id, reordered_id, changed_id;
463   gboolean stop = FALSE;
464
465   g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
466   g_return_if_fail (selection->tree_view != NULL);
467
468   if (func == NULL ||
469       selection->tree_view->priv->row_count == 0)
470     return;
471
472   if (selection->type == GTK_SELECTION_SINGLE ||
473       selection->type == GTK_SELECTION_BROWSE)
474     {
475       if (gtk_tree_row_reference_valid (selection->tree_view->priv->anchor))
476         {
477           path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor);
478           gtk_tree_model_get_iter (selection->tree_view->priv->model, &iter, path);
479           (* func) (selection->tree_view->priv->model, path, &iter, data);
480           gtk_tree_path_free (path);
481         }
482       return;
483     }
484
485   model = selection->tree_view->priv->model;
486   g_object_ref (model);
487
488   /* connect to signals to monitor changes in treemodel */
489   inserted_id = g_signal_connect_swapped (model, "row-inserted",
490                                           G_CALLBACK (model_changed),
491                                           &stop);
492   deleted_id = g_signal_connect_swapped (model, "row-deleted",
493                                          G_CALLBACK (model_changed),
494                                          &stop);
495   reordered_id = g_signal_connect_swapped (model, "rows-reordered",
496                                            G_CALLBACK (model_changed),
497                                            &stop);
498   changed_id = g_signal_connect_swapped (selection->tree_view, "notify::model",
499                                          G_CALLBACK (model_changed), 
500                                          &stop);
501
502   RANGE_TOWER_FOR_EACH (node, start, selection->tree_view->priv->selected)
503     {
504       unsigned long int width = range_tower_node_get_width (node);
505       unsigned long int index;
506
507       for (index = start; index < start + width; index++)
508         {
509           GtkTreePath *path;
510           GtkTreeIter iter;
511
512           path = gtk_tree_path_new_from_indices (index, -1);
513           gtk_tree_model_get_iter (model, &iter, path);
514           (* func) (model, path, &iter, data);
515           gtk_tree_path_free (path);
516         }
517     }
518
519   g_signal_handler_disconnect (model, inserted_id);
520   g_signal_handler_disconnect (model, deleted_id);
521   g_signal_handler_disconnect (model, reordered_id);
522   g_signal_handler_disconnect (selection->tree_view, changed_id);
523   g_object_unref (model);
524
525   /* check if we have to spew a scary message */
526   if (stop)
527     g_warning ("The model has been modified from within pspp_sheet_selection_selected_foreach.\n"
528                "This function is for observing the selections of the tree only.  If\n"
529                "you are trying to get all selected items from the tree, try using\n"
530                "pspp_sheet_selection_get_selected_rows instead.\n");
531 }
532
533 /**
534  * pspp_sheet_selection_select_path:
535  * @selection: A #PsppSheetSelection.
536  * @path: The #GtkTreePath to be selected.
537  *
538  * Select the row at @path.
539  **/
540 void
541 pspp_sheet_selection_select_path (PsppSheetSelection *selection,
542                                 GtkTreePath      *path)
543 {
544   int node;
545   GtkTreeSelectMode mode = 0;
546
547   g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
548   g_return_if_fail (selection->tree_view != NULL);
549   g_return_if_fail (path != NULL);
550
551    _pspp_sheet_view_find_node (selection->tree_view,
552                                path,
553                                &node);
554
555   if (node < 0 || pspp_sheet_view_node_is_selected (selection->tree_view, node)) 
556     return;
557
558   if (selection->type == GTK_SELECTION_MULTIPLE)
559     mode = GTK_TREE_SELECT_MODE_TOGGLE;
560
561   _pspp_sheet_selection_internal_select_node (selection,
562                                             node,
563                                             path,
564                                             mode,
565                                             FALSE);
566 }
567
568 /**
569  * pspp_sheet_selection_unselect_path:
570  * @selection: A #PsppSheetSelection.
571  * @path: The #GtkTreePath to be unselected.
572  *
573  * Unselects the row at @path.
574  **/
575 void
576 pspp_sheet_selection_unselect_path (PsppSheetSelection *selection,
577                                   GtkTreePath      *path)
578 {
579   int node;
580
581   g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
582   g_return_if_fail (selection->tree_view != NULL);
583   g_return_if_fail (path != NULL);
584
585   _pspp_sheet_view_find_node (selection->tree_view,
586                               path,
587                               &node);
588
589   if (node < 0 || !pspp_sheet_view_node_is_selected (selection->tree_view, node)) 
590     return;
591
592   _pspp_sheet_selection_internal_select_node (selection,
593                                             node,
594                                             path,
595                                             GTK_TREE_SELECT_MODE_TOGGLE,
596                                             TRUE);
597 }
598
599 /**
600  * pspp_sheet_selection_select_iter:
601  * @selection: A #PsppSheetSelection.
602  * @iter: The #GtkTreeIter to be selected.
603  *
604  * Selects the specified iterator.
605  **/
606 void
607 pspp_sheet_selection_select_iter (PsppSheetSelection *selection,
608                                 GtkTreeIter      *iter)
609 {
610   GtkTreePath *path;
611
612   g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
613   g_return_if_fail (selection->tree_view != NULL);
614   g_return_if_fail (selection->tree_view->priv->model != NULL);
615   g_return_if_fail (iter != NULL);
616
617   path = gtk_tree_model_get_path (selection->tree_view->priv->model,
618                                   iter);
619
620   if (path == NULL)
621     return;
622
623   pspp_sheet_selection_select_path (selection, path);
624   gtk_tree_path_free (path);
625 }
626
627
628 /**
629  * pspp_sheet_selection_unselect_iter:
630  * @selection: A #PsppSheetSelection.
631  * @iter: The #GtkTreeIter to be unselected.
632  *
633  * Unselects the specified iterator.
634  **/
635 void
636 pspp_sheet_selection_unselect_iter (PsppSheetSelection *selection,
637                                   GtkTreeIter      *iter)
638 {
639   GtkTreePath *path;
640
641   g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
642   g_return_if_fail (selection->tree_view != NULL);
643   g_return_if_fail (selection->tree_view->priv->model != NULL);
644   g_return_if_fail (iter != NULL);
645
646   path = gtk_tree_model_get_path (selection->tree_view->priv->model,
647                                   iter);
648
649   if (path == NULL)
650     return;
651
652   pspp_sheet_selection_unselect_path (selection, path);
653   gtk_tree_path_free (path);
654 }
655
656 /**
657  * pspp_sheet_selection_path_is_selected:
658  * @selection: A #PsppSheetSelection.
659  * @path: A #GtkTreePath to check selection on.
660  * 
661  * Returns %TRUE if the row pointed to by @path is currently selected.  If @path
662  * does not point to a valid location, %FALSE is returned
663  * 
664  * Return value: %TRUE if @path is selected.
665  **/
666 gboolean
667 pspp_sheet_selection_path_is_selected (PsppSheetSelection *selection,
668                                      GtkTreePath      *path)
669 {
670   int node;
671
672   g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), FALSE);
673   g_return_val_if_fail (path != NULL, FALSE);
674   g_return_val_if_fail (selection->tree_view != NULL, FALSE);
675
676   if (selection->tree_view->priv->model == NULL)
677     return FALSE;
678
679   _pspp_sheet_view_find_node (selection->tree_view,
680                                   path,
681                                   &node);
682
683   if (node < 0 || !pspp_sheet_view_node_is_selected (selection->tree_view, node)) 
684     return FALSE;
685
686   return TRUE;
687 }
688
689 /**
690  * pspp_sheet_selection_iter_is_selected:
691  * @selection: A #PsppSheetSelection
692  * @iter: A valid #GtkTreeIter
693  * 
694  * Returns %TRUE if the row at @iter is currently selected.
695  * 
696  * Return value: %TRUE, if @iter is selected
697  **/
698 gboolean
699 pspp_sheet_selection_iter_is_selected (PsppSheetSelection *selection,
700                                      GtkTreeIter      *iter)
701 {
702   GtkTreePath *path;
703   gboolean retval;
704
705   g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), FALSE);
706   g_return_val_if_fail (iter != NULL, FALSE);
707   g_return_val_if_fail (selection->tree_view != NULL, FALSE);
708   g_return_val_if_fail (selection->tree_view->priv->model != NULL, FALSE);
709
710   path = gtk_tree_model_get_path (selection->tree_view->priv->model, iter);
711   if (path == NULL)
712     return FALSE;
713
714   retval = pspp_sheet_selection_path_is_selected (selection, path);
715   gtk_tree_path_free (path);
716
717   return retval;
718 }
719
720
721 /* We have a real_{un,}select_all function that doesn't emit the signal, so we
722  * can use it in other places without fear of the signal being emitted.
723  */
724 static gint
725 pspp_sheet_selection_real_select_all (PsppSheetSelection *selection)
726 {
727   const struct range_tower_node *node;
728   int row_count = selection->tree_view->priv->row_count;
729
730   if (row_count == 0)
731     return FALSE;
732
733   node = range_tower_first (selection->tree_view->priv->selected);
734   if (node
735       && range_tower_node_get_start (node) == 0
736       && range_tower_node_get_width (node) >= row_count)
737     return FALSE;
738
739   range_tower_set1 (selection->tree_view->priv->selected, 0, row_count);
740
741   /* XXX we could invalidate individual visible rows instead */
742   gdk_window_invalidate_rect (selection->tree_view->priv->bin_window, NULL, TRUE);
743
744   return TRUE;
745 }
746
747 /**
748  * pspp_sheet_selection_select_all:
749  * @selection: A #PsppSheetSelection.
750  *
751  * Selects all the nodes. @selection must be set to #GTK_SELECTION_MULTIPLE
752  * mode.
753  **/
754 void
755 pspp_sheet_selection_select_all (PsppSheetSelection *selection)
756 {
757   g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
758   g_return_if_fail (selection->tree_view != NULL);
759
760   if (selection->tree_view->priv->row_count == 0 || selection->tree_view->priv->model == NULL)
761     return;
762
763   g_return_if_fail (selection->type == GTK_SELECTION_MULTIPLE);
764
765   if (pspp_sheet_selection_real_select_all (selection))
766     g_signal_emit (selection, tree_selection_signals[CHANGED], 0);
767 }
768
769 static gint
770 pspp_sheet_selection_real_unselect_all (PsppSheetSelection *selection)
771 {
772   if (selection->type == GTK_SELECTION_SINGLE ||
773       selection->type == GTK_SELECTION_BROWSE)
774     {
775       int node = -1;
776       GtkTreePath *anchor_path;
777
778       if (selection->tree_view->priv->anchor == NULL)
779         return FALSE;
780
781       anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor);
782
783       if (anchor_path == NULL)
784         return FALSE;
785
786       _pspp_sheet_view_find_node (selection->tree_view,
787                                 anchor_path,
788                                 &node);
789
790       gtk_tree_path_free (anchor_path);
791
792       if (node < 0)
793         return FALSE;
794
795       if (pspp_sheet_view_node_is_selected (selection->tree_view, node))
796         {
797           if (pspp_sheet_selection_real_select_node (selection, node, FALSE))
798             {
799               gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
800               selection->tree_view->priv->anchor = NULL;
801               return TRUE;
802             }
803         }
804       return FALSE;
805     }
806   else if (range_tower_is_empty (selection->tree_view->priv->selected))
807     return FALSE;
808   else
809     {
810       range_tower_set0 (selection->tree_view->priv->selected, 0, ULONG_MAX);
811
812       /* XXX we could invalidate individual visible rows instead */
813       gdk_window_invalidate_rect (selection->tree_view->priv->bin_window, NULL, TRUE);
814
815       return TRUE;
816     }
817 }
818
819 /**
820  * pspp_sheet_selection_unselect_all:
821  * @selection: A #PsppSheetSelection.
822  *
823  * Unselects all the nodes.
824  **/
825 void
826 pspp_sheet_selection_unselect_all (PsppSheetSelection *selection)
827 {
828   g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
829   g_return_if_fail (selection->tree_view != NULL);
830
831   if (selection->tree_view->priv->row_count == 0 || selection->tree_view->priv->model == NULL)
832     return;
833   
834   if (pspp_sheet_selection_real_unselect_all (selection))
835     g_signal_emit (selection, tree_selection_signals[CHANGED], 0);
836 }
837
838 enum
839 {
840   RANGE_SELECT,
841   RANGE_UNSELECT
842 };
843
844 static gint
845 pspp_sheet_selection_real_modify_range (PsppSheetSelection *selection,
846                                       gint              mode,
847                                       GtkTreePath      *start_path,
848                                       GtkTreePath      *end_path)
849 {
850   int start_node, end_node;
851   GtkTreePath *anchor_path = NULL;
852   gboolean dirty = FALSE;
853
854   switch (gtk_tree_path_compare (start_path, end_path))
855     {
856     case 1:
857       _pspp_sheet_view_find_node (selection->tree_view,
858                                 end_path,
859                                 &start_node);
860       _pspp_sheet_view_find_node (selection->tree_view,
861                                 start_path,
862                                 &end_node);
863       anchor_path = start_path;
864       break;
865     case 0:
866       _pspp_sheet_view_find_node (selection->tree_view,
867                                 start_path,
868                                 &start_node);
869       end_node = start_node;
870       anchor_path = start_path;
871       break;
872     case -1:
873       _pspp_sheet_view_find_node (selection->tree_view,
874                                 start_path,
875                                 &start_node);
876       _pspp_sheet_view_find_node (selection->tree_view,
877                                 end_path,
878                                 &end_node);
879       anchor_path = start_path;
880       break;
881     }
882
883   g_return_val_if_fail (start_node >= 0, FALSE);
884   g_return_val_if_fail (end_node >= 0, FALSE);
885
886   if (anchor_path)
887     {
888       if (selection->tree_view->priv->anchor)
889         gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
890
891       selection->tree_view->priv->anchor =
892         gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view),
893                                           selection->tree_view->priv->model,
894                                           anchor_path);
895     }
896
897   do
898     {
899       dirty |= pspp_sheet_selection_real_select_node (selection, start_node, (mode == RANGE_SELECT)?TRUE:FALSE);
900
901       if (start_node == end_node)
902         break;
903
904       start_node = pspp_sheet_view_node_next (selection->tree_view, start_node);
905       if (start_node < 0)
906         {
907           /* we just ran out of tree.  That means someone passed in bogus values.
908            */
909           return dirty;
910         }
911     }
912   while (TRUE);
913
914   return dirty;
915 }
916
917 /**
918  * pspp_sheet_selection_select_range:
919  * @selection: A #PsppSheetSelection.
920  * @start_path: The initial node of the range.
921  * @end_path: The final node of the range.
922  *
923  * Selects a range of nodes, determined by @start_path and @end_path inclusive.
924  * @selection must be set to #GTK_SELECTION_MULTIPLE mode. 
925  **/
926 void
927 pspp_sheet_selection_select_range (PsppSheetSelection *selection,
928                                  GtkTreePath      *start_path,
929                                  GtkTreePath      *end_path)
930 {
931   g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
932   g_return_if_fail (selection->tree_view != NULL);
933   g_return_if_fail (selection->type == GTK_SELECTION_MULTIPLE);
934   g_return_if_fail (selection->tree_view->priv->model != NULL);
935
936   if (pspp_sheet_selection_real_modify_range (selection, RANGE_SELECT, start_path, end_path))
937     g_signal_emit (selection, tree_selection_signals[CHANGED], 0);
938 }
939
940 /**
941  * pspp_sheet_selection_unselect_range:
942  * @selection: A #PsppSheetSelection.
943  * @start_path: The initial node of the range.
944  * @end_path: The initial node of the range.
945  *
946  * Unselects a range of nodes, determined by @start_path and @end_path
947  * inclusive.
948  *
949  * Since: 2.2
950  **/
951 void
952 pspp_sheet_selection_unselect_range (PsppSheetSelection *selection,
953                                    GtkTreePath      *start_path,
954                                    GtkTreePath      *end_path)
955 {
956   g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
957   g_return_if_fail (selection->tree_view != NULL);
958   g_return_if_fail (selection->tree_view->priv->model != NULL);
959
960   if (pspp_sheet_selection_real_modify_range (selection, RANGE_UNSELECT, start_path, end_path))
961     g_signal_emit (selection, tree_selection_signals[CHANGED], 0);
962 }
963
964 struct range_set *
965 pspp_sheet_selection_get_range_set (PsppSheetSelection *selection)
966 {
967   const struct range_tower_node *node;
968   unsigned long int start;
969   struct range_set *set;
970
971   g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection),
972                         range_set_create ());
973   g_return_val_if_fail (selection->tree_view != NULL, range_set_create ());
974
975   set = range_set_create ();
976   RANGE_TOWER_FOR_EACH (node, start, selection->tree_view->priv->selected)
977     range_set_set1 (set, start, range_tower_node_get_width (node));
978   return set;
979 }
980
981 /* Called internally by gtktreeview.c It handles actually selecting the tree.
982  */
983
984 /*
985  * docs about the 'override_browse_mode', we set this flag when we want to
986  * unset select the node and override the select browse mode behaviour (that is
987  * 'one node should *always* be selected').
988  */
989 void
990 _pspp_sheet_selection_internal_select_node (PsppSheetSelection *selection,
991                                           int               node,
992                                           GtkTreePath      *path,
993                                           GtkTreeSelectMode mode,
994                                           gboolean          override_browse_mode)
995 {
996   gint dirty = FALSE;
997   GtkTreePath *anchor_path = NULL;
998
999   if (selection->type == GTK_SELECTION_NONE)
1000     return;
1001
1002   if (selection->tree_view->priv->anchor)
1003     anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor);
1004
1005   if (selection->type == GTK_SELECTION_SINGLE ||
1006       selection->type == GTK_SELECTION_BROWSE)
1007     {
1008       /* just unselect */
1009       if (selection->type == GTK_SELECTION_BROWSE && override_browse_mode)
1010         {
1011           dirty = pspp_sheet_selection_real_unselect_all (selection);
1012         }
1013       /* Did we try to select the same node again? */
1014       else if (selection->type == GTK_SELECTION_SINGLE &&
1015                anchor_path && gtk_tree_path_compare (path, anchor_path) == 0)
1016         {
1017           if ((mode & GTK_TREE_SELECT_MODE_TOGGLE) == GTK_TREE_SELECT_MODE_TOGGLE)
1018             {
1019               dirty = pspp_sheet_selection_real_unselect_all (selection);
1020             }
1021         }
1022       else
1023         {
1024           if (anchor_path)
1025             {
1026               dirty = pspp_sheet_selection_real_unselect_all (selection);
1027
1028               /* if dirty is TRUE at this point, we successfully unselected the
1029                * old one, and can then select the new one */
1030               if (dirty)
1031                 {
1032                   if (selection->tree_view->priv->anchor)
1033                     {
1034                       gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
1035                       selection->tree_view->priv->anchor = NULL;
1036                     }
1037
1038                   if (pspp_sheet_selection_real_select_node (selection, node, TRUE))
1039                     {
1040                       selection->tree_view->priv->anchor =
1041                         gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path);
1042                     }
1043                 }
1044             }
1045           else
1046             {
1047               if (pspp_sheet_selection_real_select_node (selection, node, TRUE))
1048                 {
1049                   dirty = TRUE;
1050                   if (selection->tree_view->priv->anchor)
1051                     gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
1052
1053                   selection->tree_view->priv->anchor =
1054                     gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path);
1055                 }
1056             }
1057         }
1058     }
1059   else if (selection->type == GTK_SELECTION_MULTIPLE)
1060     {
1061       if ((mode & GTK_TREE_SELECT_MODE_EXTEND) == GTK_TREE_SELECT_MODE_EXTEND
1062           && (anchor_path == NULL))
1063         {
1064           if (selection->tree_view->priv->anchor)
1065             gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
1066
1067           selection->tree_view->priv->anchor =
1068             gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path);
1069           dirty = pspp_sheet_selection_real_select_node (selection, node, TRUE);
1070         }
1071       else if ((mode & (GTK_TREE_SELECT_MODE_EXTEND | GTK_TREE_SELECT_MODE_TOGGLE)) == (GTK_TREE_SELECT_MODE_EXTEND | GTK_TREE_SELECT_MODE_TOGGLE))
1072         {
1073           pspp_sheet_selection_select_range (selection,
1074                                            anchor_path,
1075                                            path);
1076         }
1077       else if ((mode & GTK_TREE_SELECT_MODE_TOGGLE) == GTK_TREE_SELECT_MODE_TOGGLE)
1078         {
1079           bool selected = pspp_sheet_view_node_is_selected (selection->tree_view, node);
1080           if (selection->tree_view->priv->anchor)
1081             gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
1082
1083           selection->tree_view->priv->anchor =
1084             gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path);
1085
1086           if (selected)
1087             dirty |= pspp_sheet_selection_real_select_node (selection, node, FALSE);
1088           else
1089             dirty |= pspp_sheet_selection_real_select_node (selection, node, TRUE);
1090         }
1091       else if ((mode & GTK_TREE_SELECT_MODE_EXTEND) == GTK_TREE_SELECT_MODE_EXTEND)
1092         {
1093           dirty = pspp_sheet_selection_real_unselect_all (selection);
1094           dirty |= pspp_sheet_selection_real_modify_range (selection,
1095                                                          RANGE_SELECT,
1096                                                          anchor_path,
1097                                                          path);
1098         }
1099       else
1100         {
1101           dirty = pspp_sheet_selection_real_unselect_all (selection);
1102
1103           if (selection->tree_view->priv->anchor)
1104             gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
1105
1106           selection->tree_view->priv->anchor =
1107             gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path);
1108
1109           dirty |= pspp_sheet_selection_real_select_node (selection, node, TRUE);
1110         }
1111     }
1112
1113   if (anchor_path)
1114     gtk_tree_path_free (anchor_path);
1115
1116   if (dirty)
1117     g_signal_emit (selection, tree_selection_signals[CHANGED], 0);  
1118 }
1119
1120
1121 void 
1122 _pspp_sheet_selection_emit_changed (PsppSheetSelection *selection)
1123 {
1124   g_signal_emit (selection, tree_selection_signals[CHANGED], 0);  
1125 }
1126
1127 /* NOTE: Any {un,}selection ever done _MUST_ be done through this function!
1128  */
1129
1130 static gint
1131 pspp_sheet_selection_real_select_node (PsppSheetSelection *selection,
1132                                      int               node,
1133                                      gboolean          select)
1134 {
1135   select = !! select;
1136   if (pspp_sheet_view_node_is_selected (selection->tree_view, node) != select)
1137     {
1138       if (select)
1139         pspp_sheet_view_node_select (selection->tree_view, node);
1140       else
1141         pspp_sheet_view_node_unselect (selection->tree_view, node);
1142
1143       _pspp_sheet_view_queue_draw_node (selection->tree_view, node, NULL);
1144       
1145       return TRUE;
1146     }
1147
1148   return FALSE;
1149 }