Author: jannis Date: 2008-11-26 16:50:36 +0000 (Wed, 26 Nov 2008) New Revision: 28917
Modified: thunar/trunk/ChangeLog thunar/trunk/thunar-vfs/thunar-vfs-monitor.c thunar/trunk/thunar/thunar-tree-model.c thunar/trunk/thunar/thunar-tree-model.h thunar/trunk/thunar/thunar-tree-view.c Log: * thunar-vfs/thunar-vfs-monitor.c, thunar/thunar-tree-model.{c,h}, thunar/thunar-tree-view.c: Apply tree view pane improvements written by Nick. It implements a GtkTreeModelFilter inside the tree model and improves CPU performance when expanding folders in the tree view. It also stops monitoring tree view folders when they are closed. It improves thread safety in ThunarVfsMonitor and a few other things. Should fix bug #4051. Modified: thunar/trunk/ChangeLog =================================================================== --- thunar/trunk/ChangeLog 2008-11-26 06:46:25 UTC (rev 28916) +++ thunar/trunk/ChangeLog 2008-11-26 16:50:36 UTC (rev 28917) @@ -1,3 +1,13 @@ +2008-11-26 Jannis Pohlmann <[EMAIL PROTECTED]> + + * thunar-vfs/thunar-vfs-monitor.c, thunar/thunar-tree-model.{c,h}, + thunar/thunar-tree-view.c: Apply tree view pane improvements written + by Nick. It implements a GtkTreeModelFilter inside the tree model + and improves CPU performance when expanding folders in the tree + view. It also stops monitoring tree view folders when they are + closed. It improves thread safety in ThunarVfsMonitor and a few + other things. Should fix bug #4051. + 2008-11-25 Jannis Pohlmann <[EMAIL PROTECTED]> * thunar/thunar-standard-view.c: Make the location selector pop up Modified: thunar/trunk/thunar/thunar-tree-model.c =================================================================== --- thunar/trunk/thunar/thunar-tree-model.c 2008-11-26 06:46:25 UTC (rev 28916) +++ thunar/trunk/thunar/thunar-tree-model.c 2008-11-26 16:50:36 UTC (rev 28917) @@ -40,6 +40,9 @@ /* convenience macros */ #define G_NODE(node) ((GNode *) (node)) #define THUNAR_TREE_MODEL_ITEM(item) ((ThunarTreeModelItem *) (item)) +#define G_NODE_HAS_DUMMY(node) (node->children != NULL \ + && node->children->data == NULL \ + && node->children->next == NULL) @@ -106,6 +109,9 @@ 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 void thunar_tree_model_file_changed (ThunarFileMonitor *file_monitor, ThunarFile *file, ThunarTreeModel *model); @@ -127,7 +133,6 @@ static void thunar_tree_model_item_free (ThunarTreeModelItem *item); static void thunar_tree_model_item_reset (ThunarTreeModelItem *item); static void thunar_tree_model_item_load_folder (ThunarTreeModelItem *item); -static void thunar_tree_model_item_unload_folder (ThunarTreeModelItem *item); static void thunar_tree_model_item_files_added (ThunarTreeModelItem *item, GList *files, ThunarFolder *folder); @@ -139,8 +144,12 @@ static void thunar_tree_model_item_notify_loading (ThunarTreeModelItem *item, GParamSpec *pspec, ThunarFolder *folder); +static void thunar_tree_model_node_insert_dummy (GNode *parent, + ThunarTreeModel *model); static void thunar_tree_model_node_drop_dummy (GNode *node, ThunarTreeModel *model); +static gboolean thunar_tree_model_node_traverse_unload (GNode *node, + gpointer user_data); static gboolean thunar_tree_model_node_traverse_changed (GNode *node, gpointer user_data); static gboolean thunar_tree_model_node_traverse_remove (GNode *node, @@ -149,6 +158,8 @@ gpointer user_data); static gboolean thunar_tree_model_node_traverse_free (GNode *node, gpointer user_data); +static gboolean thunar_tree_model_node_traverse_visible (GNode *node, + gpointer user_data); @@ -159,41 +170,44 @@ struct _ThunarTreeModel { - GObject __parent__; + GObject __parent__; /* the model stamp is only used when debugging is * enabled, to make sure we don't accept iterators * generated by another model. */ #ifndef NDEBUG - gint stamp; + gint stamp; #endif /* removable volumes */ - ThunarVfsVolumeManager *volume_manager; - GList *hidden_volumes; + ThunarVfsVolumeManager *volume_manager; + GList *hidden_volumes; - ThunarFileMonitor *file_monitor; + ThunarFileMonitor *file_monitor; - gboolean sort_case_sensitive; + gboolean sort_case_sensitive; - GNode *root; - - /* when this setting is enabled, we do not ref nodes. this is - * used to avoid a race condition when gtk traverses the tree - * and reads the iter data. See bug #2502. - */ - gboolean lock_ref_node; + ThunarTreeModelVisibleFunc visible_func; + gpointer visible_data; + + GNode *root; + + guint unload_idle_id; }; struct _ThunarTreeModelItem { gint ref_count; - gint load_idle_id; + guint load_idle_id; ThunarFile *file; ThunarFolder *folder; ThunarVfsVolume *volume; ThunarTreeModel *model; + + /* list of children of this node that are + * not visible in the treeview */ + GSList *invisible_children; }; typedef struct @@ -313,7 +327,8 @@ /* initialize the model data */ model->sort_case_sensitive = TRUE; - model->lock_ref_node = FALSE; + model->visible_func = (ThunarTreeModelVisibleFunc) exo_noop_true; + model->visible_data = NULL; /* connect to the file monitor */ model->file_monitor = thunar_file_monitor_get_default (); @@ -366,6 +381,10 @@ ThunarTreeModel *model = THUNAR_TREE_MODEL (object); GList *lp; + /* remove unload idle */ + if (model->unload_idle_id != 0) + g_source_remove (model->unload_idle_id); + /* disconnect from the file monitor */ g_signal_handlers_disconnect_by_func (G_OBJECT (model->file_monitor), thunar_tree_model_file_changed, model); g_object_unref (G_OBJECT (model->file_monitor)); @@ -712,7 +731,7 @@ { ThunarTreeModel *model = THUNAR_TREE_MODEL (tree_model); GNode *child; - + _thunar_return_val_if_fail (parent == NULL || parent->user_data != NULL, FALSE); _thunar_return_val_if_fail (parent == NULL || parent->stamp == model->stamp, FALSE); @@ -767,10 +786,6 @@ _thunar_return_if_fail (iter->user_data != NULL); _thunar_return_if_fail (iter->stamp == model->stamp); - /* leave when locked */ - if (model->lock_ref_node) - return; - /* determine the node for the iterator */ node = G_NODE (iter->user_data); if (G_UNLIKELY (node == model->root)) @@ -785,6 +800,10 @@ } else { + /* schedule a reload of the folder if it is unloaded earlier */ + if (item->ref_count == 0) + thunar_tree_model_item_load_folder (item); + /* increment the reference count */ item->ref_count += 1; } @@ -812,15 +831,12 @@ item = node->data; if (G_LIKELY (item != NULL)) { - /* check if this was the last reference */ - if (G_UNLIKELY (item->ref_count == 1)) - { - /* schedule to unload the folder contents */ - thunar_tree_model_item_unload_folder (item); - } - /* decrement the reference count */ 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); } } @@ -847,9 +863,9 @@ GtkTreeIter iter; SortTuple *sort_array; GNode *child_node; - gint n_children; + guint n_children; gint *new_order; - gint n; + guint n; /* determine the number of children of the node */ n_children = g_node_n_children (node); @@ -905,6 +921,48 @@ 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) +{ + ThunarTreeModel *model = THUNAR_TREE_MODEL (user_data); + + GDK_THREADS_ENTER (); + + /* 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); + + GDK_THREADS_LEAVE (); + + return FALSE; +} + + + +static void +thunar_tree_model_unload_idle_destroy (gpointer user_data) +{ + THUNAR_TREE_MODEL (user_data)->unload_idle_id = 0; +} + + + +static void thunar_tree_model_file_changed (ThunarFileMonitor *file_monitor, ThunarFile *file, ThunarTreeModel *model) @@ -915,7 +973,8 @@ _thunar_return_if_fail (THUNAR_IS_FILE (file)); /* traverse the model and emit "row-changed" for the file's nodes */ - g_node_traverse (model->root, G_POST_ORDER, G_TRAVERSE_ALL, -1, thunar_tree_model_node_traverse_changed, file); + if (thunar_file_is_directory (file)) + g_node_traverse (model->root, G_PRE_ORDER, G_TRAVERSE_ALL, -1, thunar_tree_model_node_traverse_changed, file); } @@ -927,7 +986,6 @@ ThunarTreeModelItem *item = NULL; GtkTreePath *path; GtkTreeIter iter; - GNode *dummy; GNode *node; GList list; GList *lp; @@ -961,16 +1019,8 @@ gtk_tree_path_free (path); /* add the dummy node */ - node = g_node_append_data (node, NULL); + thunar_tree_model_node_insert_dummy (node, model); - /* determine the iterator for the dummy node */ - GTK_TREE_ITER_INIT (iter, model->stamp, node); - - /* tell the view about the dummy node */ - path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter); - gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, &iter); - gtk_tree_path_free (path); - /* drop our reference on the volume */ g_object_unref (G_OBJECT (volume)); } @@ -1026,15 +1076,7 @@ g_node_traverse (node->children, G_POST_ORDER, G_TRAVERSE_ALL, -1, thunar_tree_model_node_traverse_remove, model); /* append the dummy node */ - dummy = g_node_append_data (node, NULL); - - /* determine the iterator for the dummy node */ - GTK_TREE_ITER_INIT (iter, model->stamp, dummy); - - /* tell the view about the dummy node */ - path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter); - gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, &iter); - gtk_tree_path_free (path); + thunar_tree_model_node_insert_dummy (node, model); } /* generate an iterator for the item */ @@ -1055,9 +1097,7 @@ ThunarVfsVolume *volume, ThunarTreeModel *model) { - GtkTreePath *path; - GtkTreeIter iter; - GNode *node; + GNode *node; _thunar_return_if_fail (THUNAR_VFS_IS_VOLUME_MANAGER (volume_manager)); _thunar_return_if_fail (THUNAR_VFS_IS_VOLUME (volume)); @@ -1080,15 +1120,7 @@ g_node_traverse (node->children, G_POST_ORDER, G_TRAVERSE_ALL, -1, thunar_tree_model_node_traverse_remove, model); /* add the dummy node */ - node = g_node_append_data (node, NULL); - - /* determine the iterator for the dummy node */ - GTK_TREE_ITER_INIT (iter, model->stamp, node); - - /* tell the view about the dummy node */ - path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter); - gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, &iter); - gtk_tree_path_free (path); + thunar_tree_model_node_insert_dummy (node, model); } @@ -1240,6 +1272,14 @@ item->folder = NULL; } + /* free all the invisible children */ + if (item->invisible_children != NULL) + { + g_slist_foreach (item->invisible_children, (GFunc) g_object_unref, NULL); + g_slist_free (item->invisible_children); + item->invisible_children = NULL; + } + /* disconnect from the file */ if (G_LIKELY (item->file != NULL)) { @@ -1269,14 +1309,6 @@ static void -thunar_tree_model_item_unload_folder (ThunarTreeModelItem *item) -{ - // FIXME: Unload the children of item and add a dummy child -} - - - -static void thunar_tree_model_item_files_added (ThunarTreeModelItem *item, GList *files, ThunarFolder *folder) @@ -1292,6 +1324,7 @@ _thunar_return_if_fail (THUNAR_IS_FOLDER (folder)); _thunar_return_if_fail (item->folder == folder); + _thunar_return_if_fail (model->visible_func != NULL); /* process all specified files */ for (lp = files; lp != NULL; lp = lp->next) @@ -1301,6 +1334,15 @@ if (!thunar_file_is_directory (file)) continue; + /* if this file should be visible */ + if (!model->visible_func (model, file, model->visible_data)) + { + /* file is invisible, insert it in the invisible list and continue */ + item->invisible_children = g_slist_prepend (item->invisible_children, + g_object_ref (G_OBJECT (file))); + continue; + } + /* 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); @@ -1309,7 +1351,7 @@ child_item = thunar_tree_model_item_new_with_file (model, file); /* check if the node has only the dummy child */ - if (g_node_n_children (node) == 1 && g_node_first_child (node)->data == NULL) + if (G_NODE_HAS_DUMMY (node)) { /* replace the dummy node with the new node */ child_node = g_node_first_child (node); @@ -1338,15 +1380,7 @@ } /* add a dummy child node */ - child_node = g_node_append_data (child_node, NULL); - - /* determine the tree iter for the dummy */ - GTK_TREE_ITER_INIT (child_iter, model->stamp, child_node); - - /* emit a "row-inserted" for the dummy node */ - child_path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &child_iter); - gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), child_path, &child_iter); - gtk_tree_path_free (child_path); + thunar_tree_model_node_insert_dummy (child_node, model); } /* sort the folders if any new ones were added */ @@ -1367,6 +1401,7 @@ GNode *child_node; GNode *node; GList *lp; + GSList *link; _thunar_return_if_fail (THUNAR_IS_FOLDER (folder)); _thunar_return_if_fail (item->folder == folder); @@ -1402,6 +1437,24 @@ gtk_tree_path_free (path); } } + + /* we also need to release all the invisible folders */ + if (item->invisible_children != NULL) + { + for (lp = files; lp != NULL; lp = lp->next) + { + /* find the file in the hidden list */ + link = g_slist_find (item->invisible_children, lp->data); + if (link != NULL) + { + /* release the file */ + g_object_unref (G_OBJECT (lp->data)); + + /* remove from the list */ + item->invisible_children = g_slist_delete_link (item->invisible_children, link); + } + } + } } @@ -1423,7 +1476,8 @@ node = g_node_find (item->model->root, G_POST_ORDER, G_TRAVERSE_ALL, item); /* ...and drop the dummy for the node */ - thunar_tree_model_node_drop_dummy (node, item->model); + if (G_NODE_HAS_DUMMY (node)) + thunar_tree_model_node_drop_dummy (node, item->model); } } @@ -1436,7 +1490,17 @@ 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); + /* debug check to make sure the node is empty or contains a dummy node. + * if this is not true, the node already contains sub folders which means + * something went wrong. */ + _thunar_return_val_if_fail (node->children == NULL || G_NODE_HAS_DUMMY (node), FALSE); +#endif + GDK_THREADS_ENTER (); /* check if we don't have a file yet and this is a mounted volume */ @@ -1485,41 +1549,93 @@ static void +thunar_tree_model_node_insert_dummy (GNode *parent, + ThunarTreeModel *model) +{ + GNode *node; + GtkTreeIter iter; + GtkTreePath *path; + + _thunar_return_if_fail (THUNAR_IS_TREE_MODEL (model)); + _thunar_return_if_fail (g_node_n_children (parent) == 0); + + /* add the dummy node */ + node = g_node_append_data (parent, NULL); + + /* determine the iterator for the dummy node */ + GTK_TREE_ITER_INIT (iter, model->stamp, node); + + /* tell the view about the dummy node */ + path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter); + gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, &iter); + gtk_tree_path_free (path); +} + + + +static void thunar_tree_model_node_drop_dummy (GNode *node, ThunarTreeModel *model) { GtkTreePath *path; GtkTreeIter iter; - /* check if we still have the dummy child node */ - if (g_node_n_children (node) == 1 && node->children->data == NULL) + _thunar_return_if_fail (THUNAR_IS_TREE_MODEL (model)); + _thunar_return_if_fail (G_NODE_HAS_DUMMY (node)); + + /* determine the iterator for the dummy */ + GTK_TREE_ITER_INIT (iter, model->stamp, node->children); + + /* determine the path for the iterator */ + path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter); + if (G_LIKELY (path != NULL)) { - /* determine the iterator for the dummy */ - GTK_TREE_ITER_INIT (iter, model->stamp, node->children); + /* notify the view */ + gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path); - /* determine the path for the iterator */ - path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter); - if (G_LIKELY (path != NULL)) - { - /* notify the view */ - gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path); + /* drop the dummy from the model */ + g_node_destroy (node->children); - /* drop the dummy from the model */ - g_node_destroy (node->children); + /* determine the iter to the parent node */ + GTK_TREE_ITER_INIT (iter, model->stamp, node); - /* determine the iter to the parent node */ - GTK_TREE_ITER_INIT (iter, model->stamp, node); + /* determine the path to the parent node */ + gtk_tree_path_up (path); - /* determine the path to the parent node */ - gtk_tree_path_up (path); + /* emit a "row-has-child-toggled" for the parent */ + gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (model), path, &iter); - /* emit a "row-has-child-toggled" for the parent */ - gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (model), path, &iter); + /* release the path */ + gtk_tree_path_free (path); + } +} - /* release the path */ - gtk_tree_path_free (path); - } + + +static gboolean +thunar_tree_model_node_traverse_unload (GNode *node, + gpointer user_data) +{ + ThunarTreeModelItem *item = node->data; + ThunarTreeModel *model = THUNAR_TREE_MODEL (user_data); + + if (item && item->folder != NULL && item->ref_count == 0) + { + /* disconnect from the folder */ + g_signal_handlers_disconnect_matched (G_OBJECT (item->folder), G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, item); + g_object_unref (G_OBJECT (item->folder)); + item->folder = NULL; + + /* remove all the children of the node */ + while (node->children) + g_node_traverse (node->children, G_POST_ORDER, G_TRAVERSE_ALL, -1, + thunar_tree_model_node_traverse_remove, model); + + /* insert a dummy node */ + thunar_tree_model_node_insert_dummy (node, model); } + + return FALSE; } @@ -1528,13 +1644,14 @@ thunar_tree_model_node_traverse_changed (GNode *node, gpointer user_data) { - ThunarTreeModel *model; - GtkTreePath *path; - GtkTreeIter iter; - ThunarFile *file = THUNAR_FILE (user_data); + ThunarTreeModel *model; + GtkTreePath *path; + GtkTreeIter iter; + ThunarFile *file = THUNAR_FILE (user_data); + ThunarTreeModelItem *item = THUNAR_TREE_MODEL_ITEM (node->data); /* check if the node's file is the file that changed */ - if (node->data != NULL && THUNAR_TREE_MODEL_ITEM (node->data)->file == file) + if (G_UNLIKELY (item != NULL && item->file == file)) { /* determine the tree model from the item */ model = THUNAR_TREE_MODEL_ITEM (node->data)->model; @@ -1557,8 +1674,12 @@ gtk_tree_model_row_changed (GTK_TREE_MODEL (model), path, &iter); gtk_tree_path_free (path); } + + /* stop traversing */ + return TRUE; } + /* continue traversing */ return FALSE; } @@ -1623,6 +1744,96 @@ +static gboolean +thunar_tree_model_node_traverse_visible (GNode *node, + gpointer user_data) +{ + ThunarTreeModelItem *item = node->data; + ThunarTreeModel *model = THUNAR_TREE_MODEL (user_data); + GtkTreePath *path; + GtkTreeIter iter; + GNode *child_node; + GSList *lp, *lnext; + ThunarTreeModelItem *parent, *child; + ThunarFile *file; + + _thunar_return_val_if_fail (model->visible_func != NULL, FALSE); + + if (G_LIKELY (item)) + { + /* check if this file should be visibily in the treeview */ + if (!model->visible_func (model, item->file, model->visible_data)) + { + /* delete all the children of the node */ + while (node->children) + g_node_traverse (node->children, G_POST_ORDER, G_TRAVERSE_ALL, -1, + thunar_tree_model_node_traverse_remove, model); + + /* generate an iterator for the item */ + GTK_TREE_ITER_INIT (iter, model->stamp, node); + + /* remove this item from the tree */ + path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter); + gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path); + gtk_tree_path_free (path); + + /* insert the file in the invisible list of the parent */ + parent = node->parent->data; + if (G_LIKELY (parent)) + parent->invisible_children = g_slist_prepend (parent->invisible_children, + g_object_ref (G_OBJECT (item->file))); + + /* free the item and destroy the node */ + thunar_tree_model_item_free (item); + g_node_destroy (node); + } + else + { + /* this node should be visible. check if the node has invisible + * files that should be visible too */ + for (lp = item->invisible_children, child_node = NULL; lp != NULL; lp = lnext) + { + lnext = lp->next; + file = THUNAR_FILE (lp->data); + + if (model->visible_func (model, file, model->visible_data)) + { + /* allocate a new item for the file */ + child = thunar_tree_model_item_new_with_file (model, file); + + /* insert a new node for the child */ + child_node = g_node_append_data (node, child); + + /* determine the tree iter for the child */ + GTK_TREE_ITER_INIT (iter, model->stamp, child_node); + + /* emit a "row-inserted" for the new node */ + path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter); + gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, &iter); + gtk_tree_path_free (path); + + /* release the reference on the file hold by the invisible list */ + g_object_unref (G_OBJECT (file)); + + /* delete the file in the list */ + item->invisible_children = g_slist_delete_link (item->invisible_children, lp); + + /* insert dummy */ + thunar_tree_model_node_insert_dummy (child_node, model); + } + } + + /* sort this node if one of new children have been added */ + if (child_node != NULL) + thunar_tree_model_sort (model, node); + } + } + + return FALSE; +} + + + /** * thunar_tree_model_get_default: * @@ -1712,11 +1923,44 @@ +/** + * thunar_tree_model_set_visible_func: + * @model : a #ThunarTreeModel. + * @func : a #ThunarTreeModelVisibleFunc, the visible function. + * @data : User data to pass to the visible function, or %NULL. + * + * Sets the visible function used when filtering the #ThunarTreeModel. + * The function should return %TRUE if the given row should be visible + * and %FALSE otherwise. + **/ void -thunar_tree_model_set_lock_ref_node (ThunarTreeModel *model, - gboolean lock_ref_node) +thunar_tree_model_set_visible_func (ThunarTreeModel *model, + ThunarTreeModelVisibleFunc func, + gpointer data) { _thunar_return_if_fail (THUNAR_IS_TREE_MODEL (model)); - - model->lock_ref_node = !!lock_ref_node; + _thunar_return_if_fail (func != NULL); + + /* set the new visiblity function and user data */ + model->visible_func = func; + model->visible_data = data; } + + + +/** + * thunar_tree_model_refilter: + * @model : a #ThunarTreeModel. + * + * Walks all the folders in the #ThunarTreeModel and updates their + * visibility. + **/ +void +thunar_tree_model_refilter (ThunarTreeModel *model) +{ + _thunar_return_if_fail (THUNAR_IS_TREE_MODEL (model)); + + /* traverse all nodes to update their visibility */ + g_node_traverse (model->root, G_POST_ORDER, G_TRAVERSE_ALL, -1, + thunar_tree_model_node_traverse_visible, model); +} Modified: thunar/trunk/thunar/thunar-tree-model.h =================================================================== --- thunar/trunk/thunar/thunar-tree-model.h 2008-11-26 06:46:25 UTC (rev 28916) +++ thunar/trunk/thunar/thunar-tree-model.h 2008-11-26 16:50:36 UTC (rev 28917) @@ -27,6 +27,10 @@ typedef struct _ThunarTreeModelClass ThunarTreeModelClass; typedef struct _ThunarTreeModel ThunarTreeModel; +typedef gboolean (* ThunarTreeModelVisibleFunc) (ThunarTreeModel *model, + ThunarFile *file, + gpointer data); + #define THUNAR_TYPE_TREE_MODEL (thunar_tree_model_get_type ()) #define THUNAR_TREE_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THUNAR_TYPE_TREE_MODEL, ThunarTreeModel)) #define THUNAR_TREE_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), THUNAR_TYPE_TREE_MODEL, ThunarTreeModelClass)) @@ -57,12 +61,14 @@ ThunarTreeModel *thunar_tree_model_get_default (void); -gboolean thunar_tree_model_get_case_sensitive (ThunarTreeModel *model); -void thunar_tree_model_set_case_sensitive (ThunarTreeModel *model, - gboolean case_sensitive); +gboolean thunar_tree_model_get_case_sensitive (ThunarTreeModel *model); +void thunar_tree_model_set_case_sensitive (ThunarTreeModel *model, + gboolean case_sensitive); -void thunar_tree_model_set_lock_ref_node (ThunarTreeModel *model, - gboolean lock_ref_node); +void thunar_tree_model_set_visible_func (ThunarTreeModel *model, + ThunarTreeModelVisibleFunc func, + gpointer data); +void thunar_tree_model_refilter (ThunarTreeModel *model); G_END_DECLS; Modified: thunar/trunk/thunar/thunar-tree-view.c =================================================================== --- thunar/trunk/thunar/thunar-tree-view.c 2008-11-26 06:46:25 UTC (rev 28916) +++ thunar/trunk/thunar/thunar-tree-view.c 2008-11-26 16:50:36 UTC (rev 28917) @@ -118,7 +118,7 @@ static gboolean thunar_tree_view_delete_selected_files (ThunarTreeView *view); static void thunar_tree_view_context_menu (ThunarTreeView *view, GdkEventButton *event, - GtkTreeModel *filter, + GtkTreeModel *model, GtkTreeIter *iter); static GdkDragAction thunar_tree_view_get_dest_actions (ThunarTreeView *view, GdkDragContext *context, @@ -148,8 +148,8 @@ static void thunar_tree_view_new_files (ThunarVfsJob *job, GList *path_list, ThunarTreeView *view); -static gboolean thunar_tree_view_filter_visible_func (GtkTreeModel *filter, - GtkTreeIter *iter, +static gboolean thunar_tree_view_visible_func (ThunarTreeModel *model, + ThunarFile *file, gpointer user_data); static gboolean thunar_tree_view_selection_func (GtkTreeSelection *selection, GtkTreeModel *model, @@ -361,7 +361,6 @@ GtkTreeViewColumn *column; GtkTreeSelection *selection; GtkCellRenderer *renderer; - GtkTreeModel *filter; /* initialize the view internals */ view->cursor_idle_id = -1; @@ -376,13 +375,9 @@ /* connect to the default tree model */ view->model = thunar_tree_model_get_default (); + thunar_tree_model_set_visible_func (view->model, thunar_tree_view_visible_func, view); + gtk_tree_view_set_model (GTK_TREE_VIEW (view), GTK_TREE_MODEL (view->model)); - /* setup the filter for the tree view */ - filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (view->model), NULL); - gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter), thunar_tree_view_filter_visible_func, view, NULL); - gtk_tree_view_set_model (GTK_TREE_VIEW (view), filter); - g_object_unref (G_OBJECT (filter)); - /* configure the tree view */ gtk_tree_view_set_enable_search (GTK_TREE_VIEW (view), FALSE); gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (view), FALSE); @@ -543,7 +538,7 @@ ThunarFile *current_directory) { ThunarTreeView *view = THUNAR_TREE_VIEW (navigator); - GtkTreeModel *filter; + ThunarFile *file, *file_parent; /* check if we already use that directory */ if (G_UNLIKELY (view->current_directory == current_directory)) @@ -562,11 +557,30 @@ /* take a reference on the directory */ g_object_ref (G_OBJECT (current_directory)); - /* update the filter if the new current directory is hidden */ - if (!view->show_hidden && thunar_file_is_hidden (current_directory)) + /* update the filter if the new current directory, or one of it's parents, is hidden */ + if (!view->show_hidden) { - filter = gtk_tree_view_get_model (GTK_TREE_VIEW (view)); - gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter)); + /* look if the file or one of it's parents is hidden */ + for (file = g_object_ref (G_OBJECT (current_directory)); file != NULL; file = file_parent) + { + /* check if this file is hidden */ + if (thunar_file_is_hidden (file)) + { + /* update the filter */ + thunar_tree_model_refilter (view->model); + + /* release the file */ + g_object_unref (G_OBJECT (file)); + + break; + } + + /* get the file parent */ + file_parent = thunar_file_get_parent (file, NULL); + + /* release the file */ + g_object_unref (G_OBJECT (file)); + } } /* schedule an idle source to set the cursor to the current directory */ @@ -624,7 +638,6 @@ GdkEventButton *event) { ThunarTreeView *view = THUNAR_TREE_VIEW (widget); - GtkTreeModel *filter; GtkTreePath *path; GtkTreeIter iter; gboolean result; @@ -646,11 +659,10 @@ if (G_UNLIKELY (event->button == 3 && event->type == GDK_BUTTON_PRESS)) { /* determine the iterator for the path */ - filter = gtk_tree_view_get_model (GTK_TREE_VIEW (view)); - if (gtk_tree_model_get_iter (filter, &iter, path)) + if (gtk_tree_model_get_iter (GTK_TREE_MODEL (view->model), &iter, path)) { /* popup the context menu */ - thunar_tree_view_context_menu (view, event, filter, &iter); + thunar_tree_view_context_menu (view, event, GTK_TREE_MODEL (view->model), &iter); /* we effectively handled the event */ result = TRUE; @@ -875,15 +887,15 @@ { GtkTreeSelection *selection; ThunarTreeView *view = THUNAR_TREE_VIEW (widget); - GtkTreeModel *filter; + GtkTreeModel *model; GtkTreeIter iter; /* determine the selected row */ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view)); - if (gtk_tree_selection_get_selected (selection, &filter, &iter)) + if (gtk_tree_selection_get_selected (selection, &model, &iter)) { /* popup the context menu */ - thunar_tree_view_context_menu (view, NULL, filter, &iter); + thunar_tree_view_context_menu (view, NULL, model, &iter); return TRUE; } else if (GTK_WIDGET_CLASS (thunar_tree_view_parent_class)->popup_menu != NULL) @@ -927,14 +939,12 @@ { ThunarVfsVolume *volume; ThunarTreeView *view = THUNAR_TREE_VIEW (tree_view); - GtkTreeModel *filter; GtkWidget *window; gboolean expandable = TRUE; GError *error = NULL; /* determine the volume for the iterator */ - filter = gtk_tree_view_get_model (tree_view); - gtk_tree_model_get (filter, iter, THUNAR_TREE_MODEL_COLUMN_VOLUME, &volume, -1); + gtk_tree_model_get (GTK_TREE_MODEL (view->model), iter, THUNAR_TREE_MODEL_COLUMN_VOLUME, &volume, -1); /* check if we have a volume */ if (G_UNLIKELY (volume != NULL)) @@ -985,7 +995,7 @@ static void thunar_tree_view_context_menu (ThunarTreeView *view, GdkEventButton *event, - GtkTreeModel *filter, + GtkTreeModel *model, GtkTreeIter *iter) { ThunarVfsVolume *volume; @@ -1000,7 +1010,7 @@ return; /* determine the file and volume for the given iter */ - gtk_tree_model_get (filter, iter, + gtk_tree_model_get (model, iter, THUNAR_TREE_MODEL_COLUMN_FILE, &file, THUNAR_TREE_MODEL_COLUMN_VOLUME, &volume, -1); @@ -1209,7 +1219,6 @@ { GdkDragAction actions = 0; GdkDragAction action = 0; - GtkTreeModel *filter; GtkTreePath *path = NULL; GtkTreeIter iter; ThunarFile *file = NULL; @@ -1221,14 +1230,11 @@ /* determine the path for x/y */ if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (view), x, y, &path, NULL, NULL, NULL)) { - /* determine the tree model filter */ - filter = gtk_tree_view_get_model (GTK_TREE_VIEW (view)); - /* determine the iter for the given path */ - if (gtk_tree_model_get_iter (filter, &iter, path)) + if (gtk_tree_model_get_iter (GTK_TREE_MODEL (view->model), &iter, path)) { /* determine the file for the given path */ - gtk_tree_model_get (filter, &iter, THUNAR_TREE_MODEL_COLUMN_FILE, &file, -1); + gtk_tree_model_get (GTK_TREE_MODEL (view->model), &iter, THUNAR_TREE_MODEL_COLUMN_FILE, &file, -1); if (G_LIKELY (file != NULL)) { /* check if the file accepts the drop */ @@ -1288,7 +1294,7 @@ GtkTreePath **ancestor_return, gboolean *exact_return) { - GtkTreeModel *filter; + GtkTreeModel *model = GTK_TREE_MODEL (view->model); GtkTreePath *child_path; GtkTreeIter child_iter; GtkTreeIter iter; @@ -1296,15 +1302,14 @@ gboolean found = FALSE; /* determine the iter for the current path */ - filter = gtk_tree_view_get_model (GTK_TREE_VIEW (view)); - if (!gtk_tree_model_get_iter (filter, &iter, path)) + if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (view->model), &iter, path)) return FALSE; /* process all items at this level */ do { /* check if the file for iter is an ancestor of the current directory */ - gtk_tree_model_get (filter, &iter, THUNAR_TREE_MODEL_COLUMN_FILE, &file, -1); + gtk_tree_model_get (model, &iter, THUNAR_TREE_MODEL_COLUMN_FILE, &file, -1); if (G_UNLIKELY (file == NULL)) continue; @@ -1312,16 +1317,16 @@ if (G_UNLIKELY (file == view->current_directory)) { /* we found the very best ancestor iter! */ - *ancestor_return = gtk_tree_model_get_path (filter, &iter); + *ancestor_return = gtk_tree_model_get_path (model, &iter); *exact_return = TRUE; found = TRUE; } else if (thunar_file_is_ancestor (view->current_directory, file)) { /* check if we can find an even better ancestor below this node */ - if (gtk_tree_model_iter_children (filter, &child_iter, &iter)) + if (gtk_tree_model_iter_children (model, &child_iter, &iter)) { - child_path = gtk_tree_model_get_path (filter, &child_iter); + child_path = gtk_tree_model_get_path (model, &child_iter); found = thunar_tree_view_find_closest_ancestor (view, child_path, ancestor_return, exact_return); gtk_tree_path_free (child_path); } @@ -1330,7 +1335,7 @@ if (G_UNLIKELY (!found)) { /* we found an ancestor, not an exact match */ - *ancestor_return = gtk_tree_model_get_path (filter, &iter); + *ancestor_return = gtk_tree_model_get_path (model, &iter); *exact_return = FALSE; found = TRUE; } @@ -1339,7 +1344,7 @@ /* release the file reference */ g_object_unref (G_OBJECT (file)); } - while (!found && gtk_tree_model_iter_next (filter, &iter)); + while (!found && gtk_tree_model_iter_next (model, &iter)); return found; } @@ -1350,14 +1355,14 @@ thunar_tree_view_get_selected_file (ThunarTreeView *view) { GtkTreeSelection *selection; - GtkTreeModel *filter; + GtkTreeModel *model; GtkTreeIter iter; ThunarFile *file = NULL; /* determine file for the selected row */ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view)); - if (gtk_tree_selection_get_selected (selection, &filter, &iter)) - gtk_tree_model_get (filter, &iter, THUNAR_TREE_MODEL_COLUMN_FILE, &file, -1); + if (gtk_tree_selection_get_selected (selection, &model, &iter)) + gtk_tree_model_get (model, &iter, THUNAR_TREE_MODEL_COLUMN_FILE, &file, -1); return file; } @@ -1369,13 +1374,13 @@ { GtkTreeSelection *selection; ThunarVfsVolume *volume = NULL; - GtkTreeModel *filter; + GtkTreeModel *model; GtkTreeIter iter; /* determine file for the selected row */ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view)); - if (gtk_tree_selection_get_selected (selection, &filter, &iter)) - gtk_tree_model_get (filter, &iter, THUNAR_TREE_MODEL_COLUMN_VOLUME, &volume, -1); + if (gtk_tree_selection_get_selected (selection, &model, &iter)) + gtk_tree_model_get (model, &iter, THUNAR_TREE_MODEL_COLUMN_VOLUME, &volume, -1); return volume; } @@ -1538,7 +1543,7 @@ { /* determine the toplevel window */ window = gtk_widget_get_toplevel (GTK_WIDGET (view)); - + /* try to eject the volume */ if (!thunar_vfs_volume_eject (volume, window, &error)) { @@ -1789,28 +1794,22 @@ static gboolean -thunar_tree_view_filter_visible_func (GtkTreeModel *filter, - GtkTreeIter *iter, - gpointer user_data) +thunar_tree_view_visible_func (ThunarTreeModel *model, + ThunarFile *file, + gpointer user_data) { ThunarTreeView *view = THUNAR_TREE_VIEW (user_data); - ThunarFile *file; gboolean visible = TRUE; + _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE); + _thunar_return_val_if_fail (THUNAR_IS_TREE_MODEL (model), FALSE); + /* if show_hidden is TRUE, nothing is filtered */ if (G_LIKELY (!view->show_hidden)) { - /* check if we should display this row */ - gtk_tree_model_get (filter, iter, THUNAR_TREE_MODEL_COLUMN_FILE, &file, -1); - if (G_LIKELY (file != NULL)) - { - /* we display all non-hidden file and hidden files that are ancestors of the current directory */ - visible = !thunar_file_is_hidden (file) || (view->current_directory == file) - || (view->current_directory != NULL && thunar_file_is_ancestor (view->current_directory, file)); - - /* release the reference on the file */ - g_object_unref (G_OBJECT (file)); - } + /* we display all non-hidden file and hidden files that are ancestors of the current directory */ + visible = !thunar_file_is_hidden (file) || (view->current_directory == file) + || (view->current_directory != NULL && thunar_file_is_ancestor (view->current_directory, file)); } return visible; @@ -1820,7 +1819,7 @@ static gboolean thunar_tree_view_selection_func (GtkTreeSelection *selection, - GtkTreeModel *filter, + GtkTreeModel *model, GtkTreePath *path, gboolean path_currently_selected, gpointer user_data) @@ -1835,10 +1834,10 @@ return TRUE; /* determine the iterator for the path */ - if (gtk_tree_model_get_iter (filter, &iter, path)) + if (gtk_tree_model_get_iter (model, &iter, path)) { /* determine the file for the iterator */ - gtk_tree_model_get (filter, &iter, THUNAR_TREE_MODEL_COLUMN_FILE, &file, -1); + gtk_tree_model_get (model, &iter, THUNAR_TREE_MODEL_COLUMN_FILE, &file, -1); if (G_LIKELY (file != NULL)) { /* rows with files can be selected */ @@ -1850,7 +1849,7 @@ else { /* but maybe the row has a volume */ - gtk_tree_model_get (filter, &iter, THUNAR_TREE_MODEL_COLUMN_VOLUME, &volume, -1); + gtk_tree_model_get (model, &iter, THUNAR_TREE_MODEL_COLUMN_VOLUME, &volume, -1); if (G_LIKELY (volume != NULL)) { /* rows with volumes can also be selected */ @@ -2120,9 +2119,8 @@ thunar_tree_view_set_show_hidden (ThunarTreeView *view, gboolean show_hidden) { - GtkTreeModel *filter; - _thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view)); + _thunar_return_if_fail (THUNAR_IS_TREE_MODEL (view->model)); /* normalize the value */ show_hidden = !!show_hidden; @@ -2133,19 +2131,10 @@ /* apply the new setting */ view->show_hidden = show_hidden; - /* lock loading nodes in the tree, see bug #2505 */ - thunar_tree_model_set_lock_ref_node (THUNAR_TREE_MODEL (view->model), TRUE); + /* update the model */ + thunar_tree_model_refilter (view->model); - /* update the filter */ - filter = gtk_tree_view_get_model (GTK_TREE_VIEW (view)); - gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter)); - - /* release the lock */ - thunar_tree_model_set_lock_ref_node (THUNAR_TREE_MODEL (view->model), FALSE); - /* notify listeners */ g_object_notify (G_OBJECT (view), "show-hidden"); } } - - Modified: thunar/trunk/thunar-vfs/thunar-vfs-monitor.c =================================================================== --- thunar/trunk/thunar-vfs/thunar-vfs-monitor.c 2008-11-26 06:46:25 UTC (rev 28916) +++ thunar/trunk/thunar-vfs/thunar-vfs-monitor.c 2008-11-26 16:50:36 UTC (rev 28917) @@ -68,6 +68,7 @@ static gboolean thunar_vfs_monitor_notifications_timer (gpointer user_data); #ifdef HAVE_LIBFAM static void thunar_vfs_monitor_fam_cancel (ThunarVfsMonitor *monitor); +static gboolean thunar_vfs_monitor_fam_process_events (ThunarVfsMonitor *monitor); static gboolean thunar_vfs_monitor_fam_watch (GIOChannel *channel, GIOCondition condition, gpointer user_data); @@ -409,32 +410,26 @@ static gboolean -thunar_vfs_monitor_fam_watch (GIOChannel *channel, - GIOCondition condition, - gpointer user_data) +thunar_vfs_monitor_fam_process_events (ThunarVfsMonitor *monitor) { ThunarVfsMonitorEvent event; - ThunarVfsMonitor *monitor = THUNAR_VFS_MONITOR (user_data); FAMEvent fe; - /* check for an error on the FAM connection */ - if (G_UNLIKELY ((condition & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) != 0)) - { -error: - /* terminate the FAM connection */ - thunar_vfs_monitor_fam_cancel (monitor); + _thunar_vfs_return_val_if_fail (THUNAR_VFS_IS_MONITOR (monitor), FALSE); - /* thats it, no more FAM */ - return FALSE; - } - /* process all pending FAM events */ while (FAMPending (&monitor->fc)) { /* query the next pending event */ if (G_UNLIKELY (FAMNextEvent (&monitor->fc, &fe) < 0)) - goto error; + { + /* terminate the FAM connection */ + thunar_vfs_monitor_fam_cancel (monitor); + /* thats it, no more FAM */ + return FALSE; + } + /* translate the event code */ switch (fe.code) { @@ -456,13 +451,42 @@ } /* schedule a notification for the monitor */ - g_mutex_lock (monitor->lock); thunar_vfs_monitor_queue_notification (monitor, fe.fr.reqnum, THUNAR_VFS_MONITOR_TAG_FAM, event, fe.filename); - g_mutex_unlock (monitor->lock); } return TRUE; } + + + +static gboolean +thunar_vfs_monitor_fam_watch (GIOChannel *channel, + GIOCondition condition, + gpointer user_data) +{ + ThunarVfsMonitor *monitor = THUNAR_VFS_MONITOR (user_data); + gboolean result = FALSE; + + /* acquire the monitor lock */ + g_mutex_lock (monitor->lock); + + /* check for an error on the FAM connection */ + if (G_UNLIKELY ((condition & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) != 0)) + { + /* terminate the FAM connection */ + thunar_vfs_monitor_fam_cancel (monitor); + } + else + { + /* process all pending FAM events */ + result = thunar_vfs_monitor_fam_process_events (monitor); + } + + /* release the monitor lock */ + g_mutex_unlock (monitor->lock); + + return result; +} #endif @@ -634,8 +658,14 @@ /* drop the FAM request from the daemon */ if (G_LIKELY (monitor->fc_watch_id >= 0 && _thunar_vfs_path_is_local (handle->path))) { - if (FAMCancelMonitor (&monitor->fc, &handle->fr) < 0) - thunar_vfs_monitor_fam_cancel (monitor); + /* make sure there are no pending events before removing the request. + * if we don't do this, fam will lock up when a lot of requests are + * removed in a short time (collapse a treeview node for example) */ + if (thunar_vfs_monitor_fam_process_events (monitor)) + { + if (FAMCancelMonitor (&monitor->fc, &handle->fr) < 0) + thunar_vfs_monitor_fam_cancel (monitor); + } } #endif _______________________________________________ Xfce4-commits mailing list Xfce4-commits@xfce.org http://foo-projects.org/mailman/listinfo/xfce4-commits