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 = GTK_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 * #GTK_SELECTION_MULTIPLE, then the anchor is kept selected, if it was
162 * previously selected.
165 pspp_sheet_selection_set_mode (PsppSheetSelection *selection,
166 GtkSelectionMode type)
168 g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
170 if (selection->type == type)
174 if (type == GTK_SELECTION_NONE)
176 pspp_sheet_selection_unselect_all (selection);
178 gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
179 selection->tree_view->priv->anchor = NULL;
181 else if (type == GTK_SELECTION_SINGLE ||
182 type == GTK_SELECTION_BROWSE)
185 gint selected = FALSE;
186 GtkTreePath *anchor_path = NULL;
188 if (selection->tree_view->priv->anchor)
190 anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor);
194 _pspp_sheet_view_find_node (selection->tree_view,
198 if (node >= 0 && pspp_sheet_view_node_is_selected (selection->tree_view, node))
203 /* We do this so that we unconditionally unset all rows
205 pspp_sheet_selection_unselect_all (selection);
207 if (node >= 0 && selected)
208 _pspp_sheet_selection_internal_select_node (selection,
214 gtk_tree_path_free (anchor_path);
217 selection->type = type;
221 * pspp_sheet_selection_get_mode:
222 * @selection: a #PsppSheetSelection
224 * Gets the selection mode for @selection. See
225 * pspp_sheet_selection_set_mode().
227 * Return value: the current selection mode
230 pspp_sheet_selection_get_mode (PsppSheetSelection *selection)
232 g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), GTK_SELECTION_SINGLE);
234 return selection->type;
238 * pspp_sheet_selection_get_tree_view:
239 * @selection: A #PsppSheetSelection
241 * Returns the tree view associated with @selection.
243 * Return value: A #PsppSheetView
246 pspp_sheet_selection_get_tree_view (PsppSheetSelection *selection)
248 g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), NULL);
250 return selection->tree_view;
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.
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.
265 * Return value: TRUE, if there is a selected node.
268 pspp_sheet_selection_get_selected (PsppSheetSelection *selection,
269 GtkTreeModel **model,
273 GtkTreePath *anchor_path;
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);
282 memset (iter, 0, sizeof (GtkTreeIter));
285 *model = selection->tree_view->priv->model;
287 if (selection->tree_view->priv->anchor == NULL)
290 anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor);
292 if (anchor_path == NULL)
297 _pspp_sheet_view_find_node (selection->tree_view,
301 if (pspp_sheet_view_node_is_selected (selection->tree_view, node))
303 /* we only want to return the anchor if it exists in the rbtree and
309 retval = gtk_tree_model_get_iter (selection->tree_view->priv->model,
315 /* We don't want to return the anchor if it isn't actually selected.
320 gtk_tree_path_free (anchor_path);
326 * pspp_sheet_selection_get_selected_rows:
327 * @selection: A #PsppSheetSelection.
328 * @model: (allow-none): A pointer to set to the #GtkTreeModel, or NULL.
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().
335 * To free the return value, use:
337 * g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
338 * g_list_free (list);
341 * Return value: (element-type GtkTreePath) (transfer full): A #GList containing a #GtkTreePath for each selected row.
346 pspp_sheet_selection_get_selected_rows (PsppSheetSelection *selection,
347 GtkTreeModel **model)
349 const struct range_tower_node *node;
350 unsigned long int start;
353 g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), NULL);
354 g_return_val_if_fail (selection->tree_view != NULL, NULL);
357 *model = selection->tree_view->priv->model;
359 if (selection->tree_view->priv->row_count == 0)
362 if (selection->type == GTK_SELECTION_NONE)
364 else if (selection->type != GTK_SELECTION_MULTIPLE)
368 if (pspp_sheet_selection_get_selected (selection, NULL, &iter))
372 path = gtk_tree_model_get_path (selection->tree_view->priv->model, &iter);
373 list = g_list_append (list, path);
381 RANGE_TOWER_FOR_EACH (node, start, selection->tree_view->priv->selected)
383 unsigned long int width = range_tower_node_get_width (node);
384 unsigned long int index;
386 for (index = start; index < start + width; index++)
387 list = g_list_prepend (list, gtk_tree_path_new_from_indices (index, -1));
390 return g_list_reverse (list);
394 * pspp_sheet_selection_count_selected_rows:
395 * @selection: A #PsppSheetSelection.
397 * Returns the number of rows that have been selected in @tree.
399 * Return value: The number of rows selected.
404 pspp_sheet_selection_count_selected_rows (PsppSheetSelection *selection)
406 const struct range_tower_node *node;
407 unsigned long int start;
410 g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), 0);
411 g_return_val_if_fail (selection->tree_view != NULL, 0);
413 if (selection->tree_view->priv->row_count == 0)
416 if (selection->type == GTK_SELECTION_SINGLE ||
417 selection->type == GTK_SELECTION_BROWSE)
419 if (pspp_sheet_selection_get_selected (selection, NULL, NULL))
426 RANGE_TOWER_FOR_EACH (node, start, selection->tree_view->priv->selected)
427 count += range_tower_node_get_width (node);
432 /* pspp_sheet_selection_selected_foreach helper */
434 model_changed (gpointer data)
436 gboolean *stop = (gboolean *)data;
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.
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.
452 pspp_sheet_selection_selected_foreach (PsppSheetSelection *selection,
453 PsppSheetSelectionForeachFunc func,
456 const struct range_tower_node *node;
457 unsigned long int start;
462 gulong inserted_id, deleted_id, reordered_id, changed_id;
463 gboolean stop = FALSE;
465 g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
466 g_return_if_fail (selection->tree_view != NULL);
469 selection->tree_view->priv->row_count == 0)
472 if (selection->type == GTK_SELECTION_SINGLE ||
473 selection->type == GTK_SELECTION_BROWSE)
475 if (gtk_tree_row_reference_valid (selection->tree_view->priv->anchor))
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);
485 model = selection->tree_view->priv->model;
486 g_object_ref (model);
488 /* connect to signals to monitor changes in treemodel */
489 inserted_id = g_signal_connect_swapped (model, "row-inserted",
490 G_CALLBACK (model_changed),
492 deleted_id = g_signal_connect_swapped (model, "row-deleted",
493 G_CALLBACK (model_changed),
495 reordered_id = g_signal_connect_swapped (model, "rows-reordered",
496 G_CALLBACK (model_changed),
498 changed_id = g_signal_connect_swapped (selection->tree_view, "notify::model",
499 G_CALLBACK (model_changed),
502 RANGE_TOWER_FOR_EACH (node, start, selection->tree_view->priv->selected)
504 unsigned long int width = range_tower_node_get_width (node);
505 unsigned long int index;
507 for (index = start; index < start + width; index++)
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);
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);
525 /* check if we have to spew a scary message */
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");
534 * pspp_sheet_selection_select_path:
535 * @selection: A #PsppSheetSelection.
536 * @path: The #GtkTreePath to be selected.
538 * Select the row at @path.
541 pspp_sheet_selection_select_path (PsppSheetSelection *selection,
545 GtkTreeSelectMode mode = 0;
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);
551 _pspp_sheet_view_find_node (selection->tree_view,
555 if (node < 0 || pspp_sheet_view_node_is_selected (selection->tree_view, node))
558 if (selection->type == GTK_SELECTION_MULTIPLE)
559 mode = GTK_TREE_SELECT_MODE_TOGGLE;
561 _pspp_sheet_selection_internal_select_node (selection,
569 * pspp_sheet_selection_unselect_path:
570 * @selection: A #PsppSheetSelection.
571 * @path: The #GtkTreePath to be unselected.
573 * Unselects the row at @path.
576 pspp_sheet_selection_unselect_path (PsppSheetSelection *selection,
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);
585 _pspp_sheet_view_find_node (selection->tree_view,
589 if (node < 0 || !pspp_sheet_view_node_is_selected (selection->tree_view, node))
592 _pspp_sheet_selection_internal_select_node (selection,
595 GTK_TREE_SELECT_MODE_TOGGLE,
600 * pspp_sheet_selection_select_iter:
601 * @selection: A #PsppSheetSelection.
602 * @iter: The #GtkTreeIter to be selected.
604 * Selects the specified iterator.
607 pspp_sheet_selection_select_iter (PsppSheetSelection *selection,
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);
617 path = gtk_tree_model_get_path (selection->tree_view->priv->model,
623 pspp_sheet_selection_select_path (selection, path);
624 gtk_tree_path_free (path);
629 * pspp_sheet_selection_unselect_iter:
630 * @selection: A #PsppSheetSelection.
631 * @iter: The #GtkTreeIter to be unselected.
633 * Unselects the specified iterator.
636 pspp_sheet_selection_unselect_iter (PsppSheetSelection *selection,
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);
646 path = gtk_tree_model_get_path (selection->tree_view->priv->model,
652 pspp_sheet_selection_unselect_path (selection, path);
653 gtk_tree_path_free (path);
657 * pspp_sheet_selection_path_is_selected:
658 * @selection: A #PsppSheetSelection.
659 * @path: A #GtkTreePath to check selection on.
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
664 * Return value: %TRUE if @path is selected.
667 pspp_sheet_selection_path_is_selected (PsppSheetSelection *selection,
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);
676 if (selection->tree_view->priv->model == NULL)
679 _pspp_sheet_view_find_node (selection->tree_view,
683 if (node < 0 || !pspp_sheet_view_node_is_selected (selection->tree_view, node))
690 * pspp_sheet_selection_iter_is_selected:
691 * @selection: A #PsppSheetSelection
692 * @iter: A valid #GtkTreeIter
694 * Returns %TRUE if the row at @iter is currently selected.
696 * Return value: %TRUE, if @iter is selected
699 pspp_sheet_selection_iter_is_selected (PsppSheetSelection *selection,
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);
710 path = gtk_tree_model_get_path (selection->tree_view->priv->model, iter);
714 retval = pspp_sheet_selection_path_is_selected (selection, path);
715 gtk_tree_path_free (path);
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.
725 pspp_sheet_selection_real_select_all (PsppSheetSelection *selection)
727 const struct range_tower_node *node;
728 int row_count = selection->tree_view->priv->row_count;
733 node = range_tower_first (selection->tree_view->priv->selected);
735 && range_tower_node_get_start (node) == 0
736 && range_tower_node_get_width (node) >= row_count)
739 range_tower_set1 (selection->tree_view->priv->selected, 0, row_count);
741 /* XXX we could invalidate individual visible rows instead */
742 gdk_window_invalidate_rect (selection->tree_view->priv->bin_window, NULL, TRUE);
748 * pspp_sheet_selection_select_all:
749 * @selection: A #PsppSheetSelection.
751 * Selects all the nodes. @selection must be set to #GTK_SELECTION_MULTIPLE
755 pspp_sheet_selection_select_all (PsppSheetSelection *selection)
757 g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
758 g_return_if_fail (selection->tree_view != NULL);
760 if (selection->tree_view->priv->row_count == 0 || selection->tree_view->priv->model == NULL)
763 g_return_if_fail (selection->type == GTK_SELECTION_MULTIPLE);
765 if (pspp_sheet_selection_real_select_all (selection))
766 g_signal_emit (selection, tree_selection_signals[CHANGED], 0);
770 pspp_sheet_selection_real_unselect_all (PsppSheetSelection *selection)
772 if (selection->type == GTK_SELECTION_SINGLE ||
773 selection->type == GTK_SELECTION_BROWSE)
776 GtkTreePath *anchor_path;
778 if (selection->tree_view->priv->anchor == NULL)
781 anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor);
783 if (anchor_path == NULL)
786 _pspp_sheet_view_find_node (selection->tree_view,
790 gtk_tree_path_free (anchor_path);
795 if (pspp_sheet_view_node_is_selected (selection->tree_view, node))
797 if (pspp_sheet_selection_real_select_node (selection, node, FALSE))
799 gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
800 selection->tree_view->priv->anchor = NULL;
806 else if (range_tower_is_empty (selection->tree_view->priv->selected))
810 range_tower_set0 (selection->tree_view->priv->selected, 0, ULONG_MAX);
812 /* XXX we could invalidate individual visible rows instead */
813 gdk_window_invalidate_rect (selection->tree_view->priv->bin_window, NULL, TRUE);
820 * pspp_sheet_selection_unselect_all:
821 * @selection: A #PsppSheetSelection.
823 * Unselects all the nodes.
826 pspp_sheet_selection_unselect_all (PsppSheetSelection *selection)
828 g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
829 g_return_if_fail (selection->tree_view != NULL);
831 if (selection->tree_view->priv->row_count == 0 || selection->tree_view->priv->model == NULL)
834 if (pspp_sheet_selection_real_unselect_all (selection))
835 g_signal_emit (selection, tree_selection_signals[CHANGED], 0);
845 pspp_sheet_selection_real_modify_range (PsppSheetSelection *selection,
847 GtkTreePath *start_path,
848 GtkTreePath *end_path)
850 int start_node, end_node;
851 GtkTreePath *anchor_path = NULL;
852 gboolean dirty = FALSE;
854 switch (gtk_tree_path_compare (start_path, end_path))
857 _pspp_sheet_view_find_node (selection->tree_view,
860 _pspp_sheet_view_find_node (selection->tree_view,
863 anchor_path = start_path;
866 _pspp_sheet_view_find_node (selection->tree_view,
869 end_node = start_node;
870 anchor_path = start_path;
873 _pspp_sheet_view_find_node (selection->tree_view,
876 _pspp_sheet_view_find_node (selection->tree_view,
879 anchor_path = start_path;
883 g_return_val_if_fail (start_node >= 0, FALSE);
884 g_return_val_if_fail (end_node >= 0, FALSE);
888 if (selection->tree_view->priv->anchor)
889 gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
891 selection->tree_view->priv->anchor =
892 gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view),
893 selection->tree_view->priv->model,
899 dirty |= pspp_sheet_selection_real_select_node (selection, start_node, (mode == RANGE_SELECT)?TRUE:FALSE);
901 if (start_node == end_node)
904 start_node = pspp_sheet_view_node_next (selection->tree_view, start_node);
907 /* we just ran out of tree. That means someone passed in bogus values.
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.
923 * Selects a range of nodes, determined by @start_path and @end_path inclusive.
924 * @selection must be set to #GTK_SELECTION_MULTIPLE mode.
927 pspp_sheet_selection_select_range (PsppSheetSelection *selection,
928 GtkTreePath *start_path,
929 GtkTreePath *end_path)
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);
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);
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.
946 * Unselects a range of nodes, determined by @start_path and @end_path
952 pspp_sheet_selection_unselect_range (PsppSheetSelection *selection,
953 GtkTreePath *start_path,
954 GtkTreePath *end_path)
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);
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);
965 pspp_sheet_selection_get_range_set (PsppSheetSelection *selection)
967 const struct range_tower_node *node;
968 unsigned long int start;
969 struct range_set *set;
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 ());
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));
981 /* Called internally by gtktreeview.c It handles actually selecting the tree.
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').
990 _pspp_sheet_selection_internal_select_node (PsppSheetSelection *selection,
993 GtkTreeSelectMode mode,
994 gboolean override_browse_mode)
997 GtkTreePath *anchor_path = NULL;
999 if (selection->type == GTK_SELECTION_NONE)
1002 if (selection->tree_view->priv->anchor)
1003 anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor);
1005 if (selection->type == GTK_SELECTION_SINGLE ||
1006 selection->type == GTK_SELECTION_BROWSE)
1009 if (selection->type == GTK_SELECTION_BROWSE && override_browse_mode)
1011 dirty = pspp_sheet_selection_real_unselect_all (selection);
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)
1017 if ((mode & GTK_TREE_SELECT_MODE_TOGGLE) == GTK_TREE_SELECT_MODE_TOGGLE)
1019 dirty = pspp_sheet_selection_real_unselect_all (selection);
1026 dirty = pspp_sheet_selection_real_unselect_all (selection);
1028 /* if dirty is TRUE at this point, we successfully unselected the
1029 * old one, and can then select the new one */
1032 if (selection->tree_view->priv->anchor)
1034 gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
1035 selection->tree_view->priv->anchor = NULL;
1038 if (pspp_sheet_selection_real_select_node (selection, node, TRUE))
1040 selection->tree_view->priv->anchor =
1041 gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path);
1047 if (pspp_sheet_selection_real_select_node (selection, node, TRUE))
1050 if (selection->tree_view->priv->anchor)
1051 gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
1053 selection->tree_view->priv->anchor =
1054 gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path);
1059 else if (selection->type == GTK_SELECTION_MULTIPLE)
1061 if ((mode & GTK_TREE_SELECT_MODE_EXTEND) == GTK_TREE_SELECT_MODE_EXTEND
1062 && (anchor_path == NULL))
1064 if (selection->tree_view->priv->anchor)
1065 gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
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);
1071 else if ((mode & (GTK_TREE_SELECT_MODE_EXTEND | GTK_TREE_SELECT_MODE_TOGGLE)) == (GTK_TREE_SELECT_MODE_EXTEND | GTK_TREE_SELECT_MODE_TOGGLE))
1073 pspp_sheet_selection_select_range (selection,
1077 else if ((mode & GTK_TREE_SELECT_MODE_TOGGLE) == GTK_TREE_SELECT_MODE_TOGGLE)
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);
1083 selection->tree_view->priv->anchor =
1084 gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path);
1087 dirty |= pspp_sheet_selection_real_select_node (selection, node, FALSE);
1089 dirty |= pspp_sheet_selection_real_select_node (selection, node, TRUE);
1091 else if ((mode & GTK_TREE_SELECT_MODE_EXTEND) == GTK_TREE_SELECT_MODE_EXTEND)
1093 dirty = pspp_sheet_selection_real_unselect_all (selection);
1094 dirty |= pspp_sheet_selection_real_modify_range (selection,
1101 dirty = pspp_sheet_selection_real_unselect_all (selection);
1103 if (selection->tree_view->priv->anchor)
1104 gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
1106 selection->tree_view->priv->anchor =
1107 gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path);
1109 dirty |= pspp_sheet_selection_real_select_node (selection, node, TRUE);
1114 gtk_tree_path_free (anchor_path);
1117 g_signal_emit (selection, tree_selection_signals[CHANGED], 0);
1122 _pspp_sheet_selection_emit_changed (PsppSheetSelection *selection)
1124 g_signal_emit (selection, tree_selection_signals[CHANGED], 0);
1127 /* NOTE: Any {un,}selection ever done _MUST_ be done through this function!
1131 pspp_sheet_selection_real_select_node (PsppSheetSelection *selection,
1136 if (pspp_sheet_view_node_is_selected (selection->tree_view, node) != select)
1139 pspp_sheet_view_node_select (selection->tree_view, node);
1141 pspp_sheet_view_node_unselect (selection->tree_view, node);
1143 _pspp_sheet_view_queue_draw_node (selection->tree_view, node, NULL);