From 4abf5549f60e568c76a7bcf0fe20b5ec42271386 Mon Sep 17 00:00:00 2001 From: Harald Judt <h.judt@gmx.at> Date: Sat, 9 May 2015 12:40:57 +0200 Subject: [PATCH] Always select the proper toplevel item in the thunar tree view When the user clicks on e.g. "Desktop" in the treeview, the treeview will expand the tree and set the cursor on home/Desktop instead on the proper toplevel item (assuming the Desktop folder resides in the user's home directory of course). This is very bad for user experience. To fix this, this commit makes the treeview a) use the cursor if set to limit the search to below the current toplevel item b) select the proper toplevel item to expand to the descendant in the following order: 1) any mounted device or network resource 2) the user's desktop directory 3) the user's home directory 4) the root filesystem --- thunar/thunar-tree-view.c | 145 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 139 insertions(+), 6 deletions(-) diff --git a/thunar/thunar-tree-view.c b/thunar/thunar-tree-view.c index fc2f0259b..06ef9973f 100644 --- a/thunar/thunar-tree-view.c +++ b/thunar/thunar-tree-view.c @@ -196,6 +196,8 @@ static void thunar_tree_view_mount_data_free (T static gboolean thunar_tree_view_get_show_hidden (ThunarTreeView *view); static void thunar_tree_view_set_show_hidden (ThunarTreeView *view, gboolean show_hidden); +static GtkTreePath *thunar_tree_view_get_preferred_toplevel_path (ThunarTreeView *view, + ThunarFile *file); @@ -2488,6 +2490,8 @@ thunar_tree_view_cursor_idle (gpointer user_data) GtkTreePath *ancestor = NULL; GtkTreePath *parent; GtkTreePath *path; + GtkTreeIter iter; + ThunarFile *file; gboolean done = TRUE; GDK_THREADS_ENTER (); @@ -2495,18 +2499,35 @@ thunar_tree_view_cursor_idle (gpointer user_data) /* verify that we still have a current directory */ if (G_LIKELY (view->current_directory != NULL)) { - /* determine the current cursor (fallback to root node) */ + /* 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); - if (G_UNLIKELY (path == NULL)) + + /* 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) + { + 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) + { + gtk_tree_path_free (path); + path = NULL; + } + if (file) + g_object_unref (file); + } + + /* 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)) { - /* be sure to start from the first path at that level */ - while (gtk_tree_path_prev (path)) - ; - /* try to find the closest ancestor relative to the current path */ if (thunar_tree_view_find_closest_ancestor (view, path, &ancestor, &done)) { @@ -2769,6 +2790,118 @@ thunar_tree_view_set_show_hidden (ThunarTreeView *view, +/** + * thunar_tree_view_get_preferred_toplevel_path: + * @view : a #ThunarTreeView. + * @file : the #ThunarFile we want the toplevel path for + * + * Searches for the best-matching toplevel path in the + * following order: + * 1) any mounted device or network resource + * 2) the user's desktop directory + * 3) the user's home directory + * 4) the root filesystem + * + * Returns the #GtkTreePath for the matching toplevel item, + * or %NULL if not found. + **/ +static GtkTreePath * +thunar_tree_view_get_preferred_toplevel_path (ThunarTreeView *view, + ThunarFile *file) +{ + GtkTreeModel *model = GTK_TREE_MODEL (view->model); + GtkTreePath *path = NULL; + GtkTreeIter iter; + ThunarFile *toplevel_file; + GFile *desktop; + GFile *home; + GFile *root; + GFile *best_match; + + _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL); + + /* check whether the root node is available */ + if (!gtk_tree_model_get_iter_first (model, &iter)) + return NULL; + + /* get GFiles for special toplevel items */ + desktop = thunar_g_file_new_for_desktop (); + home = thunar_g_file_new_for_home (); + root = thunar_g_file_new_for_root (); + + /* we prefer certain toplevel items to others */ + if (thunar_file_is_gfile_ancestor (file, desktop)) + best_match = desktop; + else if (thunar_file_is_gfile_ancestor (file, home)) + best_match = home; + else if (thunar_file_is_gfile_ancestor (file, root)) + best_match = root; + else + best_match = NULL; + + /* loop over all top-level nodes to find the best-matching top-level item */ + do + { + gtk_tree_model_get (model, &iter, THUNAR_TREE_MODEL_COLUMN_FILE, &toplevel_file, -1); + /* this toplevel item has no file, so continue with the next */ + if (toplevel_file == NULL) + continue; + + /* if the file matches the toplevel item exactly, we are done */ + if (g_file_equal (thunar_file_get_file (file), + thunar_file_get_file (toplevel_file))) + { + gtk_tree_path_free (path); + path = gtk_tree_model_get_path (model, &iter); + g_object_unref (toplevel_file); + break; + } + + if (thunar_file_is_ancestor (file, toplevel_file)) + { + /* the toplevel item could be a mounted device or network + * and we prefer this to everything else */ + if (!g_file_equal (thunar_file_get_file (toplevel_file), desktop) && + !g_file_equal (thunar_file_get_file (toplevel_file), home) && + !g_file_equal (thunar_file_get_file (toplevel_file), root)) + { + gtk_tree_path_free (path); + g_object_unref (toplevel_file); + path = gtk_tree_model_get_path (model, &iter); + break; + } + + /* continue if the toplevel item is already the best match */ + if (best_match != NULL && + g_file_equal (thunar_file_get_file (toplevel_file), best_match)) + { + gtk_tree_path_free (path); + g_object_unref (toplevel_file); + path = gtk_tree_model_get_path (model, &iter); + continue; + } + + /* remember this ancestor if we do not already have one */ + if (path == NULL) + { + gtk_tree_path_free (path); + path = gtk_tree_model_get_path (model, &iter); + } + } + g_object_unref (toplevel_file); + } + while (gtk_tree_model_iter_next (model, &iter)); + + /* cleanup */ + g_object_unref (root); + g_object_unref (home); + g_object_unref (desktop); + + return path; +} + + + /** * thunar_tree_view_new: * -- GitLab