[gtk+/multiroot-filechooser: 1/30] Add the concept of rooting the filechooser to a specific URI.



commit c3abc8a89dae25978e4140608fd1b82e6ed8d744
Author: Christian Hammond <chipx86 chipx86 com>
Date:   Mon Mar 22 19:03:14 2010 -0700

    Add the concept of rooting the filechooser to a specific URI.
    
    The only UI elements (shortcuts, bookmarks, special folders, etc.) that will
    show up are ones within the root URI.
    
    At this point, we have the UI filtering out the special sidebar entries
    that are not within the root URI, and we have some API calls set up for
    specifying the root URI.
    
    Future changes will limit browsing within the root URI, limit search
    results, and other such fixes.

 gtk/gtkfilechooser.c        |  331 ++++++++++++++++++++++++++++++++++++++++++-
 gtk/gtkfilechooser.h        |    3 +
 gtk/gtkfilechooserbutton.c  |    1 +
 gtk/gtkfilechooserdefault.c |  125 ++++++++++++++---
 gtk/gtkfilechooserentry.c   |   43 ++++++-
 gtk/gtkfilechooserentry.h   |    3 +
 gtk/gtkfilechooserprivate.h |   11 ++
 gtk/gtkfilechooserutils.c   |    3 +
 gtk/gtkfilechooserutils.h   |    3 +-
 9 files changed, 499 insertions(+), 24 deletions(-)
---
diff --git a/gtk/gtkfilechooser.c b/gtk/gtkfilechooser.c
index a0eecbd..02fbaa7 100644
--- a/gtk/gtkfilechooser.c
+++ b/gtk/gtkfilechooser.c
@@ -825,6 +825,25 @@ gtk_file_chooser_default_init (GtkFileChooserInterface *iface)
 								"will offer the user to create new folders."),
 							     TRUE,
 							     GTK_PARAM_READWRITE));
+
+  /**
+   * GtkFileChooser:root-uri:
+   *
+   * The absolute root URI that can be accessed in the file chooser.
+   * Any shortcuts (special or user-defined) or volumes not within
+   * this URI will be filtered out for this dialog.
+   *
+   * The default is an empty string or NULL, which provides the
+   * default level of access for the file chooser.
+   *
+   * Since: 2.18
+   */
+  g_object_interface_install_property (g_iface,
+				       g_param_spec_string ("root-uri",
+							     P_("Root URI"),
+							     P_("The URI, if any, to use as the root for all access in the file chooser."),
+							     NULL,
+							     GTK_PARAM_READWRITE));
 }
 
 /**
@@ -902,6 +921,9 @@ gtk_file_chooser_get_action (GtkFileChooser *chooser)
  * rather than the URI functions like
  * gtk_file_chooser_get_uri(),
  *
+ * This implies a root URI of "file://". See
+ * gtk_file_chooser_set_root_uri().
+ *
  * Since: 2.4
  **/
 void
@@ -915,10 +937,13 @@ gtk_file_chooser_set_local_only (GtkFileChooser *chooser,
 
 /**
  * gtk_file_chooser_get_local_only:
- * @chooser: a #GtkFileChoosre
+ * @chooser: a #GtkFileChooser
  * 
  * Gets whether only local files can be selected in the
  * file selector. See gtk_file_chooser_set_local_only()
+ *
+ * This is %TRUE when the root URI of the file chooser is
+ * "file://". See gtk_file_chooser_get_root_uri().
  * 
  * Return value: %TRUE if only local files can be selected.
  *
@@ -937,6 +962,66 @@ gtk_file_chooser_get_local_only (GtkFileChooser *chooser)
 }
 
 /**
+ * gtk_file_chooser_set_root_uri:
+ * @chooser: a #GtkFileChooser
+ * @root_uri: The root URI, or %NULL to allow access to everything.
+ *
+ * Sets the URI that will be the root of the file chooser. The file
+ * chooser will not display or allow access to files outside of this URI.
+ *
+ * If this is set to %NULL, then the file chooser will have access to all
+ * local and remote volumes and files.
+ *
+ * This is useful when needing to restrict the file chooser to a particular
+ * directory and its subdirectories, to a particular storage device, or to a
+ * remote server.
+ *
+ * See gtk_file_chooser_get_root_uri()
+ *
+ * Since: 2.18
+ **/
+void
+gtk_file_chooser_set_root_uri (GtkFileChooser *chooser,
+			       const gchar    *root_uri)
+{
+  g_return_if_fail (GTK_IS_FILE_CHOOSER (chooser));
+
+  g_object_set (chooser, "root-uri", root_uri, NULL);
+}
+
+/**
+ * gtk_file_chooser_get_root_uri:
+ * @chooser: a #GtkFileChooser
+ *
+ * Gets the URI that is the root of the file chooser. The file chooser
+ * will not display or allow access to files outside of this URI.
+ *
+ * If this is %NULL, then the file chooser has access to all local and
+ * remote volumes and files.
+ *
+ * This is useful when needing to restrict the file chooser to a particular
+ * directory and its subdirectories, to a particular storage device, or to a
+ * remote server.
+ *
+ * See gtk_file_chooser_set_root_uri()
+ *
+ * Return value: The root URI, or %NULL.
+ *
+ * Since: 2.18
+ **/
+const gchar *
+gtk_file_chooser_get_root_uri (GtkFileChooser *chooser)
+{
+  const gchar *root_uri;
+
+  g_return_val_if_fail (GTK_IS_FILE_CHOOSER (chooser), NULL);
+
+  g_object_get (chooser, "root-uri", &root_uri, NULL);
+
+  return root_uri;
+}
+
+/**
  * gtk_file_chooser_set_select_multiple:
  * @chooser: a #GtkFileChooser
  * @select_multiple: %TRUE if multiple files can be selected.
@@ -2567,3 +2652,247 @@ gtk_file_chooser_get_do_overwrite_confirmation (GtkFileChooser *chooser)
 
   return do_overwrite_confirmation;
 }
+
+#if defined (G_OS_WIN32) && !defined (_WIN64)
+
+/* DLL ABI stability backward compatibility versions */
+
+#undef gtk_file_chooser_get_filename
+
+gchar *
+gtk_file_chooser_get_filename (GtkFileChooser *chooser)
+{
+  gchar *utf8_filename = gtk_file_chooser_get_filename_utf8 (chooser);
+  gchar *retval = g_locale_from_utf8 (utf8_filename, -1, NULL, NULL, NULL);
+
+  g_free (utf8_filename);
+
+  return retval;
+}
+
+#undef gtk_file_chooser_set_filename
+
+gboolean
+gtk_file_chooser_set_filename (GtkFileChooser *chooser,
+			       const gchar    *filename)
+{
+  gchar *utf8_filename = g_locale_to_utf8 (filename, -1, NULL, NULL, NULL);
+  gboolean retval = gtk_file_chooser_set_filename_utf8 (chooser, utf8_filename);
+
+  g_free (utf8_filename);
+
+  return retval;
+}
+
+#undef gtk_file_chooser_select_filename
+
+gboolean
+gtk_file_chooser_select_filename (GtkFileChooser *chooser,
+				  const gchar    *filename)
+{
+  gchar *utf8_filename = g_locale_to_utf8 (filename, -1, NULL, NULL, NULL);
+  gboolean retval = gtk_file_chooser_select_filename_utf8 (chooser, utf8_filename);
+
+  g_free (utf8_filename);
+
+  return retval;
+}
+
+#undef gtk_file_chooser_unselect_filename
+
+void
+gtk_file_chooser_unselect_filename (GtkFileChooser *chooser,
+				    const char     *filename)
+{
+  gchar *utf8_filename = g_locale_to_utf8 (filename, -1, NULL, NULL, NULL);
+
+  gtk_file_chooser_unselect_filename_utf8 (chooser, utf8_filename);
+  g_free (utf8_filename);
+}
+
+#undef gtk_file_chooser_get_filenames
+
+GSList *
+gtk_file_chooser_get_filenames (GtkFileChooser *chooser)
+{
+  GSList *list = gtk_file_chooser_get_filenames_utf8 (chooser);
+  GSList *rover = list;
+  
+  while (rover)
+    {
+      gchar *tem = (gchar *) rover->data;
+      rover->data = g_locale_from_utf8 ((gchar *) rover->data, -1, NULL, NULL, NULL);
+      g_free (tem);
+      rover = rover->next;
+    }
+
+  return list;
+}
+
+#undef gtk_file_chooser_set_current_folder
+
+gboolean
+gtk_file_chooser_set_current_folder (GtkFileChooser *chooser,
+				     const gchar    *filename)
+{
+  gchar *utf8_filename = g_locale_to_utf8 (filename, -1, NULL, NULL, NULL);
+  gboolean retval = gtk_file_chooser_set_current_folder_utf8 (chooser, utf8_filename);
+
+  g_free (utf8_filename);
+
+  return retval;
+}
+
+#undef gtk_file_chooser_get_current_folder
+
+gchar *
+gtk_file_chooser_get_current_folder (GtkFileChooser *chooser)
+{
+  gchar *utf8_folder = gtk_file_chooser_get_current_folder_utf8 (chooser);
+  gchar *retval = g_locale_from_utf8 (utf8_folder, -1, NULL, NULL, NULL);
+
+  g_free (utf8_folder);
+
+  return retval;
+}
+
+#undef gtk_file_chooser_get_preview_filename
+
+char *
+gtk_file_chooser_get_preview_filename (GtkFileChooser *chooser)
+{
+  char *utf8_filename = gtk_file_chooser_get_preview_filename_utf8 (chooser);
+  char *retval = g_locale_from_utf8 (utf8_filename, -1, NULL, NULL, NULL);
+
+  g_free (utf8_filename);
+
+  return retval;
+}
+
+#undef gtk_file_chooser_add_shortcut_folder
+
+gboolean
+gtk_file_chooser_add_shortcut_folder (GtkFileChooser    *chooser,
+				      const char        *folder,
+				      GError           **error)
+{
+  char *utf8_folder = g_locale_to_utf8 (folder, -1, NULL, NULL, NULL);
+  gboolean retval =
+    gtk_file_chooser_add_shortcut_folder_utf8 (chooser, utf8_folder, error);
+
+  g_free (utf8_folder);
+
+  return retval;
+}
+
+#undef gtk_file_chooser_remove_shortcut_folder
+
+gboolean
+gtk_file_chooser_remove_shortcut_folder (GtkFileChooser    *chooser,
+					 const char        *folder,
+					 GError           **error)
+{
+  char *utf8_folder = g_locale_to_utf8 (folder, -1, NULL, NULL, NULL);
+  gboolean retval =
+    gtk_file_chooser_remove_shortcut_folder_utf8 (chooser, utf8_folder, error);
+
+  g_free (utf8_folder);
+
+  return retval;
+}
+
+#undef gtk_file_chooser_list_shortcut_folders
+
+GSList *
+gtk_file_chooser_list_shortcut_folders (GtkFileChooser *chooser)
+{
+  GSList *list = gtk_file_chooser_list_shortcut_folders_utf8 (chooser);
+  GSList *rover = list;
+  
+  while (rover)
+    {
+      gchar *tem = (gchar *) rover->data;
+      rover->data = g_locale_from_utf8 ((gchar *) rover->data, -1, NULL, NULL, NULL);
+      g_free (tem);
+      rover = rover->next;
+    }
+
+  return list;
+}
+
+#endif
+
+gboolean
+_gtk_file_chooser_uri_has_prefix (const char *uri,
+                                  const char *prefix)
+{
+  int uri_len;
+  int prefix_len;
+  gboolean result;
+  char *new_prefix = NULL;
+
+  g_return_val_if_fail (uri != NULL, FALSE);
+  g_return_val_if_fail (prefix != NULL, FALSE);
+
+  uri_len = strlen (uri);
+  prefix_len = strlen (prefix);
+
+  if (prefix[prefix_len - 1] != '/')
+    {
+      new_prefix = g_strdup_printf ("%s/", prefix);
+      prefix = new_prefix;
+      prefix_len++;
+    }
+
+  if (prefix_len == uri_len + 1)
+    {
+      /*
+       * Special case. The prefix URI may contain a trailing slash while the
+       * URI we're comparing against may not (or vice-versa), despite the
+       * URIs being equal.
+       */
+      result = !strncmp (prefix, uri, uri_len);
+    }
+  else
+    result = (uri_len >= prefix_len && g_str_has_prefix (uri, prefix));
+
+  g_free (new_prefix);
+
+  return result;
+}
+
+gboolean
+_gtk_file_chooser_is_uri_in_root (GtkFileChooser *chooser,
+                                  const char     *uri)
+{
+  const char *root_uri;
+
+  g_return_val_if_fail (GTK_IS_FILE_CHOOSER (chooser), FALSE);
+  g_return_val_if_fail (uri != NULL, FALSE);
+
+  root_uri = gtk_file_chooser_get_root_uri (chooser);
+
+  return root_uri == NULL || _gtk_file_chooser_uri_has_prefix (uri, root_uri);
+}
+
+gboolean
+_gtk_file_chooser_is_file_in_root (GtkFileChooser *chooser,
+                                   GFile          *file)
+{
+  char *uri;
+  gboolean result;
+
+  g_return_val_if_fail (GTK_IS_FILE_CHOOSER (chooser), FALSE);
+  g_return_val_if_fail (file != NULL, FALSE);
+
+  uri = g_file_get_uri (file);
+  result = _gtk_file_chooser_is_uri_in_root (chooser, uri);
+  g_free (uri);
+
+  return result;
+}
+
+#define __GTK_FILE_CHOOSER_C__
+#include "gtkaliasdef.c"
+
+// vim: et sw=2 cinoptions=(0,t0,f1s,n-1s,{1s,>2s,^-1s
diff --git a/gtk/gtkfilechooser.h b/gtk/gtkfilechooser.h
index 18369c5..ffdc2bc 100644
--- a/gtk/gtkfilechooser.h
+++ b/gtk/gtkfilechooser.h
@@ -123,6 +123,9 @@ GtkFileChooserAction gtk_file_chooser_get_action          (GtkFileChooser
 void                 gtk_file_chooser_set_local_only      (GtkFileChooser       *chooser,
 							   gboolean              local_only);
 gboolean             gtk_file_chooser_get_local_only      (GtkFileChooser       *chooser);
+void                 gtk_file_chooser_set_root_uri        (GtkFileChooser       *chooser,
+							   const gchar          *root_uri);
+const gchar         *gtk_file_chooser_get_root_uri        (GtkFileChooser       *chooser);
 void                 gtk_file_chooser_set_select_multiple (GtkFileChooser       *chooser,
 							   gboolean              select_multiple);
 gboolean             gtk_file_chooser_get_select_multiple (GtkFileChooser       *chooser);
diff --git a/gtk/gtkfilechooserbutton.c b/gtk/gtkfilechooserbutton.c
index f2af98a..431bab5 100644
--- a/gtk/gtkfilechooserbutton.c
+++ b/gtk/gtkfilechooserbutton.c
@@ -809,6 +809,7 @@ gtk_file_chooser_button_set_property (GObject      *object,
       break;
 
     case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
+    case GTK_FILE_CHOOSER_PROP_ROOT_URI:
       g_object_set_property (G_OBJECT (priv->dialog), pspec->name, value);
       fs_volumes_changed_cb (priv->fs, button);
       fs_bookmarks_changed_cb (priv->fs, button);
diff --git a/gtk/gtkfilechooserdefault.c b/gtk/gtkfilechooserdefault.c
index 5fddcfa..cb2d196 100644
--- a/gtk/gtkfilechooserdefault.c
+++ b/gtk/gtkfilechooserdefault.c
@@ -702,6 +702,7 @@ _gtk_file_chooser_default_init (GtkFileChooserDefault *impl)
   access ("MARK: *** CREATE FILE CHOOSER", F_OK);
 #endif
   impl->local_only = TRUE;
+  impl->root_uri = NULL;
   impl->preview_widget_active = TRUE;
   impl->use_preview_label = TRUE;
   impl->select_multiple = FALSE;
@@ -1794,8 +1795,15 @@ shortcuts_append_home (GtkFileChooserDefault *impl)
     }
 
   home = g_file_new_for_path (home_path);
-  shortcuts_insert_file (impl, -1, SHORTCUT_TYPE_FILE, NULL, home, NULL, FALSE, SHORTCUTS_HOME);
-  impl->has_home = TRUE;
+
+  if (_gtk_file_chooser_is_file_in_root (GTK_FILE_CHOOSER (impl), home))
+    {
+      shortcuts_insert_file (impl, -1, SHORTCUT_TYPE_FILE, NULL, home,
+                             NULL, FALSE, SHORTCUTS_HOME);
+      impl->has_home = TRUE;
+    }
+  else
+    impl->has_home = FALSE;
 
   g_object_unref (home);
 
@@ -1822,8 +1830,15 @@ shortcuts_append_desktop (GtkFileChooserDefault *impl)
     }
 
   file = g_file_new_for_path (name);
-  shortcuts_insert_file (impl, -1, SHORTCUT_TYPE_FILE, NULL, file, _("Desktop"), FALSE, SHORTCUTS_DESKTOP);
-  impl->has_desktop = TRUE;
+
+  if (_gtk_file_chooser_is_file_in_root (GTK_FILE_CHOOSER (impl), file))
+    {
+      shortcuts_insert_file (impl, -1, SHORTCUT_TYPE_FILE, NULL,
+                             file, _("Desktop"), FALSE, SHORTCUTS_DESKTOP);
+      impl->has_desktop = TRUE;
+    }
+  else
+    impl->has_desktop = FALSE;
 
   /* We do not actually pop up an error dialog if there is no desktop directory
    * because some people may really not want to have one.
@@ -1857,6 +1872,9 @@ shortcuts_append_bookmarks (GtkFileChooserDefault *impl,
       if (impl->local_only && !g_file_is_native (file))
 	continue;
 
+      if (!_gtk_file_chooser_is_file_in_root (GTK_FILE_CHOOSER (impl), file))
+        continue;
+
       if (shortcut_find_position (impl, file) != -1)
         continue;
 
@@ -1968,6 +1986,7 @@ shortcuts_add_volumes (GtkFileChooserDefault *impl)
   for (l = list; l; l = l->next)
     {
       GtkFileSystemVolume *volume;
+      gboolean skip = FALSE;
 
       volume = l->data;
 
@@ -1976,19 +1995,29 @@ shortcuts_add_volumes (GtkFileChooserDefault *impl)
 	  if (_gtk_file_system_volume_is_mounted (volume))
 	    {
 	      GFile *base_file;
-              gboolean base_is_native = TRUE;
 
 	      base_file = _gtk_file_system_volume_get_root (volume);
               if (base_file != NULL)
                 {
-                  base_is_native = g_file_is_native (base_file);
+                  skip = !g_file_is_native (base_file);
                   g_object_unref (base_file);
                 }
-
-              if (!base_is_native)
-                continue;
 	    }
 	}
+      else if (impl->root_uri != NULL)
+        {
+          GFile *base_file = _gtk_file_system_volume_get_root (volume);
+
+          if (base_file != NULL)
+            {
+              skip = !_gtk_file_chooser_is_file_in_root (GTK_FILE_CHOOSER (impl),
+                                                         base_file);
+              g_object_unref (base_file);
+            }
+        }
+
+      if (skip)
+        continue;
 
       shortcuts_insert_file (impl,
                              start_row + n,
@@ -2246,17 +2275,17 @@ shortcuts_model_create (GtkFileChooserDefault *impl)
   shortcuts_append_search (impl);
 
   if (impl->recent_manager)
-    {
-      shortcuts_append_recent (impl);
-      shortcuts_insert_separator (impl, SHORTCUTS_RECENT_SEPARATOR);
-    }
-  
+	{
+	  shortcuts_append_recent (impl);
+	  shortcuts_insert_separator (impl, SHORTCUTS_RECENT_SEPARATOR);
+	}
+
   if (impl->file_system)
-    {
-      shortcuts_append_home (impl);
-      shortcuts_append_desktop (impl);
+	{
+	  shortcuts_append_home (impl);
+	  shortcuts_append_desktop (impl);
       shortcuts_add_volumes (impl);
-    }
+	}
 
   impl->shortcuts_pane_filter_model = shortcuts_pane_model_filter_new (impl,
 							               GTK_TREE_MODEL (impl->shortcuts_model),
@@ -4648,6 +4677,7 @@ save_widgets_create (GtkFileChooserDefault *impl)
   _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (impl->location_entry),
 					   impl->file_system);
   _gtk_file_chooser_entry_set_local_only (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->local_only);
+  _gtk_file_chooser_entry_set_root_uri (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->root_uri);
   gtk_entry_set_width_chars (GTK_ENTRY (impl->location_entry), 45);
   gtk_entry_set_activates_default (GTK_ENTRY (impl->location_entry), TRUE);
   gtk_table_attach (GTK_TABLE (table), impl->location_entry,
@@ -5125,6 +5155,46 @@ set_local_only (GtkFileChooserDefault *impl,
 }
 
 static void
+set_root_uri (GtkFileChooserDefault *impl,
+              const gchar           *root_uri)
+{
+  if (root_uri == NULL || *root_uri == '\0')
+    root_uri = NULL;
+
+  if (g_strcmp0 (root_uri, impl->root_uri))
+    {
+      g_free (impl->root_uri);
+      impl->root_uri = (root_uri == NULL ? NULL : g_strdup (root_uri));
+
+      if (impl->location_entry)
+        {
+          _gtk_file_chooser_entry_set_root_uri (
+            GTK_FILE_CHOOSER_ENTRY (impl->location_entry),
+            impl->root_uri);
+        }
+
+      if (impl->shortcuts_model && impl->file_system)
+        {
+          /* Update all the sidebar entries to filter the root URI. */
+          shortcuts_add_volumes (impl);
+          shortcuts_add_bookmarks (impl);
+        }
+
+      if (impl->current_folder &&
+          _gtk_file_chooser_is_file_in_root (GTK_FILE_CHOOSER (impl),
+                                             impl->current_folder))
+        {
+          /*
+           * If we are pointing to a folder outside of the root URI, set the
+           * folder to the root URI.
+           */
+          gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (impl),
+                                                   impl->root_uri);
+        }
+    }
+}
+
+static void
 volumes_bookmarks_changed_cb (GtkFileSystem         *file_system,
 			      GtkFileChooserDefault *impl)
 {
@@ -5364,6 +5434,10 @@ gtk_file_chooser_default_set_property (GObject      *object,
       }
       break;
 
+    case GTK_FILE_CHOOSER_PROP_ROOT_URI:
+      set_root_uri (impl, g_value_get_string (value));
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -5424,6 +5498,10 @@ gtk_file_chooser_default_get_property (GObject    *object,
       g_value_set_boolean (value, impl->create_folders);
       break;
 
+    case GTK_FILE_CHOOSER_PROP_ROOT_URI:
+      g_value_set_string (value, impl->root_uri);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -5458,6 +5536,9 @@ gtk_file_chooser_default_dispose (GObject *object)
       impl->extra_widget = NULL;
     }
 
+  g_free (impl->root_uri);
+  impl->root_uri = NULL;
+
   pending_select_files_free (impl);
 
   /* cancel all pending operations */
@@ -6896,7 +6977,9 @@ update_current_folder_get_info_cb (GCancellable *cancellable,
       parent_file = g_file_get_parent (data->file);
 
       /* get parent path and try to change the folder to that */
-      if (parent_file)
+      if (parent_file &&
+          _gtk_file_chooser_is_file_in_root (GTK_FILE_CHOOSER (impl),
+                                             parent_file))
         {
 	  g_object_unref (data->file);
 	  data->file = parent_file;
@@ -8666,7 +8749,8 @@ search_add_hit (GtkFileChooserDefault *impl,
   if (!file)
     return;
 
-  if (!g_file_is_native (file))
+  if (!g_file_is_native (file) ||
+      !_gtk_file_chooser_is_file_in_root (GTK_FILE_CHOOSER (impl), file))
     {
       g_object_unref (file);
       return;
@@ -10253,3 +10337,4 @@ shortcuts_pane_model_filter_new (GtkFileChooserDefault *impl,
   return GTK_TREE_MODEL (model);
 }
 
+// vim: et sw=2 cinoptions=(0,t0,f1s,n-1s,{1s,>2s,^-1s
diff --git a/gtk/gtkfilechooserentry.c b/gtk/gtkfilechooserentry.c
index 3e5420a..936f0f5 100644
--- a/gtk/gtkfilechooserentry.c
+++ b/gtk/gtkfilechooserentry.c
@@ -28,6 +28,8 @@
 #include "gtkcelllayout.h"
 #include "gtkcellrenderertext.h"
 #include "gtkentry.h"
+#include "gtkfilechooserentry.h"
+#include "gtkfilechooserprivate.h"
 #include "gtklabel.h"
 #include "gtkmain.h"
 #include "gtksizerequest.h"
@@ -70,6 +72,7 @@ struct _GtkFileChooserEntry
   GtkFileChooserAction action;
 
   GtkFileSystem *file_system;
+  char *root_uri;
   GFile *base_folder;
   GFile *current_folder_file;
   gchar *file_part;
@@ -210,6 +213,7 @@ _gtk_file_chooser_entry_init (GtkFileChooserEntry *chooser_entry)
   GtkCellRenderer *cell;
 
   chooser_entry->local_only = TRUE;
+  chooser_entry->root_uri = NULL;
 
   g_object_set (chooser_entry, "truncate-multiline", TRUE, NULL);
 
@@ -298,6 +302,9 @@ gtk_file_chooser_entry_dispose (GObject *object)
   discard_current_folder (chooser_entry);
   discard_loading_and_current_folder_file (chooser_entry);
 
+  g_free (chooser_entry->root_uri);
+  chooser_entry->root_uri = NULL;
+
   if (chooser_entry->start_autocompletion_idle_id != 0)
     {
       g_source_remove (chooser_entry->start_autocompletion_idle_id);
@@ -450,6 +457,19 @@ beep (GtkFileChooserEntry *chooser_entry)
   gtk_widget_error_bell (GTK_WIDGET (chooser_entry));
 }
 
+static gboolean
+is_file_in_root (GtkFileChooserEntry *chooser_entry,
+                 GFile               *file)
+{
+  char *uri = g_file_get_uri (file);
+  gboolean result = chooser_entry->root_uri == NULL ||
+                    _gtk_file_chooser_uri_has_prefix (uri,
+                                                      chooser_entry->root_uri);
+  g_free (uri);
+
+  return result;
+}
+
 /* This function will append a directory separator to paths to
  * display_name iff the path associated with it is a directory.
  * maybe_append_separator_to_file will g_free the display_name and
@@ -1469,8 +1489,11 @@ start_loading_current_folder (GtkFileChooserEntry *chooser_entry)
   g_assert (chooser_entry->current_folder == NULL);
   g_assert (chooser_entry->load_folder_cancellable == NULL);
 
-  if (chooser_entry->local_only
-      && !g_file_is_native (chooser_entry->current_folder_file))
+  if ((chooser_entry->local_only
+       && !g_file_is_native (chooser_entry->current_folder_file)) ||
+      (chooser_entry->root_uri != NULL
+       && !is_file_in_root (chooser_entry,
+                            chooser_entry->current_folder_file)))
     {
       g_object_unref (chooser_entry->current_folder_file);
       chooser_entry->current_folder_file = NULL;
@@ -2006,3 +2029,19 @@ _gtk_file_chooser_entry_get_local_only (GtkFileChooserEntry *chooser_entry)
 {
   return chooser_entry->local_only;
 }
+
+void
+_gtk_file_chooser_entry_set_root_uri (GtkFileChooserEntry *chooser_entry,
+                                      const char          *root_uri)
+{
+  g_free (chooser_entry->root_uri);
+
+  chooser_entry->root_uri = (root_uri == NULL ? NULL : g_strdup(root_uri));
+  clear_completions (chooser_entry);
+}
+
+const char *
+_gtk_file_chooser_entry_get_root_uri (GtkFileChooserEntry *chooser_entry)
+{
+  return chooser_entry->root_uri;
+}
diff --git a/gtk/gtkfilechooserentry.h b/gtk/gtkfilechooserentry.h
index a9c9f83..38e4ebb 100644
--- a/gtk/gtkfilechooserentry.h
+++ b/gtk/gtkfilechooserentry.h
@@ -51,6 +51,9 @@ void               _gtk_file_chooser_entry_select_filename    (GtkFileChooserEnt
 void               _gtk_file_chooser_entry_set_local_only     (GtkFileChooserEntry *chooser_entry,
                                                                gboolean             local_only);
 gboolean           _gtk_file_chooser_entry_get_local_only     (GtkFileChooserEntry *chooser_entry);
+void               _gtk_file_chooser_entry_set_root_uri     (GtkFileChooserEntry *chooser_entry,
+                                                             const char          *root_uri);
+const char        *_gtk_file_chooser_entry_get_root_uri     (GtkFileChooserEntry *chooser_entry);
 
 G_END_DECLS
 
diff --git a/gtk/gtkfilechooserprivate.h b/gtk/gtkfilechooserprivate.h
index a3ccaae..d19b655 100644
--- a/gtk/gtkfilechooserprivate.h
+++ b/gtk/gtkfilechooserprivate.h
@@ -104,6 +104,16 @@ gboolean       _gtk_file_chooser_remove_shortcut_folder  (GtkFileChooser    *cho
 							  GError           **error);
 GSList *       _gtk_file_chooser_list_shortcut_folder_files (GtkFileChooser *chooser);
 
+GSList *       _gtk_file_chooser_list_shortcut_folder_files (GtkFileChooser *chooser);
+
+gboolean       _gtk_file_chooser_is_uri_in_root (GtkFileChooser *chooser,
+                                                 const char     *uri);
+gboolean       _gtk_file_chooser_is_file_in_root (GtkFileChooser *chooser,
+                                                  GFile          *file);
+
+gboolean       _gtk_file_chooser_uri_has_prefix (const char *uri,
+                                                 const char *prefix);
+
 /* GtkFileChooserDialog private */
 
 struct _GtkFileChooserDialogPrivate
@@ -159,6 +169,7 @@ struct _GtkFileChooserDefault
   GtkFileChooserAction action;
 
   GtkFileSystem *file_system;
+  char *root_uri;
 
   /* Save mode widgets */
   GtkWidget *save_widgets;
diff --git a/gtk/gtkfilechooserutils.c b/gtk/gtkfilechooserutils.c
index e0fefcf..df674de 100644
--- a/gtk/gtkfilechooserutils.c
+++ b/gtk/gtkfilechooserutils.c
@@ -117,6 +117,9 @@ _gtk_file_chooser_install_properties (GObjectClass *klass)
   g_object_class_override_property (klass,
 				    GTK_FILE_CHOOSER_PROP_CREATE_FOLDERS,
 				    "create-folders");
+  g_object_class_override_property (klass,
+				    GTK_FILE_CHOOSER_PROP_ROOT_URI,
+				    "root-uri");
 }
 
 /**
diff --git a/gtk/gtkfilechooserutils.h b/gtk/gtkfilechooserutils.h
index 8b80ab6..fc619c7 100644
--- a/gtk/gtkfilechooserutils.h
+++ b/gtk/gtkfilechooserutils.h
@@ -41,7 +41,8 @@ typedef enum {
   GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN,
   GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION,
   GTK_FILE_CHOOSER_PROP_CREATE_FOLDERS,
-  GTK_FILE_CHOOSER_PROP_LAST                   = GTK_FILE_CHOOSER_PROP_CREATE_FOLDERS
+  GTK_FILE_CHOOSER_PROP_ROOT_URI,
+  GTK_FILE_CHOOSER_PROP_LAST                   = GTK_FILE_CHOOSER_PROP_ROOT_URI
 } GtkFileChooserProp;
 
 void _gtk_file_chooser_install_properties (GObjectClass *klass);



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