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 static void pspp_sheet_selection_finalize (GObject *object);
46 static gint pspp_sheet_selection_real_select_all (PsppSheetSelection *selection);
47 static gint pspp_sheet_selection_real_unselect_all (PsppSheetSelection *selection);
48 static gint pspp_sheet_selection_real_select_node (PsppSheetSelection *selection,
59 static guint tree_selection_signals [LAST_SIGNAL] = { 0 };
61 G_DEFINE_TYPE (PsppSheetSelection, pspp_sheet_selection, G_TYPE_OBJECT)
64 pspp_sheet_selection_class_init (PsppSheetSelectionClass *class)
66 GObjectClass *object_class;
68 object_class = (GObjectClass*) class;
70 object_class->finalize = pspp_sheet_selection_finalize;
71 class->changed = NULL;
73 tree_selection_signals[CHANGED] =
74 g_signal_new ("changed",
75 G_OBJECT_CLASS_TYPE (object_class),
77 G_STRUCT_OFFSET (PsppSheetSelectionClass, changed),
79 g_cclosure_marshal_VOID__VOID,
84 pspp_sheet_selection_init (PsppSheetSelection *selection)
86 selection->type = GTK_SELECTION_SINGLE;
90 pspp_sheet_selection_finalize (GObject *object)
92 PsppSheetSelection *selection = PSPP_SHEET_SELECTION (object);
94 if (selection->destroy)
96 GDestroyNotify d = selection->destroy;
98 selection->destroy = NULL;
99 d (selection->user_data);
102 /* chain parent_class' handler */
103 G_OBJECT_CLASS (pspp_sheet_selection_parent_class)->finalize (object);
107 * _pspp_sheet_selection_new:
109 * Creates a new #PsppSheetSelection object. This function should not be invoked,
110 * as each #PsppSheetView will create its own #PsppSheetSelection.
112 * Return value: A newly created #PsppSheetSelection object.
115 _pspp_sheet_selection_new (void)
117 PsppSheetSelection *selection;
119 selection = g_object_new (PSPP_TYPE_SHEET_SELECTION, NULL);
125 * _pspp_sheet_selection_new_with_tree_view:
126 * @tree_view: The #PsppSheetView.
128 * Creates a new #PsppSheetSelection object. This function should not be invoked,
129 * as each #PsppSheetView will create its own #PsppSheetSelection.
131 * Return value: A newly created #PsppSheetSelection object.
134 _pspp_sheet_selection_new_with_tree_view (PsppSheetView *tree_view)
136 PsppSheetSelection *selection;
138 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
140 selection = _pspp_sheet_selection_new ();
141 _pspp_sheet_selection_set_tree_view (selection, tree_view);
147 * _pspp_sheet_selection_set_tree_view:
148 * @selection: A #PsppSheetSelection.
149 * @tree_view: The #PsppSheetView.
151 * Sets the #PsppSheetView of @selection. This function should not be invoked, as
152 * it is used internally by #PsppSheetView.
155 _pspp_sheet_selection_set_tree_view (PsppSheetSelection *selection,
156 PsppSheetView *tree_view)
158 g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
159 if (tree_view != NULL)
160 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
162 selection->tree_view = tree_view;
166 * pspp_sheet_selection_set_mode:
167 * @selection: A #PsppSheetSelection.
168 * @type: The selection mode
170 * Sets the selection mode of the @selection. If the previous type was
171 * #GTK_SELECTION_MULTIPLE, then the anchor is kept selected, if it was
172 * previously selected.
175 pspp_sheet_selection_set_mode (PsppSheetSelection *selection,
176 GtkSelectionMode type)
178 PsppSheetSelectionFunc tmp_func;
179 g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
181 if (selection->type == type)
185 if (type == GTK_SELECTION_NONE)
187 /* We do this so that we unconditionally unset all rows
189 tmp_func = selection->user_func;
190 selection->user_func = NULL;
191 pspp_sheet_selection_unselect_all (selection);
192 selection->user_func = tmp_func;
194 gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
195 selection->tree_view->priv->anchor = NULL;
197 else if (type == GTK_SELECTION_SINGLE ||
198 type == GTK_SELECTION_BROWSE)
200 GtkRBTree *tree = NULL;
201 GtkRBNode *node = NULL;
202 gint selected = FALSE;
203 GtkTreePath *anchor_path = NULL;
205 if (selection->tree_view->priv->anchor)
207 anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor);
211 _pspp_sheet_view_find_node (selection->tree_view,
216 if (node && PSPP_RBNODE_FLAG_SET (node, PSPP_RBNODE_IS_SELECTED))
221 /* We do this so that we unconditionally unset all rows
223 tmp_func = selection->user_func;
224 selection->user_func = NULL;
225 pspp_sheet_selection_unselect_all (selection);
226 selection->user_func = tmp_func;
228 if (node && selected)
229 _pspp_sheet_selection_internal_select_node (selection,
236 gtk_tree_path_free (anchor_path);
239 selection->type = type;
243 * pspp_sheet_selection_get_mode:
244 * @selection: a #PsppSheetSelection
246 * Gets the selection mode for @selection. See
247 * pspp_sheet_selection_set_mode().
249 * Return value: the current selection mode
252 pspp_sheet_selection_get_mode (PsppSheetSelection *selection)
254 g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), GTK_SELECTION_SINGLE);
256 return selection->type;
260 * pspp_sheet_selection_set_select_function:
261 * @selection: A #PsppSheetSelection.
262 * @func: The selection function.
263 * @data: The selection function's data.
264 * @destroy: The destroy function for user data. May be NULL.
266 * Sets the selection function. If set, this function is called before any node
267 * is selected or unselected, giving some control over which nodes are selected.
268 * The select function should return %TRUE if the state of the node may be toggled,
269 * and %FALSE if the state of the node should be left unchanged.
272 pspp_sheet_selection_set_select_function (PsppSheetSelection *selection,
273 PsppSheetSelectionFunc func,
275 GDestroyNotify destroy)
277 g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
278 g_return_if_fail (func != NULL);
280 if (selection->destroy)
282 GDestroyNotify d = selection->destroy;
284 selection->destroy = NULL;
285 d (selection->user_data);
288 selection->user_func = func;
289 selection->user_data = data;
290 selection->destroy = destroy;
294 * pspp_sheet_selection_get_select_function:
295 * @selection: A #PsppSheetSelection.
297 * Returns the current selection function.
299 * Return value: The function.
303 PsppSheetSelectionFunc
304 pspp_sheet_selection_get_select_function (PsppSheetSelection *selection)
306 g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), NULL);
308 return selection->user_func;
312 * pspp_sheet_selection_get_user_data:
313 * @selection: A #PsppSheetSelection.
315 * Returns the user data for the selection function.
317 * Return value: The user data.
320 pspp_sheet_selection_get_user_data (PsppSheetSelection *selection)
322 g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), NULL);
324 return selection->user_data;
328 * pspp_sheet_selection_get_tree_view:
329 * @selection: A #PsppSheetSelection
331 * Returns the tree view associated with @selection.
333 * Return value: A #PsppSheetView
336 pspp_sheet_selection_get_tree_view (PsppSheetSelection *selection)
338 g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), NULL);
340 return selection->tree_view;
344 * pspp_sheet_selection_get_selected:
345 * @selection: A #PsppSheetSelection.
346 * @model: (out) (allow-none): A pointer to set to the #GtkTreeModel, or NULL.
347 * @iter: (allow-none): The #GtkTreeIter, or NULL.
349 * Sets @iter to the currently selected node if @selection is set to
350 * #GTK_SELECTION_SINGLE or #GTK_SELECTION_BROWSE. @iter may be NULL if you
351 * just want to test if @selection has any selected nodes. @model is filled
352 * with the current model as a convenience. This function will not work if you
353 * use @selection is #GTK_SELECTION_MULTIPLE.
355 * Return value: TRUE, if there is a selected node.
358 pspp_sheet_selection_get_selected (PsppSheetSelection *selection,
359 GtkTreeModel **model,
364 GtkTreePath *anchor_path;
368 g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), FALSE);
369 g_return_val_if_fail (selection->type != GTK_SELECTION_MULTIPLE, FALSE);
370 g_return_val_if_fail (selection->tree_view != NULL, FALSE);
374 memset (iter, 0, sizeof (GtkTreeIter));
377 *model = selection->tree_view->priv->model;
379 if (selection->tree_view->priv->anchor == NULL)
382 anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor);
384 if (anchor_path == NULL)
389 found_node = !_pspp_sheet_view_find_node (selection->tree_view,
394 if (found_node && PSPP_RBNODE_FLAG_SET (node, PSPP_RBNODE_IS_SELECTED))
396 /* we only want to return the anchor if it exists in the rbtree and
402 retval = gtk_tree_model_get_iter (selection->tree_view->priv->model,
408 /* We don't want to return the anchor if it isn't actually selected.
413 gtk_tree_path_free (anchor_path);
419 * pspp_sheet_selection_get_selected_rows:
420 * @selection: A #PsppSheetSelection.
421 * @model: (allow-none): A pointer to set to the #GtkTreeModel, or NULL.
423 * Creates a list of path of all selected rows. Additionally, if you are
424 * planning on modifying the model after calling this function, you may
425 * want to convert the returned list into a list of #GtkTreeRowReference<!-- -->s.
426 * To do this, you can use gtk_tree_row_reference_new().
428 * To free the return value, use:
430 * g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
431 * g_list_free (list);
434 * Return value: (element-type GtkTreePath) (transfer full): A #GList containing a #GtkTreePath for each selected row.
439 pspp_sheet_selection_get_selected_rows (PsppSheetSelection *selection,
440 GtkTreeModel **model)
443 GtkRBTree *tree = NULL;
444 GtkRBNode *node = NULL;
447 g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), NULL);
448 g_return_val_if_fail (selection->tree_view != NULL, NULL);
451 *model = selection->tree_view->priv->model;
453 if (selection->tree_view->priv->tree == NULL ||
454 selection->tree_view->priv->tree->root == NULL)
457 if (selection->type == GTK_SELECTION_NONE)
459 else if (selection->type != GTK_SELECTION_MULTIPLE)
463 if (pspp_sheet_selection_get_selected (selection, NULL, &iter))
467 path = gtk_tree_model_get_path (selection->tree_view->priv->model, &iter);
468 list = g_list_append (list, path);
476 tree = selection->tree_view->priv->tree;
477 node = selection->tree_view->priv->tree->root;
479 while (node->left != tree->nil)
481 path = gtk_tree_path_new_first ();
485 if (PSPP_RBNODE_FLAG_SET (node, PSPP_RBNODE_IS_SELECTED))
486 list = g_list_prepend (list, gtk_tree_path_copy (path));
490 tree = node->children;
493 while (node->left != tree->nil)
496 gtk_tree_path_append_index (path, 0);
500 gboolean done = FALSE;
504 node = _pspp_rbtree_next (tree, node);
508 gtk_tree_path_next (path);
512 node = tree->parent_node;
513 tree = tree->parent_tree;
517 gtk_tree_path_free (path);
522 gtk_tree_path_up (path);
530 gtk_tree_path_free (path);
533 return g_list_reverse (list);
537 pspp_sheet_selection_count_selected_rows_helper (GtkRBTree *tree,
541 gint *count = (gint *)data;
543 if (PSPP_RBNODE_FLAG_SET (node, PSPP_RBNODE_IS_SELECTED))
547 _pspp_rbtree_traverse (node->children, node->children->root,
549 pspp_sheet_selection_count_selected_rows_helper, data);
553 * pspp_sheet_selection_count_selected_rows:
554 * @selection: A #PsppSheetSelection.
556 * Returns the number of rows that have been selected in @tree.
558 * Return value: The number of rows selected.
563 pspp_sheet_selection_count_selected_rows (PsppSheetSelection *selection)
567 g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), 0);
568 g_return_val_if_fail (selection->tree_view != NULL, 0);
570 if (selection->tree_view->priv->tree == NULL ||
571 selection->tree_view->priv->tree->root == NULL)
574 if (selection->type == GTK_SELECTION_SINGLE ||
575 selection->type == GTK_SELECTION_BROWSE)
577 if (pspp_sheet_selection_get_selected (selection, NULL, NULL))
583 _pspp_rbtree_traverse (selection->tree_view->priv->tree,
584 selection->tree_view->priv->tree->root,
586 pspp_sheet_selection_count_selected_rows_helper,
592 /* pspp_sheet_selection_selected_foreach helper */
594 model_changed (gpointer data)
596 gboolean *stop = (gboolean *)data;
602 * pspp_sheet_selection_selected_foreach:
603 * @selection: A #PsppSheetSelection.
604 * @func: The function to call for each selected node.
605 * @data: user data to pass to the function.
607 * Calls a function for each selected node. Note that you cannot modify
608 * the tree or selection from within this function. As a result,
609 * pspp_sheet_selection_get_selected_rows() might be more useful.
612 pspp_sheet_selection_selected_foreach (PsppSheetSelection *selection,
613 PsppSheetSelectionForeachFunc func,
622 gulong inserted_id, deleted_id, reordered_id, changed_id;
623 gboolean stop = FALSE;
625 g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
626 g_return_if_fail (selection->tree_view != NULL);
629 selection->tree_view->priv->tree == NULL ||
630 selection->tree_view->priv->tree->root == NULL)
633 if (selection->type == GTK_SELECTION_SINGLE ||
634 selection->type == GTK_SELECTION_BROWSE)
636 if (gtk_tree_row_reference_valid (selection->tree_view->priv->anchor))
638 path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor);
639 gtk_tree_model_get_iter (selection->tree_view->priv->model, &iter, path);
640 (* func) (selection->tree_view->priv->model, path, &iter, data);
641 gtk_tree_path_free (path);
646 tree = selection->tree_view->priv->tree;
647 node = selection->tree_view->priv->tree->root;
649 while (node->left != tree->nil)
652 model = selection->tree_view->priv->model;
653 g_object_ref (model);
655 /* connect to signals to monitor changes in treemodel */
656 inserted_id = g_signal_connect_swapped (model, "row-inserted",
657 G_CALLBACK (model_changed),
659 deleted_id = g_signal_connect_swapped (model, "row-deleted",
660 G_CALLBACK (model_changed),
662 reordered_id = g_signal_connect_swapped (model, "rows-reordered",
663 G_CALLBACK (model_changed),
665 changed_id = g_signal_connect_swapped (selection->tree_view, "notify::model",
666 G_CALLBACK (model_changed),
669 /* find the node internally */
670 path = gtk_tree_path_new_first ();
674 if (PSPP_RBNODE_FLAG_SET (node, PSPP_RBNODE_IS_SELECTED))
676 gtk_tree_model_get_iter (model, &iter, path);
677 (* func) (model, path, &iter, data);
685 tree = node->children;
688 while (node->left != tree->nil)
691 gtk_tree_path_append_index (path, 0);
695 gboolean done = FALSE;
699 node = _pspp_rbtree_next (tree, node);
703 gtk_tree_path_next (path);
707 node = tree->parent_node;
708 tree = tree->parent_tree;
712 /* we've run out of tree */
713 /* We're done with this function */
718 gtk_tree_path_up (path);
728 gtk_tree_path_free (path);
730 g_signal_handler_disconnect (model, inserted_id);
731 g_signal_handler_disconnect (model, deleted_id);
732 g_signal_handler_disconnect (model, reordered_id);
733 g_signal_handler_disconnect (selection->tree_view, changed_id);
734 g_object_unref (model);
736 /* check if we have to spew a scary message */
738 g_warning ("The model has been modified from within pspp_sheet_selection_selected_foreach.\n"
739 "This function is for observing the selections of the tree only. If\n"
740 "you are trying to get all selected items from the tree, try using\n"
741 "pspp_sheet_selection_get_selected_rows instead.\n");
745 * pspp_sheet_selection_select_path:
746 * @selection: A #PsppSheetSelection.
747 * @path: The #GtkTreePath to be selected.
749 * Select the row at @path.
752 pspp_sheet_selection_select_path (PsppSheetSelection *selection,
758 GtkTreeSelectMode mode = 0;
760 g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
761 g_return_if_fail (selection->tree_view != NULL);
762 g_return_if_fail (path != NULL);
764 ret = _pspp_sheet_view_find_node (selection->tree_view,
769 if (node == NULL || PSPP_RBNODE_FLAG_SET (node, PSPP_RBNODE_IS_SELECTED) ||
773 if (selection->type == GTK_SELECTION_MULTIPLE)
774 mode = GTK_TREE_SELECT_MODE_TOGGLE;
776 _pspp_sheet_selection_internal_select_node (selection,
785 * pspp_sheet_selection_unselect_path:
786 * @selection: A #PsppSheetSelection.
787 * @path: The #GtkTreePath to be unselected.
789 * Unselects the row at @path.
792 pspp_sheet_selection_unselect_path (PsppSheetSelection *selection,
799 g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
800 g_return_if_fail (selection->tree_view != NULL);
801 g_return_if_fail (path != NULL);
803 ret = _pspp_sheet_view_find_node (selection->tree_view,
808 if (node == NULL || !PSPP_RBNODE_FLAG_SET (node, PSPP_RBNODE_IS_SELECTED) ||
812 _pspp_sheet_selection_internal_select_node (selection,
816 GTK_TREE_SELECT_MODE_TOGGLE,
821 * pspp_sheet_selection_select_iter:
822 * @selection: A #PsppSheetSelection.
823 * @iter: The #GtkTreeIter to be selected.
825 * Selects the specified iterator.
828 pspp_sheet_selection_select_iter (PsppSheetSelection *selection,
833 g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
834 g_return_if_fail (selection->tree_view != NULL);
835 g_return_if_fail (selection->tree_view->priv->model != NULL);
836 g_return_if_fail (iter != NULL);
838 path = gtk_tree_model_get_path (selection->tree_view->priv->model,
844 pspp_sheet_selection_select_path (selection, path);
845 gtk_tree_path_free (path);
850 * pspp_sheet_selection_unselect_iter:
851 * @selection: A #PsppSheetSelection.
852 * @iter: The #GtkTreeIter to be unselected.
854 * Unselects the specified iterator.
857 pspp_sheet_selection_unselect_iter (PsppSheetSelection *selection,
862 g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
863 g_return_if_fail (selection->tree_view != NULL);
864 g_return_if_fail (selection->tree_view->priv->model != NULL);
865 g_return_if_fail (iter != NULL);
867 path = gtk_tree_model_get_path (selection->tree_view->priv->model,
873 pspp_sheet_selection_unselect_path (selection, path);
874 gtk_tree_path_free (path);
878 * pspp_sheet_selection_path_is_selected:
879 * @selection: A #PsppSheetSelection.
880 * @path: A #GtkTreePath to check selection on.
882 * Returns %TRUE if the row pointed to by @path is currently selected. If @path
883 * does not point to a valid location, %FALSE is returned
885 * Return value: %TRUE if @path is selected.
888 pspp_sheet_selection_path_is_selected (PsppSheetSelection *selection,
895 g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), FALSE);
896 g_return_val_if_fail (path != NULL, FALSE);
897 g_return_val_if_fail (selection->tree_view != NULL, FALSE);
899 if (selection->tree_view->priv->model == NULL)
902 ret = _pspp_sheet_view_find_node (selection->tree_view,
907 if ((node == NULL) || !PSPP_RBNODE_FLAG_SET (node, PSPP_RBNODE_IS_SELECTED) ||
915 * pspp_sheet_selection_iter_is_selected:
916 * @selection: A #PsppSheetSelection
917 * @iter: A valid #GtkTreeIter
919 * Returns %TRUE if the row at @iter is currently selected.
921 * Return value: %TRUE, if @iter is selected
924 pspp_sheet_selection_iter_is_selected (PsppSheetSelection *selection,
930 g_return_val_if_fail (PSPP_IS_SHEET_SELECTION (selection), FALSE);
931 g_return_val_if_fail (iter != NULL, FALSE);
932 g_return_val_if_fail (selection->tree_view != NULL, FALSE);
933 g_return_val_if_fail (selection->tree_view->priv->model != NULL, FALSE);
935 path = gtk_tree_model_get_path (selection->tree_view->priv->model, iter);
939 retval = pspp_sheet_selection_path_is_selected (selection, path);
940 gtk_tree_path_free (path);
946 /* Wish I was in python, right now... */
948 PsppSheetSelection *selection;
953 select_all_helper (GtkRBTree *tree,
957 struct _TempTuple *tuple = data;
960 _pspp_rbtree_traverse (node->children,
961 node->children->root,
965 if (!PSPP_RBNODE_FLAG_SET (node, PSPP_RBNODE_IS_SELECTED))
967 tuple->dirty = pspp_sheet_selection_real_select_node (tuple->selection, tree, node, TRUE) || tuple->dirty;
972 /* We have a real_{un,}select_all function that doesn't emit the signal, so we
973 * can use it in other places without fear of the signal being emitted.
976 pspp_sheet_selection_real_select_all (PsppSheetSelection *selection)
978 struct _TempTuple *tuple;
980 if (selection->tree_view->priv->tree == NULL)
983 /* Mark all nodes selected */
984 tuple = g_new (struct _TempTuple, 1);
985 tuple->selection = selection;
986 tuple->dirty = FALSE;
988 _pspp_rbtree_traverse (selection->tree_view->priv->tree,
989 selection->tree_view->priv->tree->root,
1003 * pspp_sheet_selection_select_all:
1004 * @selection: A #PsppSheetSelection.
1006 * Selects all the nodes. @selection must be set to #GTK_SELECTION_MULTIPLE
1010 pspp_sheet_selection_select_all (PsppSheetSelection *selection)
1012 g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
1013 g_return_if_fail (selection->tree_view != NULL);
1015 if (selection->tree_view->priv->tree == NULL || selection->tree_view->priv->model == NULL)
1018 g_return_if_fail (selection->type == GTK_SELECTION_MULTIPLE);
1020 if (pspp_sheet_selection_real_select_all (selection))
1021 g_signal_emit (selection, tree_selection_signals[CHANGED], 0);
1025 unselect_all_helper (GtkRBTree *tree,
1029 struct _TempTuple *tuple = data;
1032 _pspp_rbtree_traverse (node->children,
1033 node->children->root,
1035 unselect_all_helper,
1037 if (PSPP_RBNODE_FLAG_SET (node, PSPP_RBNODE_IS_SELECTED))
1039 tuple->dirty = pspp_sheet_selection_real_select_node (tuple->selection, tree, node, FALSE) || tuple->dirty;
1044 pspp_sheet_selection_real_unselect_all (PsppSheetSelection *selection)
1046 struct _TempTuple *tuple;
1048 if (selection->type == GTK_SELECTION_SINGLE ||
1049 selection->type == GTK_SELECTION_BROWSE)
1051 GtkRBTree *tree = NULL;
1052 GtkRBNode *node = NULL;
1053 GtkTreePath *anchor_path;
1055 if (selection->tree_view->priv->anchor == NULL)
1058 anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor);
1060 if (anchor_path == NULL)
1063 _pspp_sheet_view_find_node (selection->tree_view,
1068 gtk_tree_path_free (anchor_path);
1073 if (PSPP_RBNODE_FLAG_SET (node, PSPP_RBNODE_IS_SELECTED))
1075 if (pspp_sheet_selection_real_select_node (selection, tree, node, FALSE))
1077 gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
1078 selection->tree_view->priv->anchor = NULL;
1086 tuple = g_new (struct _TempTuple, 1);
1087 tuple->selection = selection;
1088 tuple->dirty = FALSE;
1090 _pspp_rbtree_traverse (selection->tree_view->priv->tree,
1091 selection->tree_view->priv->tree->root,
1093 unselect_all_helper,
1107 * pspp_sheet_selection_unselect_all:
1108 * @selection: A #PsppSheetSelection.
1110 * Unselects all the nodes.
1113 pspp_sheet_selection_unselect_all (PsppSheetSelection *selection)
1115 g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
1116 g_return_if_fail (selection->tree_view != NULL);
1118 if (selection->tree_view->priv->tree == NULL || selection->tree_view->priv->model == NULL)
1121 if (pspp_sheet_selection_real_unselect_all (selection))
1122 g_signal_emit (selection, tree_selection_signals[CHANGED], 0);
1132 pspp_sheet_selection_real_modify_range (PsppSheetSelection *selection,
1134 GtkTreePath *start_path,
1135 GtkTreePath *end_path)
1137 GtkRBNode *start_node, *end_node;
1138 GtkRBTree *start_tree, *end_tree;
1139 GtkTreePath *anchor_path = NULL;
1140 gboolean dirty = FALSE;
1142 switch (gtk_tree_path_compare (start_path, end_path))
1145 _pspp_sheet_view_find_node (selection->tree_view,
1149 _pspp_sheet_view_find_node (selection->tree_view,
1153 anchor_path = start_path;
1156 _pspp_sheet_view_find_node (selection->tree_view,
1160 end_tree = start_tree;
1161 end_node = start_node;
1162 anchor_path = start_path;
1165 _pspp_sheet_view_find_node (selection->tree_view,
1169 _pspp_sheet_view_find_node (selection->tree_view,
1173 anchor_path = start_path;
1177 g_return_val_if_fail (start_node != NULL, FALSE);
1178 g_return_val_if_fail (end_node != NULL, FALSE);
1182 if (selection->tree_view->priv->anchor)
1183 gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
1185 selection->tree_view->priv->anchor =
1186 gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view),
1187 selection->tree_view->priv->model,
1193 dirty |= pspp_sheet_selection_real_select_node (selection, start_tree, start_node, (mode == RANGE_SELECT)?TRUE:FALSE);
1195 if (start_node == end_node)
1198 if (start_node->children)
1200 start_tree = start_node->children;
1201 start_node = start_tree->root;
1202 while (start_node->left != start_tree->nil)
1203 start_node = start_node->left;
1207 _pspp_rbtree_next_full (start_tree, start_node, &start_tree, &start_node);
1208 if (start_tree == NULL)
1210 /* we just ran out of tree. That means someone passed in bogus values.
1222 * pspp_sheet_selection_select_range:
1223 * @selection: A #PsppSheetSelection.
1224 * @start_path: The initial node of the range.
1225 * @end_path: The final node of the range.
1227 * Selects a range of nodes, determined by @start_path and @end_path inclusive.
1228 * @selection must be set to #GTK_SELECTION_MULTIPLE mode.
1231 pspp_sheet_selection_select_range (PsppSheetSelection *selection,
1232 GtkTreePath *start_path,
1233 GtkTreePath *end_path)
1235 g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
1236 g_return_if_fail (selection->tree_view != NULL);
1237 g_return_if_fail (selection->type == GTK_SELECTION_MULTIPLE);
1238 g_return_if_fail (selection->tree_view->priv->model != NULL);
1240 if (pspp_sheet_selection_real_modify_range (selection, RANGE_SELECT, start_path, end_path))
1241 g_signal_emit (selection, tree_selection_signals[CHANGED], 0);
1245 * pspp_sheet_selection_unselect_range:
1246 * @selection: A #PsppSheetSelection.
1247 * @start_path: The initial node of the range.
1248 * @end_path: The initial node of the range.
1250 * Unselects a range of nodes, determined by @start_path and @end_path
1256 pspp_sheet_selection_unselect_range (PsppSheetSelection *selection,
1257 GtkTreePath *start_path,
1258 GtkTreePath *end_path)
1260 g_return_if_fail (PSPP_IS_SHEET_SELECTION (selection));
1261 g_return_if_fail (selection->tree_view != NULL);
1262 g_return_if_fail (selection->tree_view->priv->model != NULL);
1264 if (pspp_sheet_selection_real_modify_range (selection, RANGE_UNSELECT, start_path, end_path))
1265 g_signal_emit (selection, tree_selection_signals[CHANGED], 0);
1269 _pspp_sheet_selection_row_is_selectable (PsppSheetSelection *selection,
1274 gboolean sensitive = FALSE;
1276 if (!gtk_tree_model_get_iter (selection->tree_view->priv->model, &iter, path))
1279 if (!sensitive && selection->tree_view->priv->row_separator_func)
1281 /* never allow separators to be selected */
1282 if ((* selection->tree_view->priv->row_separator_func) (selection->tree_view->priv->model,
1284 selection->tree_view->priv->row_separator_data))
1288 if (selection->user_func)
1289 return (*selection->user_func) (selection, selection->tree_view->priv->model, path,
1290 PSPP_RBNODE_FLAG_SET (node, PSPP_RBNODE_IS_SELECTED),
1291 selection->user_data);
1297 /* Called internally by gtktreeview.c It handles actually selecting the tree.
1301 * docs about the 'override_browse_mode', we set this flag when we want to
1302 * unset select the node and override the select browse mode behaviour (that is
1303 * 'one node should *always* be selected').
1306 _pspp_sheet_selection_internal_select_node (PsppSheetSelection *selection,
1310 GtkTreeSelectMode mode,
1311 gboolean override_browse_mode)
1315 GtkTreePath *anchor_path = NULL;
1317 if (selection->type == GTK_SELECTION_NONE)
1320 if (selection->tree_view->priv->anchor)
1321 anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor);
1323 if (selection->type == GTK_SELECTION_SINGLE ||
1324 selection->type == GTK_SELECTION_BROWSE)
1327 if (selection->type == GTK_SELECTION_BROWSE && override_browse_mode)
1329 dirty = pspp_sheet_selection_real_unselect_all (selection);
1331 /* Did we try to select the same node again? */
1332 else if (selection->type == GTK_SELECTION_SINGLE &&
1333 anchor_path && gtk_tree_path_compare (path, anchor_path) == 0)
1335 if ((mode & GTK_TREE_SELECT_MODE_TOGGLE) == GTK_TREE_SELECT_MODE_TOGGLE)
1337 dirty = pspp_sheet_selection_real_unselect_all (selection);
1344 /* We only want to select the new node if we can unselect the old one,
1345 * and we can select the new one. */
1346 dirty = _pspp_sheet_selection_row_is_selectable (selection, node, path);
1348 /* if dirty is FALSE, we weren't able to select the new one, otherwise, we try to
1349 * unselect the new one
1352 dirty = pspp_sheet_selection_real_unselect_all (selection);
1354 /* if dirty is TRUE at this point, we successfully unselected the
1355 * old one, and can then select the new one */
1358 if (selection->tree_view->priv->anchor)
1360 gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
1361 selection->tree_view->priv->anchor = NULL;
1364 if (pspp_sheet_selection_real_select_node (selection, tree, node, TRUE))
1366 selection->tree_view->priv->anchor =
1367 gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path);
1373 if (pspp_sheet_selection_real_select_node (selection, tree, node, TRUE))
1376 if (selection->tree_view->priv->anchor)
1377 gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
1379 selection->tree_view->priv->anchor =
1380 gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path);
1385 else if (selection->type == GTK_SELECTION_MULTIPLE)
1387 if ((mode & GTK_TREE_SELECT_MODE_EXTEND) == GTK_TREE_SELECT_MODE_EXTEND
1388 && (anchor_path == NULL))
1390 if (selection->tree_view->priv->anchor)
1391 gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
1393 selection->tree_view->priv->anchor =
1394 gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path);
1395 dirty = pspp_sheet_selection_real_select_node (selection, tree, node, TRUE);
1397 else if ((mode & (GTK_TREE_SELECT_MODE_EXTEND | GTK_TREE_SELECT_MODE_TOGGLE)) == (GTK_TREE_SELECT_MODE_EXTEND | GTK_TREE_SELECT_MODE_TOGGLE))
1399 pspp_sheet_selection_select_range (selection,
1403 else if ((mode & GTK_TREE_SELECT_MODE_TOGGLE) == GTK_TREE_SELECT_MODE_TOGGLE)
1405 flags = node->flags;
1406 if (selection->tree_view->priv->anchor)
1407 gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
1409 selection->tree_view->priv->anchor =
1410 gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path);
1412 if ((flags & PSPP_RBNODE_IS_SELECTED) == PSPP_RBNODE_IS_SELECTED)
1413 dirty |= pspp_sheet_selection_real_select_node (selection, tree, node, FALSE);
1415 dirty |= pspp_sheet_selection_real_select_node (selection, tree, node, TRUE);
1417 else if ((mode & GTK_TREE_SELECT_MODE_EXTEND) == GTK_TREE_SELECT_MODE_EXTEND)
1419 dirty = pspp_sheet_selection_real_unselect_all (selection);
1420 dirty |= pspp_sheet_selection_real_modify_range (selection,
1427 dirty = pspp_sheet_selection_real_unselect_all (selection);
1429 if (selection->tree_view->priv->anchor)
1430 gtk_tree_row_reference_free (selection->tree_view->priv->anchor);
1432 selection->tree_view->priv->anchor =
1433 gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path);
1435 dirty |= pspp_sheet_selection_real_select_node (selection, tree, node, TRUE);
1440 gtk_tree_path_free (anchor_path);
1443 g_signal_emit (selection, tree_selection_signals[CHANGED], 0);
1448 _pspp_sheet_selection_emit_changed (PsppSheetSelection *selection)
1450 g_signal_emit (selection, tree_selection_signals[CHANGED], 0);
1453 /* NOTE: Any {un,}selection ever done _MUST_ be done through this function!
1457 pspp_sheet_selection_real_select_node (PsppSheetSelection *selection,
1462 gboolean toggle = FALSE;
1463 GtkTreePath *path = NULL;
1467 if (PSPP_RBNODE_FLAG_SET (node, PSPP_RBNODE_IS_SELECTED) != select)
1469 path = _pspp_sheet_view_find_path (selection->tree_view, tree, node);
1470 toggle = _pspp_sheet_selection_row_is_selectable (selection, node, path);
1471 gtk_tree_path_free (path);
1476 node->flags ^= PSPP_RBNODE_IS_SELECTED;
1478 _pspp_sheet_view_queue_draw_node (selection->tree_view, tree, node, NULL);