Commit d8d019a1 authored by Havoc Pennington's avatar Havoc Pennington Committed by Havoc Pennington

add GtkTreeRowReference which holds a handle to a specific row (particular

2001-01-26  Havoc Pennington  <hp@redhat.com>

	* 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.
parent ef4356b5
2001-01-26 Havoc Pennington <hp@redhat.com>
* 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 <hp@redhat.com>
* gtk/gtktextlayout.c (convert_color): adapt to handle PangoColor
......
2001-01-26 Havoc Pennington <hp@redhat.com>
* 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 <hp@redhat.com>
* gtk/gtktextlayout.c (convert_color): adapt to handle PangoColor
......
2001-01-26 Havoc Pennington <hp@redhat.com>
* 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 <hp@redhat.com>
* gtk/gtktextlayout.c (convert_color): adapt to handle PangoColor
......
2001-01-26 Havoc Pennington <hp@redhat.com>
* 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 <hp@redhat.com>
* gtk/gtktextlayout.c (convert_color): adapt to handle PangoColor
......
2001-01-26 Havoc Pennington <hp@redhat.com>
* 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 <hp@redhat.com>
* gtk/gtktextlayout.c (convert_color): adapt to handle PangoColor
......
2001-01-26 Havoc Pennington <hp@redhat.com>
* 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 <hp@redhat.com>
* gtk/gtktextlayout.c (convert_color): adapt to handle PangoColor
......
2001-01-26 Havoc Pennington <hp@redhat.com>
* 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 <hp@redhat.com>
* gtk/gtktextlayout.c (convert_color): adapt to handle PangoColor
......
......@@ -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:
......
......@@ -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);
......
......@@ -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;
......
......@@ -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);
if (node && GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
selected = TRUE;
GtkTreePath *anchor_path;
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]);
}
......
......@@ -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);