[gthumb] location chooser: added ability to browse and select a location



commit 17023e99ea9e39fb91a34106751096f977cbe21b
Author: Paolo Bacchilega <paobac src gnome org>
Date:   Sat Nov 2 08:04:11 2019 +0100

    location chooser: added ability to browse and select a location

 gthumb/gth-location-chooser-dialog.c | 241 ++++++++++++
 gthumb/gth-location-chooser-dialog.h |  59 +++
 gthumb/gth-location-chooser.c        | 192 ++++++---
 gthumb/gth-vfs-tree.c                | 727 +++++++++++++++++++++++++++++++++++
 gthumb/gth-vfs-tree.h                |  67 ++++
 gthumb/meson.build                   |   4 +
 6 files changed, 1241 insertions(+), 49 deletions(-)
---
diff --git a/gthumb/gth-location-chooser-dialog.c b/gthumb/gth-location-chooser-dialog.c
new file mode 100644
index 00000000..7dd7b4ff
--- /dev/null
+++ b/gthumb/gth-location-chooser-dialog.c
@@ -0,0 +1,241 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2019 The Free Software Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include "glib-utils.h"
+#include "gtk-utils.h"
+#include "gth-file-data.h"
+#include "gth-vfs-tree.h"
+#include "gth-location-chooser.h"
+#include "gth-location-chooser-dialog.h"
+#include "gth-main.h"
+
+
+#define MIN_WIDTH 600
+#define MIN_HEIGHT 600
+
+
+struct _GthLocationChooserDialogPrivate {
+       GFile     *folder;
+       GtkWidget *folder_tree;
+       GtkWidget *entry;
+       gulong     entry_changed_id;
+};
+
+
+G_DEFINE_TYPE_WITH_CODE (GthLocationChooserDialog,
+                        gth_location_chooser_dialog,
+                        GTK_TYPE_DIALOG,
+                        G_ADD_PRIVATE (GthLocationChooserDialog))
+
+
+static void
+gth_location_chooser_dialog_finalize (GObject *object)
+{
+       GthLocationChooserDialog *self;
+
+       self = GTH_LOCATION_CHOOSER_DIALOG (object);
+
+       _g_object_unref (self->priv->folder);
+
+       G_OBJECT_CLASS (gth_location_chooser_dialog_parent_class)->finalize (object);
+}
+
+
+static void
+gth_location_chooser_dialog_class_init (GthLocationChooserDialogClass *class)
+{
+       GObjectClass *object_class;
+
+       object_class = (GObjectClass*) class;
+       object_class->finalize = gth_location_chooser_dialog_finalize;
+}
+
+
+static void
+gth_location_chooser_dialog_init (GthLocationChooserDialog *self)
+{
+       self->priv = gth_location_chooser_dialog_get_instance_private (self);
+       self->priv->folder = NULL;
+       self->priv->entry_changed_id = 0;
+}
+
+
+static void
+_set_folder (GthLocationChooserDialog *self,
+            GFile                    *folder)
+{
+       if (self->priv->folder != folder) {
+               _g_object_unref (self->priv->folder);
+               self->priv->folder = _g_object_ref (folder);
+       }
+
+       if (self->priv->folder != NULL) {
+               g_signal_handler_block (self->priv->entry, self->priv->entry_changed_id);
+               gth_location_chooser_set_current (GTH_LOCATION_CHOOSER (self->priv->entry), 
self->priv->folder);
+               g_signal_handler_unblock (self->priv->entry, self->priv->entry_changed_id);
+       }
+
+       gtk_dialog_set_response_sensitive (GTK_DIALOG (self), GTK_RESPONSE_OK, (self->priv->folder != NULL));
+}
+
+
+static void
+folder_tree_changed_cb (GthVfsTree *tree,
+                       gpointer    user_data)
+{
+       GthLocationChooserDialog *self = user_data;
+       _set_folder (self, gth_vfs_tree_get_folder (tree));
+}
+
+
+static void
+location_entry_changed_cb (GthLocationChooser *entry,
+                          gpointer            user_data)
+{
+       GthLocationChooserDialog *self = user_data;
+       GFile                    *folder;
+
+       folder = gth_location_chooser_get_current (entry);
+       if (_g_file_equal_uris (folder, gth_folder_tree_get_root (GTH_FOLDER_TREE 
(self->priv->folder_tree)))) {
+               gtk_tree_view_collapse_all (GTK_TREE_VIEW (self->priv->folder_tree));
+               _set_folder (self, NULL);
+       }
+       else
+               gth_location_chooser_dialog_set_folder (self, folder);
+}
+
+
+static void
+hidden_files_toggled_cb (GtkToggleButton *togglebutton,
+                        gpointer         user_data)
+{
+       GthLocationChooserDialog *self = user_data;
+
+       gth_vfs_tree_set_show_hidden (GTH_VFS_TREE (self->priv->folder_tree), gtk_toggle_button_get_active 
(togglebutton));
+}
+
+
+static void
+_gth_location_chooser_dialog_construct (GthLocationChooserDialog *self)
+{
+       GtkWidget *vbox;
+       GtkWidget *scrolled_window;
+       GtkWidget *check_button;
+
+       vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+       gtk_widget_show (vbox);
+       gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (self))), vbox, TRUE, TRUE, 0);
+
+       self->priv->entry = g_object_new (GTH_TYPE_LOCATION_CHOOSER,
+                                         "show-entry-points", FALSE,
+                                         "show-root", TRUE,
+                                         NULL);
+       self->priv->entry_changed_id =
+               g_signal_connect (self->priv->entry,
+                                 "changed",
+                                 G_CALLBACK (location_entry_changed_cb),
+                                 self);
+
+       gtk_widget_show (self->priv->entry);
+       gtk_box_pack_start (GTK_BOX (vbox), self->priv->entry, FALSE, FALSE, 0);
+
+       gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (self))), 5);
+       gtk_container_set_border_width (GTK_CONTAINER (self), 5);
+
+       _gtk_dialog_add_to_window_group (GTK_DIALOG (self));
+
+       gtk_dialog_add_button (GTK_DIALOG (self), _GTK_LABEL_CANCEL, GTK_RESPONSE_CANCEL);
+       gtk_dialog_add_button (GTK_DIALOG (self), _GTK_LABEL_OK, GTK_RESPONSE_OK);
+       gtk_dialog_set_default_response (GTK_DIALOG (self), GTK_RESPONSE_OK);
+       _gtk_dialog_add_class_to_response (GTK_DIALOG (self), GTK_RESPONSE_OK, 
GTK_STYLE_CLASS_SUGGESTED_ACTION);
+       gtk_dialog_set_response_sensitive (GTK_DIALOG (self), GTK_RESPONSE_OK, FALSE);
+
+       scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+       gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+                                       GTK_POLICY_AUTOMATIC,
+                                       GTK_POLICY_AUTOMATIC);
+       gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
+                                            GTK_SHADOW_IN);
+       gtk_widget_set_size_request (scrolled_window, MIN_WIDTH, MIN_HEIGHT);
+       gtk_widget_show (scrolled_window);
+
+       self->priv->folder_tree = gth_vfs_tree_new (NULL);
+       g_signal_connect (self->priv->folder_tree,
+                         "changed",
+                         G_CALLBACK (folder_tree_changed_cb),
+                         self);
+       gtk_widget_show (self->priv->folder_tree);
+       gtk_container_add (GTK_CONTAINER (scrolled_window), self->priv->folder_tree);
+       gtk_box_pack_start (GTK_BOX (vbox), scrolled_window, TRUE, TRUE, 0);
+
+       check_button = gtk_check_button_new_with_label (_("Hidden Files"));
+       g_signal_connect (check_button,
+                         "toggled",
+                         G_CALLBACK (hidden_files_toggled_cb),
+                         self);
+       gtk_widget_show (check_button);
+       gtk_box_pack_start (GTK_BOX (vbox), check_button, TRUE, TRUE, 0);
+
+       _set_folder (self, NULL);
+}
+
+
+GtkWidget *
+gth_location_chooser_dialog_new (const char *title,
+                                GtkWindow  *parent)
+{
+       GthLocationChooserDialog *self;
+
+       self = g_object_new (GTH_TYPE_LOCATION_CHOOSER_DIALOG,
+                            "title", title,
+                            "transient-for", parent,
+                            "modal", TRUE,
+                            "resizable", TRUE,
+                            "use-header-bar", _gtk_settings_get_dialogs_use_header (),
+                            NULL);
+       _gth_location_chooser_dialog_construct (self);
+
+       return (GtkWidget *) self;
+}
+
+
+void
+gth_location_chooser_dialog_set_folder (GthLocationChooserDialog *self,
+                                       GFile                    *folder)
+{
+       g_return_if_fail (GTH_IS_LOCATION_CHOOSER_DIALOG (self));
+       g_return_if_fail (folder != NULL);
+
+       gth_vfs_tree_set_folder (GTH_VFS_TREE (self->priv->folder_tree), folder);
+}
+
+
+GFile *
+gth_location_chooser_dialog_get_folder (GthLocationChooserDialog *self)
+{
+       g_return_val_if_fail (GTH_IS_LOCATION_CHOOSER_DIALOG (self), NULL);
+
+       return self->priv->folder;
+}
diff --git a/gthumb/gth-location-chooser-dialog.h b/gthumb/gth-location-chooser-dialog.h
new file mode 100644
index 00000000..b06cc296
--- /dev/null
+++ b/gthumb/gth-location-chooser-dialog.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2019 Free Software Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GTH_LOCATION_CHOOSER_DIALOG_H
+#define GTH_LOCATION_CHOOSER_DIALOG_H
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GTH_TYPE_LOCATION_CHOOSER_DIALOG         (gth_location_chooser_dialog_get_type ())
+#define GTH_LOCATION_CHOOSER_DIALOG(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), 
GTH_TYPE_LOCATION_CHOOSER_DIALOG, GthLocationChooserDialog))
+#define GTH_LOCATION_CHOOSER_DIALOG_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), 
GTH_TYPE_LOCATION_CHOOSER_DIALOG, GthLocationChooserDialogClass))
+#define GTH_IS_LOCATION_CHOOSER_DIALOG(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), 
GTH_TYPE_LOCATION_CHOOSER_DIALOG))
+#define GTH_IS_LOCATION_CHOOSER_DIALOG_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), 
GTH_TYPE_LOCATION_CHOOSER_DIALOG))
+#define GTH_LOCATION_CHOOSER_DIALOG_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), 
GTH_TYPE_LOCATION_CHOOSER_DIALOG, GthLocationChooserDialogClass))
+
+typedef struct _GthLocationChooserDialog         GthLocationChooserDialog;
+typedef struct _GthLocationChooserDialogPrivate  GthLocationChooserDialogPrivate;
+typedef struct _GthLocationChooserDialogClass    GthLocationChooserDialogClass;
+
+struct _GthLocationChooserDialog {
+       GtkDialog __parent;
+       GthLocationChooserDialogPrivate *priv;
+};
+
+struct _GthLocationChooserDialogClass {
+       GtkDialogClass __parent_class;
+};
+
+GType          gth_location_chooser_dialog_get_type    (void) G_GNUC_CONST;
+GtkWidget *    gth_location_chooser_dialog_new         (const char                      *title,
+                                                        GtkWindow                       *parent);
+void           gth_location_chooser_dialog_set_folder  (GthLocationChooserDialog        *self,
+                                                        GFile                           *folder);
+GFile *                gth_location_chooser_dialog_get_folder  (GthLocationChooserDialog        *self);
+
+G_END_DECLS
+
+#endif /* GTH_LOCATION_CHOOSER_DIALOG_H */
diff --git a/gthumb/gth-location-chooser.c b/gthumb/gth-location-chooser.c
index 2a8dd412..fa470a18 100644
--- a/gthumb/gth-location-chooser.c
+++ b/gthumb/gth-location-chooser.c
@@ -28,6 +28,7 @@
 #include "glib-utils.h"
 #include "gth-file-source.h"
 #include "gth-location-chooser.h"
+#include "gth-location-chooser-dialog.h"
 #include "gth-main.h"
 #include "gtk-utils.h"
 #include "pixbuf-utils.h"
@@ -40,7 +41,8 @@ enum {
        ITEM_TYPE_NONE,
        ITEM_TYPE_SEPARATOR,
        ITEM_TYPE_LOCATION,
-       ITEM_TYPE_ENTRY_POINT
+       ITEM_TYPE_ENTRY_POINT,
+       ITEM_TYPE_CHOOSE_LOCATION
 };
 
 enum {
@@ -55,6 +57,7 @@ enum {
 enum {
        PROP_0,
        PROP_SHOW_ENTRY_POINTS,
+       PROP_SHOW_ROOT,
        PROP_RELIEF
 };
 
@@ -73,6 +76,7 @@ struct _GthLocationChooserPrivate {
        guint           update_entry_list_id;
        guint           update_location_list_id;
        gboolean        show_entry_points;
+       gboolean        show_root;
        GtkReliefStyle  relief;
        gboolean        reload;
 };
@@ -101,6 +105,9 @@ gth_location_chooser_set_property (GObject      *object,
        case PROP_SHOW_ENTRY_POINTS:
                gth_location_chooser_set_show_entry_points (self, g_value_get_boolean (value));
                break;
+       case PROP_SHOW_ROOT:
+               self->priv->show_root = g_value_get_boolean (value);
+               break;
        case PROP_RELIEF:
                gth_location_chooser_set_relief (self, g_value_get_enum (value));
                break;
@@ -124,6 +131,9 @@ gth_location_chooser_get_property (GObject    *object,
        case PROP_SHOW_ENTRY_POINTS:
                g_value_set_boolean (value, self->priv->show_entry_points);
                break;
+       case PROP_SHOW_ROOT:
+               g_value_set_boolean (value, self->priv->show_root);
+               break;
        case PROP_RELIEF:
                g_value_set_enum (value, self->priv->relief);
                break;
@@ -193,6 +203,44 @@ get_nth_separator_pos (GthLocationChooser *self,
 }
 
 
+static gboolean
+get_iter_from_current_file_entries (GthLocationChooser *self,
+                                   GFile              *file,
+                                   GtkTreeIter        *iter)
+{
+       gboolean  found = FALSE;
+       char     *uri;
+
+       if (! gtk_tree_model_get_iter_first (GTK_TREE_MODEL (self->priv->model), iter))
+               return FALSE;
+
+       uri = g_file_get_uri (file);
+       do {
+               int   item_type = ITEM_TYPE_NONE;
+               char *list_uri;
+
+               gtk_tree_model_get (GTK_TREE_MODEL (self->priv->model),
+                                   iter,
+                                   TYPE_COLUMN, &item_type,
+                                   URI_COLUMN, &list_uri,
+                                   -1);
+               if (item_type == ITEM_TYPE_SEPARATOR)
+                       break;
+               if (same_uri (uri, list_uri)) {
+                       found = TRUE;
+                       g_free (list_uri);
+                       break;
+               }
+               g_free (list_uri);
+       }
+       while (gtk_tree_model_iter_next (GTK_TREE_MODEL (self->priv->model), iter));
+
+       g_free (uri);
+
+       return found;
+}
+
+
 static void
 combo_changed_cb (GtkComboBox *widget,
                  gpointer     user_data)
@@ -211,7 +259,27 @@ combo_changed_cb (GtkComboBox *widget,
                            URI_COLUMN, &uri,
                            -1);
 
-       if (uri != NULL) {
+       if (item_type == ITEM_TYPE_CHOOSE_LOCATION) {
+               GtkWidget *dialog;
+
+               dialog = gth_location_chooser_dialog_new (_("Location"), _gtk_widget_get_toplevel_if_window 
(GTK_WIDGET (widget)));
+               if (self->priv->location != NULL)
+                       gth_location_chooser_dialog_set_folder (GTH_LOCATION_CHOOSER_DIALOG (dialog), 
self->priv->location);
+
+               switch (gtk_dialog_run (GTK_DIALOG (dialog))) {
+               case GTK_RESPONSE_OK:
+                       gth_location_chooser_set_current (self, gth_location_chooser_dialog_get_folder 
(GTH_LOCATION_CHOOSER_DIALOG (dialog)));
+                       break;
+
+               default:
+                       /* reset the previous value. */
+                       gth_location_chooser_set_current (self, self->priv->location);
+                       break;
+               }
+
+               gtk_widget_destroy (dialog);
+       }
+       else if (uri != NULL) {
                GFile *file;
 
                file = g_file_new_for_uri (uri);
@@ -256,12 +324,14 @@ add_file_source_entries (GthLocationChooser *self,
 
 
 static void
-clear_entry_point_list (GthLocationChooser *self)
+clear_items_from_separator (GthLocationChooser *self,
+                           int                 nth_separator,
+                           gboolean            stop_at_next_separator)
 {
        int first_position;
        int i;
 
-       if (! get_nth_separator_pos (self, 1, &first_position))
+       if (! get_nth_separator_pos (self, nth_separator, &first_position))
                return;
 
        for (i = first_position + 1; TRUE; i++) {
@@ -269,8 +339,19 @@ clear_entry_point_list (GthLocationChooser *self)
                GtkTreeIter  iter;
 
                path = gtk_tree_path_new_from_indices (first_position + 1, -1);
-               if (gtk_tree_model_get_iter (GTK_TREE_MODEL (self->priv->model), &iter, path))
+               if (gtk_tree_model_get_iter (GTK_TREE_MODEL (self->priv->model), &iter, path)) {
+                       if (stop_at_next_separator) {
+                               int item_type = ITEM_TYPE_NONE;
+
+                               gtk_tree_model_get (GTK_TREE_MODEL (self->priv->model),
+                                                   &iter,
+                                                   TYPE_COLUMN, &item_type,
+                                                   -1);
+                               if (item_type == ITEM_TYPE_SEPARATOR)
+                                       break;
+                       }
                        gtk_tree_store_remove (self->priv->model, &iter);
+               }
                else
                        break;
 
@@ -279,6 +360,13 @@ clear_entry_point_list (GthLocationChooser *self)
 }
 
 
+static void
+clear_entry_point_list (GthLocationChooser *self)
+{
+       clear_items_from_separator (self, 1, TRUE);
+}
+
+
 static void
 update_entry_point_list (GthLocationChooser *self)
 {
@@ -322,6 +410,22 @@ update_entry_point_list (GthLocationChooser *self)
                                         ITEM_TYPE_ENTRY_POINT);
        }
 
+       if (! get_nth_separator_pos (self, 2, &first_position)) {
+               GtkTreeIter iter;
+
+               gtk_tree_store_append (self->priv->model, &iter, NULL);
+               gtk_tree_store_set (self->priv->model, &iter,
+                                   TYPE_COLUMN, ITEM_TYPE_SEPARATOR,
+                                   -1);
+
+               gtk_tree_store_append (self->priv->model, &iter, NULL);
+               gtk_tree_store_set (self->priv->model, &iter,
+                                   TYPE_COLUMN, ITEM_TYPE_CHOOSE_LOCATION,
+                                   NAME_COLUMN, _("Other…"),
+                                   ELLIPSIZE_COLUMN, FALSE,
+                                   -1);
+       }
+
        _g_object_list_unref (entry_points);
 }
 
@@ -379,44 +483,6 @@ delete_current_file_entries (GthLocationChooser *self)
 }
 
 
-static gboolean
-get_iter_from_current_file_entries (GthLocationChooser *self,
-                                   GFile              *file,
-                                   GtkTreeIter        *iter)
-{
-       gboolean  found = FALSE;
-       char     *uri;
-
-       if (! gtk_tree_model_get_iter_first (GTK_TREE_MODEL (self->priv->model), iter))
-               return FALSE;
-
-       uri = g_file_get_uri (file);
-       do {
-               int   item_type = ITEM_TYPE_NONE;
-               char *list_uri;
-
-               gtk_tree_model_get (GTK_TREE_MODEL (self->priv->model),
-                                   iter,
-                                   TYPE_COLUMN, &item_type,
-                                   URI_COLUMN, &list_uri,
-                                   -1);
-               if (item_type == ITEM_TYPE_SEPARATOR)
-                       break;
-               if (same_uri (uri, list_uri)) {
-                       found = TRUE;
-                       g_free (list_uri);
-                       break;
-               }
-               g_free (list_uri);
-       }
-       while (gtk_tree_model_iter_next (GTK_TREE_MODEL (self->priv->model), iter));
-
-       g_free (uri);
-
-       return found;
-}
-
-
 static void
 update_location_list (gpointer user_data)
 {
@@ -459,6 +525,24 @@ update_location_list (gpointer user_data)
                        g_object_unref (info);
                }
 
+               if (self->priv->show_root) {
+                       GIcon *icon;
+
+                       icon = g_themed_icon_new ("computer-symbolic");
+                       gtk_tree_store_insert (self->priv->model,
+                                              &iter,
+                                              NULL,
+                                              position++);
+                       gtk_tree_store_set (self->priv->model, &iter,
+                                           TYPE_COLUMN, ITEM_TYPE_LOCATION,
+                                           ICON_COLUMN, icon,
+                                           NAME_COLUMN, _("Locations"),
+                                           URI_COLUMN, "gthumb-vfs:///",
+                                           -1);
+
+                       _g_object_unref (icon);
+               }
+
                _g_object_list_unref (list);
        }
 
@@ -505,11 +589,17 @@ gth_location_chooser_class_init (GthLocationChooserClass *klass)
        g_object_class_install_property (object_class,
                                         PROP_SHOW_ENTRY_POINTS,
                                         g_param_spec_boolean ("show-entry-points",
-                                                               "Show entry points",
-                                                               "Whether to show the entry points in the 
list",
-                                                               TRUE,
-                                                               G_PARAM_READWRITE));
-
+                                                              "Show entry points",
+                                                              "Whether to show the entry points in the list",
+                                                              TRUE,
+                                                              G_PARAM_READWRITE));
+       g_object_class_install_property (object_class,
+                                        PROP_SHOW_ROOT,
+                                        g_param_spec_boolean ("show-root",
+                                                              "Show the VFS root",
+                                                              "Whether to show the VFS root in the list",
+                                                              FALSE,
+                                                              G_PARAM_READWRITE));
        g_object_class_install_property (object_class,
                                         PROP_RELIEF,
                                         g_param_spec_enum ("relief",
@@ -544,6 +634,7 @@ gth_location_chooser_init (GthLocationChooser *self)
        self->priv = gth_location_chooser_get_instance_private (self);
        self->priv->entry_points_changed_id = 0;
        self->priv->show_entry_points = TRUE;
+       self->priv->show_root = FALSE;
        self->priv->relief = GTK_RELIEF_NORMAL;
        self->priv->reload = FALSE;
 
@@ -647,7 +738,7 @@ gth_location_chooser_set_show_entry_points (GthLocationChooser *self,
                        g_source_remove (self->priv->entry_points_changed_id);
                        self->priv->entry_points_changed_id = 0;
                }
-               clear_entry_point_list (self);
+               clear_items_from_separator (self, 1, FALSE);
        }
 
        g_object_notify (G_OBJECT (self), "show-entry-points");
@@ -665,6 +756,9 @@ void
 gth_location_chooser_set_current (GthLocationChooser *self,
                                  GFile              *file)
 {
+       if (file == NULL)
+               return;
+
        if (file != self->priv->location) {
                if (self->priv->file_source != NULL)
                        g_object_unref (self->priv->file_source);
diff --git a/gthumb/gth-vfs-tree.c b/gthumb/gth-vfs-tree.c
new file mode 100644
index 00000000..b545da75
--- /dev/null
+++ b/gthumb/gth-vfs-tree.c
@@ -0,0 +1,727 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2019 Free Software Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include "glib-utils.h"
+#include "gtk-utils.h"
+#include "gth-main.h"
+#include "gth-vfs-tree.h"
+
+
+enum {
+       PROP_0,
+       PROP_SHOW_HIDDEN
+};
+
+
+enum {
+       CHANGED,
+       LAST_SIGNAL
+};
+
+
+struct _GthVfsTreePrivate {
+       GFile    *folder;
+       gulong    monitor_folder_changed_id;
+       gulong    monitor_file_renamed_id;
+       gboolean  show_hidden;
+};
+
+
+static guint gth_vfs_tree_signals[LAST_SIGNAL] = { 0 };
+
+
+G_DEFINE_TYPE_WITH_CODE (GthVfsTree,
+                        gth_vfs_tree,
+                        GTH_TYPE_FOLDER_TREE,
+                        G_ADD_PRIVATE (GthVfsTree))
+
+
+static void
+_gth_vfs_tree_update_entry_points (GthVfsTree *self)
+{
+       GList *entry_points;
+       GFile *root;
+
+       entry_points = gth_main_get_all_entry_points ();
+       root = g_file_new_for_uri ("gthumb-vfs:///");
+       gth_folder_tree_set_children (GTH_FOLDER_TREE (self), root, entry_points);
+
+       g_object_unref (root);
+       _g_object_list_unref (entry_points);
+}
+
+
+/* -- load_data-- */
+
+
+typedef enum {
+       LOAD_ACTION_LOAD,
+       LOAD_ACTION_LIST_CHILDREN
+} LoadAction;
+
+
+typedef struct {
+       GthVfsTree    *vfs_tree;
+       LoadAction     action;
+       GthFileData   *requested_folder;
+       GFile         *entry_point;
+       GthFileSource *file_source;
+       GCancellable  *cancellable;
+       GList         *list;
+       GList         *current;
+       guint          destroy_id;
+} LoadData;
+
+
+static void
+load_data_vfs_tree_destroy_cb (GtkWidget *widget,
+                              gpointer   user_data)
+{
+       LoadData *load_data = user_data;
+
+       gth_file_source_cancel (load_data->file_source);
+       g_cancellable_cancel (load_data->cancellable);
+}
+
+
+static LoadData *
+load_data_new (GthVfsTree *vfs_tree,
+              LoadAction  action,
+              GFile      *location)
+{
+       LoadData *load_data;
+       GFile    *file;
+
+       load_data = g_new0 (LoadData, 1);
+       load_data->vfs_tree = g_object_ref (vfs_tree);
+       load_data->action = action;
+       load_data->requested_folder = gth_file_data_new (location, NULL);
+       load_data->entry_point = gth_main_get_nearest_entry_point (location);
+       load_data->file_source = gth_main_get_file_source (load_data->requested_folder->file);
+       load_data->cancellable = g_cancellable_new ();
+       load_data->list = NULL;
+       load_data->current = NULL;
+       load_data->destroy_id =
+               g_signal_connect (load_data->vfs_tree,
+                                 "destroy",
+                                 G_CALLBACK (load_data_vfs_tree_destroy_cb),
+                                 load_data);
+
+       if (load_data->entry_point == NULL)
+               return load_data;
+
+       file = g_object_ref (load_data->requested_folder->file);
+       load_data->list = g_list_prepend (NULL, g_object_ref (file));
+       while (! g_file_equal (load_data->entry_point, file)) {
+               GFile *parent;
+
+               parent = g_file_get_parent (file);
+               g_object_unref (file);
+               file = parent;
+
+               load_data->list = g_list_prepend (load_data->list, g_object_ref (file));
+       }
+       g_object_unref (file);
+       load_data->current = NULL;
+
+       return load_data;
+}
+
+
+static void
+load_data_free (LoadData *data)
+{
+       g_signal_handler_disconnect (data->vfs_tree, data->destroy_id);
+       _g_object_list_unref (data->list);
+       g_object_unref (data->cancellable);
+       _g_object_unref (data->file_source);
+       _g_object_unref (data->entry_point);
+       g_object_unref (data->requested_folder);
+       _g_object_unref (data->vfs_tree);
+       g_free (data);
+}
+
+
+static void load_data_load_next_folder (LoadData *load_data);
+
+
+static GList *
+_get_visible_files (GthVfsTree *self,
+                   GList      *list)
+{
+       GList *visible_list = NULL;
+       GList *scan;
+
+       for (scan = list; scan; scan = scan->next) {
+               GthFileData *file_data = scan->data;
+
+               if (self->priv->show_hidden || ! g_file_info_get_is_hidden (file_data->info))
+                       visible_list = g_list_prepend (visible_list, g_object_ref (file_data));
+       }
+
+       return g_list_reverse (visible_list);
+}
+
+
+static void
+load_data_ready_cb (GthFileSource *file_source,
+                   GList         *files,
+                   GError        *error,
+                   gpointer       user_data)
+{
+       LoadData    *load_data = user_data;
+       GthVfsTree  *self = load_data->vfs_tree;
+       GFile       *loaded_folder;
+       GtkTreePath *path;
+       GList       *visible_files;
+
+       if (error != NULL) {
+               /* TODO */
+               load_data_free (load_data);
+               return;
+       }
+
+       loaded_folder = (GFile *) load_data->current->data;
+       path = gth_folder_tree_get_path (GTH_FOLDER_TREE (self), loaded_folder);
+       if (path == NULL) {
+               load_data_free (load_data);
+               return;
+       }
+
+       visible_files = _get_visible_files (self, files);
+       gth_folder_tree_set_children (GTH_FOLDER_TREE (self),
+                                     loaded_folder,
+                                     visible_files);
+       gth_folder_tree_expand_row (GTH_FOLDER_TREE (self), path, FALSE);
+
+       if (! g_file_equal (loaded_folder, load_data->requested_folder->file)) {
+               load_data_load_next_folder (load_data);
+       }
+       else {
+               gth_folder_tree_select_path (GTH_FOLDER_TREE (self), path);
+               gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self),
+                                             path,
+                                             NULL,
+                                             g_file_equal (load_data->entry_point, 
load_data->requested_folder->file),
+                                             0,
+                                             0);
+
+               if (load_data->action == LOAD_ACTION_LOAD) {
+                       _g_object_unref (self->priv->folder);
+                       self->priv->folder = g_object_ref (loaded_folder);
+
+                       g_signal_emit (self, gth_vfs_tree_signals[CHANGED], 0);
+               }
+
+               load_data_free (load_data);
+       }
+
+       _g_object_list_unref (visible_files);
+       gtk_tree_path_free (path);
+}
+
+
+static void
+load_data_load_next_folder (LoadData *load_data)
+{
+       GthFolderTree *folder_tree = GTH_FOLDER_TREE (load_data->vfs_tree);
+       GFile         *folder_to_load = NULL;
+
+       do {
+               GtkTreePath *path;
+
+               if (load_data->current == NULL)
+                       load_data->current = load_data->list;
+               else
+                       load_data->current = load_data->current->next;
+               folder_to_load = (GFile *) load_data->current->data;
+
+               if (g_file_equal (folder_to_load, load_data->requested_folder->file))
+                       break;
+
+               path = gth_folder_tree_get_path (folder_tree, folder_to_load);
+               if (path == NULL)
+                       break;
+
+               if (! gth_folder_tree_is_loaded (folder_tree, path)) {
+                       gtk_tree_path_free (path);
+                       break;
+               }
+
+               gth_folder_tree_expand_row (folder_tree, path, FALSE);
+
+               gtk_tree_path_free (path);
+       }
+       while (TRUE);
+
+       if (folder_to_load == NULL) {
+               load_data_free (load_data);
+               return;
+       }
+
+       gth_folder_tree_loading_children (folder_tree, folder_to_load);
+       gth_file_source_list (load_data->file_source,
+                             folder_to_load,
+                             GFILE_STANDARD_ATTRIBUTES_WITH_FAST_CONTENT_TYPE,
+                             load_data_ready_cb,
+                             load_data);
+}
+
+
+static void
+_gth_vfs_tree_load_folder (GthVfsTree *self,
+                          LoadAction  action,
+                          GFile      *folder);
+
+
+static void
+mount_volume_ready_cb (GObject      *source_object,
+                      GAsyncResult *result,
+                      gpointer      user_data)
+{
+       LoadData *load_data = user_data;
+       GError   *error = NULL;
+
+       if (! g_file_mount_enclosing_volume_finish (G_FILE (source_object), result, &error)) {
+               /*char *title;
+
+               title = file_format (_("Could not load the position “%s”"), 
load_data->requested_folder->file);
+               _gth_browser_show_error (load_data->browser, title, error);
+               g_clear_error (&error);
+
+               g_free (title);*/
+               /* TODO: emit signal ? */
+               load_data_free (load_data);
+               return;
+       }
+
+       /* update the entry points list */
+
+       gth_monitor_entry_points_changed (gth_main_get_default_monitor());
+       _gth_vfs_tree_update_entry_points (load_data->vfs_tree);
+
+       /* try to load again */
+
+       _gth_vfs_tree_load_folder (load_data->vfs_tree,
+                                  load_data->action,
+                                  load_data->requested_folder->file);
+
+       load_data_free (load_data);
+}
+
+
+static void
+_gth_vfs_tree_load_folder (GthVfsTree *self,
+                          LoadAction  action,
+                          GFile      *folder)
+{
+       LoadData *load_data;
+
+       load_data = load_data_new (self, action, folder);
+
+       if (load_data->entry_point == NULL) {
+               GMountOperation *mount_op;
+
+               /* try to mount the enclosing volume */
+
+               mount_op = gtk_mount_operation_new (_gtk_widget_get_toplevel_if_window (GTK_WIDGET (self)));
+               g_file_mount_enclosing_volume (folder,
+                                              0,
+                                              mount_op,
+                                              load_data->cancellable,
+                                              mount_volume_ready_cb,
+                                              load_data);
+
+               g_object_unref (mount_op);
+
+               return;
+       }
+
+       load_data_load_next_folder (load_data);
+}
+
+
+static void
+vfs_tree_list_children_cb (GthFolderTree *folder_tree,
+                          GFile         *file,
+                          gpointer       user_data)
+{
+       GthVfsTree  *self = user_data;
+       GtkTreePath *path;
+
+       path = gth_folder_tree_get_path (GTH_FOLDER_TREE (self), file);
+       if (path == NULL)
+               return;
+
+       _gth_vfs_tree_load_folder (self, LOAD_ACTION_LIST_CHILDREN, file);
+
+       gtk_tree_path_free (path);
+}
+
+
+static void
+vfs_tree_open_cb (GthFolderTree *folder_tree,
+                 GFile         *file,
+                 gpointer       user_data)
+{
+       GthVfsTree  *self = user_data;
+       GtkTreePath *path;
+
+       path = gth_folder_tree_get_path (GTH_FOLDER_TREE (self), file);
+       if (path == NULL)
+               return;
+
+       _gth_vfs_tree_load_folder (self, LOAD_ACTION_LOAD, file);
+
+       gtk_tree_path_free (path);
+}
+
+
+/* -- monitor_event_data -- */
+
+
+typedef struct {
+       int              ref;
+       GthVfsTree      *vfs_tree;
+       GFile           *parent;
+       GthMonitorEvent  event;
+       GthFileSource   *file_source;
+       guint            destroy_id;
+} MonitorEventData;
+
+
+static void
+monitor_data_vfs_tree_destroy_cb (GtkWidget *widget,
+                                 gpointer   user_data)
+{
+       MonitorEventData *monitor_data = user_data;
+
+       gth_file_source_cancel (monitor_data->file_source);
+}
+
+
+static MonitorEventData *
+monitor_event_data_new (GthVfsTree *vfs_tree)
+{
+       MonitorEventData *monitor_data;
+
+       monitor_data = g_new0 (MonitorEventData, 1);
+       monitor_data->ref = 1;
+       monitor_data->vfs_tree = _g_object_ref (vfs_tree);
+       monitor_data->parent = NULL;
+       monitor_data->file_source = NULL;
+       monitor_data->destroy_id =
+               g_signal_connect (monitor_data->vfs_tree,
+                                 "destroy",
+                                 G_CALLBACK (monitor_data_vfs_tree_destroy_cb),
+                                 monitor_data);
+
+       return monitor_data;
+}
+
+
+G_GNUC_UNUSED
+static MonitorEventData *
+monitor_event_data_ref (MonitorEventData *monitor_data)
+{
+       monitor_data->ref++;
+       return monitor_data;
+}
+
+
+static void
+monitor_event_data_unref (MonitorEventData *monitor_data)
+{
+       monitor_data->ref--;
+
+       if (monitor_data->ref > 0)
+               return;
+
+       g_signal_handler_disconnect (monitor_data->vfs_tree, monitor_data->destroy_id);
+       _g_object_unref (monitor_data->vfs_tree);
+       _g_object_unref (monitor_data->parent);
+       _g_object_unref (monitor_data->file_source);
+       g_free (monitor_data);
+}
+
+
+static void
+file_attributes_ready_cb (GthFileSource *file_source,
+                         GList         *files,
+                         GError        *error,
+                         gpointer       user_data)
+{
+       MonitorEventData *monitor_data = user_data;
+       GthVfsTree       *self = monitor_data->vfs_tree;
+
+       if (error != NULL) {
+               g_warning ("%s", error->message);
+               g_clear_error (&error);
+               monitor_event_data_unref (monitor_data);
+               return;
+       }
+
+       if (monitor_data->event == GTH_MONITOR_EVENT_CREATED)
+               gth_folder_tree_add_children (GTH_FOLDER_TREE (self), monitor_data->parent, files);
+       else if (monitor_data->event == GTH_MONITOR_EVENT_CHANGED)
+               gth_folder_tree_update_children (GTH_FOLDER_TREE (self), monitor_data->parent, files);
+
+       monitor_event_data_unref (monitor_data);
+}
+
+
+static void
+monitor_folder_changed_cb (GthMonitor      *monitor,
+                          GFile           *parent,
+                          GList           *list,
+                          int              position,
+                          GthMonitorEvent  event,
+                          GthVfsTree      *self)
+{
+       GtkTreePath *path;
+
+       path = gth_folder_tree_get_path (GTH_FOLDER_TREE (self), parent);
+       if (g_file_equal (parent, gth_folder_tree_get_root (GTH_FOLDER_TREE (self)))
+           || ((path != NULL) && gtk_tree_view_row_expanded (GTK_TREE_VIEW (self), path)))
+       {
+               MonitorEventData *monitor_data;
+
+               switch (event) {
+               case GTH_MONITOR_EVENT_CREATED:
+               case GTH_MONITOR_EVENT_CHANGED:
+                       monitor_data = monitor_event_data_new (self);
+                       monitor_data->parent = g_file_dup (parent);
+                       monitor_data->event = event;
+                       monitor_data->file_source = gth_main_get_file_source (monitor_data->parent);
+                       gth_file_source_read_attributes (monitor_data->file_source,
+                                                        list,
+                                                        GFILE_STANDARD_ATTRIBUTES_WITH_FAST_CONTENT_TYPE,
+                                                        file_attributes_ready_cb,
+                                                        monitor_data);
+                       break;
+
+               case GTH_MONITOR_EVENT_DELETED:
+               case GTH_MONITOR_EVENT_REMOVED:
+                       gth_folder_tree_delete_children (GTH_FOLDER_TREE (self), parent, list);
+                       break;
+               }
+       }
+
+       gtk_tree_path_free (path);
+}
+
+
+static void
+monitor_file_renamed_cb (GthMonitor *monitor,
+                        GFile      *file,
+                        GFile      *new_file,
+                        GthVfsTree *self)
+{
+       GthFileSource *file_source;
+
+       file_source = gth_main_get_file_source (new_file);
+       if (file_source != NULL) {
+               GFileInfo *info;
+
+               info = gth_file_source_get_file_info (file_source, new_file, GFILE_BASIC_ATTRIBUTES);
+               if (info != NULL) {
+                       GthFileData *file_data;
+
+                       file_data = gth_file_data_new (new_file, info);
+                       gth_folder_tree_update_child (GTH_FOLDER_TREE (self), file, file_data);
+
+                       g_object_unref (file_data);
+               }
+
+               _g_object_unref (info);
+       }
+
+       _g_object_unref (file_source);
+}
+
+
+static void
+gth_vfs_tree_init (GthVfsTree *self)
+{
+       self->priv = gth_vfs_tree_get_instance_private (self);
+       self->priv->folder = NULL;
+
+       g_signal_connect (self,
+                         "list_children",
+                         G_CALLBACK (vfs_tree_list_children_cb),
+                         self);
+       g_signal_connect (self,
+                         "open",
+                         G_CALLBACK (vfs_tree_open_cb),
+                         self);
+
+       self->priv->monitor_folder_changed_id =
+               g_signal_connect (gth_main_get_default_monitor (),
+                                 "folder-changed",
+                                 G_CALLBACK (monitor_folder_changed_cb),
+                                 self);
+       self->priv->monitor_file_renamed_id =
+               g_signal_connect (gth_main_get_default_monitor (),
+                                 "file-renamed",
+                                 G_CALLBACK (monitor_file_renamed_cb),
+                                 self);
+
+       _gth_vfs_tree_update_entry_points (self);
+}
+
+
+static void
+gth_vfs_tree_set_property (GObject      *object,
+                          guint         property_id,
+                          const GValue *value,
+                          GParamSpec   *pspec)
+{
+       GthVfsTree *self;
+
+       self = GTH_VFS_TREE (object);
+
+       switch (property_id) {
+       case PROP_SHOW_HIDDEN:
+               self->priv->show_hidden = g_value_get_boolean (value);
+               break;
+
+       default:
+               break;
+       }
+}
+
+
+static void
+gth_vfs_tree_get_property (GObject    *object,
+                          guint       property_id,
+                          GValue     *value,
+                          GParamSpec *pspec)
+{
+       GthVfsTree *self;
+
+       self = GTH_VFS_TREE (object);
+
+       switch (property_id) {
+       case PROP_SHOW_HIDDEN:
+               g_value_set_boolean (value, self->priv->show_hidden);
+               break;
+
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+               break;
+       }
+}
+
+
+static void
+gth_vfs_tree_finalize (GObject *object)
+{
+       GthVfsTree *self = GTH_VFS_TREE (object);
+
+       g_signal_handler_disconnect (gth_main_get_default_monitor (), self->priv->monitor_folder_changed_id);
+       g_signal_handler_disconnect (gth_main_get_default_monitor (), self->priv->monitor_file_renamed_id);
+       _g_object_unref (self->priv->folder);
+
+       G_OBJECT_CLASS (gth_vfs_tree_parent_class)->finalize (object);
+}
+
+
+static void
+gth_vfs_tree_class_init (GthVfsTreeClass *klass)
+{
+       GObjectClass *gobject_class;
+
+       gobject_class = G_OBJECT_CLASS (klass);
+       gobject_class->set_property = gth_vfs_tree_set_property;
+       gobject_class->get_property = gth_vfs_tree_get_property;
+       gobject_class->finalize = gth_vfs_tree_finalize;
+
+       /* signals */
+
+       gth_vfs_tree_signals[CHANGED] =
+               g_signal_new ("changed",
+                             G_TYPE_FROM_CLASS (klass),
+                             G_SIGNAL_RUN_LAST,
+                             G_STRUCT_OFFSET (GthVfsTreeClass, changed),
+                             NULL, NULL,
+                             g_cclosure_marshal_VOID__VOID,
+                             G_TYPE_NONE,
+                             0);
+
+       /* properties */
+
+       g_object_class_install_property (gobject_class,
+                                        PROP_SHOW_HIDDEN,
+                                        g_param_spec_boolean ("show-hidden",
+                                                              "Show Hidden",
+                                                              "Show hidden folders",
+                                                              FALSE,
+                                                              G_PARAM_READWRITE));
+}
+
+
+GtkWidget *
+gth_vfs_tree_new (const char *root)
+{
+       return g_object_new (GTH_TYPE_VFS_TREE, "root-uri", root, NULL);
+}
+
+
+void
+gth_vfs_tree_set_folder (GthVfsTree *self,
+                        GFile      *folder)
+{
+       g_return_if_fail (GTH_IS_VFS_TREE (self));
+       g_return_if_fail (folder != NULL);
+
+       _gth_vfs_tree_load_folder (self, LOAD_ACTION_LOAD, folder);
+}
+
+
+GFile *
+gth_vfs_tree_get_folder (GthVfsTree *self)
+{
+       g_return_val_if_fail (GTH_IS_VFS_TREE (self), NULL);
+       return self->priv->folder;
+}
+
+
+void
+gth_vfs_tree_set_show_hidden (GthVfsTree *self,
+                             gboolean    show_hidden)
+{
+       g_return_if_fail (GTH_IS_VFS_TREE (self));
+
+       g_object_set (self, "show-hidden", show_hidden, FALSE, NULL);
+       gth_vfs_tree_set_folder (self, self->priv->folder);
+}
+
+
+gboolean
+gth_vfs_tree_get_show_hidden (GthVfsTree *self)
+{
+       g_return_val_if_fail (GTH_IS_VFS_TREE (self), FALSE);
+
+       return self->priv->show_hidden;
+}
diff --git a/gthumb/gth-vfs-tree.h b/gthumb/gth-vfs-tree.h
new file mode 100644
index 00000000..a592f705
--- /dev/null
+++ b/gthumb/gth-vfs-tree.h
@@ -0,0 +1,67 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2019 Free Software Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GTH_VFS_TREE_H
+#define GTH_VFS_TREE_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include "gth-folder-tree.h"
+
+G_BEGIN_DECLS
+
+#define GTH_TYPE_VFS_TREE            (gth_vfs_tree_get_type ())
+#define GTH_VFS_TREE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTH_TYPE_VFS_TREE, GthVfsTree))
+#define GTH_VFS_TREE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GTH_TYPE_VFS_TREE, GthVfsTreeClass))
+#define GTH_IS_VFS_TREE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTH_TYPE_VFS_TREE))
+#define GTH_IS_VFS_TREE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTH_TYPE_VFS_TREE))
+#define GTH_VFS_TREE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GTH_TYPE_VFS_TREE, GthVfsTreeClass))
+
+typedef struct _GthVfsTree GthVfsTree;
+typedef struct _GthVfsTreeClass GthVfsTreeClass;
+typedef struct _GthVfsTreePrivate GthVfsTreePrivate;
+
+struct _GthVfsTree {
+       GthFolderTree parent_instance;
+       GthVfsTreePrivate *priv;
+};
+
+struct _GthVfsTreeClass {
+       GthFolderTreeClass parent_class;
+
+       /* -- signals -- */
+
+       void (*changed) (GthVfsTree *vfs_tree);
+};
+
+GType        gth_vfs_tree_get_type             (void);
+GtkWidget *  gth_vfs_tree_new                  (const char *root);
+void         gth_vfs_tree_set_folder           (GthVfsTree *vfs_tree,
+                                                GFile      *folder);
+GFile *      gth_vfs_tree_get_folder           (GthVfsTree *vfs_tree);
+void         gth_vfs_tree_set_show_hidden      (GthVfsTree *self,
+                                                gboolean    show_hidden);
+gboolean     gth_vfs_tree_get_show_hidden      (GthVfsTree *self);
+
+G_END_DECLS
+
+#endif /* GTH_VFS_TREE_H */
diff --git a/gthumb/meson.build b/gthumb/meson.build
index 5dff6b58..a14db474 100644
--- a/gthumb/meson.build
+++ b/gthumb/meson.build
@@ -64,6 +64,7 @@ public_header_files = [
   'gth-load-file-data-task.h',
   'gth-location-bar.h',
   'gth-location-chooser.h',
+  'gth-location-chooser-dialog.h',
   'gth-main.h',
   'gth-menu-manager.h',
   'gth-metadata.h',
@@ -99,6 +100,7 @@ public_header_files = [
   'gth-toolbox.h',
   'gth-uri-list.h',
   'gth-user-dir.h',
+  'gth-vfs-tree.h',
   'gth-viewer-page.h',
   'gth-window.h',
   'gtk-utils.h',
@@ -221,6 +223,7 @@ source_files = files(
   'gth-load-file-data-task.c',
   'gth-location-bar.c',
   'gth-location-chooser.c',
+  'gth-location-chooser-dialog.c',
   'gth-main.c',
   'gth-main-default-hooks.c',
   'gth-main-default-metadata.c',
@@ -263,6 +266,7 @@ source_files = files(
   'gth-trash-task.c',
   'gth-uri-list.c',
   'gth-user-dir.c',
+  'gth-vfs-tree.c',
   'gth-viewer-page.c',
   'gth-window.c',
   'gth-window-title.c',



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