Merge 'master' into 'gtk3'.
[pspp] / src / ui / gui / pspp-sheet-selection.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2011, 2012, 2013 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 = PSPP_SHEET_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  * #PSPP_SHEET_SELECTION_MULTIPLE or #PSPP_SHEET_SELECTION_RECTANGLE, then the
162  * anchor is kept selected, if it was previously selected.
163  **/
164 void
165 pspp_sheet_selection_set_mode (PsppSheetSelection *selection,
166                              PsppSheetSelectionMode  type)
167 {
168   g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
169
170   if (selection->type == type)
171     return;
172
173   if (type == PSPP_SHEET_SELECTION_NONE)
174     {
175       pspp_sheet_selection_unselect_all (selection);
176
177       gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
178       selection->tree_view->priv->anchor = NULL;
179     }
180   else if (type == PSPP_SHEET_SELECTION_SINGLE ||
181            type == PSPP_SHEET_SELECTION_BROWSE)
182     {
183       int node = -1;
184       gint selected = FALSE;
185       GtkTreePath *anchor_path = NULL;
186
187       if (selection->tree_view->priv->anchor)
188         {
189           anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor);
190
191           if (anchor_path)
192             {
193               _pspp_sheet_view_find_node (selection->tree_view,
194                                         anchor_path,
195                                         &node);
196
197               if (node >= 0 && pspp_sheet_view_node_is_selected (selection->tree_view, node))
198                 selected = TRUE;
199             }
200         }
201
202       /* We do this so that we unconditionally unset all rows
203        */
204       pspp_sheet_selection_unselect_all (selection);
205
206       if (node >= 0 && selected)
207         _pspp_sheet_selection_internal_select_node (selection,
208                                                   node,
209                                                   anchor_path,
210                                                   0,
211                                                   FALSE);
212       if (anchor_path)
213         gtk_tree_path_free (anchor_path);
214     }
215
216   /* XXX unselect all columns when switching to/from rectangular selection? */
217
218   selection->type = type;
219 }
220
221 /**
222  * pspp_sheet_selection_get_mode:
223  * @selection: a #PsppSheetSelection
224  *
225  * Gets the selection mode for @selection. See
226  * pspp_sheet_selection_set_mode().
227  *
228  * Return value: the current selection mode
229  **/
230 PsppSheetSelectionMode
231 pspp_sheet_selection_get_mode (PsppSheetSelection *selection)
232 {
233   g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), PSPP_SHEET_SELECTION_SINGLE);
234
235   return selection->type;
236 }
237
238 /**
239  * pspp_sheet_selection_get_tree_view:
240  * @selection: A #PsppSheetSelection
241  * 
242  * Returns the tree view associated with @selection.
243  * 
244  * Return value: A #PsppSheetView
245  **/
246 PsppSheetView *
247 pspp_sheet_selection_get_tree_view (PsppSheetSelection *selection)
248 {
249   g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), NULL);
250
251   return selection->tree_view;
252 }
253
254 /**
255  * pspp_sheet_selection_get_selected:
256  * @selection: A #PsppSheetSelection.
257  * @model: (out) (allow-none): A pointer to set to the #GtkTreeModel, or NULL.
258  * @iter: (allow-none): The #GtkTreeIter, or NULL.
259  *
260  * Sets @iter to the currently selected node if @selection is set to
261  * #PSPP_SHEET_SELECTION_SINGLE or #PSPP_SHEET_SELECTION_BROWSE.  @iter may be
262  * NULL if you just want to test if @selection has any selected nodes.  @model
263  * is filled with the current model as a convenience.  This function will not
264  * work if @selection's mode is #PSPP_SHEET_SELECTION_MULTIPLE or
265  * #PSPP_SHEET_SELECTION_RECTANGLE.
266  *
267  * Return value: TRUE, if there is a selected node.
268  **/
269 gboolean
270 pspp_sheet_selection_get_selected (PsppSheetSelection  *selection,
271                                  GtkTreeModel     **model,
272                                  GtkTreeIter       *iter)
273 {
274   int node;
275   GtkTreePath *anchor_path;
276   gboolean retval;
277
278   g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), FALSE);
279   g_return_val_if_fail (selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
280                         selection->type != PSPP_SHEET_SELECTION_RECTANGLE,
281                         FALSE);
282   g_return_val_if_fail (selection->tree_view != NULL, FALSE);
283
284   /* Clear the iter */
285   if (iter)
286     memset (iter, 0, sizeof (GtkTreeIter));
287
288   if (model)
289     *model = selection->tree_view->priv->model;
290
291   if (selection->tree_view->priv->anchor == NULL)
292     return FALSE;
293
294   anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor);
295
296   if (anchor_path == NULL)
297     return FALSE;
298
299   retval = FALSE;
300
301   _pspp_sheet_view_find_node (selection->tree_view,
302                               anchor_path,
303                               &node);
304
305   if (pspp_sheet_view_node_is_selected (selection->tree_view, node))
306     {
307       /* we only want to return the anchor if it exists in the rbtree and
308        * is selected.
309        */
310       if (iter == NULL)
311         retval = TRUE;
312       else
313         retval = gtk_tree_model_get_iter (selection->tree_view->priv->model,
314                                           iter,
315                                           anchor_path);
316     }
317   else
318     {
319       /* We don't want to return the anchor if it isn't actually selected.
320        */
321       retval = FALSE;
322     }
323
324   gtk_tree_path_free (anchor_path);
325
326   return retval;
327 }
328
329 /**
330  * pspp_sheet_selection_get_selected_rows:
331  * @selection: A #PsppSheetSelection.
332  * @model: (allow-none): A pointer to set to the #GtkTreeModel, or NULL.
333  *
334  * Creates a list of path of all selected rows. Additionally, if you are
335  * planning on modifying the model after calling this function, you may
336  * want to convert the returned list into a list of #GtkTreeRowReference<!-- -->s.
337  * To do this, you can use gtk_tree_row_reference_new().
338  *
339  * To free the return value, use:
340  * |[
341  * g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
342  * g_list_free (list);
343  * ]|
344  *
345  * Return value: (element-type GtkTreePath) (transfer full): A #GList containing a #GtkTreePath for each selected row.
346  *
347  * Since: 2.2
348  **/
349 GList *
350 pspp_sheet_selection_get_selected_rows (PsppSheetSelection   *selection,
351                                       GtkTreeModel      **model)
352 {
353   const struct range_tower_node *node;
354   unsigned long int start;
355   GList *list = NULL;
356
357   g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), NULL);
358   g_return_val_if_fail (selection->tree_view != NULL, NULL);
359
360   if (model)
361     *model = selection->tree_view->priv->model;
362
363   if (selection->tree_view->priv->row_count == 0)
364     return NULL;
365
366   if (selection->type == PSPP_SHEET_SELECTION_NONE)
367     return NULL;
368   else if (selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
369            selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
370     {
371       GtkTreeIter iter;
372
373       if (pspp_sheet_selection_get_selected (selection, NULL, &iter))
374         {
375           GtkTreePath *path;
376
377           path = gtk_tree_model_get_path (selection->tree_view->priv->model, &iter);
378           list = g_list_append (list, path);
379
380           return list;
381         }
382
383       return NULL;
384     }
385
386   RANGE_TOWER_FOR_EACH (node, start, selection->tree_view->priv->selected)
387     {
388       unsigned long int width = range_tower_node_get_width (node);
389       unsigned long int index;
390
391       for (index = start; index < start + width; index++)
392         list = g_list_prepend (list, gtk_tree_path_new_from_indices (index, -1));
393     }
394
395   return g_list_reverse (list);
396 }
397
398 /**
399  * pspp_sheet_selection_count_selected_rows:
400  * @selection: A #PsppSheetSelection.
401  *
402  * Returns the number of rows that have been selected in @tree.
403  *
404  * Return value: The number of rows selected.
405  * 
406  * Since: 2.2
407  **/
408 gint
409 pspp_sheet_selection_count_selected_rows (PsppSheetSelection *selection)
410 {
411   const struct range_tower_node *node;
412   unsigned long int start;
413   gint count = 0;
414
415   g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), 0);
416   g_return_val_if_fail (selection->tree_view != NULL, 0);
417
418   if (selection->tree_view->priv->row_count == 0)
419     return 0;
420
421   if (selection->type == PSPP_SHEET_SELECTION_SINGLE ||
422       selection->type == PSPP_SHEET_SELECTION_BROWSE)
423     {
424       if (pspp_sheet_selection_get_selected (selection, NULL, NULL))
425         return 1;
426       else
427         return 0;
428     }
429
430   count = 0;
431   RANGE_TOWER_FOR_EACH (node, start, selection->tree_view->priv->selected)
432     count += range_tower_node_get_width (node);
433
434   return count;
435 }
436
437 /* pspp_sheet_selection_selected_foreach helper */
438 static void
439 model_changed (gpointer data)
440 {
441   gboolean *stop = (gboolean *)data;
442
443   *stop = TRUE;
444 }
445
446 /**
447  * pspp_sheet_selection_selected_foreach:
448  * @selection: A #PsppSheetSelection.
449  * @func: The function to call for each selected node.
450  * @data: user data to pass to the function.
451  *
452  * Calls a function for each selected node. Note that you cannot modify
453  * the tree or selection from within this function. As a result,
454  * pspp_sheet_selection_get_selected_rows() might be more useful.
455  **/
456 void
457 pspp_sheet_selection_selected_foreach (PsppSheetSelection            *selection,
458                                      PsppSheetSelectionForeachFunc  func,
459                                      gpointer                     data)
460 {
461   const struct range_tower_node *node;
462   unsigned long int start;
463   GtkTreePath *path;
464   GtkTreeIter iter;
465   GtkTreeModel *model;
466
467   gulong inserted_id, deleted_id, reordered_id, changed_id;
468   gboolean stop = FALSE;
469
470   g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
471   g_return_if_fail (selection->tree_view != NULL);
472
473   if (func == NULL ||
474       selection->tree_view->priv->row_count == 0)
475     return;
476
477   if (selection->type == PSPP_SHEET_SELECTION_SINGLE ||
478       selection->type == PSPP_SHEET_SELECTION_BROWSE)
479     {
480       if (gtk_tree_row_reference_valid (selection->tree_view->priv->anchor))
481         {
482           path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor);
483           gtk_tree_model_get_iter (selection->tree_view->priv->model, &iter, path);
484           (* func) (selection->tree_view->priv->model, path, &iter, data);
485           gtk_tree_path_free (path);
486         }
487       return;
488     }
489
490   model = selection->tree_view->priv->model;
491   g_object_ref (model);
492
493   /* connect to signals to monitor changes in treemodel */
494   inserted_id = g_signal_connect_swapped (model, "row-inserted",
495                                           G_CALLBACK (model_changed),
496                                           &stop);
497   deleted_id = g_signal_connect_swapped (model, "row-deleted",
498                                          G_CALLBACK (model_changed),
499                                          &stop);
500   reordered_id = g_signal_connect_swapped (model, "rows-reordered",
501                                            G_CALLBACK (model_changed),
502                                            &stop);
503   changed_id = g_signal_connect_swapped (selection->tree_view, "notify::model",
504                                          G_CALLBACK (model_changed), 
505                                          &stop);
506
507   RANGE_TOWER_FOR_EACH (node, start, selection->tree_view->priv->selected)
508     {
509       unsigned long int width = range_tower_node_get_width (node);
510       unsigned long int index;
511
512       for (index = start; index < start + width; index++)
513         {
514           GtkTreePath *path;
515           GtkTreeIter iter;
516
517           path = gtk_tree_path_new_from_indices (index, -1);
518           gtk_tree_model_get_iter (model, &iter, path);
519           (* func) (model, path, &iter, data);
520           gtk_tree_path_free (path);
521         }
522     }
523
524   g_signal_handler_disconnect (model, inserted_id);
525   g_signal_handler_disconnect (model, deleted_id);
526   g_signal_handler_disconnect (model, reordered_id);
527   g_signal_handler_disconnect (selection->tree_view, changed_id);
528   g_object_unref (model);
529
530   /* check if we have to spew a scary message */
531   if (stop)
532     g_warning ("The model has been modified from within pspp_sheet_selection_selected_foreach.\n"
533                "This function is for observing the selections of the tree only.  If\n"
534                "you are trying to get all selected items from the tree, try using\n"
535                "pspp_sheet_selection_get_selected_rows instead.\n");
536 }
537
538 /**
539  * pspp_sheet_selection_select_path:
540  * @selection: A #PsppSheetSelection.
541  * @path: The #GtkTreePath to be selected.
542  *
543  * Select the row at @path.
544  **/
545 void
546 pspp_sheet_selection_select_path (PsppSheetSelection *selection,
547                                 GtkTreePath      *path)
548 {
549   int node;
550   PsppSheetSelectMode mode = 0;
551
552   g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
553   g_return_if_fail (selection->tree_view != NULL);
554   g_return_if_fail (path != NULL);
555
556    _pspp_sheet_view_find_node (selection->tree_view,
557                                path,
558                                &node);
559
560   if (node < 0 || pspp_sheet_view_node_is_selected (selection->tree_view, node)) 
561     return;
562
563   if (selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
564       selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
565     mode = PSPP_SHEET_SELECT_MODE_TOGGLE;
566
567   _pspp_sheet_selection_internal_select_node (selection,
568                                             node,
569                                             path,
570                                             mode,
571                                             FALSE);
572 }
573
574 /**
575  * pspp_sheet_selection_unselect_path:
576  * @selection: A #PsppSheetSelection.
577  * @path: The #GtkTreePath to be unselected.
578  *
579  * Unselects the row at @path.
580  **/
581 void
582 pspp_sheet_selection_unselect_path (PsppSheetSelection *selection,
583                                   GtkTreePath      *path)
584 {
585   int node;
586
587   g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
588   g_return_if_fail (selection->tree_view != NULL);
589   g_return_if_fail (path != NULL);
590
591   _pspp_sheet_view_find_node (selection->tree_view,
592                               path,
593                               &node);
594
595   if (node < 0 || !pspp_sheet_view_node_is_selected (selection->tree_view, node)) 
596     return;
597
598   _pspp_sheet_selection_internal_select_node (selection,
599                                             node,
600                                             path,
601                                             PSPP_SHEET_SELECT_MODE_TOGGLE,
602                                             TRUE);
603 }
604
605 /**
606  * pspp_sheet_selection_select_iter:
607  * @selection: A #PsppSheetSelection.
608  * @iter: The #GtkTreeIter to be selected.
609  *
610  * Selects the specified iterator.
611  **/
612 void
613 pspp_sheet_selection_select_iter (PsppSheetSelection *selection,
614                                 GtkTreeIter      *iter)
615 {
616   GtkTreePath *path;
617
618   g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
619   g_return_if_fail (selection->tree_view != NULL);
620   g_return_if_fail (selection->tree_view->priv->model != NULL);
621   g_return_if_fail (iter != NULL);
622
623   path = gtk_tree_model_get_path (selection->tree_view->priv->model,
624                                   iter);
625
626   if (path == NULL)
627     return;
628
629   pspp_sheet_selection_select_path (selection, path);
630   gtk_tree_path_free (path);
631 }
632
633
634 /**
635  * pspp_sheet_selection_unselect_iter:
636  * @selection: A #PsppSheetSelection.
637  * @iter: The #GtkTreeIter to be unselected.
638  *
639  * Unselects the specified iterator.
640  **/
641 void
642 pspp_sheet_selection_unselect_iter (PsppSheetSelection *selection,
643                                   GtkTreeIter      *iter)
644 {
645   GtkTreePath *path;
646
647   g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
648   g_return_if_fail (selection->tree_view != NULL);
649   g_return_if_fail (selection->tree_view->priv->model != NULL);
650   g_return_if_fail (iter != NULL);
651
652   path = gtk_tree_model_get_path (selection->tree_view->priv->model,
653                                   iter);
654
655   if (path == NULL)
656     return;
657
658   pspp_sheet_selection_unselect_path (selection, path);
659   gtk_tree_path_free (path);
660 }
661
662 /**
663  * pspp_sheet_selection_path_is_selected:
664  * @selection: A #PsppSheetSelection.
665  * @path: A #GtkTreePath to check selection on.
666  * 
667  * Returns %TRUE if the row pointed to by @path is currently selected.  If @path
668  * does not point to a valid location, %FALSE is returned
669  * 
670  * Return value: %TRUE if @path is selected.
671  **/
672 gboolean
673 pspp_sheet_selection_path_is_selected (PsppSheetSelection *selection,
674                                      GtkTreePath      *path)
675 {
676   int node;
677
678   g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), FALSE);
679   g_return_val_if_fail (path != NULL, FALSE);
680   g_return_val_if_fail (selection->tree_view != NULL, FALSE);
681
682   if (selection->tree_view->priv->model == NULL)
683     return FALSE;
684
685   _pspp_sheet_view_find_node (selection->tree_view,
686                                   path,
687                                   &node);
688
689   if (node < 0 || !pspp_sheet_view_node_is_selected (selection->tree_view, node)) 
690     return FALSE;
691
692   return TRUE;
693 }
694
695 /**
696  * pspp_sheet_selection_iter_is_selected:
697  * @selection: A #PsppSheetSelection
698  * @iter: A valid #GtkTreeIter
699  * 
700  * Returns %TRUE if the row at @iter is currently selected.
701  * 
702  * Return value: %TRUE, if @iter is selected
703  **/
704 gboolean
705 pspp_sheet_selection_iter_is_selected (PsppSheetSelection *selection,
706                                      GtkTreeIter      *iter)
707 {
708   GtkTreePath *path;
709   gboolean retval;
710
711   g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), FALSE);
712   g_return_val_if_fail (iter != NULL, FALSE);
713   g_return_val_if_fail (selection->tree_view != NULL, FALSE);
714   g_return_val_if_fail (selection->tree_view->priv->model != NULL, FALSE);
715
716   path = gtk_tree_model_get_path (selection->tree_view->priv->model, iter);
717   if (path == NULL)
718     return FALSE;
719
720   retval = pspp_sheet_selection_path_is_selected (selection, path);
721   gtk_tree_path_free (path);
722
723   return retval;
724 }
725
726
727 /* We have a real_{un,}select_all function that doesn't emit the signal, so we
728  * can use it in other places without fear of the signal being emitted.
729  */
730 static gint
731 pspp_sheet_selection_real_select_all (PsppSheetSelection *selection)
732 {
733   const struct range_tower_node *node;
734   int row_count = selection->tree_view->priv->row_count;
735
736   if (row_count == 0)
737     return FALSE;
738
739   node = range_tower_first (selection->tree_view->priv->selected);
740   if (node
741       && range_tower_node_get_start (node) == 0
742       && range_tower_node_get_width (node) >= row_count)
743     return FALSE;
744
745   range_tower_set1 (selection->tree_view->priv->selected, 0, row_count);
746   pspp_sheet_selection_select_all_columns (selection);
747
748   /* XXX we could invalidate individual visible rows instead */
749   gdk_window_invalidate_rect (selection->tree_view->priv->bin_window, NULL, TRUE);
750
751   return TRUE;
752 }
753
754 /**
755  * pspp_sheet_selection_select_all:
756  * @selection: A #PsppSheetSelection.
757  *
758  * Selects all the nodes and column. @selection must be set to
759  * #PSPP_SHEET_SELECTION_MULTIPLE or  #PSPP_SHEET_SELECTION_RECTANGLE mode.
760  **/
761 void
762 pspp_sheet_selection_select_all (PsppSheetSelection *selection)
763 {
764   g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
765   g_return_if_fail (selection->tree_view != NULL);
766
767   if (selection->tree_view->priv->row_count == 0 || selection->tree_view->priv->model == NULL)
768     return;
769
770   g_return_if_fail (selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
771                     selection->type == PSPP_SHEET_SELECTION_RECTANGLE);
772
773   if (pspp_sheet_selection_real_select_all (selection))
774     g_signal_emit (selection, tree_selection_signals[CHANGED], 0);
775 }
776
777 static gint
778 pspp_sheet_selection_real_unselect_all (PsppSheetSelection *selection)
779 {
780   if (selection->type == PSPP_SHEET_SELECTION_SINGLE ||
781       selection->type == PSPP_SHEET_SELECTION_BROWSE)
782     {
783       int node = -1;
784       GtkTreePath *anchor_path;
785
786       if (selection->tree_view->priv->anchor == NULL)
787         return FALSE;
788
789       anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor);
790
791       if (anchor_path == NULL)
792         return FALSE;
793
794       _pspp_sheet_view_find_node (selection->tree_view,
795                                 anchor_path,
796                                 &node);
797
798       gtk_tree_path_free (anchor_path);
799
800       if (node < 0)
801         return FALSE;
802
803       if (pspp_sheet_view_node_is_selected (selection->tree_view, node))
804         {
805           if (pspp_sheet_selection_real_select_node (selection, node, FALSE))
806             {
807               gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
808               selection->tree_view->priv->anchor = NULL;
809               return TRUE;
810             }
811         }
812       return FALSE;
813     }
814   else if (range_tower_is_empty (selection->tree_view->priv->selected))
815     return FALSE;
816   else
817     {
818       range_tower_set0 (selection->tree_view->priv->selected, 0, ULONG_MAX);
819       pspp_sheet_selection_unselect_all_columns (selection);
820
821       /* XXX we could invalidate individual visible rows instead */
822       gdk_window_invalidate_rect (selection->tree_view->priv->bin_window, NULL, TRUE);
823
824       return TRUE;
825     }
826 }
827
828 /**
829  * pspp_sheet_selection_unselect_all:
830  * @selection: A #PsppSheetSelection.
831  *
832  * Unselects all the nodes and columns.
833  **/
834 void
835 pspp_sheet_selection_unselect_all (PsppSheetSelection *selection)
836 {
837   g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
838   g_return_if_fail (selection->tree_view != NULL);
839
840   if (selection->tree_view->priv->row_count == 0 || selection->tree_view->priv->model == NULL)
841     return;
842   
843   if (pspp_sheet_selection_real_unselect_all (selection))
844     g_signal_emit (selection, tree_selection_signals[CHANGED], 0);
845 }
846
847 enum
848 {
849   RANGE_SELECT,
850   RANGE_UNSELECT
851 };
852
853 static gint
854 pspp_sheet_selection_real_modify_range (PsppSheetSelection *selection,
855                                       gint              mode,
856                                       GtkTreePath      *start_path,
857                                       GtkTreePath      *end_path)
858 {
859   int start_node, end_node;
860   GtkTreePath *anchor_path = NULL;
861   gboolean dirty = FALSE;
862
863   switch (gtk_tree_path_compare (start_path, end_path))
864     {
865     case 1:
866       _pspp_sheet_view_find_node (selection->tree_view,
867                                 end_path,
868                                 &start_node);
869       _pspp_sheet_view_find_node (selection->tree_view,
870                                 start_path,
871                                 &end_node);
872       anchor_path = start_path;
873       break;
874     case 0:
875       _pspp_sheet_view_find_node (selection->tree_view,
876                                 start_path,
877                                 &start_node);
878       end_node = start_node;
879       anchor_path = start_path;
880       break;
881     case -1:
882       _pspp_sheet_view_find_node (selection->tree_view,
883                                 start_path,
884                                 &start_node);
885       _pspp_sheet_view_find_node (selection->tree_view,
886                                 end_path,
887                                 &end_node);
888       anchor_path = start_path;
889       break;
890     }
891
892   g_return_val_if_fail (start_node >= 0, FALSE);
893   g_return_val_if_fail (end_node >= 0, FALSE);
894
895   if (anchor_path)
896     {
897       if (selection->tree_view->priv->anchor)
898         gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
899
900       selection->tree_view->priv->anchor =
901         gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view),
902                                           selection->tree_view->priv->model,
903                                           anchor_path);
904     }
905
906   do
907     {
908       dirty |= pspp_sheet_selection_real_select_node (selection, start_node, (mode == RANGE_SELECT)?TRUE:FALSE);
909
910       if (start_node == end_node)
911         break;
912
913       start_node = pspp_sheet_view_node_next (selection->tree_view, start_node);
914       if (start_node < 0)
915         {
916           /* we just ran out of tree.  That means someone passed in bogus values.
917            */
918           return dirty;
919         }
920     }
921   while (TRUE);
922
923   return dirty;
924 }
925
926 /**
927  * pspp_sheet_selection_select_range:
928  * @selection: A #PsppSheetSelection.
929  * @start_path: The initial node of the range.
930  * @end_path: The final node of the range.
931  *
932  * Selects a range of nodes, determined by @start_path and @end_path inclusive.
933  * @selection must be set to #PSPP_SHEET_SELECTION_MULTIPLE or
934  * #PSPP_SHEET_SELECTION_RECTANGLE mode.
935  **/
936 void
937 pspp_sheet_selection_select_range (PsppSheetSelection *selection,
938                                  GtkTreePath      *start_path,
939                                  GtkTreePath      *end_path)
940 {
941   g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
942   g_return_if_fail (selection->tree_view != NULL);
943   g_return_if_fail (selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
944                     selection->type == PSPP_SHEET_SELECTION_RECTANGLE);
945   g_return_if_fail (selection->tree_view->priv->model != NULL);
946
947   if (pspp_sheet_selection_real_modify_range (selection, RANGE_SELECT, start_path, end_path))
948     g_signal_emit (selection, tree_selection_signals[CHANGED], 0);
949 }
950
951 /**
952  * pspp_sheet_selection_unselect_range:
953  * @selection: A #PsppSheetSelection.
954  * @start_path: The initial node of the range.
955  * @end_path: The initial node of the range.
956  *
957  * Unselects a range of nodes, determined by @start_path and @end_path
958  * inclusive.
959  *
960  * Since: 2.2
961  **/
962 void
963 pspp_sheet_selection_unselect_range (PsppSheetSelection *selection,
964                                    GtkTreePath      *start_path,
965                                    GtkTreePath      *end_path)
966 {
967   g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
968   g_return_if_fail (selection->tree_view != NULL);
969   g_return_if_fail (selection->tree_view->priv->model != NULL);
970
971   if (pspp_sheet_selection_real_modify_range (selection, RANGE_UNSELECT, start_path, end_path))
972     g_signal_emit (selection, tree_selection_signals[CHANGED], 0);
973 }
974
975 struct range_set *
976 pspp_sheet_selection_get_range_set (PsppSheetSelection *selection)
977 {
978   const struct range_tower_node *node;
979   unsigned long int start;
980   struct range_set *set;
981
982   g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection),
983                         range_set_create ());
984   g_return_val_if_fail (selection->tree_view != NULL, range_set_create ());
985
986   set = range_set_create ();
987   RANGE_TOWER_FOR_EACH (node, start, selection->tree_view->priv->selected)
988     range_set_set1 (set, start, range_tower_node_get_width (node));
989   return set;
990 }
991
992 /* Called internally by gtktreeview.c It handles actually selecting the tree.
993  */
994
995 /*
996  * docs about the 'override_browse_mode', we set this flag when we want to
997  * unset select the node and override the select browse mode behaviour (that is
998  * 'one node should *always* be selected').
999  */
1000 void
1001 _pspp_sheet_selection_internal_select_node (PsppSheetSelection *selection,
1002                                           int               node,
1003                                           GtkTreePath      *path,
1004                                           PsppSheetSelectMode mode,
1005                                           gboolean          override_browse_mode)
1006 {
1007   gint dirty = FALSE;
1008   GtkTreePath *anchor_path = NULL;
1009
1010   if (selection->type == PSPP_SHEET_SELECTION_NONE)
1011     return;
1012
1013   if (selection->tree_view->priv->anchor)
1014     anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor);
1015
1016   if (selection->type == PSPP_SHEET_SELECTION_SINGLE ||
1017       selection->type == PSPP_SHEET_SELECTION_BROWSE)
1018     {
1019       /* just unselect */
1020       if (selection->type == PSPP_SHEET_SELECTION_BROWSE && override_browse_mode)
1021         {
1022           dirty = pspp_sheet_selection_real_unselect_all (selection);
1023         }
1024       /* Did we try to select the same node again? */
1025       else if (selection->type == PSPP_SHEET_SELECTION_SINGLE &&
1026                anchor_path && gtk_tree_path_compare (path, anchor_path) == 0)
1027         {
1028           if ((mode & PSPP_SHEET_SELECT_MODE_TOGGLE) == PSPP_SHEET_SELECT_MODE_TOGGLE)
1029             {
1030               dirty = pspp_sheet_selection_real_unselect_all (selection);
1031             }
1032         }
1033       else
1034         {
1035           if (anchor_path)
1036             {
1037               dirty = pspp_sheet_selection_real_unselect_all (selection);
1038
1039               /* if dirty is TRUE at this point, we successfully unselected the
1040                * old one, and can then select the new one */
1041               if (dirty)
1042                 {
1043                   if (selection->tree_view->priv->anchor)
1044                     {
1045                       gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
1046                       selection->tree_view->priv->anchor = NULL;
1047                     }
1048
1049                   if (pspp_sheet_selection_real_select_node (selection, node, TRUE))
1050                     {
1051                       selection->tree_view->priv->anchor =
1052                         gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path);
1053                     }
1054                 }
1055             }
1056           else
1057             {
1058               if (pspp_sheet_selection_real_select_node (selection, node, TRUE))
1059                 {
1060                   dirty = TRUE;
1061                   if (selection->tree_view->priv->anchor)
1062                     gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
1063
1064                   selection->tree_view->priv->anchor =
1065                     gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path);
1066                 }
1067             }
1068         }
1069     }
1070   else if (selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
1071            selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
1072     {
1073       if ((mode & PSPP_SHEET_SELECT_MODE_EXTEND) == PSPP_SHEET_SELECT_MODE_EXTEND
1074           && (anchor_path == NULL))
1075         {
1076           if (selection->tree_view->priv->anchor)
1077             gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
1078
1079           selection->tree_view->priv->anchor =
1080             gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path);
1081           dirty = pspp_sheet_selection_real_select_node (selection, node, TRUE);
1082         }
1083       else if ((mode & (PSPP_SHEET_SELECT_MODE_EXTEND | PSPP_SHEET_SELECT_MODE_TOGGLE)) == (PSPP_SHEET_SELECT_MODE_EXTEND | PSPP_SHEET_SELECT_MODE_TOGGLE))
1084         {
1085           pspp_sheet_selection_select_range (selection,
1086                                            anchor_path,
1087                                            path);
1088         }
1089       else if ((mode & PSPP_SHEET_SELECT_MODE_TOGGLE) == PSPP_SHEET_SELECT_MODE_TOGGLE)
1090         {
1091           bool selected = pspp_sheet_view_node_is_selected (selection->tree_view, node);
1092           if (selection->tree_view->priv->anchor)
1093             gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
1094
1095           selection->tree_view->priv->anchor =
1096             gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path);
1097
1098           if (selected)
1099             dirty |= pspp_sheet_selection_real_select_node (selection, node, FALSE);
1100           else
1101             dirty |= pspp_sheet_selection_real_select_node (selection, node, TRUE);
1102         }
1103       else if ((mode & PSPP_SHEET_SELECT_MODE_EXTEND) == PSPP_SHEET_SELECT_MODE_EXTEND)
1104         {
1105           dirty = pspp_sheet_selection_real_unselect_all (selection);
1106           dirty |= pspp_sheet_selection_real_modify_range (selection,
1107                                                          RANGE_SELECT,
1108                                                          anchor_path,
1109                                                          path);
1110         }
1111       else
1112         {
1113           dirty = pspp_sheet_selection_real_unselect_all (selection);
1114
1115           if (selection->tree_view->priv->anchor)
1116             gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
1117
1118           selection->tree_view->priv->anchor =
1119             gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path);
1120
1121           dirty |= pspp_sheet_selection_real_select_node (selection, node, TRUE);
1122         }
1123     }
1124
1125   if (anchor_path)
1126     gtk_tree_path_free (anchor_path);
1127
1128   if (dirty)
1129     g_signal_emit (selection, tree_selection_signals[CHANGED], 0);  
1130 }
1131
1132
1133 void 
1134 _pspp_sheet_selection_emit_changed (PsppSheetSelection *selection)
1135 {
1136   g_signal_emit (selection, tree_selection_signals[CHANGED], 0);  
1137 }
1138
1139 /* NOTE: Any {un,}selection ever done _MUST_ be done through this function!
1140  */
1141
1142 static gint
1143 pspp_sheet_selection_real_select_node (PsppSheetSelection *selection,
1144                                      int               node,
1145                                      gboolean          select)
1146 {
1147   select = !! select;
1148   if (pspp_sheet_view_node_is_selected (selection->tree_view, node) != select)
1149     {
1150       if (select)
1151         pspp_sheet_view_node_select (selection->tree_view, node);
1152       else
1153         pspp_sheet_view_node_unselect (selection->tree_view, node);
1154
1155       _pspp_sheet_view_queue_draw_node (selection->tree_view, node, NULL);
1156       
1157       return TRUE;
1158     }
1159
1160   return FALSE;
1161 }
1162
1163 void
1164 pspp_sheet_selection_unselect_all_columns (PsppSheetSelection *selection)
1165 {
1166   PsppSheetView *sheet_view = selection->tree_view;
1167   gboolean changed;
1168   GList *list;
1169
1170   changed = FALSE;
1171   for (list = sheet_view->priv->columns; list; list = list->next)
1172     {
1173       PsppSheetViewColumn *column = list->data;
1174       if (column->selected)
1175         {
1176           column->selected = FALSE;
1177           changed = TRUE;
1178         }
1179     }
1180   if (changed && selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
1181     {
1182       gtk_widget_queue_draw (GTK_WIDGET (selection->tree_view));
1183       _pspp_sheet_selection_emit_changed (selection);
1184     }
1185 }
1186
1187 GList *
1188 pspp_sheet_selection_get_selected_columns (PsppSheetSelection *selection)
1189 {
1190   PsppSheetView *sheet_view = selection->tree_view;
1191   GList *selected_columns = NULL;
1192   GList *iter;
1193
1194   g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), NULL);
1195   g_return_val_if_fail (selection->tree_view != NULL, NULL);
1196
1197   if (selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
1198     return NULL;
1199
1200   for (iter = sheet_view->priv->columns; iter; iter = iter->next)
1201     {
1202       PsppSheetViewColumn *column = iter->data;
1203       if (column->selected)
1204         selected_columns = g_list_prepend (selected_columns, column);
1205     }
1206   return g_list_reverse (selected_columns);
1207 }
1208
1209 gint
1210 pspp_sheet_selection_count_selected_columns (PsppSheetSelection *selection)
1211 {
1212   PsppSheetView *sheet_view = selection->tree_view;
1213   GList *list;
1214   gint n;
1215
1216   n = 0;
1217   for (list = sheet_view->priv->columns; list; list = list->next)
1218     {
1219       PsppSheetViewColumn *column = list->data;
1220       if (column->selected)
1221         n++;
1222     }
1223   return n;
1224 }
1225
1226 void
1227 pspp_sheet_selection_select_all_columns (PsppSheetSelection *selection)
1228 {
1229   PsppSheetView *sheet_view = selection->tree_view;
1230   gboolean changed;
1231   GList *list;
1232
1233   changed = FALSE;
1234   for (list = sheet_view->priv->columns; list; list = list->next)
1235     {
1236       PsppSheetViewColumn *column = list->data;
1237       if (!column->selected && column->selectable)
1238         {
1239           /* XXX should use pspp_sheet_view_column_set_selected() here (and
1240              elsewhere) but we want to call
1241              _pspp_sheet_selection_emit_changed() only once for all the
1242              columns. */
1243           column->selected = TRUE;
1244           changed = TRUE;
1245         }
1246     }
1247   if (changed && selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
1248     {
1249       _pspp_sheet_selection_emit_changed (selection);
1250       gtk_widget_queue_draw (GTK_WIDGET (selection->tree_view));
1251     }
1252 }
1253
1254 void
1255 pspp_sheet_selection_select_column (PsppSheetSelection        *selection,
1256                                     PsppSheetViewColumn       *column)
1257 {
1258   if (!column->selected && column->selectable)
1259     {
1260       column->selected = TRUE;
1261       if (selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
1262         {
1263           _pspp_sheet_selection_emit_changed (selection);
1264           gtk_widget_queue_draw (GTK_WIDGET (selection->tree_view));
1265         }
1266     }
1267 }
1268
1269 void
1270 pspp_sheet_selection_select_column_range  (PsppSheetSelection        *selection,
1271                                            PsppSheetViewColumn       *first,
1272                                            PsppSheetViewColumn       *last)
1273 {
1274   PsppSheetView *sheet_view = selection->tree_view;
1275   gboolean in_range;
1276   gboolean changed;
1277   GList *list;
1278
1279   in_range = FALSE;
1280   changed = FALSE;
1281   for (list = sheet_view->priv->columns; list; list = list->next)
1282     {
1283       PsppSheetViewColumn *column = list->data;
1284       gboolean c0 = column == first;
1285       gboolean c1 = column == last;
1286
1287       if (in_range || c0 || c1)
1288         {
1289           if (!column->selected && column->selectable)
1290             {
1291               column->selected = TRUE;
1292               changed = TRUE;
1293             }
1294         }
1295
1296       in_range = in_range ^ c0 ^ c1;
1297     }
1298   if (changed && selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
1299     {
1300       _pspp_sheet_selection_emit_changed (selection);
1301       gtk_widget_queue_draw (GTK_WIDGET (selection->tree_view));
1302     }
1303 }