diff --git a/ChangeLog b/ChangeLog index d980ed7af1dbb121f42b6bf1c6d1a8f7883c77b2..991b82d663d1ede0ed8020ffed0e454b53f7bc48 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2008-11-29 Nick Schermer <nick@xfce.org> + + * thunar/thunar-tree-model.c, thunar/thunar-tree-model.h, + thunar/thunar-tree-view.c: Instead of unloading folders + when their ref count is zero, we schedule a tree cleanup when + a row is collapsed. The reason is simple: the reffing the + treeview does is weird which results in glitches when scrolling + a large tree. + Also added some extra debug code. + 2008-11-28 Nick Schermer <nick@xfce.org> * Revert pervious commit, not a good way to fix this. diff --git a/thunar/thunar-tree-model.c b/thunar/thunar-tree-model.c index e09a1b8765055a8c47eb5c1e44c1eddc24b0bbb9..94d8ffdcd8f1dca874a0a7e8f8f5f33872296b51 100644 --- a/thunar/thunar-tree-model.c +++ b/thunar/thunar-tree-model.c @@ -109,9 +109,8 @@ static gint thunar_tree_model_cmp_array (gconstpoi gpointer user_data); static void thunar_tree_model_sort (ThunarTreeModel *model, GNode *node); -static void thunar_tree_model_unload (ThunarTreeModel *model); -static gboolean thunar_tree_model_unload_idle (gpointer user_data); -static void thunar_tree_model_unload_idle_destroy (gpointer user_data); +static gboolean thunar_tree_model_cleanup_idle (gpointer user_data); +static void thunar_tree_model_cleanup_idle_destroy (gpointer user_data); static void thunar_tree_model_file_changed (ThunarFileMonitor *file_monitor, ThunarFile *file, ThunarTreeModel *model); @@ -148,7 +147,7 @@ static void thunar_tree_model_node_insert_dummy (GNode ThunarTreeModel *model); static void thunar_tree_model_node_drop_dummy (GNode *node, ThunarTreeModel *model); -static gboolean thunar_tree_model_node_traverse_unload (GNode *node, +static gboolean thunar_tree_model_node_traverse_cleanup (GNode *node, gpointer user_data); static gboolean thunar_tree_model_node_traverse_changed (GNode *node, gpointer user_data); @@ -193,7 +192,7 @@ struct _ThunarTreeModel GNode *root; - guint unload_idle_id; + guint cleanup_idle_id; }; struct _ThunarTreeModelItem @@ -253,7 +252,7 @@ thunar_tree_model_get_type (void) type = g_type_register_static (G_TYPE_OBJECT, I_("ThunarTreeModel"), &info, 0); g_type_add_interface_static (type, GTK_TYPE_TREE_MODEL, &tree_model_info); } - + return type; } @@ -329,6 +328,7 @@ thunar_tree_model_init (ThunarTreeModel *model) model->sort_case_sensitive = TRUE; model->visible_func = (ThunarTreeModelVisibleFunc) exo_noop_true; model->visible_data = NULL; + model->cleanup_idle_id = 0; /* connect to the file monitor */ model->file_monitor = thunar_file_monitor_get_default (); @@ -381,9 +381,9 @@ thunar_tree_model_finalize (GObject *object) ThunarTreeModel *model = THUNAR_TREE_MODEL (object); GList *lp; - /* remove unload idle */ - if (model->unload_idle_id != 0) - g_source_remove (model->unload_idle_id); + /* remove the cleanup idle */ + if (model->cleanup_idle_id != 0) + g_source_remove (model->cleanup_idle_id); /* disconnect from the file monitor */ g_signal_handlers_disconnect_by_func (G_OBJECT (model->file_monitor), thunar_tree_model_file_changed, model); @@ -800,8 +800,8 @@ thunar_tree_model_ref_node (GtkTreeModel *tree_model, } else { - /* schedule a reload of the folder if it is unloaded earlier */ - if (item->ref_count == 0) + /* schedule a reload of the folder if it is cleaned earlier */ + if (G_UNLIKELY (item->ref_count == 0)) thunar_tree_model_item_load_folder (item); /* increment the reference count */ @@ -827,17 +827,14 @@ thunar_tree_model_unref_node (GtkTreeModel *tree_model, if (G_UNLIKELY (node == model->root)) return; - /* check if this a non-dummy item */ + /* check if this a non-dummy item, if so, decrement the reference count */ item = node->data; if (G_LIKELY (item != NULL)) - { - /* decrement the reference count */ - item->ref_count -= 1; + item->ref_count -= 1; - /* schedule an unload of the tree if an item is released by the treeview */ - if (G_UNLIKELY (item->ref_count == 0)) - thunar_tree_model_unload (model); - } + /* NOTE: we don't cleanup nodes when the item ref count is zero, + * because GtkTreeView also does a lot of reffing when scrolling the + * tree, which results in all sorts for glitches */ } @@ -920,24 +917,8 @@ thunar_tree_model_sort (ThunarTreeModel *model, -static void -thunar_tree_model_unload (ThunarTreeModel *model) -{ - /* schedule an idle unload, if not already done */ - if (model->unload_idle_id == 0) - { - /* the unload idle has a delay of 500 ms to make sure all the nodes - * are unreffed by the treeview. this allows the traverse unref work - * more efficiently */ - model->unload_idle_id = g_timeout_add_full (G_PRIORITY_LOW, 500, thunar_tree_model_unload_idle, - model, thunar_tree_model_unload_idle_destroy); - } -} - - - static gboolean -thunar_tree_model_unload_idle (gpointer user_data) +thunar_tree_model_cleanup_idle (gpointer user_data) { ThunarTreeModel *model = THUNAR_TREE_MODEL (user_data); @@ -945,7 +926,7 @@ thunar_tree_model_unload_idle (gpointer user_data) /* walk through the tree and release all the nodes with a ref count of 0 */ g_node_traverse (model->root, G_PRE_ORDER, G_TRAVERSE_ALL, -1, - thunar_tree_model_node_traverse_unload, model); + thunar_tree_model_node_traverse_cleanup, model); GDK_THREADS_LEAVE (); @@ -955,9 +936,9 @@ thunar_tree_model_unload_idle (gpointer user_data) static void -thunar_tree_model_unload_idle_destroy (gpointer user_data) +thunar_tree_model_cleanup_idle_destroy (gpointer user_data) { - THUNAR_TREE_MODEL (user_data)->unload_idle_id = 0; + THUNAR_TREE_MODEL (user_data)->cleanup_idle_id = 0; } @@ -1065,6 +1046,9 @@ thunar_tree_model_volume_changed (ThunarVfsVolume *volume, { /* try to determine the file for the mount point */ item->file = thunar_file_get_for_path (thunar_vfs_volume_get_mount_point (volume), NULL); + + /* because the volume node is already reffed, we need to load the folder manually here */ + thunar_tree_model_item_load_folder (item); } else if (!thunar_vfs_volume_is_mounted (volume) && item->file != NULL) { @@ -1298,8 +1282,10 @@ thunar_tree_model_item_reset (ThunarTreeModelItem *item) static void thunar_tree_model_item_load_folder (ThunarTreeModelItem *item) { + _thunar_return_if_fail (item->file != NULL || item->volume != NULL); + /* schedule the "load" idle source (if not already done) */ - if (G_LIKELY (item->folder == NULL && item->load_idle_id == 0)) + if (G_LIKELY (item->load_idle_id == 0 && item->folder == NULL)) { item->load_idle_id = g_idle_add_full (G_PRIORITY_HIGH, thunar_tree_model_item_load_idle, item, thunar_tree_model_item_load_idle_destroy); @@ -1346,6 +1332,7 @@ thunar_tree_model_item_files_added (ThunarTreeModelItem *item, /* lookup the node for the item (on-demand) */ if (G_UNLIKELY (node == NULL)) node = g_node_find (model->root, G_POST_ORDER, G_TRAVERSE_ALL, item); + _thunar_return_if_fail (node != NULL); /* allocate a new item for the file */ child_item = thunar_tree_model_item_new_with_file (model, file); @@ -1408,6 +1395,7 @@ thunar_tree_model_item_files_removed (ThunarTreeModelItem *item, /* determine the node for the folder */ node = g_node_find (model->root, G_POST_ORDER, G_TRAVERSE_ALL, item); + _thunar_return_if_fail (node != NULL); /* check if the node has any visible children */ if (G_LIKELY (node->children != NULL)) @@ -1468,12 +1456,14 @@ thunar_tree_model_item_notify_loading (ThunarTreeModelItem *item, _thunar_return_if_fail (THUNAR_IS_FOLDER (folder)); _thunar_return_if_fail (item->folder == folder); + _thunar_return_if_fail (THUNAR_IS_TREE_MODEL (item->model)); /* be sure to drop the dummy child node once the folder is loaded */ if (G_LIKELY (!thunar_folder_get_loading (folder))) { /* lookup the node for the item... */ node = g_node_find (item->model->root, G_POST_ORDER, G_TRAVERSE_ALL, item); + _thunar_return_if_fail (node != NULL); /* ...and drop the dummy for the node */ if (G_NODE_HAS_DUMMY (node)) @@ -1490,7 +1480,7 @@ thunar_tree_model_item_load_idle (gpointer user_data) GList *files; _thunar_return_val_if_fail (item->folder == NULL, FALSE); - + #ifndef NDEBUG /* find the node in the tree */ GNode *node = g_node_find (item->model->root, G_POST_ORDER, G_TRAVERSE_ALL, item); @@ -1581,7 +1571,7 @@ thunar_tree_model_node_drop_dummy (GNode *node, GtkTreeIter iter; _thunar_return_if_fail (THUNAR_IS_TREE_MODEL (model)); - _thunar_return_if_fail (G_NODE_HAS_DUMMY (node)); + _thunar_return_if_fail (G_NODE_HAS_DUMMY (node) && g_node_n_children (node) == 1); /* determine the iterator for the dummy */ GTK_TREE_ITER_INIT (iter, model->stamp, node->children); @@ -1613,8 +1603,8 @@ thunar_tree_model_node_drop_dummy (GNode *node, static gboolean -thunar_tree_model_node_traverse_unload (GNode *node, - gpointer user_data) +thunar_tree_model_node_traverse_cleanup (GNode *node, + gpointer user_data) { ThunarTreeModelItem *item = node->data; ThunarTreeModel *model = THUNAR_TREE_MODEL (user_data); @@ -1693,6 +1683,8 @@ thunar_tree_model_node_traverse_remove (GNode *node, GtkTreeIter iter; GtkTreePath *path; + _thunar_return_val_if_fail (node->children == NULL, FALSE); + /* determine the iterator for the node */ GTK_TREE_ITER_INIT (iter, model->stamp, node); @@ -1967,3 +1959,26 @@ thunar_tree_model_refilter (ThunarTreeModel *model) g_node_traverse (model->root, G_POST_ORDER, G_TRAVERSE_ALL, -1, thunar_tree_model_node_traverse_visible, model); } + + + +/** + * thunar_tree_model_cleanup: + * @model : a #ThunarTreeModel. + * + * Walks all the folders in the #ThunarTreeModel and release them when + * they are unused by the treeview. + **/ +void +thunar_tree_model_cleanup (ThunarTreeModel *model) +{ + _thunar_return_if_fail (THUNAR_IS_TREE_MODEL (model)); + + /* schedule an idle cleanup, if not already done */ + if (model->cleanup_idle_id == 0) + { + model->cleanup_idle_id = g_timeout_add_full (G_PRIORITY_LOW, 500, thunar_tree_model_cleanup_idle, + model, thunar_tree_model_cleanup_idle_destroy); + } +} + diff --git a/thunar/thunar-tree-model.h b/thunar/thunar-tree-model.h index a08f83f20bb20b74a0fdc766f2bafeee3d227759..e040dc5b2dcdd6f0650c20d3071d35ace932700b 100644 --- a/thunar/thunar-tree-model.h +++ b/thunar/thunar-tree-model.h @@ -70,6 +70,8 @@ void thunar_tree_model_set_visible_func (ThunarTreeModel gpointer data); void thunar_tree_model_refilter (ThunarTreeModel *model); +void thunar_tree_model_cleanup (ThunarTreeModel *model); + G_END_DECLS; #endif /* !__THUNAR_TREE_MODEL_H__ */ diff --git a/thunar/thunar-tree-view.c b/thunar/thunar-tree-view.c index 828b5418a47d35ecfc12a9f211e4b8cabb869ddf..def8d141d2c4e1077363b85ed557c72000ccf3c7 100644 --- a/thunar/thunar-tree-view.c +++ b/thunar/thunar-tree-view.c @@ -115,6 +115,9 @@ static void thunar_tree_view_row_activated (GtkTreeView static gboolean thunar_tree_view_test_expand_row (GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path); +static void thunar_tree_view_row_collapsed (GtkTreeView *tree_view, + GtkTreeIter *iter, + GtkTreePath *path); static gboolean thunar_tree_view_delete_selected_files (ThunarTreeView *view); static void thunar_tree_view_context_menu (ThunarTreeView *view, GdkEventButton *event, @@ -299,6 +302,7 @@ thunar_tree_view_class_init (ThunarTreeViewClass *klass) gtktree_view_class = GTK_TREE_VIEW_CLASS (klass); gtktree_view_class->row_activated = thunar_tree_view_row_activated; gtktree_view_class->test_expand_row = thunar_tree_view_test_expand_row; + gtktree_view_class->row_collapsed = thunar_tree_view_row_collapsed; klass->delete_selected_files = thunar_tree_view_delete_selected_files; @@ -978,6 +982,17 @@ thunar_tree_view_test_expand_row (GtkTreeView *tree_view, +static void +thunar_tree_view_row_collapsed (GtkTreeView *tree_view, + GtkTreeIter *iter, + GtkTreePath *path) +{ + /* schedule a cleanup of the tree model */ + thunar_tree_model_cleanup (THUNAR_TREE_VIEW (tree_view)->model); +} + + + static gboolean thunar_tree_view_delete_selected_files (ThunarTreeView *view) {