From 898f53ce91d270f3af0e37051a2b01257051dc83 Mon Sep 17 00:00:00 2001 From: Alexander Schwinn Date: Thu, 13 Dec 2018 23:12:47 +0100 Subject: [PATCH] Thunar utilizes 100%CPU when the parent directory is not readable (Bug #14900) - as well fixes tree-view not showing the related folder --- thunar/thunar-tree-model.c | 117 +++++++++++++++++++---------- thunar/thunar-tree-model.h | 5 ++ thunar/thunar-tree-view.c | 178 +++++++++++++++------------------------------ 3 files changed, 142 insertions(+), 158 deletions(-) diff --git a/thunar/thunar-tree-model.c b/thunar/thunar-tree-model.c index 61f56f22..3424f480 100644 --- a/thunar/thunar-tree-model.c +++ b/thunar/thunar-tree-model.c @@ -1239,12 +1239,8 @@ thunar_tree_model_item_files_added (ThunarTreeModelItem *item, GList *files, ThunarFolder *folder) { - ThunarTreeModelItem *child_item; ThunarTreeModel *model = THUNAR_TREE_MODEL (item->model); - GtkTreePath *child_path; - GtkTreeIter child_iter; ThunarFile *file; - GNode *child_node; GNode *node = NULL; GList *lp; @@ -1274,40 +1270,7 @@ thunar_tree_model_item_files_added (ThunarTreeModelItem *item, 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); - - /* check if the node has only the dummy child */ - if (G_UNLIKELY (G_NODE_HAS_DUMMY (node))) - { - /* replace the dummy node with the new node */ - child_node = g_node_first_child (node); - child_node->data = child_item; - - /* determine the tree iter for the child */ - GTK_TREE_ITER_INIT (child_iter, model->stamp, child_node); - - /* emit a "row-changed" for the new node */ - child_path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &child_iter); - gtk_tree_model_row_changed (GTK_TREE_MODEL (model), child_path, &child_iter); - gtk_tree_path_free (child_path); - } - else - { - /* insert a new item for the child */ - child_node = g_node_append_data (node, child_item); - - /* determine the tree iter for the child */ - GTK_TREE_ITER_INIT (child_iter, model->stamp, child_node); - - /* emit a "row-inserted" for the new 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); - } - - /* add a dummy child node */ - thunar_tree_model_node_insert_dummy (child_node, model); + thunar_tree_model_add_child (model, node, file); } /* sort the folders if any new ones were added */ @@ -1893,3 +1856,81 @@ thunar_tree_model_cleanup (ThunarTreeModel *model) } } + + +/** + * thunar_tree_model_node_has_dummy: + * @model : a #ThunarTreeModel. + * @node : GNode to check + * + * Checks if node is a dummy node ( if it only has a dummy item ) + * + * Return value: %TRUE if @node has a dummy item + **/ +gboolean +thunar_tree_model_node_has_dummy (ThunarTreeModel *model, + GNode *node) +{ + _thunar_return_val_if_fail (THUNAR_IS_TREE_MODEL (model), TRUE); + return G_NODE_HAS_DUMMY(node); +} + + + +/** + * thunar_tree_model_add_child: + * @model : a #ThunarTreeModel. + * @node : GNode to add a child + * @file : #ThunarFile to be added + * + * Creates a new #ThunarTreeModelItem as a child of @node and stores a reference to the passed @file + * Automatically creates/removes dummy items if required + **/ +void +thunar_tree_model_add_child (ThunarTreeModel *model, + GNode *node, + ThunarFile *file) +{ + ThunarTreeModelItem *child_item; + GNode *child_node; + GtkTreeIter child_iter; + GtkTreePath *child_path; + + _thunar_return_if_fail (THUNAR_IS_TREE_MODEL (model)); + _thunar_return_if_fail (THUNAR_IS_FILE (file)); + + /* allocate a new item for the file */ + child_item = thunar_tree_model_item_new_with_file (model, file); + + /* check if the node has only the dummy child */ + if (G_UNLIKELY (G_NODE_HAS_DUMMY (node))) + { + /* replace the dummy node with the new node */ + child_node = g_node_first_child (node); + child_node->data = child_item; + + /* determine the tree iter for the child */ + GTK_TREE_ITER_INIT (child_iter, model->stamp, child_node); + + /* emit a "row-changed" for the new node */ + child_path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &child_iter); + gtk_tree_model_row_changed (GTK_TREE_MODEL (model), child_path, &child_iter); + gtk_tree_path_free (child_path); + } + else + { + /* insert a new item for the child */ + child_node = g_node_append_data (node, child_item); + + /* determine the tree iter for the child */ + GTK_TREE_ITER_INIT (child_iter, model->stamp, child_node); + + /* emit a "row-inserted" for the new 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); + } + + /* add a dummy to the new child */ + thunar_tree_model_node_insert_dummy (child_node, model); +} diff --git a/thunar/thunar-tree-model.h b/thunar/thunar-tree-model.h index 0bc0cf26..97b9ecc7 100644 --- a/thunar/thunar-tree-model.h +++ b/thunar/thunar-tree-model.h @@ -65,6 +65,11 @@ void thunar_tree_model_set_visible_func (ThunarTreeModel void thunar_tree_model_refilter (ThunarTreeModel *model); void thunar_tree_model_cleanup (ThunarTreeModel *model); +gboolean thunar_tree_model_node_has_dummy (ThunarTreeModel *model, + GNode *node); +void thunar_tree_model_add_child (ThunarTreeModel *model, + GNode *node, + ThunarFile *file); G_END_DECLS; diff --git a/thunar/thunar-tree-view.c b/thunar/thunar-tree-view.c index 77048d73..81b9a15c 100644 --- a/thunar/thunar-tree-view.c +++ b/thunar/thunar-tree-view.c @@ -140,10 +140,6 @@ static GdkDragAction thunar_tree_view_get_dest_actions (T gint y, guint time, ThunarFile **file_return); -static gboolean thunar_tree_view_find_closest_ancestor (ThunarTreeView *view, - GtkTreePath *path, - GtkTreePath **ancestor_return, - gboolean *exact_return); static ThunarFile *thunar_tree_view_get_selected_file (ThunarTreeView *view); static ThunarDevice *thunar_tree_view_get_selected_device (ThunarTreeView *view); static void thunar_tree_view_action_copy (ThunarTreeView *view); @@ -1669,69 +1665,6 @@ thunar_tree_view_get_dest_actions (ThunarTreeView *view, -static gboolean -thunar_tree_view_find_closest_ancestor (ThunarTreeView *view, - GtkTreePath *path, - GtkTreePath **ancestor_return, - gboolean *exact_return) -{ - GtkTreeModel *model = GTK_TREE_MODEL (view->model); - GtkTreePath *child_path; - GtkTreeIter child_iter; - GtkTreeIter iter; - ThunarFile *file; - gboolean found = FALSE; - - /* determine the iter for the current 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 (model, &iter, THUNAR_TREE_MODEL_COLUMN_FILE, &file, -1); - if (G_UNLIKELY (file == NULL)) - continue; - - /* check if this is the file we're looking for */ - if (G_UNLIKELY (file == view->current_directory)) - { - /* we found the very best ancestor 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 (model, &child_iter, &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); - } - - /* maybe not exact, but still an ancestor */ - if (G_UNLIKELY (!found)) - { - /* we found an ancestor, not an exact match */ - *ancestor_return = gtk_tree_model_get_path (model, &iter); - *exact_return = FALSE; - found = TRUE; - } - } - - /* release the file reference */ - g_object_unref (G_OBJECT (file)); - } - while (!found && gtk_tree_model_iter_next (model, &iter)); - - return found; -} - - - static ThunarFile* thunar_tree_view_get_selected_file (ThunarTreeView *view) { @@ -2565,12 +2498,14 @@ static gboolean thunar_tree_view_cursor_idle (gpointer user_data) { ThunarTreeView *view = THUNAR_TREE_VIEW (user_data); - GtkTreePath *ancestor = NULL; - GtkTreePath *parent; GtkTreePath *path; GtkTreeIter iter; ThunarFile *file; + GtkTreeIter child_iter; + ThunarFile *file_in_tree; gboolean done = TRUE; + GList *lp; + GList *path_as_list = NULL; THUNAR_THREADS_ENTER @@ -2580,71 +2515,74 @@ THUNAR_THREADS_ENTER gtk_tree_view_set_cursor (GTK_TREE_VIEW (view), view->select_path, NULL, FALSE); gtk_tree_path_free (view->select_path); view->select_path = NULL; + return done; } /* verify that we still have a current directory */ - else if (G_LIKELY (view->current_directory != NULL)) + if (G_LIKELY (view->current_directory == NULL)) + return done; + + /* get the preferred toplevel path for the current directory */ + path = thunar_tree_view_get_preferred_toplevel_path (view, view->current_directory); + + /* fallback to a newly created root node */ + if (path == NULL) + path = gtk_tree_path_new_first (); + + gtk_tree_model_get_iter (GTK_TREE_MODEL (view->model), &iter, path); + gtk_tree_path_free (path); + + /* collect all ThunarFiles in the path of current_directory in a List. root is on the very left side */ + for (file = view->current_directory; file != NULL; file = thunar_file_get_parent (file, NULL)) + path_as_list = g_list_prepend (path_as_list, file); + + /* note that iter may start at e.g. $HOME where "path_as_list" usually starts at "/" */ + /* So the first few iterations most times will do nothing */ + for (lp = path_as_list; lp != NULL; lp = lp->next) { - /* use the current cursor to limit the search to only the top-level of the selected node */ - gtk_tree_view_get_cursor (GTK_TREE_VIEW (view), &path, NULL); + file = THUNAR_FILE (lp->data); - /* if we have a path from the cursor but the current directory does not match it, - * unset it so that the correct toplevel item will be selected later */ - if (path) + /* check if iter has only a dummy node (tree not fully loaded yet) */ + if( thunar_tree_model_node_has_dummy (view->model, iter.user_data) ) { - gtk_tree_model_get_iter (GTK_TREE_MODEL (view->model), &iter, path); - gtk_tree_model_get (GTK_TREE_MODEL (view->model), &iter, THUNAR_TREE_MODEL_COLUMN_FILE, &file, -1); - if (file != view->current_directory) + done = FALSE; + break; + } + + /* initialize child_iter */ + if (!gtk_tree_model_iter_children (GTK_TREE_MODEL (view->model), &child_iter, &iter)) + { + /* E.g. folders for which we dont have read permission dont have any child in the tree */ + /* Make sure that missing read permissions are the problem */ + if (!g_file_info_get_attribute_boolean (thunar_file_get_info (thunar_file_get_parent (file, NULL)), G_FILE_ATTRIBUTE_ACCESS_CAN_READ)) { - gtk_tree_path_free (path); - path = NULL; + /* We know that there is a File. Lets just create the required tree-node */ + thunar_tree_model_add_child (view->model, iter.user_data, file); + done = FALSE; } - if (file) - g_object_unref (file); + break; } - /* no cursor set, get the preferred toplevel path for the current directory */ - if (path == NULL) - path = thunar_tree_view_get_preferred_toplevel_path (view, view->current_directory); - - /* fallback to a newly created root node */ - if (path == NULL) - path = gtk_tree_path_new_first (); - - /* look for the closest ancestor in the whole tree, starting from the current path */ - for (; gtk_tree_path_get_depth (path) > 0; gtk_tree_path_up (path)) + /* loop on children to see if any folder matches */ + while (TRUE) { - /* try to find the closest ancestor relative to the current path */ - if (thunar_tree_view_find_closest_ancestor (view, path, &ancestor, &done)) + gtk_tree_model_get (GTK_TREE_MODEL (view->model), &child_iter, THUNAR_TREE_MODEL_COLUMN_FILE, &file_in_tree, -1); + if (file == file_in_tree) { - /* expand to the best ancestor if not an exact match */ - if (G_LIKELY (!done)) - { - /* just expand everything up to the row and its children */ - gtk_tree_view_expand_to_path (GTK_TREE_VIEW (view), ancestor); - } - else if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (view), ancestor)) - { - /* just expand everything up to the row, but not the children */ - parent = gtk_tree_path_copy (ancestor); - if (gtk_tree_path_up (parent)) - gtk_tree_view_expand_to_path (GTK_TREE_VIEW (view), parent); - gtk_tree_path_free (parent); - } - - /* place cursor on the ancestor */ - gtk_tree_view_set_cursor (GTK_TREE_VIEW (view), ancestor, NULL, FALSE); - - /* release the ancestor path */ - gtk_tree_path_free (ancestor); - - /* we did it */ + g_object_unref (file_in_tree); + path = gtk_tree_model_get_path (GTK_TREE_MODEL (view->model), &child_iter); + gtk_tree_view_expand_to_path (GTK_TREE_VIEW (view), path); + gtk_tree_view_set_cursor (GTK_TREE_VIEW (view), path, NULL, FALSE); + gtk_tree_path_free (path); + iter = child_iter; /* next tree level */ break; } - } + if (file_in_tree) + g_object_unref (file_in_tree); - /* release the tree path */ - gtk_tree_path_free (path); + if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (view->model), &child_iter)) + break; + } } THUNAR_THREADS_LEAVE @@ -2896,7 +2834,7 @@ thunar_tree_view_set_show_hidden (ThunarTreeView *view, * 4) the root filesystem * * Returns the #GtkTreePath for the matching toplevel item, - * or %NULL if not found. + * or %NULL if not found. The path should be freed with gtk_tree_path_free(). **/ static GtkTreePath * thunar_tree_view_get_preferred_toplevel_path (ThunarTreeView *view, -- 2.11.0