pathbar: Handle webdav where is the root is a path
authorOlivier Crête <olivier.crete@collabora.com>
Thu, 13 Apr 2023 22:09:48 +0000 (16:09 -0600)
committerOlivier Crête <olivier.crete@collabora.com>
Thu, 13 Apr 2023 23:54:57 +0000 (17:54 -0600)
Our webdav server has a root which is davs://mynextcloud/remote.php/webdav
When once creates a GFile out of or out of a subdirectory, and one call
g_file_get_parent(), it recurses too far up and try to query
davs://mynextcloud/remote.php which fails, resulting in a broken pathbar.

To fix that, before querying the metadata of each element of the path,
I query the "enclosing mount", then use it's root to compare the GFile
against.

With the right GMount, we can also fix the icon drawing code in the
pathbar for network drives.

gtk/gtkpathbar.c

index a19985732e21573af7c0644cb4076702bf574a07..41e61c20a5a7e5a19dd6f6228b13af722aa0ffd1 100644 (file)
@@ -125,6 +125,7 @@ struct _ButtonData
   GCancellable *cancellable;
   guint ignore_changes : 1;
   guint file_is_hidden : 1;
+  GMount *mount;
 };
 /* This macro is used to check if a button can be used as a fake root.
  * All buttons in front of a fake root are automatically hidden when in a
@@ -417,30 +418,30 @@ set_button_image (GtkPathBar *path_bar,
                   ButtonData *button_data)
 {
   struct SetButtonImageData *data;
-  GMount *mount;
 
   switch (button_data->type)
     {
     case ROOT_BUTTON:
 
-      if (path_bar->root_icon != NULL)
+      GIcon *root_icon = NULL;
+
+      if (!button_data->mount && path_bar->root_icon != NULL &&
+          g_file_is_native (button_data->file))
         {
           gtk_image_set_from_gicon (GTK_IMAGE (button_data->image), path_bar->root_icon);
-         break;
-       }
-
-      mount = g_file_find_enclosing_mount (button_data->file, NULL, NULL);
+          break;
+        }
 
-      if (!mount && g_file_is_native (button_data->file))
-        path_bar->root_icon = g_themed_icon_new ("drive-harddisk-symbolic");
-      else if (mount)
-        path_bar->root_icon = g_mount_get_symbolic_icon (mount);
+      if (!button_data->mount && g_file_is_native (button_data->file))
+        root_icon = path_bar->root_icon = g_object_ref (g_themed_icon_new ("drive-harddisk-symbolic"));
+      else if (button_data->mount)
+        root_icon = g_mount_get_symbolic_icon (button_data->mount);
       else
-        path_bar->root_icon = NULL;
+        root_icon = NULL;
 
-      g_clear_object (&mount);
+      gtk_image_set_from_gicon (GTK_IMAGE (button_data->image), root_icon);
 
-      gtk_image_set_from_gicon (GTK_IMAGE (button_data->image), path_bar->root_icon);
+      g_clear_object (&root_icon);
 
       break;
 
@@ -515,6 +516,7 @@ static void
 button_data_free (ButtonData *button_data)
 {
   g_clear_object (&button_data->file);
+  g_clear_object (&button_data->mount);
   g_free (button_data->dir_name);
   g_free (button_data);
 }
@@ -571,11 +573,11 @@ file_is_recent_uri (GFile *file)
 }
 
 static ButtonType
-find_button_type (GtkPathBar  *path_bar,
-                 GFile       *file)
+find_button_type (GtkPathBar *path_bar,
+                  GFile *file,
+                  GFile *root_file)
 {
-  if (path_bar->root_file != NULL &&
-      g_file_equal (file, path_bar->root_file))
+  if (root_file != NULL && g_file_equal (file, root_file))
     return ROOT_BUTTON;
   if (path_bar->home_file != NULL &&
       g_file_equal (file, path_bar->home_file))
@@ -593,6 +595,8 @@ static ButtonData *
 make_directory_button (GtkPathBar  *path_bar,
                       const char  *dir_name,
                       GFile       *file,
+                      GMount      *mount,
+                      GFile       *root_file,
                       gboolean     current_dir,
                       gboolean     file_is_hidden)
 {
@@ -604,17 +608,26 @@ make_directory_button (GtkPathBar  *path_bar,
   file_is_hidden = !! file_is_hidden;
   /* Is it a special button? */
   button_data = g_new0 (ButtonData, 1);
-  button_data->type = find_button_type (path_bar, file);
+  button_data->type = find_button_type (path_bar, file, root_file);
   button_data->button = gtk_toggle_button_new ();
   gtk_widget_set_focus_on_click (button_data->button, FALSE);
 
   switch (button_data->type)
     {
     case ROOT_BUTTON:
-      button_data->image = gtk_image_new ();
-      child = button_data->image;
-      button_data->label = NULL;
-      break;
+      if (mount)
+        {
+          button_data->dir_name = g_mount_get_name (mount);
+          button_data->mount = g_object_ref (mount);
+        }
+      else
+        {
+          button_data->image = gtk_image_new ();
+          child = button_data->image;
+          button_data->label = NULL;
+          break;
+        }
+      G_GNUC_FALLTHROUGH;
     case HOME_BUTTON:
     case DESKTOP_BUTTON:
     case RECENT_BUTTON:
@@ -631,7 +644,8 @@ make_directory_button (GtkPathBar  *path_bar,
       button_data->image = NULL;
     }
 
-  button_data->dir_name = g_strdup (dir_name);
+  if (button_data->dir_name == NULL)
+    button_data->dir_name = g_strdup (dir_name);
   button_data->file = g_object_ref (file);
   button_data->file_is_hidden = file_is_hidden;
 
@@ -715,6 +729,8 @@ gtk_path_bar_check_parent_path (GtkPathBar         *path_bar,
 struct SetFileInfo
 {
   GFile *file;
+  GMount *mount;
+  GFile *root_file;
   GFile *parent_file;
   GtkPathBar *path_bar;
   GList *new_buttons;
@@ -762,10 +778,15 @@ gtk_path_bar_set_file_finish (struct SetFileInfo *info,
     g_object_unref (info->file);
   if (info->parent_file)
     g_object_unref (info->parent_file);
+  if (info->root_file)
+    g_object_unref (info->root_file);
+  if (info->mount)
+    g_object_unref (info->mount);
 
   g_free (info);
 }
 
+
 static void
 gtk_path_bar_get_info_callback (GObject      *source,
                                 GAsyncResult *result,
@@ -802,6 +823,8 @@ gtk_path_bar_get_info_callback (GObject      *source,
 
   button_data = make_directory_button (file_info->path_bar, display_name,
                                        file_info->file,
+                                       file_info->mount,
+                                       file_info->root_file,
                                        file_info->first_directory, is_hidden);
   g_clear_object (&file_info->file);
 
@@ -824,7 +847,54 @@ gtk_path_bar_get_info_callback (GObject      *source,
       return;
     }
 
-  file_info->parent_file = g_file_get_parent (file_info->file);
+  if (g_file_equal (file_info->file, file_info->root_file))
+    file_info->parent_file = NULL;
+  else
+    file_info->parent_file = g_file_get_parent (file_info->file);
+
+  /* Recurse asynchronously */
+  file_info->cancellable = g_cancellable_new ();
+  file_info->path_bar->get_info_cancellable = file_info->cancellable;
+  g_file_query_info_async (file_info->file,
+                           "standard::display-name,"
+                           "standard::is-hidden,"
+                           "standard::is-backup",
+                           G_FILE_QUERY_INFO_NONE,
+                           G_PRIORITY_DEFAULT,
+                           file_info->cancellable,
+                           gtk_path_bar_get_info_callback,
+                           file_info);
+  add_cancellable (file_info->path_bar, file_info->cancellable);
+}
+
+static void
+gtk_path_bar_get_mount_callback (GObject      *source,
+                                GAsyncResult *result,
+                                gpointer      data)
+{
+  GFile *file = G_FILE (source);
+  struct SetFileInfo *file_info = data;
+
+  file_info->mount = g_file_find_enclosing_mount_finish (file, result, NULL);
+
+  if (file_info->mount)
+    file_info->root_file = g_mount_get_root (file_info->mount);
+
+  g_assert (GTK_IS_PATH_BAR (file_info->path_bar));
+  g_assert (G_OBJECT (file_info->path_bar)->ref_count > 0);
+
+  if (file_info->root_file == NULL)
+    file_info->root_file = g_object_ref (file_info->path_bar->root_file);
+
+  if (g_file_equal (file_info->file, file_info->root_file))
+    file_info->parent_file = NULL;
+  else
+    file_info->parent_file = g_file_get_parent (file_info->file);
+
+  cancellable_async_done (file_info->path_bar, file_info->cancellable);
+  if (file_info->path_bar->get_info_cancellable == file_info->cancellable)
+    file_info->path_bar->get_info_cancellable = NULL;
+  file_info->cancellable = NULL;
 
   /* Recurse asynchronously */
   file_info->cancellable = g_cancellable_new ();
@@ -861,22 +931,36 @@ _gtk_path_bar_set_file (GtkPathBar *path_bar,
   info->file = g_object_ref (file);
   info->path_bar = path_bar;
   info->first_directory = TRUE;
-  info->parent_file = g_file_get_parent (info->file);
 
   if (path_bar->get_info_cancellable)
     cancel_cancellable (path_bar, path_bar->get_info_cancellable);
 
   info->cancellable = g_cancellable_new ();
   path_bar->get_info_cancellable = info->cancellable;
-  g_file_query_info_async (info->file,
-                           "standard::display-name,"
-                           "standard::is-hidden,"
-                           "standard::is-backup",
-                           G_FILE_QUERY_INFO_NONE,
-                           G_PRIORITY_DEFAULT,
-                           info->cancellable,
-                           gtk_path_bar_get_info_callback,
-                           info);
+
+  if (g_file_is_native (info->file))
+    {
+      info->root_file = g_object_ref (path_bar->root_file);
+      info->parent_file = g_file_get_parent (info->file);
+
+      g_file_query_info_async (info->file,
+                               "standard::display-name,"
+                               "standard::is-hidden,"
+                               "standard::is-backup",
+                               G_FILE_QUERY_INFO_NONE,
+                               G_PRIORITY_DEFAULT,
+                               info->cancellable,
+                               gtk_path_bar_get_info_callback,
+                               info);
+    }
+  else
+    {
+      g_file_find_enclosing_mount_async (info->file,
+                                         G_PRIORITY_DEFAULT,
+                                         info->cancellable,
+                                         gtk_path_bar_get_mount_callback,
+                                         info);
+    }
   add_cancellable (path_bar, info->cancellable);
 }