From d8d019a1e9deb525607a1077f7cb198fe88399cb Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Sat, 27 Jan 2001 00:50:38 +0000 Subject: [PATCH] add GtkTreeRowReference which holds a handle to a specific row (particular 2001-01-26 Havoc Pennington * gtk/gtktreemodel.c, gtk/gtktreemodel.h: add GtkTreeRowReference which holds a handle to a specific row (particular set of values in the model, i.e. pointer-identity row). * gtk/gtktreeview.c, gtk/gtktreeprivate.h, gtk/gtktreeselection.c: use GtkTreeRowReference for anchor, cursor, and drag_dest_row. Still need to use it for the src/dest row saved on the drag context. --- ChangeLog | 10 ++ ChangeLog.pre-2-0 | 10 ++ ChangeLog.pre-2-10 | 10 ++ ChangeLog.pre-2-2 | 10 ++ ChangeLog.pre-2-4 | 10 ++ ChangeLog.pre-2-6 | 10 ++ ChangeLog.pre-2-8 | 10 ++ gtk/gtktreemodel.c | 291 +++++++++++++++++++++++++++++++++++++ gtk/gtktreemodel.h | 19 ++- gtk/gtktreeprivate.h | 6 +- gtk/gtktreeselection.c | 114 +++++++++++---- gtk/gtktreeview.c | 315 +++++++++++++++++++++++++++-------------- 12 files changed, 674 insertions(+), 141 deletions(-) diff --git a/ChangeLog b/ChangeLog index d0e5037365..5a769d3540 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2001-01-26 Havoc Pennington + + * gtk/gtktreemodel.c, gtk/gtktreemodel.h: add GtkTreeRowReference + which holds a handle to a specific row (particular set of values + in the model, i.e. pointer-identity row). + + * gtk/gtktreeview.c, gtk/gtktreeprivate.h, gtk/gtktreeselection.c: + use GtkTreeRowReference for anchor, cursor, and drag_dest_row. + Still need to use it for the src/dest row saved on the drag context. + 2001-01-26 Havoc Pennington * gtk/gtktextlayout.c (convert_color): adapt to handle PangoColor diff --git a/ChangeLog.pre-2-0 b/ChangeLog.pre-2-0 index d0e5037365..5a769d3540 100644 --- a/ChangeLog.pre-2-0 +++ b/ChangeLog.pre-2-0 @@ -1,3 +1,13 @@ +2001-01-26 Havoc Pennington + + * gtk/gtktreemodel.c, gtk/gtktreemodel.h: add GtkTreeRowReference + which holds a handle to a specific row (particular set of values + in the model, i.e. pointer-identity row). + + * gtk/gtktreeview.c, gtk/gtktreeprivate.h, gtk/gtktreeselection.c: + use GtkTreeRowReference for anchor, cursor, and drag_dest_row. + Still need to use it for the src/dest row saved on the drag context. + 2001-01-26 Havoc Pennington * gtk/gtktextlayout.c (convert_color): adapt to handle PangoColor diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index d0e5037365..5a769d3540 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -1,3 +1,13 @@ +2001-01-26 Havoc Pennington + + * gtk/gtktreemodel.c, gtk/gtktreemodel.h: add GtkTreeRowReference + which holds a handle to a specific row (particular set of values + in the model, i.e. pointer-identity row). + + * gtk/gtktreeview.c, gtk/gtktreeprivate.h, gtk/gtktreeselection.c: + use GtkTreeRowReference for anchor, cursor, and drag_dest_row. + Still need to use it for the src/dest row saved on the drag context. + 2001-01-26 Havoc Pennington * gtk/gtktextlayout.c (convert_color): adapt to handle PangoColor diff --git a/ChangeLog.pre-2-2 b/ChangeLog.pre-2-2 index d0e5037365..5a769d3540 100644 --- a/ChangeLog.pre-2-2 +++ b/ChangeLog.pre-2-2 @@ -1,3 +1,13 @@ +2001-01-26 Havoc Pennington + + * gtk/gtktreemodel.c, gtk/gtktreemodel.h: add GtkTreeRowReference + which holds a handle to a specific row (particular set of values + in the model, i.e. pointer-identity row). + + * gtk/gtktreeview.c, gtk/gtktreeprivate.h, gtk/gtktreeselection.c: + use GtkTreeRowReference for anchor, cursor, and drag_dest_row. + Still need to use it for the src/dest row saved on the drag context. + 2001-01-26 Havoc Pennington * gtk/gtktextlayout.c (convert_color): adapt to handle PangoColor diff --git a/ChangeLog.pre-2-4 b/ChangeLog.pre-2-4 index d0e5037365..5a769d3540 100644 --- a/ChangeLog.pre-2-4 +++ b/ChangeLog.pre-2-4 @@ -1,3 +1,13 @@ +2001-01-26 Havoc Pennington + + * gtk/gtktreemodel.c, gtk/gtktreemodel.h: add GtkTreeRowReference + which holds a handle to a specific row (particular set of values + in the model, i.e. pointer-identity row). + + * gtk/gtktreeview.c, gtk/gtktreeprivate.h, gtk/gtktreeselection.c: + use GtkTreeRowReference for anchor, cursor, and drag_dest_row. + Still need to use it for the src/dest row saved on the drag context. + 2001-01-26 Havoc Pennington * gtk/gtktextlayout.c (convert_color): adapt to handle PangoColor diff --git a/ChangeLog.pre-2-6 b/ChangeLog.pre-2-6 index d0e5037365..5a769d3540 100644 --- a/ChangeLog.pre-2-6 +++ b/ChangeLog.pre-2-6 @@ -1,3 +1,13 @@ +2001-01-26 Havoc Pennington + + * gtk/gtktreemodel.c, gtk/gtktreemodel.h: add GtkTreeRowReference + which holds a handle to a specific row (particular set of values + in the model, i.e. pointer-identity row). + + * gtk/gtktreeview.c, gtk/gtktreeprivate.h, gtk/gtktreeselection.c: + use GtkTreeRowReference for anchor, cursor, and drag_dest_row. + Still need to use it for the src/dest row saved on the drag context. + 2001-01-26 Havoc Pennington * gtk/gtktextlayout.c (convert_color): adapt to handle PangoColor diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8 index d0e5037365..5a769d3540 100644 --- a/ChangeLog.pre-2-8 +++ b/ChangeLog.pre-2-8 @@ -1,3 +1,13 @@ +2001-01-26 Havoc Pennington + + * gtk/gtktreemodel.c, gtk/gtktreemodel.h: add GtkTreeRowReference + which holds a handle to a specific row (particular set of values + in the model, i.e. pointer-identity row). + + * gtk/gtktreeview.c, gtk/gtktreeprivate.h, gtk/gtktreeselection.c: + use GtkTreeRowReference for anchor, cursor, and drag_dest_row. + Still need to use it for the src/dest row saved on the drag context. + 2001-01-26 Havoc Pennington * gtk/gtktextlayout.c (convert_color): adapt to handle PangoColor diff --git a/gtk/gtktreemodel.c b/gtk/gtktreemodel.c index c1afaa3e51..2b719a3d49 100644 --- a/gtk/gtktreemodel.c +++ b/gtk/gtktreemodel.c @@ -393,6 +393,297 @@ gtk_tree_path_down (GtkTreePath *path) gtk_tree_path_append_index (path, 0); } +struct _GtkTreeRowReference +{ + GtkTreeModel *model; + GtkTreePath *path; +}; + +typedef struct _RowRefList RowRefList; + +struct _RowRefList +{ + GSList *list; +}; + +static void +release_row_references (gpointer data) +{ + RowRefList *refs = data; + GSList *tmp_list = NULL; + + tmp_list = refs->list; + while (tmp_list != NULL) + { + GtkTreeRowReference *reference = tmp_list->data; + + reference->model = NULL; + + /* we don't free the reference, users are responsible for that. */ + + tmp_list = g_slist_next (tmp_list); + } + + g_slist_free (refs->list); + g_free (refs); +} + +static void +inserted_callback (GtkTreeModel *tree_model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + RowRefList *refs = data; + GSList *tmp_list; + + tmp_list = refs->list; + + while (tmp_list != NULL) + { + GtkTreeRowReference *reference = tmp_list->data; + + /* if reference->path == NULL then the reference was already + * deleted. + */ + + if (reference->path) + { + gint i; + gint depth = gtk_tree_path_get_depth (path); + gint *indices = gtk_tree_path_get_indices (path); + gint ref_depth = gtk_tree_path_get_depth (reference->path); + gint *ref_indices = gtk_tree_path_get_indices (reference->path); + + for (i = 0; i < depth && i < ref_depth; i++) + { + if (indices[i] < ref_indices[i]) + { + /* inserted node was before the referenced row; + * move referenced path down 1 + */ + ref_indices[i] += 1; + break; + } + else if (indices[i] > ref_indices[i]) + { + /* inserted node was past the referenced row */ + break; + } + else if (i == depth - 1) + { + /* referenced row or its parent was inserted, this + * is possible if you create the path and row reference + * before you actually insert the row. + */ + break; + } + } + + /* If we didn't break out of the for loop, the inserted path + * was a child of the referenced path + */ + } + + tmp_list = g_slist_next (tmp_list); + } +} + +static void +deleted_callback (GtkTreeModel *tree_model, + GtkTreePath *path, + gpointer data) +{ + RowRefList *refs = data; + GSList *tmp_list; + + tmp_list = refs->list; + + while (tmp_list != NULL) + { + GtkTreeRowReference *reference = tmp_list->data; + + /* if reference->path == NULL then the reference was already + * deleted. + */ + + if (reference->path) + { + gint i; + gint depth = gtk_tree_path_get_depth (path); + gint *indices = gtk_tree_path_get_indices (path); + gint ref_depth = gtk_tree_path_get_depth (reference->path); + gint *ref_indices = gtk_tree_path_get_indices (reference->path); + + for (i = 0; i < depth && i < ref_depth; i++) + { + if (indices[i] < ref_indices[i]) + { + /* deleted node was before the referenced row; + * move referenced path up 1 + */ + ref_indices[i] -= 1; + break; + } + else if (indices[i] > ref_indices[i]) + { + /* deleted node is past the referenced row */ + break; + } + else if (i == depth - 1) + { + /* referenced row or its parent was deleted, mark it + * invalid + */ + gtk_tree_path_free (reference->path); + reference->path = NULL; + break; + } + } + + /* If we didn't break out of the for loop, the deleted path + * was a child of the referenced path + */ + } + + tmp_list = g_slist_next (tmp_list); + } +} + +static void +reordered_callback (GtkTreeModel *tree_model, + GtkTreePath *path, + gint *new_order, + gpointer data) +{ + + /* FIXME */ +} + +static void +connect_ref_callbacks (GtkTreeModel *model, + RowRefList *refs) +{ + g_signal_connect_data (G_OBJECT (model), + "inserted", + (GCallback) inserted_callback, + refs, + NULL, + FALSE, + FALSE); + + g_signal_connect_data (G_OBJECT (model), + "deleted", + (GCallback) deleted_callback, + refs, + NULL, + FALSE, + FALSE); + +#if 0 + /* FIXME */ + g_signal_connect_data (G_OBJECT (model), + "reordered", + (GCallback) reordered_callback, + refs, + NULL, + FALSE, + FALSE); +#endif +} + +static void +disconnect_ref_callbacks (GtkTreeModel *model, + RowRefList *refs) +{ + g_signal_handlers_disconnect_matched (G_OBJECT (model), + G_SIGNAL_MATCH_DATA, + 0, + 0, + NULL, + NULL, + refs); +} + +GtkTreeRowReference* +gtk_tree_row_reference_new (GtkTreeModel *model, + GtkTreePath *path) +{ + GtkTreeRowReference *reference; + RowRefList *refs; + + reference = g_new (GtkTreeRowReference, 1); + + reference->model = model; + reference->path = gtk_tree_path_copy (path); + + refs = g_object_get_data (G_OBJECT (model), + "gtk-tree-row-refs"); + + if (refs == NULL) + { + refs = g_new (RowRefList, 1); + refs->list = NULL; + connect_ref_callbacks (model, refs); + g_object_set_data_full (G_OBJECT (model), + "gtk-tree-row-refs", + refs, + release_row_references); + } + + refs->list = g_slist_prepend (refs->list, reference); + + return reference; +} + +GtkTreePath* +gtk_tree_row_reference_get_path (GtkTreeRowReference *reference) +{ + g_return_val_if_fail (reference != NULL, NULL); + + if (reference->model == NULL) + return NULL; + + if (reference->path == NULL) + return NULL; + + return gtk_tree_path_copy (reference->path); +} + +void +gtk_tree_row_reference_free (GtkTreeRowReference *reference) +{ + RowRefList *refs; + + g_return_if_fail (reference != NULL); + + if (reference->model) + { + refs = g_object_get_data (G_OBJECT (reference->model), + "gtk-tree-row-refs"); + + if (refs == NULL) + { + g_warning (G_STRLOC": bad row reference, model has no outstanding row references"); + return; + } + + refs->list = g_slist_remove (refs->list, reference); + + if (refs->list == NULL) + { + disconnect_ref_callbacks (reference->model, refs); + g_object_set_data (G_OBJECT (reference->model), + "gtk-tree-row-refs", + NULL); + } + } + + if (reference->path) + gtk_tree_path_free (reference->path); + + g_free (reference); +} /** * gtk_tree_iter_copy: diff --git a/gtk/gtktreemodel.h b/gtk/gtktreemodel.h index b15b92b673..202d874bcf 100644 --- a/gtk/gtktreemodel.h +++ b/gtk/gtktreemodel.h @@ -32,10 +32,11 @@ extern "C" { #define GTK_TREE_MODEL_GET_IFACE(obj) ((GtkTreeModelIface *)g_type_interface_peek (((GTypeInstance *)GTK_TREE_MODEL (obj))->g_class, GTK_TYPE_TREE_MODEL)) -typedef struct _GtkTreeIter GtkTreeIter; -typedef struct _GtkTreePath GtkTreePath; -typedef struct _GtkTreeModel GtkTreeModel; /* Dummy typedef */ -typedef struct _GtkTreeModelIface GtkTreeModelIface; +typedef struct _GtkTreeIter GtkTreeIter; +typedef struct _GtkTreePath GtkTreePath; +typedef struct _GtkTreeRowReference GtkTreeRowReference; +typedef struct _GtkTreeModel GtkTreeModel; /* Dummy typedef */ +typedef struct _GtkTreeModelIface GtkTreeModelIface; typedef enum @@ -133,6 +134,16 @@ gboolean gtk_tree_path_prev (GtkTreePath *path); gboolean gtk_tree_path_up (GtkTreePath *path); void gtk_tree_path_down (GtkTreePath *path); +/* Row reference (an object that tracks model changes so it refers to the + * same row always; a path refers to a position, not a fixed row) + */ + +GtkTreeRowReference *gtk_tree_row_reference_new (GtkTreeModel *model, + GtkTreePath *path); +/* returns NULL if the row was deleted or the model was destroyed. */ +GtkTreePath *gtk_tree_row_reference_get_path (GtkTreeRowReference *reference); +void gtk_tree_row_reference_free (GtkTreeRowReference *reference); + /* GtkTreeIter operations */ GtkTreeIter *gtk_tree_iter_copy (GtkTreeIter *iter); diff --git a/gtk/gtktreeprivate.h b/gtk/gtktreeprivate.h index 0a09ee0d60..6b2daa5696 100644 --- a/gtk/gtktreeprivate.h +++ b/gtk/gtktreeprivate.h @@ -72,8 +72,8 @@ struct _GtkTreeViewPrivate gint expander_column; /* Selection stuff */ - GtkTreePath *anchor; - GtkTreePath *cursor; + GtkTreeRowReference *anchor; + GtkTreeRowReference *cursor; /* Column Resizing */ GdkCursor *cursor_drag; @@ -97,7 +97,7 @@ struct _GtkTreeViewPrivate guint scroll_timeout; /* Row drag-and-drop */ - GtkTreePath *drag_dest_row; + GtkTreeRowReference *drag_dest_row; GtkTreeViewDropPosition drag_dest_pos; guint open_dest_timeout; diff --git a/gtk/gtktreeselection.c b/gtk/gtktreeselection.c index 628bcb323c..6baf5d2ff7 100644 --- a/gtk/gtktreeselection.c +++ b/gtk/gtktreeselection.c @@ -180,13 +180,22 @@ gtk_tree_selection_set_mode (GtkTreeSelection *selection, if (selection->tree_view->priv->anchor) { - _gtk_tree_view_find_node (selection->tree_view, - selection->tree_view->priv->anchor, - &tree, - &node); + GtkTreePath *anchor_path; - if (node && GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED)) - selected = TRUE; + anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor); + + 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); + } } /* FIXME: if user_func is set, then it needs to unconditionally unselect * all. @@ -267,7 +276,9 @@ gtk_tree_selection_get_selected (GtkTreeSelection *selection, { GtkRBTree *tree; GtkRBNode *node; - + GtkTreePath *anchor_path; + gboolean retval; + g_return_val_if_fail (selection != NULL, FALSE); g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), FALSE); @@ -276,24 +287,43 @@ gtk_tree_selection_get_selected (GtkTreeSelection *selection, if (selection->tree_view->priv->anchor == NULL) return FALSE; - else if (iter == NULL) - return TRUE; + + anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor); + + if (anchor_path == NULL) + return FALSE; + + if (iter == NULL) + { + gtk_tree_path_free (anchor_path); + return TRUE; + } g_return_val_if_fail (selection->tree_view != NULL, FALSE); g_return_val_if_fail (selection->tree_view->priv->model != NULL, FALSE); + retval = FALSE; + if (!_gtk_tree_view_find_node (selection->tree_view, - selection->tree_view->priv->anchor, - &tree, - &node) && + anchor_path, + &tree, + &node) && ! GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED)) - /* We don't want to return the anchor if it isn't actually selected. - */ - return FALSE; + { + /* We don't want to return the anchor if it isn't actually selected. + */ + retval = FALSE; + } + else + { + retval = gtk_tree_model_get_iter (selection->tree_view->priv->model, + iter, + anchor_path); + } - return gtk_tree_model_get_iter (selection->tree_view->priv->model, - iter, - selection->tree_view->priv->anchor); + gtk_tree_path_free (anchor_path); + + return retval; } /** @@ -661,13 +691,23 @@ gtk_tree_selection_real_unselect_all (GtkTreeSelection *selection) { 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); + + if (anchor_path == NULL) + return FALSE; + _gtk_tree_view_find_node (selection->tree_view, - selection->tree_view->priv->anchor, + anchor_path, &tree, &node); + + gtk_tree_path_free (anchor_path); + if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED)) { gtk_tree_selection_real_select_node (selection, tree, node, FALSE); @@ -839,16 +879,25 @@ _gtk_tree_selection_internal_select_node (GtkTreeSelection *selection, { gint flags; gint dirty = FALSE; + GtkTreePath *anchor_path = NULL; - if (((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) && (selection->tree_view->priv->anchor == NULL)) + if (selection->tree_view->priv->anchor) + anchor_path = gtk_tree_row_reference_get_path (selection->tree_view->priv->anchor); + + if (((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) && (anchor_path == NULL)) { - selection->tree_view->priv->anchor = gtk_tree_path_copy (path); + 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 (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)) { gtk_tree_selection_select_range (selection, - selection->tree_view->priv->anchor, + anchor_path, path); } else if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK) @@ -858,8 +907,11 @@ _gtk_tree_selection_internal_select_node (GtkTreeSelection *selection, dirty = gtk_tree_selection_real_unselect_all (selection); if (selection->tree_view->priv->anchor) - gtk_tree_path_free (selection->tree_view->priv->anchor); - selection->tree_view->priv->anchor = gtk_tree_path_copy (path); + gtk_tree_row_reference_free (selection->tree_view->priv->anchor); + + selection->tree_view->priv->anchor = + gtk_tree_row_reference_new (selection->tree_view->priv->model, + path); if ((flags & GTK_RBNODE_IS_SELECTED) == GTK_RBNODE_IS_SELECTED) dirty |= gtk_tree_selection_real_select_node (selection, tree, node, FALSE); @@ -870,18 +922,26 @@ _gtk_tree_selection_internal_select_node (GtkTreeSelection *selection, { dirty = gtk_tree_selection_real_unselect_all (selection); dirty |= gtk_tree_selection_real_select_range (selection, - selection->tree_view->priv->anchor, + anchor_path, path); } else { dirty = gtk_tree_selection_real_unselect_all (selection); + if (selection->tree_view->priv->anchor) - gtk_tree_path_free (selection->tree_view->priv->anchor); - selection->tree_view->priv->anchor = gtk_tree_path_copy (path); + gtk_tree_row_reference_free (selection->tree_view->priv->anchor); + + selection->tree_view->priv->anchor = + gtk_tree_row_reference_new (selection->tree_view->priv->model, + path); + dirty |= gtk_tree_selection_real_select_node (selection, tree, node, TRUE); } + if (anchor_path) + gtk_tree_path_free (anchor_path); + if (dirty) gtk_signal_emit (GTK_OBJECT (selection), tree_selection_signals[SELECTION_CHANGED]); } diff --git a/gtk/gtktreeview.c b/gtk/gtktreeview.c index 73d5a9fd37..9d26b0a33b 100644 --- a/gtk/gtktreeview.c +++ b/gtk/gtktreeview.c @@ -362,7 +362,7 @@ gtk_tree_view_finalize (GObject *object) gtk_tree_path_free (tree_view->priv->scroll_to_path); if (tree_view->priv->drag_dest_row) - gtk_tree_path_free (tree_view->priv->drag_dest_row); + gtk_tree_row_reference_free (tree_view->priv->drag_dest_row); g_free (tree_view->priv); if (G_OBJECT_CLASS (parent_class)->finalize) @@ -987,6 +987,8 @@ gtk_tree_view_bin_expose (GtkWidget *widget, gboolean last_selected; gint highlight_x; gint bin_window_width; + GtkTreePath *cursor_path; + GtkTreePath *drag_dest_path; g_return_val_if_fail (widget != NULL, FALSE); g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE); @@ -1026,11 +1028,21 @@ gtk_tree_view_bin_expose (GtkWidget *widget, depth = gtk_tree_path_get_depth (path); gtk_tree_path_free (path); + cursor_path = NULL; + drag_dest_path = NULL; + if (tree_view->priv->cursor) - _gtk_tree_view_find_node (tree_view, tree_view->priv->cursor, &cursor_tree, &cursor); + cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor); + + if (cursor_path) + _gtk_tree_view_find_node (tree_view, cursor_path, + &cursor_tree, &cursor); if (tree_view->priv->drag_dest_row) - _gtk_tree_view_find_node (tree_view, tree_view->priv->drag_dest_row, + drag_dest_path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row); + + if (drag_dest_path) + _gtk_tree_view_find_node (tree_view, drag_dest_path, &drag_highlight_tree, &drag_highlight); gdk_drawable_get_size (tree_view->priv->bin_window, @@ -1161,7 +1173,7 @@ gtk_tree_view_bin_expose (GtkWidget *widget, case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: gtk_tree_view_draw_node_focus_rect (widget, - tree_view->priv->drag_dest_row); + drag_dest_path); break; } @@ -1235,6 +1247,12 @@ gtk_tree_view_bin_expose (GtkWidget *widget, } while (y_offset < event->area.height); + if (cursor_path) + gtk_tree_path_free (cursor_path); + + if (drag_dest_path) + gtk_tree_path_free (drag_dest_path); + return TRUE; } @@ -1778,7 +1796,8 @@ static void gtk_tree_view_draw_focus (GtkWidget *widget) { GtkTreeView *tree_view; - + GtkTreePath *cursor_path; + g_return_if_fail (widget != NULL); g_return_if_fail (GTK_IS_TREE_VIEW (widget)); @@ -1786,10 +1805,15 @@ gtk_tree_view_draw_focus (GtkWidget *widget) if (! GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS)) return; + if (tree_view->priv->cursor == NULL) return; - gtk_tree_view_draw_node_focus_rect (widget, tree_view->priv->cursor); + cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor); + if (cursor_path == NULL) + return; + + gtk_tree_view_draw_node_focus_rect (widget, cursor_path); } @@ -2034,7 +2058,8 @@ gtk_tree_view_focus (GtkContainer *container, GdkEvent *event; GtkRBTree *cursor_tree; GtkRBNode *cursor_node; - + GtkTreePath *cursor_path; + g_return_val_if_fail (container != NULL, FALSE); g_return_val_if_fail (GTK_IS_TREE_VIEW (container), FALSE); g_return_val_if_fail (GTK_WIDGET_VISIBLE (container), FALSE); @@ -2075,13 +2100,32 @@ gtk_tree_view_focus (GtkContainer *container, /* if there is no keyboard focus yet, we select the first node */ - if (tree_view->priv->cursor == NULL) - tree_view->priv->cursor = gtk_tree_path_new_root (); + cursor_path = NULL; + + if (tree_view->priv->cursor) + cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor); + + if (cursor_path == NULL) + { + GtkTreePath *tmp_path = gtk_tree_path_new_root (); + + if (tree_view->priv->cursor) + gtk_tree_row_reference_free (tree_view->priv->cursor); + + tree_view->priv->cursor = + gtk_tree_row_reference_new (tree_view->priv->model, + tmp_path); + cursor_path = tmp_path; + } + gtk_tree_selection_select_path (tree_view->priv->selection, - tree_view->priv->cursor); - /* FIXME make this more efficient */ - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + cursor_path); + + gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL); + + gtk_tree_path_free (cursor_path); + return TRUE; } } @@ -2105,36 +2149,64 @@ gtk_tree_view_focus (GtkContainer *container, tree_view->priv->selection = _gtk_tree_selection_new_with_tree_view (tree_view); - if (tree_view->priv->cursor == NULL) - tree_view->priv->cursor = gtk_tree_path_new_root (); + cursor_path = NULL; + if (tree_view->priv->cursor) + cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor); + + if (cursor_path == NULL) + { + GtkTreePath *tmp_path = gtk_tree_path_new_root (); + + if (tree_view->priv->cursor) + gtk_tree_row_reference_free (tree_view->priv->cursor); + + tree_view->priv->cursor = + gtk_tree_row_reference_new (tree_view->priv->model, + tmp_path); + cursor_path = tmp_path; + } gtk_tree_selection_select_path (tree_view->priv->selection, - tree_view->priv->cursor); - /* FIXME make this more efficient */ - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + cursor_path); + + gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL); + + gtk_tree_path_free (cursor_path); + return TRUE; } + cursor_path = NULL; + if (tree_view->priv->cursor) + cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor); + /* Case 3. We have focus already, but no cursor. We pick the first one - * and run with it. */ - if (tree_view->priv->cursor == NULL) + * and run with it. + */ + + if (cursor_path == NULL) { - /* We lost our cursor somehow. Arbitrarily select the first node, and - * return - */ - tree_view->priv->cursor = gtk_tree_path_new_root (); - + GtkTreePath *tmp_path = gtk_tree_path_new_root (); + + if (tree_view->priv->cursor) + gtk_tree_row_reference_free (tree_view->priv->cursor); + + tree_view->priv->cursor = + gtk_tree_row_reference_new (tree_view->priv->model, + tmp_path); + cursor_path = tmp_path; + gtk_tree_selection_select_path (tree_view->priv->selection, - tree_view->priv->cursor); - gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), - 0.0); - /* FIXME make this more efficient */ - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + cursor_path); + + gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL); + + gtk_tree_path_free (cursor_path); + return TRUE; } - - /* Case 3. We have focus already. Move the cursor. */ + /* Case 4. We have focus already. Move the cursor. */ if (direction == GTK_DIR_LEFT) { gfloat val; @@ -2142,6 +2214,9 @@ gtk_tree_view_focus (GtkContainer *container, val = MAX (val, 0.0); gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->hadjustment), val); gtk_widget_grab_focus (GTK_WIDGET (tree_view)); + + gtk_tree_path_free (cursor_path); + return TRUE; } if (direction == GTK_DIR_RIGHT) @@ -2151,14 +2226,31 @@ gtk_tree_view_focus (GtkContainer *container, val = MIN (tree_view->priv->hadjustment->upper - tree_view->priv->hadjustment->page_size, val); gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->hadjustment), val); gtk_widget_grab_focus (GTK_WIDGET (tree_view)); + + gtk_tree_path_free (cursor_path); + return TRUE; } + cursor_tree = NULL; cursor_node = NULL; - _gtk_tree_view_find_node (tree_view, tree_view->priv->cursor, + _gtk_tree_view_find_node (tree_view, cursor_path, &cursor_tree, &cursor_node); + + /* undraw the old row */ + gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL); + + gtk_tree_path_free (cursor_path); + cursor_path = NULL; + + if (tree_view->priv->cursor) + { + gtk_tree_row_reference_free (tree_view->priv->cursor); + tree_view->priv->cursor = NULL; + } + switch (direction) { case GTK_DIR_TAB_BACKWARD: @@ -2189,21 +2281,32 @@ gtk_tree_view_focus (GtkContainer *container, if (event) gdk_event_free (event); - gtk_tree_path_free (tree_view->priv->cursor); + + cursor_path = _gtk_tree_view_find_path (tree_view, + cursor_tree, + cursor_node); - tree_view->priv->cursor = _gtk_tree_view_find_path (tree_view, - cursor_tree, - cursor_node); - if (tree_view->priv->cursor) - _gtk_tree_selection_internal_select_node (tree_view->priv->selection, - cursor_node, - cursor_tree, - tree_view->priv->cursor, - state); + if (cursor_path) + { + _gtk_tree_selection_internal_select_node (tree_view->priv->selection, + cursor_node, + cursor_tree, + cursor_path, + state); + + tree_view->priv->cursor = gtk_tree_row_reference_new (tree_view->priv->model, + cursor_path); + + + /* draw the newly-selected row */ + gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL); + + gtk_tree_path_free (cursor_path); + } + gtk_tree_view_clamp_node_visible (tree_view, cursor_tree, cursor_node); gtk_widget_grab_focus (GTK_WIDGET (tree_view)); - /* FIXME make this more efficient */ - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + return TRUE; } @@ -2402,29 +2505,6 @@ gtk_tree_view_inserted (GtkTreeModel *model, if (tree == NULL) return; - /* next, update the selection */ - if (tree_view->priv->anchor) - { - gint *select_indices = gtk_tree_path_get_indices (tree_view->priv->anchor); - gint select_depth = gtk_tree_path_get_depth (tree_view->priv->anchor); - - for (i = 0; i < depth && i < select_depth; i++) - { - if (indices[i] < select_indices[i]) - { - select_indices[i]++; - break; - } - else if (indices[i] > select_indices[i]) - break; - else if (i == depth - 1) - { - select_indices[i]++; - break; - } - } - } - /* ref the node */ gtk_tree_model_ref_iter (tree_view->priv->model, iter); max_height = gtk_tree_view_insert_iter_height (tree_view, @@ -2525,37 +2605,27 @@ gtk_tree_view_deleted (GtkTreeModel *model, /* next, update the selection */ if (tree_view->priv->anchor) { - gint i; - gint depth = gtk_tree_path_get_depth (path); - gint *indices = gtk_tree_path_get_indices (path); - gint select_depth = gtk_tree_path_get_depth (tree_view->priv->anchor); - gint *select_indices = gtk_tree_path_get_indices (tree_view->priv->anchor); + GtkTreePath *anchor_path; - if (gtk_tree_path_compare (path, tree_view->priv->anchor) == 0) + /* the row reference may not have been updated yet. If it has not, + * then anchor_path and path being equal indicates that the anchor + * row was deleted. If it has, then anchor_path == NULL indicates the + * the anchor row was deleted. + */ + + anchor_path = gtk_tree_row_reference_get_path (tree_view->priv->anchor); + + if (anchor_path == NULL || + gtk_tree_path_compare (path, anchor_path) == 0) { if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED) && - tree_view->priv->selection) + tree_view->priv->selection) gtk_signal_emit_by_name (GTK_OBJECT (tree_view->priv->selection), "selection_changed"); } - else - { - for (i = 0; i < depth && i < select_depth; i++) - { - if (indices[i] < select_indices[i]) - { - select_indices[i] = MAX (select_indices[i], 0); - break; - } - else if (indices[i] > select_indices[i]) - break; - else if (i == depth - 1) - { - select_indices[i] = MAX (select_indices[i], 0); - break; - } - } - } + + if (anchor_path) + gtk_tree_path_free (anchor_path); } for (list = tree_view->priv->columns; list; list = list->next) @@ -3432,7 +3502,7 @@ gtk_tree_view_set_model (GtkTreeView *tree_view, #endif if (tree_view->priv->drag_dest_row) - gtk_tree_path_free (tree_view->priv->drag_dest_row); + gtk_tree_row_reference_free (tree_view->priv->drag_dest_row); GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_MODEL_SETUP); } @@ -5018,26 +5088,35 @@ gtk_tree_view_set_drag_dest_row (GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewDropPosition pos) { + GtkTreePath *current_dest; /* Note; this function is exported to allow a custom DND * implementation, so it can't touch TreeViewDragInfo */ g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - g_print ("drag dest row %p -> %p\n", tree_view->priv->drag_dest_row, path); + current_dest = NULL; if (tree_view->priv->drag_dest_row) + current_dest = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row); + + if (current_dest) { - gtk_tree_view_queue_draw_path (tree_view, tree_view->priv->drag_dest_row, NULL); - gtk_tree_path_free (tree_view->priv->drag_dest_row); + gtk_tree_view_queue_draw_path (tree_view, current_dest, NULL); + gtk_tree_path_free (current_dest); } + if (tree_view->priv->drag_dest_row) + gtk_tree_row_reference_free (tree_view->priv->drag_dest_row); + tree_view->priv->drag_dest_pos = pos; if (path) { - tree_view->priv->drag_dest_row = gtk_tree_path_copy (path); - gtk_tree_view_queue_draw_path (tree_view, tree_view->priv->drag_dest_row, NULL); + tree_view->priv->drag_dest_row = + gtk_tree_row_reference_new (tree_view->priv->model, + path); + gtk_tree_view_queue_draw_path (tree_view, path, NULL); } else tree_view->priv->drag_dest_row = NULL; @@ -5053,7 +5132,7 @@ gtk_tree_view_get_drag_dest_row (GtkTreeView *tree_view, if (path) { if (tree_view->priv->drag_dest_row) - *path = gtk_tree_path_copy (tree_view->priv->drag_dest_row); + *path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row); else *path = NULL; } @@ -5375,19 +5454,32 @@ static gint open_row_timeout (gpointer data) { GtkTreeView *tree_view = data; + GtkTreePath *dest_path = NULL; + GtkTreeViewDropPosition pos; - if (tree_view->priv->drag_dest_row && - (tree_view->priv->drag_dest_pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER || - tree_view->priv->drag_dest_pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) + gtk_tree_view_get_drag_dest_row (tree_view, + &dest_path, + &pos); + + if (dest_path && + (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER || + pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) { gtk_tree_view_expand_row (tree_view, - tree_view->priv->drag_dest_row, + dest_path, FALSE); tree_view->priv->open_dest_timeout = 0; + + gtk_tree_path_free (dest_path); + return FALSE; } else - return TRUE; + { + if (dest_path) + gtk_tree_path_free (dest_path); + return TRUE; + } } /* Returns TRUE if event should not be propagated to parent widgets */ @@ -5400,9 +5492,11 @@ set_destination_row (GtkTreeView *tree_view, { GtkTreePath *path = NULL; GtkTreeViewDropPosition pos; + GtkTreeViewDropPosition old_pos; TreeViewDragInfo *di; GtkWidget *widget; - + GtkTreePath *old_dest_path = NULL; + *suggested_action = 0; widget = GTK_WIDGET (tree_view); @@ -5455,11 +5549,18 @@ set_destination_row (GtkTreeView *tree_view, /* If we left the current row's "open" zone, unset the timeout for * opening the row */ - if (tree_view->priv->drag_dest_row && - (gtk_tree_path_compare (path, tree_view->priv->drag_dest_row) != 0 || + gtk_tree_view_get_drag_dest_row (tree_view, + &old_dest_path, + &old_pos); + + if (old_dest_path && + (gtk_tree_path_compare (path, old_dest_path) != 0 || !(pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER || pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE))) remove_open_timeout (tree_view); + + if (old_dest_path) + gtk_tree_path_free (old_dest_path); if (TRUE /* FIXME if the location droppable predicate */) {