X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtktreeselection.c;h=10c9cd717a6908cf686251f22bd0ee58fb1e49d4;hb=9d81b69e06cd9c2ad9cb5204056bfae5a3128e9c;hp=338b76e8d6cc50bdff63c64718c9ef9034597920;hpb=988275cf14a5d3994affb83f70be38c65768162b;p=~andy%2Fgtk diff --git a/gtk/gtktreeselection.c b/gtk/gtktreeselection.c index 338b76e8d..10c9cd717 100644 --- a/gtk/gtktreeselection.c +++ b/gtk/gtktreeselection.c @@ -12,18 +12,62 @@ * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * License along with this library. If not, see . */ +#include "config.h" +#include #include "gtktreeselection.h" #include "gtktreeprivate.h" #include "gtkrbtree.h" -#include "gtksignal.h" +#include "gtkmarshalers.h" +#include "gtkintl.h" +#include "gtktypebuiltins.h" +#include "a11y/gtktreeviewaccessibleprivate.h" -static void gtk_tree_selection_init (GtkTreeSelection *selection); -static void gtk_tree_selection_class_init (GtkTreeSelectionClass *class); + +/** + * SECTION:gtktreeselection + * @Short_description: The selection object for GtkTreeView + * @Title: GtkTreeSelection + * @See_also: #GtkTreeView, #GtkTreeViewColumn, #GtkTreeDnd, #GtkTreeMode, + * #GtkTreeSortable, #GtkTreeModelSort, #GtkListStore, #GtkTreeStore, + * #GtkCellRenderer, #GtkCellEditable, #GtkCellRendererPixbuf, + * #GtkCellRendererText, #GtkCellRendererToggle + * + * The #GtkTreeSelection object is a helper object to manage the selection + * for a #GtkTreeView widget. The #GtkTreeSelection object is + * automatically created when a new #GtkTreeView widget is created, and + * cannot exist independentally of this widget. The primary reason the + * #GtkTreeSelection objects exists is for cleanliness of code and API. + * That is, there is no conceptual reason all these functions could not be + * methods on the #GtkTreeView widget instead of a separate function. + * + * The #GtkTreeSelection object is gotten from a #GtkTreeView by calling + * gtk_tree_view_get_selection(). It can be manipulated to check the + * selection status of the tree, as well as select and deselect individual + * rows. Selection is done completely view side. As a result, multiple + * views of the same model can have completely different selections. + * Additionally, you cannot change the selection of a row on the model that + * is not currently displayed by the view without expanding its parents + * first. + * + * One of the important things to remember when monitoring the selection of + * a view is that the #GtkTreeSelection::changed signal is mostly a hint. + * That is, it may only emit one signal when a range of rows is selected. + * Additionally, it may on occasion emit a #GtkTreeSelection::changed signal + * when nothing has happened (mostly as a result of programmers calling + * select_row on an already selected row). + */ + +struct _GtkTreeSelectionPrivate +{ + GtkTreeView *tree_view; + GtkSelectionMode type; + GtkTreeSelectionFunc user_func; + gpointer user_data; + GDestroyNotify destroy; +}; static void gtk_tree_selection_finalize (GObject *object); static gint gtk_tree_selection_real_select_all (GtkTreeSelection *selection); @@ -32,41 +76,32 @@ static gint gtk_tree_selection_real_select_node (GtkTreeSelection *selecti GtkRBTree *tree, GtkRBNode *node, gboolean select); +static void gtk_tree_selection_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gtk_tree_selection_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); enum { - CHANGED, - LAST_SIGNAL + PROP_0, + PROP_MODE, + N_PROPERTIES }; -static GObjectClass *parent_class = NULL; -static guint tree_selection_signals[LAST_SIGNAL] = { 0 }; - -GtkType -gtk_tree_selection_get_type (void) +enum { - static GtkType selection_type = 0; + CHANGED, + LAST_SIGNAL +}; - if (!selection_type) - { - static const GTypeInfo selection_info = - { - sizeof (GtkTreeSelectionClass), - NULL, /* base_init */ - NULL, /* base_finalize */ - (GClassInitFunc) gtk_tree_selection_class_init, - NULL, /* class_finalize */ - NULL, /* class_data */ - sizeof (GtkTreeSelection), - 0, /* n_preallocs */ - (GInstanceInitFunc) gtk_tree_selection_init - }; - - selection_type = g_type_register_static (G_TYPE_OBJECT, "GtkTreeSelection", &selection_info, 0); - } +static GParamSpec *properties[N_PROPERTIES]; +static guint tree_selection_signals [LAST_SIGNAL] = { 0 }; - return selection_type; -} +G_DEFINE_TYPE (GtkTreeSelection, gtk_tree_selection, G_TYPE_OBJECT) static void gtk_tree_selection_class_init (GtkTreeSelectionClass *class) @@ -74,41 +109,125 @@ gtk_tree_selection_class_init (GtkTreeSelectionClass *class) GObjectClass *object_class; object_class = (GObjectClass*) class; - parent_class = g_type_class_peek_parent (class); object_class->finalize = gtk_tree_selection_finalize; + object_class->set_property = gtk_tree_selection_set_property; + object_class->get_property = gtk_tree_selection_get_property; class->changed = NULL; + /* Properties */ + + /** + * GtkTreeSelection:mode: + * + * Selection mode. + * See gtk_tree_selection_set_mode() for more information on this property. + * + * Since: 3.2 + */ + properties[PROP_MODE] = g_param_spec_enum ("mode", + P_("Mode"), + P_("Selection mode"), + GTK_TYPE_SELECTION_MODE, + GTK_SELECTION_SINGLE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + /* Install all properties */ + g_object_class_install_properties (object_class, N_PROPERTIES, properties); + + /* Signals */ + + /** + * GtkTreeSelection::changed: + * @treeselection: the object which received the signal. + * + * Emitted whenever the selection has (possibly) changed. Please note that + * this signal is mostly a hint. It may only be emitted once when a range + * of rows are selected, and it may occasionally be emitted when nothing + * has happened. + */ tree_selection_signals[CHANGED] = - gtk_signal_new ("changed", - GTK_RUN_FIRST, - GTK_CLASS_TYPE (object_class), - GTK_SIGNAL_OFFSET (GtkTreeSelectionClass, changed), - gtk_marshal_VOID__VOID, - GTK_TYPE_NONE, 0); + g_signal_new (I_("changed"), + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GtkTreeSelectionClass, changed), + NULL, NULL, + _gtk_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + g_type_class_add_private (class, sizeof (GtkTreeSelectionPrivate)); } static void gtk_tree_selection_init (GtkTreeSelection *selection) { - selection->type = GTK_TREE_SELECTION_SINGLE; + GtkTreeSelectionPrivate *priv; + + selection->priv = G_TYPE_INSTANCE_GET_PRIVATE (selection, + GTK_TYPE_TREE_SELECTION, + GtkTreeSelectionPrivate); + priv = selection->priv; + + priv->type = GTK_SELECTION_SINGLE; } static void gtk_tree_selection_finalize (GObject *object) { - if (GTK_TREE_SELECTION (object)->destroy) - (* GTK_TREE_SELECTION (object)->destroy) (GTK_TREE_SELECTION (object)->user_data); + GtkTreeSelection *selection = GTK_TREE_SELECTION (object); + GtkTreeSelectionPrivate *priv = selection->priv; + + if (priv->destroy) + priv->destroy (priv->user_data); /* chain parent_class' handler */ - G_OBJECT_CLASS (parent_class)->finalize (object); + G_OBJECT_CLASS (gtk_tree_selection_parent_class)->finalize (object); +} + +static void +gtk_tree_selection_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + g_return_if_fail (GTK_IS_TREE_SELECTION (object)); + + switch (prop_id) + { + case PROP_MODE: + gtk_tree_selection_set_mode (GTK_TREE_SELECTION (object), g_value_get_enum (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_tree_selection_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + g_return_if_fail (GTK_IS_TREE_SELECTION (object)); + + switch (prop_id) + { + case PROP_MODE: + g_value_set_enum (value, gtk_tree_selection_get_mode (GTK_TREE_SELECTION (object))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } } /** * _gtk_tree_selection_new: * * Creates a new #GtkTreeSelection object. This function should not be invoked, - * as each #GtkTreeView will create it's own #GtkTreeSelection. + * as each #GtkTreeView will create its own #GtkTreeSelection. * * Return value: A newly created #GtkTreeSelection object. **/ @@ -117,7 +236,7 @@ _gtk_tree_selection_new (void) { GtkTreeSelection *selection; - selection = GTK_TREE_SELECTION (g_object_new (GTK_TYPE_TREE_SELECTION, NULL)); + selection = g_object_new (GTK_TYPE_TREE_SELECTION, NULL); return selection; } @@ -127,7 +246,7 @@ _gtk_tree_selection_new (void) * @tree_view: The #GtkTreeView. * * Creates a new #GtkTreeSelection object. This function should not be invoked, - * as each #GtkTreeView will create it's own #GtkTreeSelection. + * as each #GtkTreeView will create its own #GtkTreeSelection. * * Return value: A newly created #GtkTreeSelection object. **/ @@ -156,11 +275,15 @@ void _gtk_tree_selection_set_tree_view (GtkTreeSelection *selection, GtkTreeView *tree_view) { + GtkTreeSelectionPrivate *priv; + g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); if (tree_view != NULL) g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - selection->tree_view = tree_view; + priv = selection->priv; + + priv->tree_view = tree_view; } /** @@ -169,53 +292,76 @@ _gtk_tree_selection_set_tree_view (GtkTreeSelection *selection, * @type: The selection mode * * Sets the selection mode of the @selection. If the previous type was - * #GTK_TREE_SELECTION_MULTI and @type is #GTK_TREE_SELECTION_SINGLE, then - * the anchor is kept selected, if it was previously selected. + * #GTK_SELECTION_MULTIPLE, then the anchor is kept selected, if it was + * previously selected. **/ void -gtk_tree_selection_set_mode (GtkTreeSelection *selection, - GtkTreeSelectionMode type) +gtk_tree_selection_set_mode (GtkTreeSelection *selection, + GtkSelectionMode type) { + GtkTreeSelectionPrivate *priv; + GtkTreeSelectionFunc tmp_func; + g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); - if (selection->type == type) + priv = selection->priv; + + if (priv->type == type) return; - if (type == GTK_TREE_SELECTION_SINGLE) + if (type == GTK_SELECTION_NONE) + { + /* We do this so that we unconditionally unset all rows + */ + tmp_func = priv->user_func; + priv->user_func = NULL; + gtk_tree_selection_unselect_all (selection); + priv->user_func = tmp_func; + + _gtk_tree_view_set_anchor_path (priv->tree_view, NULL); + } + else if (type == GTK_SELECTION_SINGLE || + type == GTK_SELECTION_BROWSE) { GtkRBTree *tree = NULL; GtkRBNode *node = NULL; gint selected = FALSE; + GtkTreePath *anchor_path = NULL; - if (selection->tree_view->priv->anchor) - { - GtkTreePath *anchor_path; - - anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor); + anchor_path = _gtk_tree_view_get_anchor_path (priv->tree_view); - if (anchor_path) - { - _gtk_tree_view_find_node (selection->tree_view, - anchor_path, - &tree, - &node); - - if (node && GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED)) - selected = TRUE; - - gtk_tree_path_free (anchor_path); - } + if (anchor_path) + { + _gtk_tree_view_find_node (priv->tree_view, + anchor_path, + &tree, + &node); + + if (node && GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED)) + selected = TRUE; } - /* FIXME: if user_func is set, then it needs to unconditionally unselect - * all. + + /* We do this so that we unconditionally unset all rows */ + tmp_func = priv->user_func; + priv->user_func = NULL; gtk_tree_selection_unselect_all (selection); + priv->user_func = tmp_func; - /* FIXME are we properly emitting the selection_changed signal here? */ if (node && selected) - GTK_RBNODE_SET_FLAG (node, GTK_RBNODE_IS_SELECTED); + _gtk_tree_selection_internal_select_node (selection, + node, + tree, + anchor_path, + 0, + FALSE); + if (anchor_path) + gtk_tree_path_free (anchor_path); } - selection->type = type; + + priv->type = type; + + g_object_notify_by_pspec (G_OBJECT (selection), properties[PROP_MODE]); } /** @@ -227,40 +373,68 @@ gtk_tree_selection_set_mode (GtkTreeSelection *selection, * * Return value: the current selection mode **/ -GtkTreeSelectionMode +GtkSelectionMode gtk_tree_selection_get_mode (GtkTreeSelection *selection) { - g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), GTK_TREE_SELECTION_SINGLE); + g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), GTK_SELECTION_SINGLE); - return selection->type; + return selection->priv->type; } /** * gtk_tree_selection_set_select_function: * @selection: A #GtkTreeSelection. - * @func: The selection function. - * @data: The selection function's data. - * @destroy: The destroy function for user data. May be NULL. + * @func: The selection function. May be %NULL + * @data: The selection function's data. May be %NULL + * @destroy: The destroy function for user data. May be %NULL * - * Sets the selection function. If set, this function is called before any node - * is selected or unselected, giving some control over which nodes are selected. - **/ + * Sets the selection function. + * + * If set, this function is called before any node is selected or unselected, + * giving some control over which nodes are selected. The select function + * should return %TRUE if the state of the node may be toggled, and %FALSE + * if the state of the node should be left unchanged. + */ void gtk_tree_selection_set_select_function (GtkTreeSelection *selection, GtkTreeSelectionFunc func, gpointer data, - GtkDestroyNotify destroy) + GDestroyNotify destroy) { + GtkTreeSelectionPrivate *priv; + g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); - g_return_if_fail (func != NULL); - selection->user_func = func; - selection->user_data = data; - selection->destroy = destroy; + priv = selection->priv; + + if (priv->destroy) + priv->destroy (priv->user_data); + + priv->user_func = func; + priv->user_data = data; + priv->destroy = destroy; } /** - * gtk_tree_selection_get_user_data: + * gtk_tree_selection_get_select_function: (skip) + * @selection: A #GtkTreeSelection. + * + * Returns the current selection function. + * + * Return value: The function. + * + * Since: 2.14 + **/ +GtkTreeSelectionFunc +gtk_tree_selection_get_select_function (GtkTreeSelection *selection) +{ + g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), NULL); + + return selection->priv->user_func; +} + +/** + * gtk_tree_selection_get_user_data: (skip) * @selection: A #GtkTreeSelection. * * Returns the user data for the selection function. @@ -272,28 +446,36 @@ gtk_tree_selection_get_user_data (GtkTreeSelection *selection) { g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), NULL); - return selection->user_data; + return selection->priv->user_data; } -GtkTreeView* +/** + * gtk_tree_selection_get_tree_view: + * @selection: A #GtkTreeSelection + * + * Returns the tree view associated with @selection. + * + * Return value: (transfer none): A #GtkTreeView + **/ +GtkTreeView * gtk_tree_selection_get_tree_view (GtkTreeSelection *selection) { g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), NULL); - return selection->tree_view; + return selection->priv->tree_view; } /** * gtk_tree_selection_get_selected: * @selection: A #GtkTreeSelection. - * @model: A pointer set to the #GtkTreeModel, or NULL. - * @iter: The #GtkTreeIter, or NULL. + * @model: (out) (allow-none) (transfer none): A pointer to set to the #GtkTreeModel, or NULL. + * @iter: (out) (allow-none): The #GtkTreeIter, or NULL. * * Sets @iter to the currently selected node if @selection is set to - * #GTK_TREE_SELECTION_SINGLE. @iter may be NULL if you just want to test if - * @selection has any selected nodes. @model is filled with the current model - * as a convenience. This function will not work if you use @selection is - * #GTK_TREE_SELECTION_MULTI. + * #GTK_SELECTION_SINGLE or #GTK_SELECTION_BROWSE. @iter may be NULL if you + * just want to test if @selection has any selected nodes. @model is filled + * with the current model as a convenience. This function will not work if you + * use @selection is #GTK_SELECTION_MULTIPLE. * * Return value: TRUE, if there is a selected node. **/ @@ -302,50 +484,56 @@ gtk_tree_selection_get_selected (GtkTreeSelection *selection, GtkTreeModel **model, GtkTreeIter *iter) { + GtkTreeSelectionPrivate *priv; GtkRBTree *tree; GtkRBNode *node; GtkTreePath *anchor_path; gboolean retval; + gboolean found_node; g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), FALSE); - g_return_val_if_fail (selection->type == GTK_TREE_SELECTION_SINGLE, FALSE); - g_return_val_if_fail (selection->tree_view != NULL, FALSE); - g_return_val_if_fail (selection->tree_view->priv->model != NULL, FALSE); - if (model) - *model = selection->tree_view->priv->model; + priv = selection->priv; - if (selection->tree_view->priv->anchor == NULL) - return FALSE; + g_return_val_if_fail (priv->type != GTK_SELECTION_MULTIPLE, FALSE); + g_return_val_if_fail (priv->tree_view != NULL, FALSE); + + /* Clear the iter */ + if (iter) + memset (iter, 0, sizeof (GtkTreeIter)); + + if (model) + *model = gtk_tree_view_get_model (priv->tree_view); - anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor); + anchor_path = _gtk_tree_view_get_anchor_path (priv->tree_view); if (anchor_path == NULL) return FALSE; - if (iter == NULL) - { - gtk_tree_path_free (anchor_path); - return TRUE; - } - retval = FALSE; - if (!_gtk_tree_view_find_node (selection->tree_view, - anchor_path, - &tree, - &node) && - ! GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED)) + found_node = !_gtk_tree_view_find_node (priv->tree_view, + anchor_path, + &tree, + &node); + + if (found_node && GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED)) { - /* We don't want to return the anchor if it isn't actually selected. + /* we only want to return the anchor if it exists in the rbtree and + * is selected. */ - retval = FALSE; + if (iter == NULL) + retval = TRUE; + else + retval = gtk_tree_model_get_iter (gtk_tree_view_get_model (priv->tree_view), + iter, + anchor_path); } else { - retval = gtk_tree_model_get_iter (selection->tree_view->priv->model, - iter, - anchor_path); + /* We don't want to return the anchor if it isn't actually selected. + */ + retval = FALSE; } gtk_tree_path_free (anchor_path); @@ -353,116 +541,331 @@ gtk_tree_selection_get_selected (GtkTreeSelection *selection, return retval; } +/** + * gtk_tree_selection_get_selected_rows: + * @selection: A #GtkTreeSelection. + * @model: (out) (allow-none) (transfer none): A pointer to set to the #GtkTreeModel, or %NULL. + * + * Creates a list of path of all selected rows. Additionally, if you are + * planning on modifying the model after calling this function, you may + * want to convert the returned list into a list of #GtkTreeRowReferences. + * To do this, you can use gtk_tree_row_reference_new(). + * + * To free the return value, use: + * |[ + * g_list_free_full (list, (GDestroyNotify) gtk_tree_path_free); + * ]| + * + * Return value: (element-type GtkTreePath) (transfer full): A #GList containing a #GtkTreePath for each selected row. + * + * Since: 2.2 + **/ +GList * +gtk_tree_selection_get_selected_rows (GtkTreeSelection *selection, + GtkTreeModel **model) +{ + GtkTreeSelectionPrivate *priv; + GList *list = NULL; + GtkRBTree *tree = NULL; + GtkRBNode *node = NULL; + GtkTreePath *path; + + g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), NULL); + + priv = selection->priv; + + g_return_val_if_fail (priv->tree_view != NULL, NULL); + + if (model) + *model = gtk_tree_view_get_model (priv->tree_view); + + tree = _gtk_tree_view_get_rbtree (priv->tree_view); + + if (tree == NULL || tree->root == NULL) + return NULL; + + if (priv->type == GTK_SELECTION_NONE) + return NULL; + else if (priv->type != GTK_SELECTION_MULTIPLE) + { + GtkTreeIter iter; + + if (gtk_tree_selection_get_selected (selection, NULL, &iter)) + { + GtkTreePath *path; + + path = gtk_tree_model_get_path (gtk_tree_view_get_model (priv->tree_view), &iter); + list = g_list_append (list, path); + + return list; + } + + return NULL; + } + + node = _gtk_rbtree_first (tree); + path = gtk_tree_path_new_first (); + + do + { + if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED)) + list = g_list_prepend (list, gtk_tree_path_copy (path)); + + if (node->children) + { + tree = node->children; + node = _gtk_rbtree_first (tree); + + gtk_tree_path_append_index (path, 0); + } + else + { + gboolean done = FALSE; + + do + { + node = _gtk_rbtree_next (tree, node); + if (node != NULL) + { + done = TRUE; + gtk_tree_path_next (path); + } + else + { + node = tree->parent_node; + tree = tree->parent_tree; + + if (!tree) + { + gtk_tree_path_free (path); + + goto done; + } + + gtk_tree_path_up (path); + } + } + while (!done); + } + } + while (TRUE); + + gtk_tree_path_free (path); + + done: + return g_list_reverse (list); +} + +static void +gtk_tree_selection_count_selected_rows_helper (GtkRBTree *tree, + GtkRBNode *node, + gpointer data) +{ + gint *count = (gint *)data; + + if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED)) + (*count)++; + + if (node->children) + _gtk_rbtree_traverse (node->children, node->children->root, + G_PRE_ORDER, + gtk_tree_selection_count_selected_rows_helper, data); +} + +/** + * gtk_tree_selection_count_selected_rows: + * @selection: A #GtkTreeSelection. + * + * Returns the number of rows that have been selected in @tree. + * + * Return value: The number of rows selected. + * + * Since: 2.2 + **/ +gint +gtk_tree_selection_count_selected_rows (GtkTreeSelection *selection) +{ + GtkTreeSelectionPrivate *priv; + gint count = 0; + GtkRBTree *tree; + + g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), 0); + + priv = selection->priv; + + g_return_val_if_fail (priv->tree_view != NULL, 0); + + tree = _gtk_tree_view_get_rbtree (priv->tree_view); + + if (tree == NULL || tree->root == NULL) + return 0; + + if (priv->type == GTK_SELECTION_SINGLE || + priv->type == GTK_SELECTION_BROWSE) + { + if (gtk_tree_selection_get_selected (selection, NULL, NULL)) + return 1; + else + return 0; + } + + _gtk_rbtree_traverse (tree, tree->root, + G_PRE_ORDER, + gtk_tree_selection_count_selected_rows_helper, + &count); + + return count; +} + +/* gtk_tree_selection_selected_foreach helper */ +static void +model_changed (gpointer data) +{ + gboolean *stop = (gboolean *)data; + + *stop = TRUE; +} + /** * gtk_tree_selection_selected_foreach: * @selection: A #GtkTreeSelection. - * @func: The function to call for each selected node. + * @func: (scope call): The function to call for each selected node. * @data: user data to pass to the function. * - * Calls a function for each selected node. + * Calls a function for each selected node. Note that you cannot modify + * the tree or selection from within this function. As a result, + * gtk_tree_selection_get_selected_rows() might be more useful. **/ void gtk_tree_selection_selected_foreach (GtkTreeSelection *selection, GtkTreeSelectionForeachFunc func, gpointer data) { + GtkTreeSelectionPrivate *priv; GtkTreePath *path; GtkRBTree *tree; GtkRBNode *node; GtkTreeIter iter; + GtkTreeModel *model; + + gulong inserted_id, deleted_id, reordered_id, changed_id; + gboolean stop = FALSE; g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); - g_return_if_fail (selection->tree_view != NULL); - g_return_if_fail (selection->tree_view->priv->model != NULL); - if (func == NULL || - selection->tree_view->priv->tree == NULL || - selection->tree_view->priv->tree->root == NULL) + priv = selection->priv; + + g_return_if_fail (priv->tree_view != NULL); + + tree = _gtk_tree_view_get_rbtree (priv->tree_view); + + if (func == NULL || tree == NULL || tree->root == NULL) return; - if (selection->type == GTK_TREE_SELECTION_SINGLE) + model = gtk_tree_view_get_model (priv->tree_view); + + if (priv->type == GTK_SELECTION_SINGLE || + priv->type == GTK_SELECTION_BROWSE) { - if (gtk_tree_row_reference_valid (selection->tree_view->priv->anchor)) + path = _gtk_tree_view_get_anchor_path (priv->tree_view); + + if (path) { - path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor); - gtk_tree_model_get_iter (selection->tree_view->priv->model, &iter, path); - (* func) (selection->tree_view->priv->model, path, &iter, data); + gtk_tree_model_get_iter (model, &iter, path); + (* func) (model, path, &iter, data); gtk_tree_path_free (path); } return; } - tree = selection->tree_view->priv->tree; - node = selection->tree_view->priv->tree->root; - - while (node->left != tree->nil) - node = node->left; + node = _gtk_rbtree_first (tree); + + g_object_ref (model); + + /* connect to signals to monitor changes in treemodel */ + inserted_id = g_signal_connect_swapped (model, "row-inserted", + G_CALLBACK (model_changed), + &stop); + deleted_id = g_signal_connect_swapped (model, "row-deleted", + G_CALLBACK (model_changed), + &stop); + reordered_id = g_signal_connect_swapped (model, "rows-reordered", + G_CALLBACK (model_changed), + &stop); + changed_id = g_signal_connect_swapped (priv->tree_view, "notify::model", + G_CALLBACK (model_changed), + &stop); /* find the node internally */ - path = gtk_tree_path_new_root (); - gtk_tree_model_get_iter (selection->tree_view->priv->model, - &iter, path); + path = gtk_tree_path_new_first (); do { if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED)) - (* func) (selection->tree_view->priv->model, path, &iter, data); + { + gtk_tree_model_get_iter (model, &iter, path); + (* func) (model, path, &iter, data); + } + + if (stop) + goto out; + if (node->children) { - gboolean has_child; - GtkTreeIter tmp; - tree = node->children; - node = tree->root; - while (node->left != tree->nil) - node = node->left; - tmp = iter; - has_child = gtk_tree_model_iter_children (selection->tree_view->priv->model, &iter, &tmp); + node = _gtk_rbtree_first (tree); + gtk_tree_path_append_index (path, 0); - /* Sanity Check! */ - TREE_VIEW_INTERNAL_ASSERT_VOID (has_child); } else { gboolean done = FALSE; + do { node = _gtk_rbtree_next (tree, node); if (node != NULL) { - gboolean has_next; - - has_next = gtk_tree_model_iter_next (selection->tree_view->priv->model, &iter); done = TRUE; gtk_tree_path_next (path); - - /* Sanity Check! */ - TREE_VIEW_INTERNAL_ASSERT_VOID (has_next); } else { - gboolean has_parent; - GtkTreeIter tmp_iter = iter; - node = tree->parent_node; tree = tree->parent_tree; + if (tree == NULL) { - gtk_tree_path_free (path); /* we've run out of tree */ /* We're done with this function */ - return; + + goto out; } - has_parent = gtk_tree_model_iter_parent (selection->tree_view->priv->model, &iter, &tmp_iter); + gtk_tree_path_up (path); - /* Sanity check */ - TREE_VIEW_INTERNAL_ASSERT_VOID (has_parent); } } while (!done); } } while (TRUE); + +out: + if (path) + gtk_tree_path_free (path); + + g_signal_handler_disconnect (model, inserted_id); + g_signal_handler_disconnect (model, deleted_id); + g_signal_handler_disconnect (model, reordered_id); + g_signal_handler_disconnect (priv->tree_view, changed_id); + g_object_unref (model); + + /* check if we have to spew a scary message */ + if (stop) + g_warning ("The model has been modified from within gtk_tree_selection_selected_foreach.\n" + "This function is for observing the selections of the tree only. If\n" + "you are trying to get all selected items from the tree, try using\n" + "gtk_tree_selection_get_selected_rows instead.\n"); } /** @@ -476,30 +879,37 @@ void gtk_tree_selection_select_path (GtkTreeSelection *selection, GtkTreePath *path) { + GtkTreeSelectionPrivate *priv; GtkRBNode *node; GtkRBTree *tree; - GdkModifierType state = 0; + gboolean ret; + GtkTreeSelectMode mode = 0; g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); - g_return_if_fail (selection->tree_view != NULL); + + priv = selection->priv; + + g_return_if_fail (priv->tree_view != NULL); g_return_if_fail (path != NULL); - _gtk_tree_view_find_node (selection->tree_view, - path, - &tree, - &node); + ret = _gtk_tree_view_find_node (priv->tree_view, + path, + &tree, + &node); - if (node == NULL || GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED)) + if (node == NULL || GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED) || + ret == TRUE) return; - if (selection->type == GTK_TREE_SELECTION_MULTI) - state = GDK_CONTROL_MASK; + if (priv->type == GTK_SELECTION_MULTIPLE) + mode = GTK_TREE_SELECT_MODE_TOGGLE; _gtk_tree_selection_internal_select_node (selection, node, tree, path, - state); + mode, + FALSE); } /** @@ -513,26 +923,33 @@ void gtk_tree_selection_unselect_path (GtkTreeSelection *selection, GtkTreePath *path) { + GtkTreeSelectionPrivate *priv; GtkRBNode *node; GtkRBTree *tree; + gboolean ret; g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); - g_return_if_fail (selection->tree_view != NULL); + + priv = selection->priv; + + g_return_if_fail (priv->tree_view != NULL); g_return_if_fail (path != NULL); - _gtk_tree_view_find_node (selection->tree_view, - path, - &tree, - &node); + ret = _gtk_tree_view_find_node (priv->tree_view, + path, + &tree, + &node); - if (node == NULL || !GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED)) + if (node == NULL || !GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED) || + ret == TRUE) return; _gtk_tree_selection_internal_select_node (selection, node, tree, path, - GDK_CONTROL_MASK); + GTK_TREE_SELECT_MODE_TOGGLE, + TRUE); } /** @@ -546,15 +963,21 @@ void gtk_tree_selection_select_iter (GtkTreeSelection *selection, GtkTreeIter *iter) { + GtkTreeSelectionPrivate *priv; GtkTreePath *path; + GtkTreeModel *model; g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); - g_return_if_fail (selection->tree_view != NULL); - g_return_if_fail (selection->tree_view->priv->model != NULL); + + priv = selection->priv; + + g_return_if_fail (priv->tree_view != NULL); + + model = gtk_tree_view_get_model (priv->tree_view); + g_return_if_fail (model != NULL); g_return_if_fail (iter != NULL); - path = gtk_tree_model_get_path (selection->tree_view->priv->model, - iter); + path = gtk_tree_model_get_path (model, iter); if (path == NULL) return; @@ -575,23 +998,109 @@ void gtk_tree_selection_unselect_iter (GtkTreeSelection *selection, GtkTreeIter *iter) { + GtkTreeSelectionPrivate *priv; GtkTreePath *path; + GtkTreeModel *model; g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); - g_return_if_fail (selection->tree_view != NULL); - g_return_if_fail (selection->tree_view->priv->model != NULL); + + priv = selection->priv; + + g_return_if_fail (priv->tree_view != NULL); + + model = gtk_tree_view_get_model (priv->tree_view); + g_return_if_fail (model != NULL); g_return_if_fail (iter != NULL); - path = gtk_tree_model_get_path (selection->tree_view->priv->model, - iter); + path = gtk_tree_model_get_path (model, iter); if (path == NULL) return; - gtk_tree_selection_select_path (selection, path); + gtk_tree_selection_unselect_path (selection, path); + gtk_tree_path_free (path); +} + +/** + * gtk_tree_selection_path_is_selected: + * @selection: A #GtkTreeSelection. + * @path: A #GtkTreePath to check selection on. + * + * Returns %TRUE if the row pointed to by @path is currently selected. If @path + * does not point to a valid location, %FALSE is returned + * + * Return value: %TRUE if @path is selected. + **/ +gboolean +gtk_tree_selection_path_is_selected (GtkTreeSelection *selection, + GtkTreePath *path) +{ + GtkTreeSelectionPrivate *priv; + GtkRBNode *node; + GtkRBTree *tree; + gboolean ret; + + g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), FALSE); + + priv = selection->priv; + + g_return_val_if_fail (path != NULL, FALSE); + g_return_val_if_fail (priv->tree_view != NULL, FALSE); + + if (gtk_tree_view_get_model (priv->tree_view) == NULL) + return FALSE; + + ret = _gtk_tree_view_find_node (priv->tree_view, + path, + &tree, + &node); + + if ((node == NULL) || !GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED) || + ret == TRUE) + return FALSE; + + return TRUE; +} + +/** + * gtk_tree_selection_iter_is_selected: + * @selection: A #GtkTreeSelection + * @iter: A valid #GtkTreeIter + * + * Returns %TRUE if the row at @iter is currently selected. + * + * Return value: %TRUE, if @iter is selected + **/ +gboolean +gtk_tree_selection_iter_is_selected (GtkTreeSelection *selection, + GtkTreeIter *iter) +{ + GtkTreeSelectionPrivate *priv; + GtkTreePath *path; + GtkTreeModel *model; + gboolean retval; + + g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), FALSE); + + priv = selection->priv; + + g_return_val_if_fail (iter != NULL, FALSE); + g_return_val_if_fail (priv->tree_view != NULL, FALSE); + + model = gtk_tree_view_get_model (priv->tree_view); + g_return_val_if_fail (model != NULL, FALSE); + + path = gtk_tree_model_get_path (model, iter); + if (path == NULL) + return FALSE; + + retval = gtk_tree_selection_path_is_selected (selection, path); gtk_tree_path_free (path); + + return retval; } + /* Wish I was in python, right now... */ struct _TempTuple { GtkTreeSelection *selection; @@ -624,9 +1133,13 @@ select_all_helper (GtkRBTree *tree, static gint gtk_tree_selection_real_select_all (GtkTreeSelection *selection) { + GtkTreeSelectionPrivate *priv = selection->priv; struct _TempTuple *tuple; + GtkRBTree *tree; - if (selection->tree_view->priv->tree == NULL) + tree = _gtk_tree_view_get_rbtree (priv->tree_view); + + if (tree == NULL) return FALSE; /* Mark all nodes selected */ @@ -634,8 +1147,7 @@ gtk_tree_selection_real_select_all (GtkTreeSelection *selection) tuple->selection = selection; tuple->dirty = FALSE; - _gtk_rbtree_traverse (selection->tree_view->priv->tree, - selection->tree_view->priv->tree->root, + _gtk_rbtree_traverse (tree, tree->root, G_PRE_ORDER, select_all_helper, tuple); @@ -652,20 +1164,28 @@ gtk_tree_selection_real_select_all (GtkTreeSelection *selection) * gtk_tree_selection_select_all: * @selection: A #GtkTreeSelection. * - * Selects all the nodes. @selection is must be set to - * #GTK_TREE_SELECTION_MULTI mode. + * Selects all the nodes. @selection must be set to #GTK_SELECTION_MULTIPLE + * mode. **/ void gtk_tree_selection_select_all (GtkTreeSelection *selection) { + GtkTreeSelectionPrivate *priv; + g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); - g_return_if_fail (selection->tree_view != NULL); - if (selection->tree_view->priv->tree == NULL) + + priv = selection->priv; + + g_return_if_fail (priv->tree_view != NULL); + + if (_gtk_tree_view_get_rbtree (priv->tree_view) == NULL || + gtk_tree_view_get_model (priv->tree_view) == NULL) return; - g_return_if_fail (selection->type != GTK_TREE_SELECTION_MULTI); + + g_return_if_fail (priv->type == GTK_SELECTION_MULTIPLE); if (gtk_tree_selection_real_select_all (selection)) - g_signal_emit (G_OBJECT (selection), tree_selection_signals[CHANGED], 0); + g_signal_emit (selection, tree_selection_signals[CHANGED], 0); } static void @@ -687,26 +1207,25 @@ unselect_all_helper (GtkRBTree *tree, } } -static gint +static gboolean gtk_tree_selection_real_unselect_all (GtkTreeSelection *selection) { + GtkTreeSelectionPrivate *priv = selection->priv; struct _TempTuple *tuple; - if (selection->type == GTK_TREE_SELECTION_SINGLE) + if (priv->type == GTK_SELECTION_SINGLE || + priv->type == GTK_SELECTION_BROWSE) { GtkRBTree *tree = NULL; GtkRBNode *node = NULL; GtkTreePath *anchor_path; - if (selection->tree_view->priv->anchor == NULL) - return FALSE; - - anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor); + anchor_path = _gtk_tree_view_get_anchor_path (priv->tree_view); if (anchor_path == NULL) return FALSE; - _gtk_tree_view_find_node (selection->tree_view, + _gtk_tree_view_find_node (priv->tree_view, anchor_path, &tree, &node); @@ -720,8 +1239,7 @@ gtk_tree_selection_real_unselect_all (GtkTreeSelection *selection) { if (gtk_tree_selection_real_select_node (selection, tree, node, FALSE)) { - gtk_tree_row_reference_free (selection->tree_view->priv->anchor); - selection->tree_view->priv->anchor = NULL; + _gtk_tree_view_set_anchor_path (priv->tree_view, NULL); return TRUE; } } @@ -729,12 +1247,14 @@ gtk_tree_selection_real_unselect_all (GtkTreeSelection *selection) } else { + GtkRBTree *tree; + tuple = g_new (struct _TempTuple, 1); tuple->selection = selection; tuple->dirty = FALSE; - _gtk_rbtree_traverse (selection->tree_view->priv->tree, - selection->tree_view->priv->tree->root, + tree = _gtk_tree_view_get_rbtree (priv->tree_view); + _gtk_rbtree_traverse (tree, tree->root, G_PRE_ORDER, unselect_all_helper, tuple); @@ -758,68 +1278,84 @@ gtk_tree_selection_real_unselect_all (GtkTreeSelection *selection) void gtk_tree_selection_unselect_all (GtkTreeSelection *selection) { + GtkTreeSelectionPrivate *priv; + g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); - g_return_if_fail (selection->tree_view != NULL); - if (selection->tree_view->priv->tree == NULL) + + priv = selection->priv; + + g_return_if_fail (priv->tree_view != NULL); + + if (_gtk_tree_view_get_rbtree (priv->tree_view) == NULL || + gtk_tree_view_get_model (priv->tree_view) == NULL) return; - if (selection->tree_view->priv->tree == NULL) - return; - if (gtk_tree_selection_real_unselect_all (selection)) - g_signal_emit (G_OBJECT (selection), tree_selection_signals[CHANGED], 0); + g_signal_emit (selection, tree_selection_signals[CHANGED], 0); } +enum +{ + RANGE_SELECT, + RANGE_UNSELECT +}; + static gint -gtk_tree_selection_real_select_range (GtkTreeSelection *selection, +gtk_tree_selection_real_modify_range (GtkTreeSelection *selection, + gint mode, GtkTreePath *start_path, GtkTreePath *end_path) { + GtkTreeSelectionPrivate *priv = selection->priv; GtkRBNode *start_node, *end_node; GtkRBTree *start_tree, *end_tree; + GtkTreePath *anchor_path = NULL; gboolean dirty = FALSE; switch (gtk_tree_path_compare (start_path, end_path)) { case 1: - _gtk_tree_view_find_node (selection->tree_view, + _gtk_tree_view_find_node (priv->tree_view, end_path, &start_tree, &start_node); - _gtk_tree_view_find_node (selection->tree_view, + _gtk_tree_view_find_node (priv->tree_view, start_path, &end_tree, &end_node); + anchor_path = start_path; break; case 0: - _gtk_tree_view_find_node (selection->tree_view, + _gtk_tree_view_find_node (priv->tree_view, start_path, &start_tree, &start_node); end_tree = start_tree; end_node = start_node; + anchor_path = start_path; break; case -1: - _gtk_tree_view_find_node (selection->tree_view, + _gtk_tree_view_find_node (priv->tree_view, start_path, &start_tree, &start_node); - _gtk_tree_view_find_node (selection->tree_view, + _gtk_tree_view_find_node (priv->tree_view, end_path, &end_tree, &end_node); + anchor_path = start_path; break; } g_return_val_if_fail (start_node != NULL, FALSE); g_return_val_if_fail (end_node != NULL, FALSE); + if (anchor_path) + _gtk_tree_view_set_anchor_path (priv->tree_view, anchor_path); + do { - if (GTK_RBNODE_FLAG_SET (start_node, GTK_RBNODE_IS_SELECTED)) - { - dirty = gtk_tree_selection_real_select_node (selection, start_tree, start_node, FALSE); - } + dirty |= gtk_tree_selection_real_select_node (selection, start_tree, start_node, (mode == RANGE_SELECT)?TRUE:FALSE); if (start_node == end_node) break; @@ -827,32 +1363,17 @@ gtk_tree_selection_real_select_range (GtkTreeSelection *selection, if (start_node->children) { start_tree = start_node->children; - start_node = start_tree->root; - while (start_node->left != start_tree->nil) - start_node = start_node->left; + start_node = _gtk_rbtree_first (start_tree); } else { - gboolean done = FALSE; - do + _gtk_rbtree_next_full (start_tree, start_node, &start_tree, &start_node); + if (start_tree == NULL) { - start_node = _gtk_rbtree_next (start_tree, start_node); - if (start_node != NULL) - { - done = TRUE; - } - else - { - start_node = start_tree->parent_node; - start_tree = start_tree->parent_tree; - if (start_tree == NULL) - /* FIXME should this really be silent, or should it g_warning? */ - /* we've run out of tree */ - /* This means we never found end node!! */ - break; - } + /* we just ran out of tree. That means someone passed in bogus values. + */ + return dirty; } - while (!done); } } while (TRUE); @@ -867,63 +1388,158 @@ gtk_tree_selection_real_select_range (GtkTreeSelection *selection, * @end_path: The final node of the range. * * Selects a range of nodes, determined by @start_path and @end_path inclusive. + * @selection must be set to #GTK_SELECTION_MULTIPLE mode. **/ void gtk_tree_selection_select_range (GtkTreeSelection *selection, GtkTreePath *start_path, GtkTreePath *end_path) { + GtkTreeSelectionPrivate *priv; + g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); - g_return_if_fail (selection->tree_view != NULL); - if (gtk_tree_selection_real_select_range (selection, start_path, end_path)) - g_signal_emit (G_OBJECT (selection), tree_selection_signals[CHANGED], 0); + priv = selection->priv; + + g_return_if_fail (priv->tree_view != NULL); + g_return_if_fail (priv->type == GTK_SELECTION_MULTIPLE); + g_return_if_fail (gtk_tree_view_get_model (priv->tree_view) != NULL); + + if (gtk_tree_selection_real_modify_range (selection, RANGE_SELECT, start_path, end_path)) + g_signal_emit (selection, tree_selection_signals[CHANGED], 0); } +/** + * gtk_tree_selection_unselect_range: + * @selection: A #GtkTreeSelection. + * @start_path: The initial node of the range. + * @end_path: The initial node of the range. + * + * Unselects a range of nodes, determined by @start_path and @end_path + * inclusive. + * + * Since: 2.2 + **/ +void +gtk_tree_selection_unselect_range (GtkTreeSelection *selection, + GtkTreePath *start_path, + GtkTreePath *end_path) +{ + GtkTreeSelectionPrivate *priv; + + g_return_if_fail (GTK_IS_TREE_SELECTION (selection)); + + priv = selection->priv; + + g_return_if_fail (priv->tree_view != NULL); + g_return_if_fail (gtk_tree_view_get_model (priv->tree_view) != NULL); + + if (gtk_tree_selection_real_modify_range (selection, RANGE_UNSELECT, start_path, end_path)) + g_signal_emit (selection, tree_selection_signals[CHANGED], 0); +} + +gboolean +_gtk_tree_selection_row_is_selectable (GtkTreeSelection *selection, + GtkRBNode *node, + GtkTreePath *path) +{ + GtkTreeSelectionPrivate *priv = selection->priv; + GtkTreeIter iter; + GtkTreeModel *model; + GtkTreeViewRowSeparatorFunc separator_func; + gpointer separator_data; + gboolean sensitive = FALSE; + + model = gtk_tree_view_get_model (priv->tree_view); + + _gtk_tree_view_get_row_separator_func (priv->tree_view, + &separator_func, &separator_data); + + if (!gtk_tree_model_get_iter (model, &iter, path)) + sensitive = TRUE; + + if (!sensitive && separator_func) + { + /* never allow separators to be selected */ + if ((* separator_func) (model, &iter, separator_data)) + return FALSE; + } + + if (priv->user_func) + return (*priv->user_func) (selection, model, path, + GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED), + priv->user_data); + else + return TRUE; +} + + /* Called internally by gtktreeview.c It handles actually selecting the tree. */ + +/* + * docs about the 'override_browse_mode', we set this flag when we want to + * unset select the node and override the select browse mode behaviour (that is + * 'one node should *always* be selected'). + */ void _gtk_tree_selection_internal_select_node (GtkTreeSelection *selection, GtkRBNode *node, GtkRBTree *tree, GtkTreePath *path, - GdkModifierType state) + GtkTreeSelectMode mode, + gboolean override_browse_mode) { + GtkTreeSelectionPrivate *priv = selection->priv; gint flags; gint dirty = FALSE; GtkTreePath *anchor_path = NULL; + if (priv->type == GTK_SELECTION_NONE) + return; - if (selection->tree_view->priv->anchor) - anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor); + anchor_path = _gtk_tree_view_get_anchor_path (priv->tree_view); - if (selection->type == GTK_TREE_SELECTION_SINGLE) + if (priv->type == GTK_SELECTION_SINGLE || + priv->type == GTK_SELECTION_BROWSE) { + /* just unselect */ + if (priv->type == GTK_SELECTION_BROWSE && override_browse_mode) + { + dirty = gtk_tree_selection_real_unselect_all (selection); + } /* Did we try to select the same node again? */ - if (anchor_path && gtk_tree_path_compare (path, anchor_path) == 0) + else if (priv->type == GTK_SELECTION_SINGLE && + anchor_path && gtk_tree_path_compare (path, anchor_path) == 0) { - if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK) + if ((mode & GTK_TREE_SELECT_MODE_TOGGLE) == GTK_TREE_SELECT_MODE_TOGGLE) { dirty = gtk_tree_selection_real_unselect_all (selection); } } else { - /* FIXME: We only want to select the new node if we can unselect the - * old one, and we can select the new one. We are currently - * unselecting the old one first, then trying the new one. */ if (anchor_path) { - dirty = gtk_tree_selection_real_unselect_all (selection); + /* We only want to select the new node if we can unselect the old one, + * and we can select the new one. */ + dirty = _gtk_tree_selection_row_is_selectable (selection, node, path); + + /* if dirty is FALSE, we weren't able to select the new one, otherwise, we try to + * unselect the new one + */ + if (dirty) + dirty = gtk_tree_selection_real_unselect_all (selection); + + /* if dirty is TRUE at this point, we successfully unselected the + * old one, and can then select the new one */ if (dirty) { - if (selection->tree_view->priv->anchor) - gtk_tree_row_reference_free (selection->tree_view->priv->anchor); + + _gtk_tree_view_set_anchor_path (priv->tree_view, NULL); + if (gtk_tree_selection_real_select_node (selection, tree, node, TRUE)) - { - selection->tree_view->priv->anchor = - gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path); - } + _gtk_tree_view_set_anchor_path (priv->tree_view, path); } } else @@ -931,47 +1547,43 @@ _gtk_tree_selection_internal_select_node (GtkTreeSelection *selection, if (gtk_tree_selection_real_select_node (selection, tree, node, TRUE)) { dirty = TRUE; - selection->tree_view->priv->anchor = - gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path); + + _gtk_tree_view_set_anchor_path (priv->tree_view, path); } } } } - else if (selection->type == GTK_TREE_SELECTION_MULTI) + else if (priv->type == GTK_SELECTION_MULTIPLE) { - if (((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) && (anchor_path == NULL)) + if ((mode & GTK_TREE_SELECT_MODE_EXTEND) == GTK_TREE_SELECT_MODE_EXTEND + && (anchor_path == NULL)) { - if (selection->tree_view->priv->anchor) - gtk_tree_row_reference_free (selection->tree_view->priv->anchor); + _gtk_tree_view_set_anchor_path (priv->tree_view, path); - selection->tree_view->priv->anchor = - gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path); dirty = gtk_tree_selection_real_select_node (selection, tree, node, TRUE); } - else if ((state & (GDK_CONTROL_MASK|GDK_SHIFT_MASK)) == (GDK_SHIFT_MASK|GDK_CONTROL_MASK)) + else if ((mode & (GTK_TREE_SELECT_MODE_EXTEND | GTK_TREE_SELECT_MODE_TOGGLE)) == (GTK_TREE_SELECT_MODE_EXTEND | GTK_TREE_SELECT_MODE_TOGGLE)) { gtk_tree_selection_select_range (selection, anchor_path, path); } - else if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK) + else if ((mode & GTK_TREE_SELECT_MODE_TOGGLE) == GTK_TREE_SELECT_MODE_TOGGLE) { flags = node->flags; - if (selection->tree_view->priv->anchor) - gtk_tree_row_reference_free (selection->tree_view->priv->anchor); - selection->tree_view->priv->anchor = - gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path); + _gtk_tree_view_set_anchor_path (priv->tree_view, path); if ((flags & GTK_RBNODE_IS_SELECTED) == GTK_RBNODE_IS_SELECTED) dirty |= gtk_tree_selection_real_select_node (selection, tree, node, FALSE); else dirty |= gtk_tree_selection_real_select_node (selection, tree, node, TRUE); } - else if ((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) + else if ((mode & GTK_TREE_SELECT_MODE_EXTEND) == GTK_TREE_SELECT_MODE_EXTEND) { dirty = gtk_tree_selection_real_unselect_all (selection); - dirty |= gtk_tree_selection_real_select_range (selection, + dirty |= gtk_tree_selection_real_modify_range (selection, + RANGE_SELECT, anchor_path, path); } @@ -979,11 +1591,7 @@ _gtk_tree_selection_internal_select_node (GtkTreeSelection *selection, { dirty = gtk_tree_selection_real_unselect_all (selection); - if (selection->tree_view->priv->anchor) - gtk_tree_row_reference_free (selection->tree_view->priv->anchor); - - selection->tree_view->priv->anchor = - gtk_tree_row_reference_new_proxy (G_OBJECT (selection->tree_view), selection->tree_view->priv->model, path); + _gtk_tree_view_set_anchor_path (priv->tree_view, path); dirty |= gtk_tree_selection_real_select_node (selection, tree, node, TRUE); } @@ -993,49 +1601,55 @@ _gtk_tree_selection_internal_select_node (GtkTreeSelection *selection, gtk_tree_path_free (anchor_path); if (dirty) - g_signal_emit (G_OBJECT (selection), tree_selection_signals[CHANGED], 0); + g_signal_emit (selection, tree_selection_signals[CHANGED], 0); +} + + +void +_gtk_tree_selection_emit_changed (GtkTreeSelection *selection) +{ + g_signal_emit (selection, tree_selection_signals[CHANGED], 0); } /* NOTE: Any {un,}selection ever done _MUST_ be done through this function! */ -/* FIXME: user_func can screw up GTK_TREE_SELECTION_SINGLE. If it prevents - * unselection of a node, it can keep more then one node selected. - */ -/* Perhaps the correct solution is to prevent selecting the new node, if - * we fail to unselect the old node. - */ static gint gtk_tree_selection_real_select_node (GtkTreeSelection *selection, GtkRBTree *tree, GtkRBNode *node, gboolean select) { - gboolean selected = FALSE; + GtkTreeSelectionPrivate *priv = selection->priv; + gboolean toggle = FALSE; GtkTreePath *path = NULL; + select = !! select; + if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED) != select) { - path = _gtk_tree_view_find_path (selection->tree_view, tree, node); - if (selection->user_func) - { - if ((*selection->user_func) (selection, selection->tree_view->priv->model, path, selection->user_data)) - selected = TRUE; - } - else - selected = TRUE; + path = _gtk_tree_path_new_from_rbtree (tree, node); + toggle = _gtk_tree_selection_row_is_selectable (selection, node, path); gtk_tree_path_free (path); } - if (selected == TRUE) + if (toggle) { - node->flags ^= GTK_RBNODE_IS_SELECTED; + if (!GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED)) + { + GTK_RBNODE_SET_FLAG (node, GTK_RBNODE_IS_SELECTED); + _gtk_tree_view_accessible_add_state (priv->tree_view, tree, node, GTK_CELL_RENDERER_SELECTED); + } + else + { + GTK_RBNODE_UNSET_FLAG (node, GTK_RBNODE_IS_SELECTED); + _gtk_tree_view_accessible_remove_state (priv->tree_view, tree, node, GTK_CELL_RENDERER_SELECTED); + } + + _gtk_tree_view_queue_draw_node (priv->tree_view, tree, node, NULL); - /* FIXME: just draw the one node*/ - gtk_widget_queue_draw (GTK_WIDGET (selection->tree_view)); return TRUE; } return FALSE; } -