diff --git a/ChangeLog b/ChangeLog index 3d676d25e3..6f11860751 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,58 @@ +2001-01-30 Havoc Pennington + + * gtk/gtkliststore.c (gtk_list_store_insert_before): fix bug in + here where prev pointer was set to the wrong thing + + * gtk/gtktreemodel.c (gtk_tree_path_is_ancestor): new function + (gtk_tree_path_is_descendant): new function + + * gtk/gtkliststore.c (gtk_list_store_iter_n_children): return + cached length + (gtk_list_store_get_iter): don't modify iter if we can't get the + path. + + * gtk/gtkliststore.h (struct _GtkListStore): cache the length + + * gtk/gtktreednd.h: add virtual function row_drop_possible() to + GtkTreeDragDest + + * gtk/gtktreestore.c (copy_node_data): fix varargs type error that + was causing segfault + + * gtk/gtktreedatalist.c (_gtk_tree_data_list_node_copy): set next + pointer to NULL + + * gtk/gtktreestore.c (gtk_tree_store_append): fix memleak + + * gtk/gtkliststore.c (gtk_list_store_iter_next): don't modify iter + on returning FALSE + (gtk_list_store_iter_children): ditto + (gtk_list_store_iter_nth_child): ditto + (gtk_list_store_iter_nth_child): ditto + (gtk_list_store_iter_parent): ditto + + * gtk/gtktreestore.c (gtk_tree_store_get_path): g_return_if_fail + on iter->user_data != NULL instead of silently accepting it. + (gtk_tree_store_iter_next): ditto. Also, don't modify iter unless + we are returning TRUE. + (gtk_tree_store_iter_children): ditto + (gtk_tree_store_iter_nth_child): ditto + (gtk_tree_store_iter_parent): ditto + (gtk_tree_store_insert): remove handling of parent->user_data == + NULL, replace with parent == NULL + + * gtk/gtktreemodel.c (inserted_callback): put some fixes in here, + and a comment explaining things + + * gtk/gtktreestore.c: add GtkTreeDragSource/GtkTreeDragDest + interface support to GtkTreeStore. + + * gtk/gtktreemodel.c (gtk_tree_path_prev): didn't properly return + FALSE if no prev, fix + + * gtk/gtktreeview.c (set_source_row): use a row reference + (set_dest_row): use a row reference + Sat Jan 27 15:52:02 2001 Jonathan Blandford * gtk/gtktreeselection.c (gtk_tree_selection_unselect_iter): Fix diff --git a/ChangeLog.pre-2-0 b/ChangeLog.pre-2-0 index 3d676d25e3..6f11860751 100644 --- a/ChangeLog.pre-2-0 +++ b/ChangeLog.pre-2-0 @@ -1,3 +1,58 @@ +2001-01-30 Havoc Pennington + + * gtk/gtkliststore.c (gtk_list_store_insert_before): fix bug in + here where prev pointer was set to the wrong thing + + * gtk/gtktreemodel.c (gtk_tree_path_is_ancestor): new function + (gtk_tree_path_is_descendant): new function + + * gtk/gtkliststore.c (gtk_list_store_iter_n_children): return + cached length + (gtk_list_store_get_iter): don't modify iter if we can't get the + path. + + * gtk/gtkliststore.h (struct _GtkListStore): cache the length + + * gtk/gtktreednd.h: add virtual function row_drop_possible() to + GtkTreeDragDest + + * gtk/gtktreestore.c (copy_node_data): fix varargs type error that + was causing segfault + + * gtk/gtktreedatalist.c (_gtk_tree_data_list_node_copy): set next + pointer to NULL + + * gtk/gtktreestore.c (gtk_tree_store_append): fix memleak + + * gtk/gtkliststore.c (gtk_list_store_iter_next): don't modify iter + on returning FALSE + (gtk_list_store_iter_children): ditto + (gtk_list_store_iter_nth_child): ditto + (gtk_list_store_iter_nth_child): ditto + (gtk_list_store_iter_parent): ditto + + * gtk/gtktreestore.c (gtk_tree_store_get_path): g_return_if_fail + on iter->user_data != NULL instead of silently accepting it. + (gtk_tree_store_iter_next): ditto. Also, don't modify iter unless + we are returning TRUE. + (gtk_tree_store_iter_children): ditto + (gtk_tree_store_iter_nth_child): ditto + (gtk_tree_store_iter_parent): ditto + (gtk_tree_store_insert): remove handling of parent->user_data == + NULL, replace with parent == NULL + + * gtk/gtktreemodel.c (inserted_callback): put some fixes in here, + and a comment explaining things + + * gtk/gtktreestore.c: add GtkTreeDragSource/GtkTreeDragDest + interface support to GtkTreeStore. + + * gtk/gtktreemodel.c (gtk_tree_path_prev): didn't properly return + FALSE if no prev, fix + + * gtk/gtktreeview.c (set_source_row): use a row reference + (set_dest_row): use a row reference + Sat Jan 27 15:52:02 2001 Jonathan Blandford * gtk/gtktreeselection.c (gtk_tree_selection_unselect_iter): Fix diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index 3d676d25e3..6f11860751 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -1,3 +1,58 @@ +2001-01-30 Havoc Pennington + + * gtk/gtkliststore.c (gtk_list_store_insert_before): fix bug in + here where prev pointer was set to the wrong thing + + * gtk/gtktreemodel.c (gtk_tree_path_is_ancestor): new function + (gtk_tree_path_is_descendant): new function + + * gtk/gtkliststore.c (gtk_list_store_iter_n_children): return + cached length + (gtk_list_store_get_iter): don't modify iter if we can't get the + path. + + * gtk/gtkliststore.h (struct _GtkListStore): cache the length + + * gtk/gtktreednd.h: add virtual function row_drop_possible() to + GtkTreeDragDest + + * gtk/gtktreestore.c (copy_node_data): fix varargs type error that + was causing segfault + + * gtk/gtktreedatalist.c (_gtk_tree_data_list_node_copy): set next + pointer to NULL + + * gtk/gtktreestore.c (gtk_tree_store_append): fix memleak + + * gtk/gtkliststore.c (gtk_list_store_iter_next): don't modify iter + on returning FALSE + (gtk_list_store_iter_children): ditto + (gtk_list_store_iter_nth_child): ditto + (gtk_list_store_iter_nth_child): ditto + (gtk_list_store_iter_parent): ditto + + * gtk/gtktreestore.c (gtk_tree_store_get_path): g_return_if_fail + on iter->user_data != NULL instead of silently accepting it. + (gtk_tree_store_iter_next): ditto. Also, don't modify iter unless + we are returning TRUE. + (gtk_tree_store_iter_children): ditto + (gtk_tree_store_iter_nth_child): ditto + (gtk_tree_store_iter_parent): ditto + (gtk_tree_store_insert): remove handling of parent->user_data == + NULL, replace with parent == NULL + + * gtk/gtktreemodel.c (inserted_callback): put some fixes in here, + and a comment explaining things + + * gtk/gtktreestore.c: add GtkTreeDragSource/GtkTreeDragDest + interface support to GtkTreeStore. + + * gtk/gtktreemodel.c (gtk_tree_path_prev): didn't properly return + FALSE if no prev, fix + + * gtk/gtktreeview.c (set_source_row): use a row reference + (set_dest_row): use a row reference + Sat Jan 27 15:52:02 2001 Jonathan Blandford * gtk/gtktreeselection.c (gtk_tree_selection_unselect_iter): Fix diff --git a/ChangeLog.pre-2-2 b/ChangeLog.pre-2-2 index 3d676d25e3..6f11860751 100644 --- a/ChangeLog.pre-2-2 +++ b/ChangeLog.pre-2-2 @@ -1,3 +1,58 @@ +2001-01-30 Havoc Pennington + + * gtk/gtkliststore.c (gtk_list_store_insert_before): fix bug in + here where prev pointer was set to the wrong thing + + * gtk/gtktreemodel.c (gtk_tree_path_is_ancestor): new function + (gtk_tree_path_is_descendant): new function + + * gtk/gtkliststore.c (gtk_list_store_iter_n_children): return + cached length + (gtk_list_store_get_iter): don't modify iter if we can't get the + path. + + * gtk/gtkliststore.h (struct _GtkListStore): cache the length + + * gtk/gtktreednd.h: add virtual function row_drop_possible() to + GtkTreeDragDest + + * gtk/gtktreestore.c (copy_node_data): fix varargs type error that + was causing segfault + + * gtk/gtktreedatalist.c (_gtk_tree_data_list_node_copy): set next + pointer to NULL + + * gtk/gtktreestore.c (gtk_tree_store_append): fix memleak + + * gtk/gtkliststore.c (gtk_list_store_iter_next): don't modify iter + on returning FALSE + (gtk_list_store_iter_children): ditto + (gtk_list_store_iter_nth_child): ditto + (gtk_list_store_iter_nth_child): ditto + (gtk_list_store_iter_parent): ditto + + * gtk/gtktreestore.c (gtk_tree_store_get_path): g_return_if_fail + on iter->user_data != NULL instead of silently accepting it. + (gtk_tree_store_iter_next): ditto. Also, don't modify iter unless + we are returning TRUE. + (gtk_tree_store_iter_children): ditto + (gtk_tree_store_iter_nth_child): ditto + (gtk_tree_store_iter_parent): ditto + (gtk_tree_store_insert): remove handling of parent->user_data == + NULL, replace with parent == NULL + + * gtk/gtktreemodel.c (inserted_callback): put some fixes in here, + and a comment explaining things + + * gtk/gtktreestore.c: add GtkTreeDragSource/GtkTreeDragDest + interface support to GtkTreeStore. + + * gtk/gtktreemodel.c (gtk_tree_path_prev): didn't properly return + FALSE if no prev, fix + + * gtk/gtktreeview.c (set_source_row): use a row reference + (set_dest_row): use a row reference + Sat Jan 27 15:52:02 2001 Jonathan Blandford * gtk/gtktreeselection.c (gtk_tree_selection_unselect_iter): Fix diff --git a/ChangeLog.pre-2-4 b/ChangeLog.pre-2-4 index 3d676d25e3..6f11860751 100644 --- a/ChangeLog.pre-2-4 +++ b/ChangeLog.pre-2-4 @@ -1,3 +1,58 @@ +2001-01-30 Havoc Pennington + + * gtk/gtkliststore.c (gtk_list_store_insert_before): fix bug in + here where prev pointer was set to the wrong thing + + * gtk/gtktreemodel.c (gtk_tree_path_is_ancestor): new function + (gtk_tree_path_is_descendant): new function + + * gtk/gtkliststore.c (gtk_list_store_iter_n_children): return + cached length + (gtk_list_store_get_iter): don't modify iter if we can't get the + path. + + * gtk/gtkliststore.h (struct _GtkListStore): cache the length + + * gtk/gtktreednd.h: add virtual function row_drop_possible() to + GtkTreeDragDest + + * gtk/gtktreestore.c (copy_node_data): fix varargs type error that + was causing segfault + + * gtk/gtktreedatalist.c (_gtk_tree_data_list_node_copy): set next + pointer to NULL + + * gtk/gtktreestore.c (gtk_tree_store_append): fix memleak + + * gtk/gtkliststore.c (gtk_list_store_iter_next): don't modify iter + on returning FALSE + (gtk_list_store_iter_children): ditto + (gtk_list_store_iter_nth_child): ditto + (gtk_list_store_iter_nth_child): ditto + (gtk_list_store_iter_parent): ditto + + * gtk/gtktreestore.c (gtk_tree_store_get_path): g_return_if_fail + on iter->user_data != NULL instead of silently accepting it. + (gtk_tree_store_iter_next): ditto. Also, don't modify iter unless + we are returning TRUE. + (gtk_tree_store_iter_children): ditto + (gtk_tree_store_iter_nth_child): ditto + (gtk_tree_store_iter_parent): ditto + (gtk_tree_store_insert): remove handling of parent->user_data == + NULL, replace with parent == NULL + + * gtk/gtktreemodel.c (inserted_callback): put some fixes in here, + and a comment explaining things + + * gtk/gtktreestore.c: add GtkTreeDragSource/GtkTreeDragDest + interface support to GtkTreeStore. + + * gtk/gtktreemodel.c (gtk_tree_path_prev): didn't properly return + FALSE if no prev, fix + + * gtk/gtktreeview.c (set_source_row): use a row reference + (set_dest_row): use a row reference + Sat Jan 27 15:52:02 2001 Jonathan Blandford * gtk/gtktreeselection.c (gtk_tree_selection_unselect_iter): Fix diff --git a/ChangeLog.pre-2-6 b/ChangeLog.pre-2-6 index 3d676d25e3..6f11860751 100644 --- a/ChangeLog.pre-2-6 +++ b/ChangeLog.pre-2-6 @@ -1,3 +1,58 @@ +2001-01-30 Havoc Pennington + + * gtk/gtkliststore.c (gtk_list_store_insert_before): fix bug in + here where prev pointer was set to the wrong thing + + * gtk/gtktreemodel.c (gtk_tree_path_is_ancestor): new function + (gtk_tree_path_is_descendant): new function + + * gtk/gtkliststore.c (gtk_list_store_iter_n_children): return + cached length + (gtk_list_store_get_iter): don't modify iter if we can't get the + path. + + * gtk/gtkliststore.h (struct _GtkListStore): cache the length + + * gtk/gtktreednd.h: add virtual function row_drop_possible() to + GtkTreeDragDest + + * gtk/gtktreestore.c (copy_node_data): fix varargs type error that + was causing segfault + + * gtk/gtktreedatalist.c (_gtk_tree_data_list_node_copy): set next + pointer to NULL + + * gtk/gtktreestore.c (gtk_tree_store_append): fix memleak + + * gtk/gtkliststore.c (gtk_list_store_iter_next): don't modify iter + on returning FALSE + (gtk_list_store_iter_children): ditto + (gtk_list_store_iter_nth_child): ditto + (gtk_list_store_iter_nth_child): ditto + (gtk_list_store_iter_parent): ditto + + * gtk/gtktreestore.c (gtk_tree_store_get_path): g_return_if_fail + on iter->user_data != NULL instead of silently accepting it. + (gtk_tree_store_iter_next): ditto. Also, don't modify iter unless + we are returning TRUE. + (gtk_tree_store_iter_children): ditto + (gtk_tree_store_iter_nth_child): ditto + (gtk_tree_store_iter_parent): ditto + (gtk_tree_store_insert): remove handling of parent->user_data == + NULL, replace with parent == NULL + + * gtk/gtktreemodel.c (inserted_callback): put some fixes in here, + and a comment explaining things + + * gtk/gtktreestore.c: add GtkTreeDragSource/GtkTreeDragDest + interface support to GtkTreeStore. + + * gtk/gtktreemodel.c (gtk_tree_path_prev): didn't properly return + FALSE if no prev, fix + + * gtk/gtktreeview.c (set_source_row): use a row reference + (set_dest_row): use a row reference + Sat Jan 27 15:52:02 2001 Jonathan Blandford * gtk/gtktreeselection.c (gtk_tree_selection_unselect_iter): Fix diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8 index 3d676d25e3..6f11860751 100644 --- a/ChangeLog.pre-2-8 +++ b/ChangeLog.pre-2-8 @@ -1,3 +1,58 @@ +2001-01-30 Havoc Pennington + + * gtk/gtkliststore.c (gtk_list_store_insert_before): fix bug in + here where prev pointer was set to the wrong thing + + * gtk/gtktreemodel.c (gtk_tree_path_is_ancestor): new function + (gtk_tree_path_is_descendant): new function + + * gtk/gtkliststore.c (gtk_list_store_iter_n_children): return + cached length + (gtk_list_store_get_iter): don't modify iter if we can't get the + path. + + * gtk/gtkliststore.h (struct _GtkListStore): cache the length + + * gtk/gtktreednd.h: add virtual function row_drop_possible() to + GtkTreeDragDest + + * gtk/gtktreestore.c (copy_node_data): fix varargs type error that + was causing segfault + + * gtk/gtktreedatalist.c (_gtk_tree_data_list_node_copy): set next + pointer to NULL + + * gtk/gtktreestore.c (gtk_tree_store_append): fix memleak + + * gtk/gtkliststore.c (gtk_list_store_iter_next): don't modify iter + on returning FALSE + (gtk_list_store_iter_children): ditto + (gtk_list_store_iter_nth_child): ditto + (gtk_list_store_iter_nth_child): ditto + (gtk_list_store_iter_parent): ditto + + * gtk/gtktreestore.c (gtk_tree_store_get_path): g_return_if_fail + on iter->user_data != NULL instead of silently accepting it. + (gtk_tree_store_iter_next): ditto. Also, don't modify iter unless + we are returning TRUE. + (gtk_tree_store_iter_children): ditto + (gtk_tree_store_iter_nth_child): ditto + (gtk_tree_store_iter_parent): ditto + (gtk_tree_store_insert): remove handling of parent->user_data == + NULL, replace with parent == NULL + + * gtk/gtktreemodel.c (inserted_callback): put some fixes in here, + and a comment explaining things + + * gtk/gtktreestore.c: add GtkTreeDragSource/GtkTreeDragDest + interface support to GtkTreeStore. + + * gtk/gtktreemodel.c (gtk_tree_path_prev): didn't properly return + FALSE if no prev, fix + + * gtk/gtktreeview.c (set_source_row): use a row reference + (set_dest_row): use a row reference + Sat Jan 27 15:52:02 2001 Jonathan Blandford * gtk/gtktreeselection.c (gtk_tree_selection_unselect_iter): Fix diff --git a/gtk/gtkliststore.c b/gtk/gtkliststore.c index ef3300430f..2191146157 100644 --- a/gtk/gtkliststore.c +++ b/gtk/gtkliststore.c @@ -80,7 +80,20 @@ static gboolean gtk_list_store_drag_data_get (GtkTreeDragSource *drag_sourc static gboolean gtk_list_store_drag_data_received (GtkTreeDragDest *drag_dest, GtkTreePath *dest, GtkSelectionData *selection_data); - +static gboolean gtk_list_store_row_drop_possible (GtkTreeDragDest *drag_dest, + GtkTreeModel *src_model, + GtkTreePath *src_path, + GtkTreePath *dest_path); +static void +validate_list_store (GtkListStore *list_store) +{ + if (gtk_debug_flags & GTK_DEBUG_TREE) + { + g_assert (g_slist_length (list_store->root) == list_store->length); + + g_assert (g_slist_last (list_store->root) == list_store->tail); + } +} GtkType gtk_list_store_get_type (void) @@ -210,6 +223,7 @@ static void gtk_list_store_drag_dest_init (GtkTreeDragDestIface *iface) { iface->drag_data_received = gtk_list_store_drag_data_received; + iface->row_drop_possible = gtk_list_store_row_drop_possible; } static void @@ -218,6 +232,7 @@ gtk_list_store_init (GtkListStore *list_store) list_store->root = NULL; list_store->tail = NULL; list_store->stamp = g_random_int (); + list_store->length = 0; } GtkListStore * @@ -323,14 +338,26 @@ gtk_list_store_get_iter (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreePath *path) { + GSList *list; + gint i; + g_return_val_if_fail (GTK_IS_LIST_STORE (tree_model), FALSE); - g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE); + g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE); + + i = gtk_tree_path_get_indices (path)[0]; + + if (i >= GTK_LIST_STORE (tree_model)->length) + return FALSE; + + list = g_slist_nth (G_SLIST (GTK_LIST_STORE (tree_model)->root), + i); + + /* If this fails, list_store->length has gotten mangled. */ + g_assert (list); iter->stamp = GTK_LIST_STORE (tree_model)->stamp; - iter->user_data = g_slist_nth (G_SLIST (GTK_LIST_STORE (tree_model)->root), - gtk_tree_path_get_indices (path)[0]); - - return iter->user_data != NULL; + iter->user_data = list; + return TRUE; } static GtkTreePath * @@ -391,9 +418,13 @@ gtk_list_store_iter_next (GtkTreeModel *tree_model, g_return_val_if_fail (GTK_IS_LIST_STORE (tree_model), FALSE); g_return_val_if_fail (GTK_LIST_STORE (tree_model)->stamp == iter->stamp, FALSE); - iter->user_data = G_SLIST (iter->user_data)->next; - - return (iter->user_data != NULL); + if (G_SLIST (iter->user_data)->next) + { + iter->user_data = G_SLIST (iter->user_data)->next; + return TRUE; + } + else + return FALSE; } static gboolean @@ -401,18 +432,22 @@ gtk_list_store_iter_children (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent) { + /* this is a list, nodes have no children */ if (parent) - { - iter->stamp = 0; - iter->user_data = NULL; - return FALSE; - } - else + return FALSE; + + /* but if parent == NULL we return the list itself as children of the + * "root" + */ + + if (GTK_LIST_STORE (tree_model)->root) { iter->stamp = GTK_LIST_STORE (tree_model)->stamp; iter->user_data = GTK_LIST_STORE (tree_model)->root; return TRUE; } + else + return FALSE; } static gboolean @@ -427,9 +462,9 @@ gtk_list_store_iter_n_children (GtkTreeModel *tree_model, GtkTreeIter *iter) { if (iter == NULL) - return g_slist_length (G_SLIST (GTK_LIST_STORE (tree_model)->root)); - - return 0; + return GTK_LIST_STORE (tree_model)->length; + else + return 0; } static gboolean @@ -438,24 +473,23 @@ gtk_list_store_iter_nth_child (GtkTreeModel *tree_model, GtkTreeIter *parent, gint n) { + GSList *child; + g_return_val_if_fail (GTK_IS_LIST_STORE (tree_model), FALSE); if (parent) + return FALSE; + + child = g_slist_nth (G_SLIST (GTK_LIST_STORE (tree_model)->root), n); + + if (child) { - g_return_val_if_fail (iter->stamp == GTK_LIST_STORE (tree_model)->stamp, FALSE); - iter->stamp = 0; - iter->user_data = NULL; - - return FALSE; + iter->user_data = child; + iter->stamp = GTK_LIST_STORE (tree_model)->stamp; + return TRUE; } - - iter->user_data = g_slist_nth (G_SLIST (GTK_LIST_STORE (tree_model)->root), n); - if (iter->user_data) - iter->stamp = GTK_LIST_STORE (tree_model)->stamp; else - iter->stamp = 0; - - return (iter->user_data != NULL); + return FALSE; } static gboolean @@ -463,9 +497,6 @@ gtk_list_store_iter_parent (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *child) { - iter->stamp = 0; - iter->user_data = NULL; - return FALSE; } @@ -674,11 +705,12 @@ remove_link_saving_prev (GSList *list, if (tmp == link) { if (prev) - prev->next = tmp->next; - if (list == tmp) + prev->next = link->next; + + if (list == link) list = list->next; - tmp->next = NULL; + link->next = NULL; break; } @@ -709,6 +741,8 @@ gtk_list_store_remove_silently (GtkListStore *list_store, list_store->root = remove_link_saving_prev (G_SLIST (list_store->root), G_SLIST (iter->user_data), &prev); + + list_store->length -= 1; if (iter->user_data == list_store->tail) list_store->tail = prev; @@ -725,13 +759,16 @@ gtk_list_store_remove (GtkListStore *list_store, g_return_if_fail (list_store != NULL); g_return_if_fail (GTK_IS_LIST_STORE (list_store)); - g_return_if_fail (iter->user_data != NULL); - + g_return_if_fail (iter->user_data != NULL); path = gtk_list_store_get_path (GTK_TREE_MODEL (list_store), iter); + validate_list_store (list_store); + gtk_list_store_remove_silently (list_store, iter, path); + validate_list_store (list_store); + gtk_signal_emit_by_name (GTK_OBJECT (list_store), "deleted", path); @@ -753,6 +790,8 @@ insert_after (GtkListStore *list_store, /* if list was the tail, the new node is the new tail */ if (sibling == list_store->tail) list_store->tail = new_list; + + list_store->length += 1; } void @@ -789,6 +828,8 @@ gtk_list_store_insert (GtkListStore *list_store, iter->stamp = list_store->stamp; iter->user_data = new_list; + + validate_list_store (list_store); path = gtk_tree_path_new (); gtk_tree_path_append_index (path, position); @@ -819,7 +860,8 @@ gtk_list_store_insert_before (GtkListStore *list_store, new_list = g_slist_alloc (); - prev = list = list_store->root; + prev = NULL; + list = list_store->root; while (list && list != sibling->user_data) { prev = list; @@ -854,6 +896,10 @@ gtk_list_store_insert_before (GtkListStore *list_store, iter->stamp = list_store->stamp; iter->user_data = new_list; + + list_store->length += 1; + + validate_list_store (list_store); path = gtk_tree_path_new (); gtk_tree_path_append_index (path, i); @@ -895,6 +941,8 @@ gtk_list_store_insert_after (GtkListStore *list_store, iter->stamp = list_store->stamp; iter->user_data = new_list; + + validate_list_store (list_store); path = gtk_tree_path_new (); gtk_tree_path_append_index (path, i); @@ -923,6 +971,10 @@ gtk_list_store_prepend (GtkListStore *list_store, G_SLIST (iter->user_data)->next = G_SLIST (list_store->root); list_store->root = iter->user_data; + list_store->length += 1; + + validate_list_store (list_store); + path = gtk_tree_path_new (); gtk_tree_path_append_index (path, 0); gtk_signal_emit_by_name (GTK_OBJECT (list_store), @@ -951,6 +1003,10 @@ gtk_list_store_append (GtkListStore *list_store, list_store->root = iter->user_data; list_store->tail = iter->user_data; + + list_store->length += 1; + + validate_list_store (list_store); path = gtk_tree_path_new (); gtk_tree_path_append_index (path, i); @@ -1037,7 +1093,10 @@ gtk_list_store_drag_data_received (GtkTreeDragDest *drag_dest, if (!gtk_tree_model_get_iter (src_model, &src_iter, src_path)) - goto out; + { + g_print ("can't get source path as iter\n"); + goto out; + } /* Get the path to insert _after_ (dest is the path to insert _before_) */ prev = gtk_tree_path_copy (dest); @@ -1051,6 +1110,8 @@ gtk_list_store_drag_data_received (GtkTreeDragDest *drag_dest, &dest_iter); retval = TRUE; + + g_print ("prepending to list\n"); } else { @@ -1063,7 +1124,11 @@ gtk_list_store_drag_data_received (GtkTreeDragDest *drag_dest, &dest_iter, &tmp_iter); retval = TRUE; + + g_print ("inserting into list\n"); } + else + g_print ("can't get iter to insert after\n"); } gtk_tree_path_free (prev); @@ -1111,6 +1176,7 @@ gtk_list_store_drag_data_received (GtkTreeDragDest *drag_dest, /* FIXME maybe add some data targets eventually, or handle text * targets in the simple case. */ + g_print ("not accepting target\n"); } out: @@ -1120,3 +1186,29 @@ gtk_list_store_drag_data_received (GtkTreeDragDest *drag_dest, return retval; } + +static gboolean +gtk_list_store_row_drop_possible (GtkTreeDragDest *drag_dest, + GtkTreeModel *src_model, + GtkTreePath *src_path, + GtkTreePath *dest_path) +{ + gint *indices; + + g_return_val_if_fail (GTK_IS_LIST_STORE (drag_dest), FALSE); + + if (src_model != GTK_TREE_MODEL (drag_dest)) + return FALSE; + + if (gtk_tree_path_get_depth (dest_path) != 1) + return FALSE; + + /* can drop before any existing node, or before one past any existing. */ + + indices = gtk_tree_path_get_indices (dest_path); + + if (indices[0] <= GTK_LIST_STORE (drag_dest)->length) + return TRUE; + else + return FALSE; +} diff --git a/gtk/gtkliststore.h b/gtk/gtkliststore.h index 55ad276a01..de591de53f 100644 --- a/gtk/gtkliststore.h +++ b/gtk/gtkliststore.h @@ -45,6 +45,7 @@ struct _GtkListStore GSList *tail; gint n_columns; GType *column_headers; + gint length; }; struct _GtkListStoreClass diff --git a/gtk/gtktreedatalist.c b/gtk/gtktreedatalist.c index b55b730af5..862a64107b 100644 --- a/gtk/gtktreedatalist.c +++ b/gtk/gtktreedatalist.c @@ -239,9 +239,12 @@ _gtk_tree_data_list_node_copy (GtkTreeDataList *list, GType type) { GtkTreeDataList *new_list; + + g_return_val_if_fail (list != NULL, NULL); new_list = _gtk_tree_data_list_alloc (); - + new_list->next = NULL; + switch (type) { case G_TYPE_UINT: @@ -279,3 +282,6 @@ _gtk_tree_data_list_node_copy (GtkTreeDataList *list, return new_list; } + + + diff --git a/gtk/gtktreednd.c b/gtk/gtktreednd.c index f798022c25..bdf9e4abde 100644 --- a/gtk/gtktreednd.c +++ b/gtk/gtktreednd.c @@ -155,6 +155,38 @@ gtk_tree_drag_dest_drag_data_received (GtkTreeDragDest *drag_dest, return (* iface->drag_data_received) (drag_dest, dest, selection_data); } + +/** + * gtk_tree_drag_dest_drop_possible: + * @drag_dest: a #GtkTreeDragDest + * @src_model: #GtkTreeModel being dragged from + * @src_path: row being dragged + * @dest_path: destination row + * + * Determines whether a drop is possible before the given @dest_path, + * at the same depth as @dest_path. i.e., can we drop @src_model such + * that it now resides at @dest_path. @dest_path does not have to + * exist; the return value will almost certainly be %FALSE if the + * parent of @dest_path doesn't exist, though. + * + * Return value: %TRUE if a drop is possible before @dest_path + **/ +gboolean +gtk_tree_drag_dest_row_drop_possible (GtkTreeDragDest *drag_dest, + GtkTreeModel *src_model, + GtkTreePath *src_path, + GtkTreePath *dest_path) +{ + GtkTreeDragDestIface *iface = GTK_TREE_DRAG_DEST_GET_IFACE (drag_dest); + + g_return_val_if_fail (iface->row_drop_possible != NULL, FALSE); + g_return_val_if_fail (GTK_IS_TREE_MODEL (src_model), FALSE); + g_return_val_if_fail (src_path != NULL, FALSE); + g_return_val_if_fail (dest_path != NULL, FALSE); + + return (* iface->row_drop_possible) (drag_dest, src_model, src_path, dest_path); +} + typedef struct _TreeRowData TreeRowData; struct _TreeRowData diff --git a/gtk/gtktreednd.h b/gtk/gtktreednd.h index 1257ed25f6..8e9513ec24 100644 --- a/gtk/gtktreednd.h +++ b/gtk/gtktreednd.h @@ -41,11 +41,11 @@ struct _GtkTreeDragSourceIface /* VTable - not signals */ - gboolean (* drag_data_get) (GtkTreeDragSource *dragsource, + gboolean (* drag_data_get) (GtkTreeDragSource *drag_source, GtkTreePath *path, GtkSelectionData *selection_data); - gboolean (* drag_data_delete) (GtkTreeDragSource *dragsource, + gboolean (* drag_data_delete) (GtkTreeDragSource *drag_source, GtkTreePath *path); }; @@ -76,10 +76,14 @@ struct _GtkTreeDragDestIface /* VTable - not signals */ - gboolean (* drag_data_received) (GtkTreeDragDest *dragdest, + gboolean (* drag_data_received) (GtkTreeDragDest *drag_dest, GtkTreePath *dest, GtkSelectionData *selection_data); + gboolean (* row_drop_possible) (GtkTreeDragDest *drag_dest, + GtkTreeModel *src_model, + GtkTreePath *src_path, + GtkTreePath *dest_path); }; GType gtk_tree_drag_dest_get_type (void) G_GNUC_CONST; @@ -91,6 +95,12 @@ gboolean gtk_tree_drag_dest_drag_data_received (GtkTreeDragDest *drag_dest, GtkTreePath *dest, GtkSelectionData *selection_data); +/* Returns TRUE if we can drop before path; path may not exist. */ +gboolean gtk_tree_drag_dest_row_drop_possible (GtkTreeDragDest *drag_dest, + GtkTreeModel *src_model, + GtkTreePath *src_path, + GtkTreePath *dest_path); + /* The selection data would normally have target type GTK_TREE_MODEL_ROW in this * case. If the target is wrong these functions return FALSE. */ diff --git a/gtk/gtktreemodel.c b/gtk/gtktreemodel.c index 2b719a3d49..da6e5feea8 100644 --- a/gtk/gtktreemodel.c +++ b/gtk/gtktreemodel.c @@ -322,6 +322,73 @@ gtk_tree_path_compare (const GtkTreePath *a, return (a->depth < b->depth?1:-1); } +/** + * gtk_tree_path_is_ancestor: + * @path: a #GtkTreePath + * @descendant: another #GtkTreePath + * + * + * + * Return value: %TRUE if @descendant is contained inside @path + **/ +gboolean +gtk_tree_path_is_ancestor (GtkTreePath *path, + GtkTreePath *descendant) +{ + gint i; + + g_return_val_if_fail (path != NULL, FALSE); + g_return_val_if_fail (descendant != NULL, FALSE); + + /* can't be an ancestor if we're deeper */ + if (path->depth >= descendant->depth) + return FALSE; + + i = 0; + while (i < path->depth) + { + if (path->indices[i] != descendant->indices[i]) + return FALSE; + ++i; + } + + return TRUE; +} + +/** + * gtk_tree_path_is_descendant: + * @path: a #GtkTreePath + * @ancestor: another #GtkTreePath + * + * + * + * Return value: %TRUE if @ancestor contains @path somewhere below it + **/ +gboolean +gtk_tree_path_is_descendant (GtkTreePath *path, + GtkTreePath *ancestor) +{ + gint i; + + g_return_val_if_fail (path != NULL, FALSE); + g_return_val_if_fail (ancestor != NULL, FALSE); + + /* can't be a descendant if we're shallower in the tree */ + if (path->depth <= ancestor->depth) + return FALSE; + + i = 0; + while (i < ancestor->depth) + { + if (path->indices[i] != ancestor->indices[i]) + return FALSE; + ++i; + } + + return TRUE; +} + + /** * gtk_tree_path_next: * @path: A #GtkTreePath. @@ -350,10 +417,10 @@ gtk_tree_path_prev (GtkTreePath *path) { g_return_val_if_fail (path != NULL, FALSE); - if (path->indices[path->depth] == 0) + if (path->indices[path->depth - 1] == 0) return FALSE; - path->indices[path->depth - 1] --; + path->indices[path->depth - 1] -= 1; return TRUE; } @@ -437,6 +504,15 @@ inserted_callback (GtkTreeModel *tree_model, RowRefList *refs = data; GSList *tmp_list; + /* This function corrects the path stored in the reference to + * account for an insertion. Note that it's called _after_ the insertion + * with the path to the newly-inserted row. Which means that + * the inserted path is in a different "coordinate system" than + * the old path (e.g. if the inserted path was just before the old path, + * then inserted path and old path will be the same, and old path must be + * moved down one). + */ + tmp_list = refs->list; while (tmp_list != NULL) @@ -449,40 +525,21 @@ inserted_callback (GtkTreeModel *tree_model, 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 (ref_depth >= depth) { - 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; - } - } + gint *indices = gtk_tree_path_get_indices (path); + gint *ref_indices = gtk_tree_path_get_indices (reference->path); + gint i; - /* If we didn't break out of the for loop, the inserted path - * was a child of the referenced path - */ + /* This is the depth that might affect us. */ + i = depth - 1; + + if (indices[i] <= ref_indices[i]) + ref_indices[i] += 1; + } } tmp_list = g_slist_next (tmp_list); @@ -497,6 +554,17 @@ deleted_callback (GtkTreeModel *tree_model, RowRefList *refs = data; GSList *tmp_list; + /* This function corrects the path stored in the reference to + * account for an deletion. Note that it's called _after_ the + * deletion with the old path of the just-deleted row. Which means + * that the deleted path is the same now-defunct "coordinate system" + * as the path saved in the reference, which is what we want to fix. + * + * Note that this is different from the situation in "inserted," so + * while you might think you can cut-and-paste between these + * functions, it's not going to work. ;-) + */ + tmp_list = refs->list; while (tmp_list != NULL) @@ -509,41 +577,29 @@ deleted_callback (GtkTreeModel *tree_model, 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 (ref_depth >= depth) { + /* Need to adjust path upward */ + gint *indices = gtk_tree_path_get_indices (path); + gint *ref_indices = gtk_tree_path_get_indices (reference->path); + gint i; + + i = depth - 1; if (indices[i] < ref_indices[i]) + ref_indices[i] -= 1; + else 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 + /* the referenced node itself, or its parent, was + * deleted, mark 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); @@ -722,7 +778,6 @@ gtk_tree_iter_free (GtkTreeIter *iter) g_return_if_fail (iter != NULL); g_free (iter); - } /** diff --git a/gtk/gtktreemodel.h b/gtk/gtktreemodel.h index 202d874bcf..3ede12308b 100644 --- a/gtk/gtktreemodel.h +++ b/gtk/gtktreemodel.h @@ -134,6 +134,11 @@ gboolean gtk_tree_path_prev (GtkTreePath *path); gboolean gtk_tree_path_up (GtkTreePath *path); void gtk_tree_path_down (GtkTreePath *path); +gboolean gtk_tree_path_is_ancestor (GtkTreePath *path, + GtkTreePath *descendant); +gboolean gtk_tree_path_is_descendant (GtkTreePath *path, + GtkTreePath *ancestor); + /* 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) */ diff --git a/gtk/gtktreestore.c b/gtk/gtktreestore.c index aa8c658368..d44c468197 100644 --- a/gtk/gtktreestore.c +++ b/gtk/gtktreestore.c @@ -20,6 +20,7 @@ #include "gtktreemodel.h" #include "gtktreestore.h" #include "gtktreedatalist.h" +#include "gtktreednd.h" #include "gtksignal.h" #include #include @@ -40,6 +41,8 @@ static guint tree_store_signals[LAST_SIGNAL] = { 0 }; static void gtk_tree_store_init (GtkTreeStore *tree_store); static void gtk_tree_store_class_init (GtkTreeStoreClass *tree_store_class); static void gtk_tree_store_tree_model_init (GtkTreeModelIface *iface); +static void gtk_tree_store_drag_source_init(GtkTreeDragSourceIface *iface); +static void gtk_tree_store_drag_dest_init (GtkTreeDragDestIface *iface); static guint gtk_tree_store_get_flags (GtkTreeModel *tree_model); static gint gtk_tree_store_get_n_columns (GtkTreeModel *tree_model); static GType gtk_tree_store_get_column_type (GtkTreeModel *tree_model, @@ -68,6 +71,32 @@ static gboolean gtk_tree_store_iter_parent (GtkTreeModel *tree_mode GtkTreeIter *child); +static gboolean gtk_tree_store_drag_data_delete (GtkTreeDragSource *drag_source, + GtkTreePath *path); +static gboolean gtk_tree_store_drag_data_get (GtkTreeDragSource *drag_source, + GtkTreePath *path, + GtkSelectionData *selection_data); +static gboolean gtk_tree_store_drag_data_received (GtkTreeDragDest *drag_dest, + GtkTreePath *dest, + GtkSelectionData *selection_data); +static gboolean gtk_tree_store_row_drop_possible (GtkTreeDragDest *drag_dest, + GtkTreeModel *src_model, + GtkTreePath *src_path, + GtkTreePath *dest_path); + +static void validate_gnode (GNode *node); + +static inline void +validate_tree (GtkTreeStore *tree_store) +{ + if (gtk_debug_flags & GTK_DEBUG_TREE) + { + g_assert (G_NODE (tree_store->root)->parent == NULL); + + validate_gnode (G_NODE (tree_store->root)); + } +} + GtkType gtk_tree_store_get_type (void) { @@ -95,10 +124,34 @@ gtk_tree_store_get_type (void) NULL }; + static const GInterfaceInfo drag_source_info = + { + (GInterfaceInitFunc) gtk_tree_store_drag_source_init, + NULL, + NULL + }; + + static const GInterfaceInfo drag_dest_info = + { + (GInterfaceInitFunc) gtk_tree_store_drag_dest_init, + NULL, + NULL + }; + tree_store_type = g_type_register_static (GTK_TYPE_OBJECT, "GtkTreeStore", &tree_store_info, 0); + g_type_add_interface_static (tree_store_type, GTK_TYPE_TREE_MODEL, &tree_model_info); + + g_type_add_interface_static (tree_store_type, + GTK_TYPE_TREE_DRAG_SOURCE, + &drag_source_info); + g_type_add_interface_static (tree_store_type, + GTK_TYPE_TREE_DRAG_DEST, + &drag_dest_info); + + } return tree_store_type; @@ -164,6 +217,20 @@ gtk_tree_store_tree_model_init (GtkTreeModelIface *iface) iface->iter_parent = gtk_tree_store_iter_parent; } +static void +gtk_tree_store_drag_source_init (GtkTreeDragSourceIface *iface) +{ + iface->drag_data_delete = gtk_tree_store_drag_data_delete; + iface->drag_data_get = gtk_tree_store_drag_data_get; +} + +static void +gtk_tree_store_drag_dest_init (GtkTreeDragDestIface *iface) +{ + iface->drag_data_received = gtk_tree_store_drag_data_received; + iface->row_drop_possible = gtk_tree_store_row_drop_possible; +} + static void gtk_tree_store_init (GtkTreeStore *tree_store) { @@ -242,7 +309,7 @@ gtk_tree_store_set_column_type (GtkTreeStore *tree_store, /* fulfill the GtkTreeModel requirements */ /* NOTE: GtkTreeStore::root is a GNode, that acts as the parent node. However, - * it is not visible to the tree or to the user., and the path "1" refers to the + * it is not visible to the tree or to the user., and the path "0" refers to the * first child of GtkTreeStore::root. */ @@ -281,13 +348,13 @@ gtk_tree_store_get_path (GtkTreeModel *tree_model, GtkTreePath *retval; GNode *tmp_node; gint i = 0; - + g_return_val_if_fail (tree_model != NULL, NULL); g_return_val_if_fail (GTK_IS_TREE_STORE (tree_model), NULL); g_return_val_if_fail (iter != NULL, NULL); + g_return_val_if_fail (iter->user_data != NULL, NULL); - if (iter->user_data == NULL) - return NULL; + validate_tree ((GtkTreeStore*)tree_model); g_assert (G_NODE (iter->user_data)->parent != NULL); @@ -299,7 +366,8 @@ gtk_tree_store_get_path (GtkTreeModel *tree_model, else { GtkTreeIter tmp_iter = *iter; - tmp_iter.user_data = G_NODE (tmp_iter.user_data)->parent; + + tmp_iter.user_data = G_NODE (iter->user_data)->parent; retval = gtk_tree_store_get_path (tree_model, &tmp_iter); @@ -372,12 +440,15 @@ static gboolean gtk_tree_store_iter_next (GtkTreeModel *tree_model, GtkTreeIter *iter) { - if (iter->user_data == NULL) + g_return_val_if_fail (iter->user_data != NULL, FALSE); + + if (G_NODE (iter->user_data)->next) + { + iter->user_data = G_NODE (iter->user_data)->next; + return TRUE; + } + else return FALSE; - - iter->user_data = G_NODE (iter->user_data)->next; - - return (iter->user_data != NULL); } static gboolean @@ -385,13 +456,23 @@ gtk_tree_store_iter_children (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent) { - iter->stamp = GTK_TREE_STORE (tree_model)->stamp; - if (parent) - iter->user_data = G_NODE (parent->user_data)->children; - else - iter->user_data = G_NODE (GTK_TREE_STORE (tree_model)->root)->children; + GNode *children; - return iter->user_data != NULL; + g_return_val_if_fail (parent == NULL || parent->user_data != NULL, FALSE); + + if (parent) + children = G_NODE (parent->user_data)->children; + else + children = G_NODE (GTK_TREE_STORE (tree_model)->root)->children; + + if (children) + { + iter->stamp = GTK_TREE_STORE (tree_model)->stamp; + iter->user_data = children; + return TRUE; + } + else + return FALSE; } static gboolean @@ -401,6 +482,7 @@ gtk_tree_store_iter_has_child (GtkTreeModel *tree_model, g_return_val_if_fail (tree_model != NULL, FALSE); g_return_val_if_fail (GTK_IS_TREE_STORE (tree_model), FALSE); g_return_val_if_fail (iter != NULL, FALSE); + g_return_val_if_fail (iter->user_data != NULL, FALSE); return G_NODE (iter->user_data)->children != NULL; } @@ -414,11 +496,14 @@ gtk_tree_store_iter_n_children (GtkTreeModel *tree_model, g_return_val_if_fail (tree_model != NULL, 0); g_return_val_if_fail (GTK_IS_TREE_STORE (tree_model), 0); - + g_return_val_if_fail (iter != NULL, FALSE); + g_return_val_if_fail (iter->user_data != NULL, FALSE); + if (iter == NULL) node = G_NODE (GTK_TREE_STORE (tree_model)->root)->children; else node = G_NODE (iter->user_data)->children; + while (node) { i++; @@ -435,24 +520,27 @@ gtk_tree_store_iter_nth_child (GtkTreeModel *tree_model, gint n) { GNode *parent_node; + GNode *child; g_return_val_if_fail (tree_model != NULL, FALSE); g_return_val_if_fail (GTK_IS_TREE_STORE (tree_model), FALSE); - g_return_val_if_fail (iter != NULL, FALSE); + g_return_val_if_fail (parent == NULL || parent->user_data != NULL, FALSE); if (parent == NULL) parent_node = GTK_TREE_STORE (tree_model)->root; else parent_node = parent->user_data; - iter->user_data = g_node_nth_child (parent_node, n); + child = g_node_nth_child (parent_node, n); - if (iter->user_data == NULL) - iter->stamp = 0; + if (child) + { + iter->user_data = child; + iter->stamp = GTK_TREE_STORE (tree_model)->stamp; + return TRUE; + } else - iter->stamp = GTK_TREE_STORE (tree_model)->stamp; - - return (iter->user_data != NULL); + return FALSE; } static gboolean @@ -460,19 +548,23 @@ gtk_tree_store_iter_parent (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *child) { - g_assert (G_NODE (child->user_data)->parent != NULL); + GNode *parent; - iter->stamp = GTK_TREE_STORE (tree_model)->stamp; - iter->user_data = G_NODE (child->user_data)->parent; + g_return_val_if_fail (iter != NULL, FALSE); + g_return_val_if_fail (iter->user_data != NULL, FALSE); - if (iter->user_data == GTK_TREE_STORE (tree_model)->root) + parent = G_NODE (child->user_data)->parent; + + g_assert (parent != NULL); + + if (parent != GTK_TREE_STORE (tree_model)->root) { - iter->stamp = 0; - iter->user_data = NULL; - return FALSE; + iter->user_data = parent; + iter->stamp = GTK_TREE_STORE (tree_model)->stamp; + return TRUE; } - - return TRUE; + else + return FALSE; } /* @@ -708,22 +800,27 @@ gtk_tree_store_insert (GtkTreeStore *model, gint position) { GtkTreePath *path; + GNode *parent_node; g_return_if_fail (model != NULL); g_return_if_fail (GTK_IS_TREE_STORE (model)); - if (parent->user_data == NULL) - parent->user_data = model->root; + if (parent) + parent_node = parent->user_data; + else + parent_node = model->root; iter->stamp = model->stamp; iter->user_data = g_node_new (NULL); - g_node_insert (G_NODE (parent->user_data), position, G_NODE (iter->user_data)); + g_node_insert (parent_node, position, G_NODE (iter->user_data)); path = gtk_tree_store_get_path (GTK_TREE_MODEL (model), iter); gtk_signal_emit_by_name (GTK_OBJECT (model), "inserted", path, iter); gtk_tree_path_free (path); + + validate_tree ((GtkTreeStore*)model); } void @@ -733,7 +830,7 @@ gtk_tree_store_insert_before (GtkTreeStore *model, GtkTreeIter *sibling) { GtkTreePath *path; - GNode *parent_node = NULL; + GNode *parent_node = NULL; GNode *new_node; g_return_if_fail (model != NULL); @@ -767,6 +864,8 @@ gtk_tree_store_insert_before (GtkTreeStore *model, "inserted", path, iter); gtk_tree_path_free (path); + + validate_tree ((GtkTreeStore*)model); } void @@ -811,6 +910,8 @@ gtk_tree_store_insert_after (GtkTreeStore *model, "inserted", path, iter); gtk_tree_path_free (path); + + validate_tree ((GtkTreeStore*)model); } void @@ -861,6 +962,8 @@ gtk_tree_store_prepend (GtkTreeStore *model, { gtk_tree_store_insert_after (model, iter, parent, NULL); } + + validate_tree ((GtkTreeStore*)model); } void @@ -873,19 +976,19 @@ gtk_tree_store_append (GtkTreeStore *model, g_return_if_fail (model != NULL); g_return_if_fail (GTK_IS_TREE_STORE (model)); g_return_if_fail (iter != NULL); - + if (parent == NULL) parent_node = model->root; else parent_node = parent->user_data; - iter->stamp = model->stamp; - iter->user_data = g_node_new (NULL); - if (parent_node->children == NULL) { GtkTreePath *path; + iter->stamp = model->stamp; + iter->user_data = g_node_new (NULL); + g_node_append (parent_node, G_NODE (iter->user_data)); if (parent_node != model->root) @@ -901,6 +1004,7 @@ gtk_tree_store_append (GtkTreeStore *model, { path = gtk_tree_store_get_path (GTK_TREE_MODEL (model), iter); } + gtk_signal_emit_by_name (GTK_OBJECT (model), "inserted", path, @@ -911,6 +1015,8 @@ gtk_tree_store_append (GtkTreeStore *model, { gtk_tree_store_insert_before (model, iter, parent, NULL); } + + validate_tree ((GtkTreeStore*)model); } gboolean @@ -938,3 +1044,309 @@ gtk_tree_store_iter_depth (GtkTreeStore *model, return g_node_depth (G_NODE (iter->user_data)) - 1; } + +/* DND */ + + + +static gboolean +gtk_tree_store_drag_data_delete (GtkTreeDragSource *drag_source, + GtkTreePath *path) +{ + GtkTreeIter iter; + + g_return_val_if_fail (GTK_IS_TREE_STORE (drag_source), FALSE); + + if (gtk_tree_model_get_iter (GTK_TREE_MODEL (drag_source), + &iter, + path)) + { + g_print ("data_delete deleting tree row\n"); + gtk_tree_store_remove (GTK_TREE_STORE (drag_source), + &iter); + return TRUE; + } + else + { + g_print ("data_delete path not in tree\n"); + return FALSE; + } +} + +static gboolean +gtk_tree_store_drag_data_get (GtkTreeDragSource *drag_source, + GtkTreePath *path, + GtkSelectionData *selection_data) +{ + g_return_val_if_fail (GTK_IS_TREE_STORE (drag_source), FALSE); + + /* Note that we don't need to handle the GTK_TREE_MODEL_ROW + * target, because the default handler does it for us, but + * we do anyway for the convenience of someone maybe overriding the + * default handler. + */ + + if (gtk_selection_data_set_tree_row (selection_data, + GTK_TREE_MODEL (drag_source), + path)) + { + return TRUE; + } + else + { + /* FIXME handle text targets at least. */ + } + + return FALSE; +} + +static void +copy_node_data (GtkTreeStore *tree_store, + GtkTreeIter *src_iter, + GtkTreeIter *dest_iter) +{ + GtkTreeDataList *dl = G_NODE (src_iter->user_data)->data; + GtkTreeDataList *copy_head = NULL; + GtkTreeDataList *copy_prev = NULL; + GtkTreeDataList *copy_iter = NULL; + gint col; + + col = 0; + while (dl) + { + copy_iter = _gtk_tree_data_list_node_copy (dl, + tree_store->column_headers[col]); + + g_print ("copied col %d type %s\n", col, + g_type_name (tree_store->column_headers[col])); + + if (copy_head == NULL) + copy_head = copy_iter; + + if (copy_prev) + copy_prev->next = copy_iter; + + copy_prev = copy_iter; + + dl = dl->next; + ++col; + } + + G_NODE (dest_iter->user_data)->data = copy_head; + + gtk_signal_emit_by_name (GTK_OBJECT (tree_store), + "changed", + NULL, dest_iter); +} + +static void +recursive_node_copy (GtkTreeStore *tree_store, + GtkTreeIter *src_iter, + GtkTreeIter *dest_iter) +{ + GtkTreeIter child; + GtkTreeModel *model; + + model = GTK_TREE_MODEL (tree_store); + + copy_node_data (tree_store, src_iter, dest_iter); + + if (gtk_tree_model_iter_children (model, + &child, + src_iter)) + { + /* Need to create children and recurse. Note our + * dependence on persistent iterators here. + */ + do + { + GtkTreeIter copy; + + /* Gee, a really slow algorithm... ;-) FIXME */ + gtk_tree_store_append (tree_store, + ©, + dest_iter); + + recursive_node_copy (tree_store, &child, ©); + } + while (gtk_tree_model_iter_next (model, &child)); + } +} + +static gboolean +gtk_tree_store_drag_data_received (GtkTreeDragDest *drag_dest, + GtkTreePath *dest, + GtkSelectionData *selection_data) +{ + GtkTreeModel *tree_model; + GtkTreeStore *tree_store; + GtkTreeModel *src_model = NULL; + GtkTreePath *src_path = NULL; + gboolean retval = FALSE; + + g_return_val_if_fail (GTK_IS_TREE_STORE (drag_dest), FALSE); + + tree_model = GTK_TREE_MODEL (drag_dest); + tree_store = GTK_TREE_STORE (drag_dest); + + validate_tree (tree_store); + + if (gtk_selection_data_get_tree_row (selection_data, + &src_model, + &src_path) && + src_model == tree_model) + { + /* Copy the given row to a new position */ + GtkTreeIter src_iter; + GtkTreeIter dest_iter; + GtkTreePath *prev; + + if (!gtk_tree_model_get_iter (src_model, + &src_iter, + src_path)) + { + g_print ("can't get source path as iter\n"); + goto out; + } + + /* Get the path to insert _after_ (dest is the path to insert _before_) */ + prev = gtk_tree_path_copy (dest); + + if (!gtk_tree_path_prev (prev)) + { + GtkTreeIter dest_parent; + GtkTreePath *parent; + GtkTreeIter *dest_parent_p; + + /* dest was the first spot at the current depth; which means + * we are supposed to prepend. + */ + + /* Get the parent, NULL if parent is the root */ + dest_parent_p = NULL; + parent = gtk_tree_path_copy (dest); + if (gtk_tree_path_up (parent)) + { + gtk_tree_model_get_iter (tree_model, + &dest_parent, + parent); + dest_parent_p = &dest_parent; + } + gtk_tree_path_free (parent); + parent = NULL; + + gtk_tree_store_prepend (GTK_TREE_STORE (tree_model), + &dest_iter, + dest_parent_p); + + retval = TRUE; + + g_print ("prepending to tree\n"); + } + else + { + if (gtk_tree_model_get_iter (GTK_TREE_MODEL (tree_model), + &dest_iter, + prev)) + { + GtkTreeIter tmp_iter = dest_iter; + gtk_tree_store_insert_after (GTK_TREE_STORE (tree_model), + &dest_iter, + NULL, + &tmp_iter); + retval = TRUE; + + g_print ("inserting into tree\n"); + } + else + g_print ("can't get iter to insert after\n"); + } + + gtk_tree_path_free (prev); + + /* If we succeeded in creating dest_iter, walk src_iter tree branch, + * duplicating it below dest_iter. + */ + + if (retval) + { + recursive_node_copy (tree_store, + &src_iter, + &dest_iter); + } + } + else + { + /* FIXME maybe add some data targets eventually, or handle text + * targets in the simple case. + */ + g_print ("not accepting target\n"); + } + + out: + + if (src_path) + gtk_tree_path_free (src_path); + + return retval; +} + +static gboolean +gtk_tree_store_row_drop_possible (GtkTreeDragDest *drag_dest, + GtkTreeModel *src_model, + GtkTreePath *src_path, + GtkTreePath *dest_path) +{ + /* can only drag to ourselves */ + if (src_model != GTK_TREE_MODEL (drag_dest)) + return FALSE; + + /* Can't drop into ourself. */ + if (gtk_tree_path_is_ancestor (src_path, + dest_path)) + return FALSE; + + /* Can't drop if dest_path's parent doesn't exist */ + { + GtkTreeIter iter; + GtkTreePath *tmp = gtk_tree_path_copy (dest_path); + + /* if we can't go up, we know the parent exists, the root + * always exists. + */ + if (gtk_tree_path_up (tmp)) + { + if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (drag_dest), + &iter, tmp)) + { + if (tmp) + gtk_tree_path_free (tmp); + return FALSE; + } + } + + if (tmp) + gtk_tree_path_free (tmp); + } + + /* Can otherwise drop anywhere. */ + return TRUE; +} + +static void +validate_gnode (GNode* node) +{ + GNode *iter; + + iter = node->children; + while (iter != NULL) + { + g_assert (iter->parent == node); + if (iter->prev) + g_assert (iter->prev->next == iter); + validate_gnode (iter); + iter = iter->next; + } +} + + diff --git a/gtk/gtktreeview.c b/gtk/gtktreeview.c index 9d26b0a33b..db5de4e46b 100644 --- a/gtk/gtktreeview.c +++ b/gtk/gtktreeview.c @@ -4787,37 +4787,71 @@ gtk_tree_view_tree_to_widget_coords (GtkTreeView *tree_view, /* Drag-and-drop */ -void +static void set_source_row (GdkDragContext *context, + GtkTreeModel *model, GtkTreePath *source_row) { g_object_set_data_full (G_OBJECT (context), "gtk-tree-view-source-row", - source_row ? gtk_tree_path_copy (source_row) : NULL, - (GDestroyNotify) (source_row ? gtk_tree_path_free : NULL)); + source_row ? gtk_tree_row_reference_new (model, source_row) : NULL, + (GDestroyNotify) (source_row ? gtk_tree_row_reference_free : NULL)); } -GtkTreePath* +static GtkTreePath* get_source_row (GdkDragContext *context) { - return g_object_get_data (G_OBJECT (context), "gtk-tree-view-source-row"); + GtkTreeRowReference *ref = + g_object_get_data (G_OBJECT (context), "gtk-tree-view-source-row"); + + if (ref) + return gtk_tree_row_reference_get_path (ref); + else + return NULL; } -void +static void set_dest_row (GdkDragContext *context, + GtkTreeModel *model, GtkTreePath *dest_row) { g_object_set_data_full (G_OBJECT (context), "gtk-tree-view-dest-row", - dest_row ? gtk_tree_path_copy (dest_row) : NULL, - (GDestroyNotify) (dest_row ? gtk_tree_path_free : NULL)); + dest_row ? gtk_tree_row_reference_new (model, dest_row) : NULL, + (GDestroyNotify) (dest_row ? gtk_tree_row_reference_free : NULL)); } -GtkTreePath* +static GtkTreePath* get_dest_row (GdkDragContext *context) { - return g_object_get_data (G_OBJECT (context), "gtk-tree-view-dest-row"); + GtkTreeRowReference *ref = + g_object_get_data (G_OBJECT (context), "gtk-tree-view-dest-row"); + + if (ref) + return gtk_tree_row_reference_get_path (ref); + else + return NULL; +} + +/* Get/set whether drag_motion requested the drag data and + * drag_data_received should thus not actually insert the data, + * since the data doesn't result from a drop. + */ +static void +set_status_pending (GdkDragContext *context, + GdkDragAction suggested_action) +{ + g_object_set_data (G_OBJECT (context), + "gtk-tree-view-status-pending", + GINT_TO_POINTER (suggested_action)); +} + +static GdkDragAction +get_status_pending (GdkDragContext *context) +{ + return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context), + "gtk-tree-view-status-pending")); } typedef struct _TreeViewDragInfo TreeViewDragInfo; @@ -5234,6 +5268,7 @@ gtk_tree_view_maybe_begin_dragging_row (GtkTreeView *tree_view, GtkTreePath *path = NULL; gint button; gint cell_x, cell_y; + GtkTreeModel *model; di = get_info (tree_view); @@ -5242,12 +5277,17 @@ gtk_tree_view_maybe_begin_dragging_row (GtkTreeView *tree_view, if (tree_view->priv->pressed_button < 0) return FALSE; - + if (!gtk_drag_check_threshold (GTK_WIDGET (tree_view), tree_view->priv->press_start_x, tree_view->priv->press_start_y, event->x, event->y)) return FALSE; + + model = gtk_tree_view_get_model (tree_view); + + if (model == NULL) + return FALSE; button = tree_view->priv->pressed_button; tree_view->priv->pressed_button = -1; @@ -5297,7 +5337,7 @@ gtk_tree_view_maybe_begin_dragging_row (GtkTreeView *tree_view, gdk_pixmap_unref (row_pix); } - set_source_row (context, path); + set_source_row (context, model, path); gtk_tree_path_free (path); return TRUE; @@ -5357,7 +5397,7 @@ gtk_tree_view_drag_data_get (GtkWidget *widget, gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (model), source_row, selection_data)) - return; + goto done; /* If drag_data_get does nothing, try providing row data. */ if (selection_data->target == gdk_atom_intern ("GTK_TREE_MODEL_ROW", FALSE)) @@ -5366,6 +5406,9 @@ gtk_tree_view_drag_data_get (GtkWidget *widget, model, source_row); } + + done: + gtk_tree_path_free (source_row); } @@ -5378,13 +5421,14 @@ check_model_dnd (GtkTreeModel *model, { g_warning ("You must override the default '%s' handler " "on GtkTreeView when using models that don't support " - "the %s interface. The simplest way to do this " + "the %s interface and enabling drag-and-drop. The simplest way to do this " "is to connect to '%s' and call " "gtk_signal_emit_stop_by_name() in your signal handler to prevent " "the default handler from running. Look at the source code " "for the default handler in gtktreeview.c to get an idea what " "your handler should do. (gtktreeview.c is in the GTK source " - "code.)", + "code.) If you're using GTK from a language other than C, " + "there may be a more natural way to override default handlers, e.g. via derivation.", signal, g_type_name (required_iface), signal); return FALSE; } @@ -5404,6 +5448,8 @@ gtk_tree_view_drag_data_delete (GtkWidget *widget, tree_view = GTK_TREE_VIEW (widget); model = gtk_tree_view_get_model (tree_view); + g_print ("data_delete\n"); + if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_SOURCE, "drag_data_delete")) return; @@ -5414,11 +5460,15 @@ gtk_tree_view_drag_data_delete (GtkWidget *widget, source_row = get_source_row (context); - + if (source_row == NULL) + return; + gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (model), source_row); - - set_source_row (context, NULL); + + gtk_tree_path_free (source_row); + + set_source_row (context, NULL, NULL); } static void @@ -5488,7 +5538,8 @@ set_destination_row (GtkTreeView *tree_view, GdkDragContext *context, gint x, gint y, - GdkDragAction *suggested_action) + GdkDragAction *suggested_action, + GdkAtom *target) { GtkTreePath *path = NULL; GtkTreeViewDropPosition pos; @@ -5498,6 +5549,7 @@ set_destination_row (GtkTreeView *tree_view, GtkTreePath *old_dest_path = NULL; *suggested_action = 0; + *target = GDK_NONE; widget = GTK_WIDGET (tree_view); @@ -5520,8 +5572,8 @@ set_destination_row (GtkTreeView *tree_view, return FALSE; /* no longer a drop site */ } - /* We don't take this target */ - if (gtk_drag_dest_find_target (widget, context, di->dest_target_list) == GDK_NONE) + *target = gtk_drag_dest_find_target (widget, context, di->dest_target_list); + if (*target == GDK_NONE) { g_print ("bad target, not accepting\n"); return FALSE; @@ -5608,12 +5660,13 @@ gtk_tree_view_drag_motion (GtkWidget *widget, GtkTreeViewDropPosition pos; GtkTreeView *tree_view; GdkDragAction suggested_action = 0; + GdkAtom target; tree_view = GTK_TREE_VIEW (widget); g_print ("motion\n"); - if (!set_destination_row (tree_view, context, x, y, &suggested_action)) + if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target)) return FALSE; ensure_scroll_timeout (tree_view); @@ -5633,11 +5686,24 @@ gtk_tree_view_drag_motion (GtkWidget *widget, pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) { tree_view->priv->open_dest_timeout = - gtk_timeout_add (250, open_row_timeout, tree_view); + gtk_timeout_add (500, open_row_timeout, tree_view); } - g_print ("status\n"); - gdk_drag_status (context, suggested_action, time); + if (target == gdk_atom_intern ("GTK_TREE_MODEL_ROW", FALSE)) + { + /* Request data so we can use the source row when + * determining whether to accept the drop + */ + set_status_pending (context, suggested_action); + g_print ("motion requesting the drop data\n"); + gtk_drag_get_data (widget, context, target, time); + } + else + { + set_status_pending (context, 0); + g_print ("motion sending positive status\n"); + gdk_drag_status (context, suggested_action, time); + } } if (path) @@ -5646,6 +5712,36 @@ gtk_tree_view_drag_motion (GtkWidget *widget, return TRUE; } +static GtkTreePath* +get_logical_dest_row (GtkTreeView *tree_view) + +{ + /* adjust path to point to the row the drop goes in front of */ + GtkTreePath *path = NULL; + GtkTreeViewDropPosition pos; + + gtk_tree_view_get_drag_dest_row (tree_view, &path, &pos); + + if (path == NULL) + return NULL; + + if (pos == GTK_TREE_VIEW_DROP_BEFORE) + ; /* do nothing */ + else if (pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE || + pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER) + { + /* get first child, drop before it */ + gtk_tree_path_append_index (path, 0); + } + else + { + g_assert (pos == GTK_TREE_VIEW_DROP_AFTER); + gtk_tree_path_next (path); + } + + return path; +} + static gboolean gtk_tree_view_drag_drop (GtkWidget *widget, GdkDragContext *context, @@ -5653,9 +5749,8 @@ gtk_tree_view_drag_drop (GtkWidget *widget, gint y, guint time) { - GtkTreePath *path = NULL; - GtkTreeViewDropPosition pos; GtkTreeView *tree_view; + GtkTreePath *path; GdkDragAction suggested_action = 0; GdkAtom target = GDK_NONE; TreeViewDragInfo *di; @@ -5669,30 +5764,30 @@ gtk_tree_view_drag_drop (GtkWidget *widget, remove_scroll_timeout (GTK_TREE_VIEW (widget)); remove_open_timeout (GTK_TREE_VIEW (widget)); - - di = get_info (tree_view); - + + di = get_info (tree_view); + if (di == NULL) return FALSE; if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_drop")) return FALSE; - if (!set_destination_row (tree_view, context, x, y, &suggested_action)) + if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target)) return FALSE; - gtk_tree_view_get_drag_dest_row (tree_view, &path, &pos); + path = get_logical_dest_row (tree_view); - if (path != NULL && di->dest_target_list) + if (target != GDK_NONE && path != NULL) { - target = gtk_drag_dest_find_target (widget, context, di->dest_target_list); + g_print ("have target\n"); - if (target != GDK_NONE) - { - g_print ("have target\n"); - - set_dest_row (context, path); - } + /* in case a motion had requested drag data, change things so we + * treat drag data receives as a drop. + */ + set_status_pending (context, 0); + + set_dest_row (context, model, path); } if (path) @@ -5722,11 +5817,13 @@ gtk_tree_view_drag_data_received (GtkWidget *widget, guint info, guint time) { + GtkTreePath *path; TreeViewDragInfo *di; gboolean accepted = FALSE; GtkTreeModel *model; GtkTreeView *tree_view; GtkTreePath *dest_row; + GdkDragAction suggested_action; tree_view = GTK_TREE_VIEW (widget); @@ -5742,6 +5839,58 @@ gtk_tree_view_drag_data_received (GtkWidget *widget, if (di == NULL) return; + suggested_action = get_status_pending (context); + + if (suggested_action) + { + /* We are getting this data due to a request in drag_motion, + * rather than due to a request in drag_drop, so we are just + * supposed to call drag_status, not actually paste in the + * data. + */ + path = get_logical_dest_row (tree_view); + + if (path == NULL) + suggested_action = 0; + + if (suggested_action) + { + GtkTreeModel *src_model = NULL; + GtkTreePath *src_path = NULL; + + if (!gtk_selection_data_get_tree_row (selection_data, + &src_model, + &src_path)) + suggested_action = 0; + + if (suggested_action) + { + if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model), + src_model, + src_path, + path)) + suggested_action = 0; + + gtk_tree_path_free (src_path); + } + } + + g_print ("suggested action %d in drag_data_received\n", suggested_action); + + gdk_drag_status (context, suggested_action, time); + + if (path) + gtk_tree_path_free (path); + + /* If you can't drop, remove user drop indicator until the next motion */ + if (suggested_action == 0) + gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), + NULL, + GTK_TREE_VIEW_DROP_BEFORE); + + return; + } + dest_row = get_dest_row (context); if (dest_row == NULL) @@ -5753,16 +5902,17 @@ gtk_tree_view_drag_data_received (GtkWidget *widget, dest_row, selection_data)) accepted = TRUE; - - - /* Don't clear drop_row, we may need it in drag_data_delete */ } + + g_print ("accepted: %d\n", accepted); gtk_drag_finish (context, accepted, (context->action == GDK_ACTION_MOVE), time); - g_print ("accepted: %d\n", accepted); + + gtk_tree_path_free (dest_row); + + /* drop dest_row */ + set_dest_row (context, NULL, NULL); } - - diff --git a/gtk/gtktreeview.h b/gtk/gtktreeview.h index 22890ae66a..fd36f4d0a6 100644 --- a/gtk/gtktreeview.h +++ b/gtk/gtktreeview.h @@ -194,9 +194,9 @@ void gtk_tree_view_unset_rows_drag_dest (GtkTreeView *tree_view); void gtk_tree_view_set_drag_dest_row (GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewDropPosition pos); -void gtk_tree_view_get_drag_dest_row (GtkTreeView *tree_view, - GtkTreePath **path, - GtkTreeViewDropPosition *pos); +void gtk_tree_view_get_drag_dest_row (GtkTreeView *tree_view, + GtkTreePath **path, + GtkTreeViewDropPosition *pos); gboolean gtk_tree_view_get_dest_row_at_pos (GtkTreeView *tree_view, gint drag_x, gint drag_y,