[xdg-desktop-portal-gnome/gbsneto/remember-last-location] filechooser: Remember last app folder




commit 4254d1c8acc78911c713da5a72bb175c23ef32a8
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date:   Tue Aug 30 17:23:28 2022 -0300

    filechooser: Remember last app folder
    
    The GNOME implementation of the FileChooser portal doesn't make any
    effort to remember the last folder of any given app. This can be
    quite frustrating, and a significant usability problem, especially
    when doing repetitive operations on the same app that require picking
    files over and over.
    
    Remembering the last folder selected by a given app is a quick win
    that doesn't require too much introspection design-wise, so let's
    just do it.

 src/filechooser.c                        | 92 ++++++++++++++++++++++++++++++++
 src/meson.build                          |  7 +++
 src/xdg-desktop-portal-gnome.gschema.xml | 10 ++++
 3 files changed, 109 insertions(+)
---
diff --git a/src/filechooser.c b/src/filechooser.c
index 0f45f84..72d4071 100644
--- a/src/filechooser.c
+++ b/src/filechooser.c
@@ -44,6 +44,8 @@
 #include "utils.h"
 #include "externalwindow.h"
 
+#define FILECHOOSER_SETTINGS_SCHEMA "org.gnome.portal.filechooser"
+#define FILECHOOSER_SETTINGS_PATH "/org/gnome/portal/filechooser/"
 
 typedef struct {
   XdpImplFileChooser *impl;
@@ -89,6 +91,83 @@ file_dialog_handle_close (FileDialogHandle *handle)
   file_dialog_handle_free (handle);
 }
 
+static GSettings*
+get_filechooser_settings_for_app_id (const char *app_id)
+{
+  g_autoptr(GSettings) settings = NULL;
+  g_autoptr(GString) path = NULL;
+
+  g_assert (app_id && g_utf8_validate (app_id, -1, NULL));
+
+  path = g_string_new (FILECHOOSER_SETTINGS_PATH);
+  g_string_append (path, app_id);
+  g_string_append (path, "/");
+
+  settings = g_settings_new_with_path (FILECHOOSER_SETTINGS_SCHEMA, path->str);
+
+  return g_steal_pointer (&settings);
+}
+
+static void
+restore_last_folder (FileDialogHandle *handle,
+                     GtkFileChooser   *filechooser)
+{
+  g_autoptr(GSettings) settings = NULL;
+  g_autofree char *last_folder_path = NULL;
+
+  if (!handle->app_id || *handle->app_id == '\0')
+    return;
+
+  settings = get_filechooser_settings_for_app_id (handle->app_id);
+  last_folder_path = g_settings_get_string (settings, "last-folder-path");
+
+  if (last_folder_path && *last_folder_path)
+    {
+      g_autoptr(GFile) last_folder = g_file_new_for_path (last_folder_path);
+      gtk_file_chooser_set_current_folder (filechooser, last_folder, NULL);
+    }
+}
+
+static void
+save_last_folder (FileDialogHandle *handle,
+                  GtkFileChooser   *filechooser)
+{
+  g_autoptr(GListModel) files = NULL;
+  g_autofree char *path = NULL;
+
+  if (!handle->app_id || *handle->app_id == '\0')
+    return;
+
+  files = gtk_file_chooser_get_files (filechooser);
+
+  for (guint i = g_list_model_get_n_items (files); i > 0; i--)
+    {
+      g_autoptr(GFile) file = g_list_model_get_item (files, i - 1);
+
+      if (g_file_is_native (file))
+        {
+          path = g_file_get_path (file);
+
+          if (!g_file_test (path, G_FILE_TEST_IS_DIR))
+            {
+              g_autoptr(GFile) parent = g_file_get_parent (file);
+
+              g_clear_pointer (&path, g_free);
+              path = g_file_get_path (parent);
+            }
+
+          if (path)
+            break;
+        }
+    }
+
+  if (path)
+    {
+      g_autoptr(GSettings) settings = get_filechooser_settings_for_app_id (handle->app_id);
+      g_settings_set_string (settings, "last-folder-path", path);
+    }
+}
+
 static void
 add_choices (FileDialogHandle *handle,
              GVariantBuilder *builder)
@@ -285,6 +364,7 @@ file_chooser_response (GtkWidget *widget,
       handle->response = 0;
       handle->filter = gtk_file_chooser_get_filter (GTK_FILE_CHOOSER(widget));
       handle->uris = get_uris (GTK_FILE_CHOOSER (widget));
+      save_last_folder (handle, GTK_FILE_CHOOSER (widget));
       break;
     }
 
@@ -588,6 +668,10 @@ handle_open (XdpImplFileChooser    *object,
               g_autoptr(GFile) file = g_file_new_for_path (path);
               gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), file, NULL);
             }
+          else
+            {
+              restore_last_folder (handle, GTK_FILE_CHOOSER (dialog));
+            }
         }
     }
   else if (strcmp (method_name, "SaveFiles") == 0)
@@ -601,6 +685,10 @@ handle_open (XdpImplFileChooser    *object,
           g_autoptr(GFile) file = g_file_new_for_path (path);
           gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), file, NULL);
         }
+      else
+        {
+          restore_last_folder (handle, GTK_FILE_CHOOSER (dialog));
+        }
 
       if (g_variant_lookup (arg_options, "files", "aay", &iter))
         {
@@ -611,6 +699,10 @@ handle_open (XdpImplFileChooser    *object,
           g_variant_iter_free (iter);
         }
     }
+  else
+    {
+      restore_last_folder (handle, GTK_FILE_CHOOSER (dialog));
+    }
 
   g_object_unref (fake_parent);
 
diff --git a/src/meson.build b/src/meson.build
index c472f98..c53b448 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -141,3 +141,10 @@ executable(
   install: true,
   install_dir: libexecdir,
 )
+
+install_data (
+  'xdg-desktop-portal-gnome.gschema.xml',
+  install_dir : datadir / 'glib-2.0' / 'schemas',
+)
+
+gnome.post_install(glib_compile_schemas: true)
diff --git a/src/xdg-desktop-portal-gnome.gschema.xml b/src/xdg-desktop-portal-gnome.gschema.xml
new file mode 100644
index 0000000..d4c61cb
--- /dev/null
+++ b/src/xdg-desktop-portal-gnome.gschema.xml
@@ -0,0 +1,10 @@
+<schemalist>
+  <schema id="org.gnome.portal" path="/org/gnome/portal/" gettext-domain="xdg-desktop-portal-gnome">
+    <child name="filechooser" schema="org.gnome.portal.filechooser"/>
+  </schema>
+  <schema id="org.gnome.portal.filechooser" gettext-domain="xdg-desktop-portal-gnome">
+    <key name="last-folder-path" type="s">
+      <default>""</default>
+    </key>
+  </schema>
+</schemalist>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]