1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2011, 2012 Free Software Foundation, Inc.
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.
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.
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/>. */
18 * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
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.
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.
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.
38 #include "ui/gui/pspp-sheet-private.h"
43 #include "ui/gui/pspp-sheet-selection.h"
45 #include "libpspp/range-set.h"
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,
60 static guint tree_selection_signals [LAST_SIGNAL] = { 0 };
62 G_DEFINE_TYPE (PsppSheetSelection, pspp_sheet_selection, G_TYPE_OBJECT)
65 pspp_sheet_selection_class_init (PsppSheetSelectionClass *class)
67 GObjectClass *object_class;
69 object_class = (GObjectClass*) class;
71 object_class->finalize = pspp_sheet_selection_finalize;
72 class->changed = NULL;
74 tree_selection_signals[CHANGED] =
75 g_signal_new ("changed",
76 G_OBJECT_CLASS_TYPE (object_class),
78 G_STRUCT_OFFSET (PsppSheetSelectionClass, changed),
80 g_cclosure_marshal_VOID__VOID,
85 pspp_sheet_selection_init (PsppSheetSelection *selection)
87 selection->type = PSPP_SHEET_SELECTION_SINGLE;
91 pspp_sheet_selection_finalize (GObject *object)
93 G_OBJECT_CLASS (pspp_sheet_selection_parent_class)->finalize (object);
97 * _pspp_sheet_selection_new:
99 * Creates a new #PsppSheetSelection object. This function should not be invoked,
100 * as each #PsppSheetView will create its own #PsppSheetSelection.
102 * Return value: A newly created #PsppSheetSelection object.
105 _pspp_sheet_selection_new (void)
107 PsppSheetSelection *selection;
109 selection = g_object_new (PSPP_TYPE_SHEET_SELECTION, NULL);
115 * _pspp_sheet_selection_new_with_tree_view:
116 * @tree_view: The #PsppSheetView.
118 * Creates a new #PsppSheetSelection object. This function should not be invoked,
119 * as each #PsppSheetView will create its own #PsppSheetSelection.
121 * Return value: A newly created #PsppSheetSelection object.
124 _pspp_sheet_selection_new_with_tree_view (PsppSheetView *tree_view)
126 PsppSheetSelection *selection;
128 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
130 selection = _pspp_sheet_selection_new ();
131 _pspp_sheet_selection_set_tree_view (selection, tree_view);
137 * _pspp_sheet_selection_set_tree_view:
138 * @selection: A #PsppSheetSelection.
139 * @tree_view: The #PsppSheetView.
141 * Sets the #PsppSheetView of @selection. This function should not be invoked, as
142 * it is used internally by #PsppSheetView.
145 _pspp_sheet_selection_set_tree_view (PsppSheetSelection *selection,
146 PsppSheetView *tree_view)
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));
152 selection->tree_view = tree_view;
156 * pspp_sheet_selection_set_mode:
157 * @selection: A #PsppSheetSelection.
158 * @type: The selection mode
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.
165 pspp_sheet_selection_set_mode (PsppSheetSelection *selection,
166 PsppSheetSelectionMode type)
168 g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
170 if (selection->type == type)
173 if (type == PSPP_SHEET_SELECTION_NONE)
175 pspp_sheet_selection_unselect_all (selection);
177 gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
178 selection->tree_view->priv->anchor = NULL;
180 else if (type == PSPP_SHEET_SELECTION_SINGLE ||
181 type == PSPP_SHEET_SELECTION_BROWSE)
184 gint selected = FALSE;
185 GtkTreePath *anchor_path = NULL;
187 if (selection->tree_view->priv->anchor)
189 anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor);
193 _pspp_sheet_view_find_node (selection->tree_view,
197 if (node >= 0 && pspp_sheet_view_node_is_selected (selection->tree_view, node))
202 /* We do this so that we unconditionally unset all rows
204 pspp_sheet_selection_unselect_all (selection);
206 if (node >= 0 && selected)
207 _pspp_sheet_selection_internal_select_node (selection,
213 gtk_tree_path_free (anchor_path);
216 /* XXX unselect all columns when switching to/from rectangular selection? */
218 selection->type = type;
222 * pspp_sheet_selection_get_mode:
223 * @selection: a #PsppSheetSelection
225 * Gets the selection mode for @selection. See
226 * pspp_sheet_selection_set_mode().
228 * Return value: the current selection mode
230 PsppSheetSelectionMode
231 pspp_sheet_selection_get_mode (PsppSheetSelection *selection)
233 g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), PSPP_SHEET_SELECTION_SINGLE);
235 return selection->type;
239 * pspp_sheet_selection_get_tree_view:
240 * @selection: A #PsppSheetSelection
242 * Returns the tree view associated with @selection.
244 * Return value: A #PsppSheetView
247 pspp_sheet_selection_get_tree_view (PsppSheetSelection *selection)
249 g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), NULL);
251 return selection->tree_view;
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.
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.
267 * Return value: TRUE, if there is a selected node.
270 pspp_sheet_selection_get_selected (PsppSheetSelection *selection,
271 GtkTreeModel **model,
275 GtkTreePath *anchor_path;
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,
282 g_return_val_if_fail (selection->tree_view != NULL, FALSE);
286 memset (iter, 0, sizeof (GtkTreeIter));
289 *model = selection->tree_view->priv->model;
291 if (selection->tree_view->priv->anchor == NULL)
294 anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor);
296 if (anchor_path == NULL)
301 _pspp_sheet_view_find_node (selection->tree_view,
305 if (pspp_sheet_view_node_is_selected (selection->tree_view, node))
307 /* we only want to return the anchor if it exists in the rbtree and
313 retval = gtk_tree_model_get_iter (selection->tree_view->priv->model,
319 /* We don't want to return the anchor if it isn't actually selected.
324 gtk_tree_path_free (anchor_path);
330 * pspp_sheet_selection_get_selected_rows:
331 * @selection: A #PsppSheetSelection.
332 * @model: (allow-none): A pointer to set to the #GtkTreeModel, or NULL.
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().
339 * To free the return value, use:
341 * g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
342 * g_list_free (list);
345 * Return value: (element-type GtkTreePath) (transfer full): A #GList containing a #GtkTreePath for each selected row.
350 pspp_sheet_selection_get_selected_rows (PsppSheetSelection *selection,
351 GtkTreeModel **model)
353 const struct range_tower_node *node;
354 unsigned long int start;
357 g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), NULL);
358 g_return_val_if_fail (selection->tree_view != NULL, NULL);
361 *model = selection->tree_view->priv->model;
363 if (selection->tree_view->priv->row_count == 0)
366 if (selection->type == PSPP_SHEET_SELECTION_NONE)
368 else if (selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
369 selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
373 if (pspp_sheet_selection_get_selected (selection, NULL, &iter))
377 path = gtk_tree_model_get_path (selection->tree_view->priv->model, &iter);
378 list = g_list_append (list, path);
386 RANGE_TOWER_FOR_EACH (node, start, selection->tree_view->priv->selected)
388 unsigned long int width = range_tower_node_get_width (node);
389 unsigned long int index;
391 for (index = start; index < start + width; index++)
392 list = g_list_prepend (list, gtk_tree_path_new_from_indices (index, -1));
395 return g_list_reverse (list);
399 * pspp_sheet_selection_count_selected_rows:
400 * @selection: A #PsppSheetSelection.
402 * Returns the number of rows that have been selected in @tree.
404 * Return value: The number of rows selected.
409 pspp_sheet_selection_count_selected_rows (PsppSheetSelection *selection)
411 const struct range_tower_node *node;
412 unsigned long int start;
415 g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), 0);
416 g_return_val_if_fail (selection->tree_view != NULL, 0);
418 if (selection->tree_view->priv->row_count == 0)
421 if (selection->type == PSPP_SHEET_SELECTION_SINGLE ||
422 selection->type == PSPP_SHEET_SELECTION_BROWSE)
424 if (pspp_sheet_selection_get_selected (selection, NULL, NULL))
431 RANGE_TOWER_FOR_EACH (node, start, selection->tree_view->priv->selected)
432 count += range_tower_node_get_width (node);
437 /* pspp_sheet_selection_selected_foreach helper */
439 model_changed (gpointer data)
441 gboolean *stop = (gboolean *)data;
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.
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.
457 pspp_sheet_selection_selected_foreach (PsppSheetSelection *selection,
458 PsppSheetSelectionForeachFunc func,
461 const struct range_tower_node *node;
462 unsigned long int start;
467 gulong inserted_id, deleted_id, reordered_id, changed_id;
468 gboolean stop = FALSE;
470 g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
471 g_return_if_fail (selection->tree_view != NULL);
474 selection->tree_view->priv->row_count == 0)
477 if (selection->type == PSPP_SHEET_SELECTION_SINGLE ||
478 selection->type == PSPP_SHEET_SELECTION_BROWSE)
480 if (gtk_tree_row_reference_valid (selection->tree_view->priv->anchor))
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);
490 model = selection->tree_view->priv->model;
491 g_object_ref (model);
493 /* connect to signals to monitor changes in treemodel */
494 inserted_id = g_signal_connect_swapped (model, "row-inserted",
495 G_CALLBACK (model_changed),
497 deleted_id = g_signal_connect_swapped (model, "row-deleted",
498 G_CALLBACK (model_changed),
500 reordered_id = g_signal_connect_swapped (model, "rows-reordered",
501 G_CALLBACK (model_changed),
503 changed_id = g_signal_connect_swapped (selection->tree_view, "notify::model",
504 G_CALLBACK (model_changed),
507 RANGE_TOWER_FOR_EACH (node, start, selection->tree_view->priv->selected)
509 unsigned long int width = range_tower_node_get_width (node);
510 unsigned long int index;
512 for (index = start; index < start + width; index++)
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);
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);
530 /* check if we have to spew a scary message */
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");
539 * pspp_sheet_selection_select_path:
540 * @selection: A #PsppSheetSelection.
541 * @path: The #GtkTreePath to be selected.
543 * Select the row at @path.
546 pspp_sheet_selection_select_path (PsppSheetSelection *selection,
550 GtkTreeSelectMode mode = 0;
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);
556 _pspp_sheet_view_find_node (selection->tree_view,
560 if (node < 0 || pspp_sheet_view_node_is_selected (selection->tree_view, node))
563 if (selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
564 selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
565 mode = GTK_TREE_SELECT_MODE_TOGGLE;
567 _pspp_sheet_selection_internal_select_node (selection,
575 * pspp_sheet_selection_unselect_path:
576 * @selection: A #PsppSheetSelection.
577 * @path: The #GtkTreePath to be unselected.
579 * Unselects the row at @path.
582 pspp_sheet_selection_unselect_path (PsppSheetSelection *selection,
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);
591 _pspp_sheet_view_find_node (selection->tree_view,
595 if (node < 0 || !pspp_sheet_view_node_is_selected (selection->tree_view, node))
598 _pspp_sheet_selection_internal_select_node (selection,
601 GTK_TREE_SELECT_MODE_TOGGLE,
606 * pspp_sheet_selection_select_iter:
607 * @selection: A #PsppSheetSelection.
608 * @iter: The #GtkTreeIter to be selected.
610 * Selects the specified iterator.
613 pspp_sheet_selection_select_iter (PsppSheetSelection *selection,
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);
623 path = gtk_tree_model_get_path (selection->tree_view->priv->model,
629 pspp_sheet_selection_select_path (selection, path);
630 gtk_tree_path_free (path);
635 * pspp_sheet_selection_unselect_iter:
636 * @selection: A #PsppSheetSelection.
637 * @iter: The #GtkTreeIter to be unselected.
639 * Unselects the specified iterator.
642 pspp_sheet_selection_unselect_iter (PsppSheetSelection *selection,
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);
652 path = gtk_tree_model_get_path (selection->tree_view->priv->model,
658 pspp_sheet_selection_unselect_path (selection, path);
659 gtk_tree_path_free (path);
663 * pspp_sheet_selection_path_is_selected:
664 * @selection: A #PsppSheetSelection.
665 * @path: A #GtkTreePath to check selection on.
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
670 * Return value: %TRUE if @path is selected.
673 pspp_sheet_selection_path_is_selected (PsppSheetSelection *selection,
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);
682 if (selection->tree_view->priv->model == NULL)
685 _pspp_sheet_view_find_node (selection->tree_view,
689 if (node < 0 || !pspp_sheet_view_node_is_selected (selection->tree_view, node))
696 * pspp_sheet_selection_iter_is_selected:
697 * @selection: A #PsppSheetSelection
698 * @iter: A valid #GtkTreeIter
700 * Returns %TRUE if the row at @iter is currently selected.
702 * Return value: %TRUE, if @iter is selected
705 pspp_sheet_selection_iter_is_selected (PsppSheetSelection *selection,
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);
716 path = gtk_tree_model_get_path (selection->tree_view->priv->model, iter);
720 retval = pspp_sheet_selection_path_is_selected (selection, path);
721 gtk_tree_path_free (path);
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.
731 pspp_sheet_selection_real_select_all (PsppSheetSelection *selection)
733 const struct range_tower_node *node;
734 int row_count = selection->tree_view->priv->row_count;
739 node = range_tower_first (selection->tree_view->priv->selected);
741 && range_tower_node_get_start (node) == 0
742 && range_tower_node_get_width (node) >= row_count)
745 range_tower_set1 (selection->tree_view->priv->selected, 0, row_count);
746 pspp_sheet_selection_select_all_columns (selection);
748 /* XXX we could invalidate individual visible rows instead */
749 gdk_window_invalidate_rect (selection->tree_view->priv->bin_window, NULL, TRUE);
755 * pspp_sheet_selection_select_all:
756 * @selection: A #PsppSheetSelection.
758 * Selects all the nodes and column. @selection must be set to
759 * #PSPP_SHEET_SELECTION_MULTIPLE or #PSPP_SHEET_SELECTION_RECTANGLE mode.
762 pspp_sheet_selection_select_all (PsppSheetSelection *selection)
764 g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
765 g_return_if_fail (selection->tree_view != NULL);
767 if (selection->tree_view->priv->row_count == 0 || selection->tree_view->priv->model == NULL)
770 g_return_if_fail (selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
771 selection->type == PSPP_SHEET_SELECTION_RECTANGLE);
773 if (pspp_sheet_selection_real_select_all (selection))
774 g_signal_emit (selection, tree_selection_signals[CHANGED], 0);
778 pspp_sheet_selection_real_unselect_all (PsppSheetSelection *selection)
780 if (selection->type == PSPP_SHEET_SELECTION_SINGLE ||
781 selection->type == PSPP_SHEET_SELECTION_BROWSE)
784 GtkTreePath *anchor_path;
786 if (selection->tree_view->priv->anchor == NULL)
789 anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor);
791 if (anchor_path == NULL)
794 _pspp_sheet_view_find_node (selection->tree_view,
798 gtk_tree_path_free (anchor_path);
803 if (pspp_sheet_view_node_is_selected (selection->tree_view, node))
805 if (pspp_sheet_selection_real_select_node (selection, node, FALSE))
807 gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
808 selection->tree_view->priv->anchor = NULL;
814 else if (range_tower_is_empty (selection->tree_view->priv->selected))
818 range_tower_set0 (selection->tree_view->priv->selected, 0, ULONG_MAX);
819 pspp_sheet_selection_unselect_all_columns (selection);
821 /* XXX we could invalidate individual visible rows instead */
822 gdk_window_invalidate_rect (selection->tree_view->priv->bin_window, NULL, TRUE);
829 * pspp_sheet_selection_unselect_all:
830 * @selection: A #PsppSheetSelection.
832 * Unselects all the nodes and columns.
835 pspp_sheet_selection_unselect_all (PsppSheetSelection *selection)
837 g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
838 g_return_if_fail (selection->tree_view != NULL);
840 if (selection->tree_view->priv->row_count == 0 || selection->tree_view->priv->model == NULL)
843 if (pspp_sheet_selection_real_unselect_all (selection))
844 g_signal_emit (selection, tree_selection_signals[CHANGED], 0);
854 pspp_sheet_selection_real_modify_range (PsppSheetSelection *selection,
856 GtkTreePath *start_path,
857 GtkTreePath *end_path)
859 int start_node, end_node;
860 GtkTreePath *anchor_path = NULL;
861 gboolean dirty = FALSE;
863 switch (gtk_tree_path_compare (start_path, end_path))
866 _pspp_sheet_view_find_node (selection->tree_view,
869 _pspp_sheet_view_find_node (selection->tree_view,
872 anchor_path = start_path;
875 _pspp_sheet_view_find_node (selection->tree_view,
878 end_node = start_node;
879 anchor_path = start_path;
882 _pspp_sheet_view_find_node (selection->tree_view,
885 _pspp_sheet_view_find_node (selection->tree_view,
888 anchor_path = start_path;
892 g_return_val_if_fail (start_node >= 0, FALSE);
893 g_return_val_if_fail (end_node >= 0, FALSE);
897 if (selection->tree_view->priv->anchor)
898 gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
900 selection->tree_view->priv->anchor =
901 gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view),
902 selection->tree_view->priv->model,
908 dirty |= pspp_sheet_selection_real_select_node (selection, start_node, (mode == RANGE_SELECT)?TRUE:FALSE);
910 if (start_node == end_node)
913 start_node = pspp_sheet_view_node_next (selection->tree_view, start_node);
916 /* we just ran out of tree. That means someone passed in bogus values.
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.
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.
937 pspp_sheet_selection_select_range (PsppSheetSelection *selection,
938 GtkTreePath *start_path,
939 GtkTreePath *end_path)
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);
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);
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.
957 * Unselects a range of nodes, determined by @start_path and @end_path
963 pspp_sheet_selection_unselect_range (PsppSheetSelection *selection,
964 GtkTreePath *start_path,
965 GtkTreePath *end_path)
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);
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);
976 pspp_sheet_selection_get_range_set (PsppSheetSelection *selection)
978 const struct range_tower_node *node;
979 unsigned long int start;
980 struct range_set *set;
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 ());
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));
992 /* Called internally by gtktreeview.c It handles actually selecting the tree.
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').
1001 _pspp_sheet_selection_internal_select_node (PsppSheetSelection *selection,
1004 GtkTreeSelectMode mode,
1005 gboolean override_browse_mode)
1008 GtkTreePath *anchor_path = NULL;
1010 if (selection->type == PSPP_SHEET_SELECTION_NONE)
1013 if (selection->tree_view->priv->anchor)
1014 anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor);
1016 if (selection->type == PSPP_SHEET_SELECTION_SINGLE ||
1017 selection->type == PSPP_SHEET_SELECTION_BROWSE)
1020 if (selection->type == PSPP_SHEET_SELECTION_BROWSE && override_browse_mode)
1022 dirty = pspp_sheet_selection_real_unselect_all (selection);
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)
1028 if ((mode & GTK_TREE_SELECT_MODE_TOGGLE) == GTK_TREE_SELECT_MODE_TOGGLE)
1030 dirty = pspp_sheet_selection_real_unselect_all (selection);
1037 dirty = pspp_sheet_selection_real_unselect_all (selection);
1039 /* if dirty is TRUE at this point, we successfully unselected the
1040 * old one, and can then select the new one */
1043 if (selection->tree_view->priv->anchor)
1045 gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
1046 selection->tree_view->priv->anchor = NULL;
1049 if (pspp_sheet_selection_real_select_node (selection, node, TRUE))
1051 selection->tree_view->priv->anchor =
1052 gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path);
1058 if (pspp_sheet_selection_real_select_node (selection, node, TRUE))
1061 if (selection->tree_view->priv->anchor)
1062 gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
1064 selection->tree_view->priv->anchor =
1065 gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path);
1070 else if (selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
1071 selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
1073 if ((mode & GTK_TREE_SELECT_MODE_EXTEND) == GTK_TREE_SELECT_MODE_EXTEND
1074 && (anchor_path == NULL))
1076 if (selection->tree_view->priv->anchor)
1077 gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
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);
1083 else if ((mode & (GTK_TREE_SELECT_MODE_EXTEND | GTK_TREE_SELECT_MODE_TOGGLE)) == (GTK_TREE_SELECT_MODE_EXTEND | GTK_TREE_SELECT_MODE_TOGGLE))
1085 pspp_sheet_selection_select_range (selection,
1089 else if ((mode & GTK_TREE_SELECT_MODE_TOGGLE) == GTK_TREE_SELECT_MODE_TOGGLE)
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);
1095 selection->tree_view->priv->anchor =
1096 gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path);
1099 dirty |= pspp_sheet_selection_real_select_node (selection, node, FALSE);
1101 dirty |= pspp_sheet_selection_real_select_node (selection, node, TRUE);
1103 else if ((mode & GTK_TREE_SELECT_MODE_EXTEND) == GTK_TREE_SELECT_MODE_EXTEND)
1105 dirty = pspp_sheet_selection_real_unselect_all (selection);
1106 dirty |= pspp_sheet_selection_real_modify_range (selection,
1113 dirty = pspp_sheet_selection_real_unselect_all (selection);
1115 if (selection->tree_view->priv->anchor)
1116 gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
1118 selection->tree_view->priv->anchor =
1119 gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path);
1121 dirty |= pspp_sheet_selection_real_select_node (selection, node, TRUE);
1126 gtk_tree_path_free (anchor_path);
1129 g_signal_emit (selection, tree_selection_signals[CHANGED], 0);
1134 _pspp_sheet_selection_emit_changed (PsppSheetSelection *selection)
1136 g_signal_emit (selection, tree_selection_signals[CHANGED], 0);
1139 /* NOTE: Any {un,}selection ever done _MUST_ be done through this function!
1143 pspp_sheet_selection_real_select_node (PsppSheetSelection *selection,
1148 if (pspp_sheet_view_node_is_selected (selection->tree_view, node) != select)
1151 pspp_sheet_view_node_select (selection->tree_view, node);
1153 pspp_sheet_view_node_unselect (selection->tree_view, node);
1155 _pspp_sheet_view_queue_draw_node (selection->tree_view, node, NULL);
1164 pspp_sheet_selection_unselect_all_columns (PsppSheetSelection *selection)
1166 PsppSheetView *sheet_view = selection->tree_view;
1171 for (list = sheet_view->priv->columns; list; list = list->next)
1173 PsppSheetViewColumn *column = list->data;
1174 if (column->selected)
1176 column->selected = FALSE;
1180 if (changed && selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
1182 gtk_widget_queue_draw (GTK_WIDGET (selection->tree_view));
1183 _pspp_sheet_selection_emit_changed (selection);
1188 pspp_sheet_selection_get_selected_columns (PsppSheetSelection *selection)
1190 PsppSheetView *sheet_view = selection->tree_view;
1191 GList *selected_columns = NULL;
1194 g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), NULL);
1195 g_return_val_if_fail (selection->tree_view != NULL, NULL);
1197 if (selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
1200 for (iter = sheet_view->priv->columns; iter; iter = iter->next)
1202 PsppSheetViewColumn *column = iter->data;
1203 if (column->selected)
1204 selected_columns = g_list_prepend (selected_columns, column);
1206 return g_list_reverse (selected_columns);
1210 pspp_sheet_selection_count_selected_columns (PsppSheetSelection *selection)
1212 PsppSheetView *sheet_view = selection->tree_view;
1217 for (list = sheet_view->priv->columns; list; list = list->next)
1219 PsppSheetViewColumn *column = list->data;
1220 if (column->selected)
1227 pspp_sheet_selection_select_all_columns (PsppSheetSelection *selection)
1229 PsppSheetView *sheet_view = selection->tree_view;
1234 for (list = sheet_view->priv->columns; list; list = list->next)
1236 PsppSheetViewColumn *column = list->data;
1237 if (!column->selected && column->selectable)
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
1243 column->selected = TRUE;
1247 if (changed && selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
1249 _pspp_sheet_selection_emit_changed (selection);
1250 gtk_widget_queue_draw (GTK_WIDGET (selection->tree_view));
1255 pspp_sheet_selection_select_column (PsppSheetSelection *selection,
1256 PsppSheetViewColumn *column)
1258 if (!column->selected && column->selectable)
1260 column->selected = TRUE;
1261 if (selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
1263 _pspp_sheet_selection_emit_changed (selection);
1264 gtk_widget_queue_draw (GTK_WIDGET (selection->tree_view));
1270 pspp_sheet_selection_select_column_range (PsppSheetSelection *selection,
1271 PsppSheetViewColumn *first,
1272 PsppSheetViewColumn *last)
1274 PsppSheetView *sheet_view = selection->tree_view;
1281 for (list = sheet_view->priv->columns; list; list = list->next)
1283 PsppSheetViewColumn *column = list->data;
1284 gboolean c0 = column == first;
1285 gboolean c1 = column == last;
1287 if (in_range || c0 || c1)
1289 if (!column->selected && column->selectable)
1291 column->selected = TRUE;
1296 in_range = in_range ^ c0 ^ c1;
1298 if (changed && selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
1300 _pspp_sheet_selection_emit_changed (selection);
1301 gtk_widget_queue_draw (GTK_WIDGET (selection->tree_view));