diff --git a/gtk/gtkfilechooser.c b/gtk/gtkfilechooser.c
index 705a845..2398ce2 100644
--- a/gtk/gtkfilechooser.c
+++ b/gtk/gtkfilechooser.c
@@ -849,6 +849,24 @@ gtk_file_chooser_class_init (gpointer g_iface)
 								"will offer the user to create new folders."),
 							     TRUE,
 							     GTK_PARAM_READWRITE));
+
+  /**
+   * GtkFileChooser:root-uris:
+   *
+   * The absolute root URIs that can be accessed in the file chooser.
+   * Any shortcuts (special or user-defined) or volumes not within
+   * these URIs will be filtered out for this dialog.
+   *
+   * The default is 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_pointer ("root-uris",
+							   P_("Root URIs"),
+							   P_("The URIs, if any, to use as the root for all access in the file chooser."),
+							   GTK_PARAM_READWRITE));
 }
 
 /**
@@ -926,6 +944,9 @@ gtk_file_chooser_get_action (GtkFileChooser *chooser)
  * rather than the URI functions like
  * gtk_file_chooser_get_uri(),
  *
+ * This implies a single root URI of "file://". See
+ * gtk_file_chooser_set_root_uris().
+ *
  * Since: 2.4
  **/
 void
@@ -939,10 +960,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_uris().
  * 
  * Return value: %TRUE if only local files can be selected.
  *
@@ -961,6 +985,74 @@ gtk_file_chooser_get_local_only (GtkFileChooser *chooser)
 }
 
 /**
+ * gtk_file_chooser_set_root_uris:
+ * @chooser: a #GtkFileChooser
+ * @root_uri: The root URI, or %NULL to allow access to everything.
+ *
+ * Sets the URIs that will be the roots of the file chooser. The file
+ * chooser will not display or allow access to files outside of these URIs.
+ *
+ * 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.
+ *
+ * If more than one root URI is provided for the same filesystem or protocol
+ * (for example, "file:///home" and "file:///"), then the most permissive will
+ * be used (in this case, "file:///").
+ *
+ * See gtk_file_chooser_get_root_uris()
+ *
+ * Since: 2.18
+ **/
+void
+gtk_file_chooser_set_root_uris (GtkFileChooser *chooser,
+			        GSList         *root_uris)
+{
+  g_return_if_fail (GTK_IS_FILE_CHOOSER (chooser));
+
+  g_object_set (chooser, "root-uris", root_uris, NULL);
+}
+
+/**
+ * gtk_file_chooser_get_root_uris:
+ * @chooser: a #GtkFileChooser
+ *
+ * Gets the URIs that are the roots of the file chooser. The file chooser
+ * will not display or allow access to files outside of these URIs.
+ *
+ * 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.
+ *
+ * If more than one root URI is provided for the same filesystem or protocol
+ * (for example, "file:///home" and "file:///"), then the most permissive will
+ * be used (in this case, "file:///").
+ *
+ * See gtk_file_chooser_set_root_uris()
+ *
+ * Return value: The root URIs, or %NULL.
+ *
+ * Since: 2.18
+ **/
+GSList *
+gtk_file_chooser_get_root_uris (GtkFileChooser *chooser)
+{
+  GSList *root_uris;
+
+  g_return_val_if_fail (GTK_IS_FILE_CHOOSER (chooser), NULL);
+
+  g_object_get (chooser, "root-uris", &root_uris, NULL);
+
+  return root_uris;
+}
+
+/**
  * gtk_file_chooser_set_select_multiple:
  * @chooser: a #GtkFileChooser
  * @select_multiple: %TRUE if multiple files can be selected.
@@ -1729,9 +1821,21 @@ gtk_file_chooser_unselect_file (GtkFileChooser *chooser,
 GSList *
 gtk_file_chooser_get_files (GtkFileChooser *chooser)
 {
+  GSList *files, *l;
+
   g_return_val_if_fail (GTK_IS_FILE_CHOOSER (chooser), NULL);
 
-  return GTK_FILE_CHOOSER_GET_IFACE (chooser)->get_files (chooser);
+  files = GTK_FILE_CHOOSER_GET_IFACE (chooser)->get_files (chooser);
+
+  for (l = files; l != NULL; l = l->next)
+    {
+      GFile *file = (GFile *)l->data;
+
+      g_return_val_if_fail (_gtk_file_chooser_is_file_in_root (chooser, file),
+                            NULL);
+    }
+
+  return files;
 }
 
 /**
@@ -2761,5 +2865,161 @@ gtk_file_chooser_list_shortcut_folders (GtkFileChooser *chooser)
 
 #endif
 
+gboolean
+_gtk_file_chooser_uri_has_prefix (const char   *uri,
+                                  const GSList *prefixes)
+{
+  int uri_len;
+  gboolean result = FALSE;
+  const GSList *l;
+
+  g_return_val_if_fail (uri != NULL, FALSE);
+  g_return_val_if_fail (prefixes != NULL, FALSE);
+
+  uri_len = strlen (uri);
+
+  for (l = prefixes; l != NULL && !result; l = l->next)
+    {
+      char *new_prefix = NULL;
+      char *prefix = (char *)l->data;
+      int 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)
+{
+  GSList *root_uris;
+
+  g_return_val_if_fail (GTK_IS_FILE_CHOOSER (chooser), FALSE);
+  g_return_val_if_fail (uri != NULL, FALSE);
+
+  root_uris = gtk_file_chooser_get_root_uris (chooser);
+
+  return root_uris == NULL ||
+         _gtk_file_chooser_uri_has_prefix (uri, root_uris);
+}
+
+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;
+}
+
+GSList *
+_gtk_file_chooser_get_visible_roots (GtkFileChooser *chooser)
+{
+  GSList *l, *results = NULL;
+  GtkFileSystem *file_system = _gtk_file_chooser_get_file_system (chooser);
+
+  for (l = gtk_file_chooser_get_root_uris (chooser); l != NULL; l = l->next)
+    {
+      GFile *file = g_file_new_for_uri ((char *)l->data);
+      gboolean skip = FALSE;
+      GtkFileSystemVolume *volume;
+      GFileInfo *file_info;
+      char *file_path;
+
+      if (file == NULL)
+        continue;
+
+      file_path = g_file_get_path (file);
+
+      /*
+       * See if this is the Desktop directory or Home directory, which will
+       * already be listed.
+       */
+      if (!g_strcmp0 (file_path,
+                      g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP)) ||
+          !g_strcmp0 (file_path, g_get_home_dir()))
+        {
+          g_object_unref (file);
+          g_free (file_path);
+          continue;
+        }
+
+      /* Make sure the file exists in some form. */
+      file_info = g_file_query_info (file,
+                                     G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
+                                     G_FILE_QUERY_INFO_NONE,
+                                     NULL,
+                                     NULL);
+
+      if (file_info == NULL)
+        {
+          g_object_unref (file_info);
+          g_object_unref (file);
+          continue;
+        }
+
+      volume = _gtk_file_system_get_volume_for_file (file_system, file);
+
+      if (volume != NULL)
+        {
+          GFile *fs_root = _gtk_file_system_volume_get_root (volume);
+
+          if (fs_root != NULL)
+            {
+              if (_gtk_file_chooser_is_file_in_root (GTK_FILE_CHOOSER (chooser),
+                                                     fs_root))
+                {
+                  // This is going to be listed already. Ignore it for now.
+                  skip = TRUE;
+                }
+
+              g_object_unref (fs_root);
+            }
+        }
+
+      if (skip)
+        {
+          g_object_unref (file);
+          continue;
+        }
+
+      results = g_slist_append (results, file);
+    }
+
+  return results;
+}
+
+
 #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 21ba7ee..63ec0a1 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_uris       (GtkFileChooser       *chooser,
+							   GSList               *root_uris);
+GSList              *gtk_file_chooser_get_root_uris       (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 771597a..1c79a75 100644
--- a/gtk/gtkfilechooserbutton.c
+++ b/gtk/gtkfilechooserbutton.c
@@ -106,6 +106,7 @@ enum
 typedef enum
 {
   ROW_TYPE_SPECIAL,
+  ROW_TYPE_ROOT,
   ROW_TYPE_VOLUME,
   ROW_TYPE_SHORTCUT,
   ROW_TYPE_BOOKMARK_SEPARATOR,
@@ -140,6 +141,8 @@ struct _GtkFileChooserButtonPrivate
   GtkFileSystem *fs;
   GFile *old_file;
 
+  GSList *shortcuts;
+
   gulong combo_box_changed_id;
   gulong dialog_file_activated_id;
   gulong dialog_folder_changed_id;
@@ -154,6 +157,7 @@ struct _GtkFileChooserButtonPrivate
   gint icon_size;
 
   guint8 n_special;
+  guint8 n_roots;
   guint8 n_volumes;
   guint8 n_shortcuts;
   guint8 n_bookmarks;
@@ -439,6 +443,8 @@ gtk_file_chooser_button_init (GtkFileChooserButton *button)
 
   priv->icon_size = FALLBACK_ICON_SIZE;
   priv->focus_on_click = TRUE;
+  priv->shortcuts = NULL;
+  priv->n_shortcuts = 0;
 
   gtk_widget_push_composite_child ();
 
@@ -531,6 +537,98 @@ gtk_file_chooser_button_file_chooser_iface_init (GtkFileChooserIface *iface)
   iface->remove_shortcut_folder = gtk_file_chooser_button_remove_shortcut_folder;
 }
 
+static void
+reload_roots (GtkFileChooserButton *button)
+{
+  GtkFileChooser *filechooser;
+  GtkFileChooserButtonPrivate *priv = button->priv;
+  GSList *l, *roots;
+  gint start_pos;
+
+  start_pos = model_get_type_position (button, ROW_TYPE_ROOT);
+  model_remove_rows (button, start_pos, priv->n_roots);
+  priv->n_roots = 0;
+
+  filechooser = GTK_FILE_CHOOSER (button->priv->dialog);
+  roots = _gtk_file_chooser_get_visible_roots (GTK_FILE_CHOOSER (filechooser));
+
+  for (l = roots; l != NULL; l = l->next)
+    {
+      GFile *file = (GFile *)l->data;
+      gint pos = start_pos + priv->n_roots;
+      GtkTreeIter iter;
+
+      gtk_list_store_insert (GTK_LIST_STORE (priv->model), &iter, pos);
+      gtk_list_store_set (GTK_LIST_STORE (priv->model), &iter,
+                          ICON_COLUMN, NULL,
+                          DISPLAY_NAME_COLUMN, _(FALLBACK_DISPLAY_NAME),
+                          TYPE_COLUMN, ROW_TYPE_ROOT,
+                          DATA_COLUMN, file,
+                          IS_FOLDER_COLUMN, FALSE,
+                          -1);
+      set_info_for_file_at_iter (button, file, &iter);
+      priv->n_roots++;
+    }
+
+  g_slist_free (roots);
+
+  gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
+
+  update_label_and_image (button);
+  update_combo_box (button);
+}
+
+static void
+add_shortcut_to_list (GtkFileChooserButton *button,
+                      GFile                *shortcut)
+{
+  GtkFileChooserButtonPrivate *priv = button->priv;
+  gint pos = model_get_type_position (button, ROW_TYPE_SHORTCUT) +
+             priv->n_shortcuts;
+  GtkTreeIter iter;
+
+  gtk_list_store_insert (GTK_LIST_STORE (priv->model), &iter, pos);
+  gtk_list_store_set (GTK_LIST_STORE (priv->model), &iter,
+                      ICON_COLUMN, NULL,
+                      DISPLAY_NAME_COLUMN, _(FALLBACK_DISPLAY_NAME),
+                      TYPE_COLUMN, ROW_TYPE_SHORTCUT,
+                      DATA_COLUMN, g_object_ref (shortcut),
+                      IS_FOLDER_COLUMN, FALSE,
+                      -1);
+  set_info_for_file_at_iter (button, shortcut, &iter);
+  priv->n_shortcuts++;
+}
+
+static void
+reload_shortcuts (GtkFileChooserButton *button)
+{
+  GtkFileChooser *filechooser;
+  GtkFileChooserButtonPrivate *priv = button->priv;
+  GSList *l;
+
+  model_remove_rows (button,
+                     model_get_type_position (button, ROW_TYPE_SHORTCUT),
+                     priv->n_shortcuts);
+  priv->n_shortcuts = 0;
+
+  filechooser = GTK_FILE_CHOOSER (button->priv->dialog);
+
+  for (l = priv->shortcuts; l != NULL; l = l->next)
+    {
+      GFile *shortcut = (GFile *)l->data;
+
+      if (_gtk_file_chooser_is_file_in_root (filechooser, shortcut))
+        {
+          add_shortcut_to_list (button, shortcut);
+        }
+    }
+
+  gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
+
+  update_label_and_image (button);
+  update_combo_box (button);
+}
+
 static gboolean
 gtk_file_chooser_button_add_shortcut_folder (GtkFileChooser  *chooser,
 					     GFile           *file,
@@ -547,24 +645,16 @@ gtk_file_chooser_button_add_shortcut_folder (GtkFileChooser  *chooser,
     {
       GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser);
       GtkFileChooserButtonPrivate *priv = button->priv;
-      GtkTreeIter iter;
-      gint pos;
-
-      pos = model_get_type_position (button, ROW_TYPE_SHORTCUT);
-      pos += priv->n_shortcuts;
 
-      gtk_list_store_insert (GTK_LIST_STORE (priv->model), &iter, pos);
-      gtk_list_store_set (GTK_LIST_STORE (priv->model), &iter,
-			  ICON_COLUMN, NULL,
-			  DISPLAY_NAME_COLUMN, _(FALLBACK_DISPLAY_NAME),
-			  TYPE_COLUMN, ROW_TYPE_SHORTCUT,
-			  DATA_COLUMN, g_object_ref (file),
-			  IS_FOLDER_COLUMN, FALSE,
-			  -1);
-      set_info_for_file_at_iter (button, file, &iter);
-      priv->n_shortcuts++;
+      g_object_ref (G_OBJECT (file));
+      priv->shortcuts = g_slist_append (priv->shortcuts, file);
 
-      gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
+      if (_gtk_file_chooser_is_file_in_root (GTK_FILE_CHOOSER (priv->dialog),
+                                             file))
+        {
+          add_shortcut_to_list (button, file);
+          gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
+        }
     }
 
   return retval;
@@ -590,6 +680,7 @@ gtk_file_chooser_button_remove_shortcut_folder (GtkFileChooser  *chooser,
       GtkTreeIter iter;
       gint pos;
       gchar type;
+      GSList *l;
 
       pos = model_get_type_position (button, ROW_TYPE_SHORTCUT);
       gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, pos);
@@ -616,6 +707,18 @@ gtk_file_chooser_button_remove_shortcut_folder (GtkFileChooser  *chooser,
 	}
       while (type == ROW_TYPE_SHORTCUT &&
 	     gtk_tree_model_iter_next (priv->model, &iter));
+
+      for (l = priv->shortcuts; l != NULL; l = l->next)
+        {
+          GFile *shortcut = (GFile *)l->data;
+
+          if (g_file_equal (shortcut, file))
+            {
+              g_object_unref (G_OBJECT (shortcut));
+              priv->shortcuts = g_slist_remove_link (priv->shortcuts, l);
+              break;
+            }
+        }
     }
 
   return retval;
@@ -749,6 +852,7 @@ gtk_file_chooser_button_set_property (GObject      *object,
 {
   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (object);
   GtkFileChooserButtonPrivate *priv = button->priv;
+  GtkFileChooser *filechooser = GTK_FILE_CHOOSER (priv->dialog);
 
   switch (param_id)
     {
@@ -815,9 +919,28 @@ gtk_file_chooser_button_set_property (GObject      *object,
       break;
 
     case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
+    case GTK_FILE_CHOOSER_PROP_ROOT_URIS:
       g_object_set_property (G_OBJECT (priv->dialog), pspec->name, value);
+
+      if (!_gtk_file_chooser_is_file_in_root (filechooser,
+            gtk_file_chooser_get_current_folder_file (filechooser)))
+        {
+          GSList *root_uris = gtk_file_chooser_get_root_uris (filechooser);
+
+          GFile *file = g_file_new_for_uri (root_uris == NULL
+                                            ? "file:///"
+                                            : (char *)root_uris->data);
+
+          gtk_file_chooser_set_current_folder_file (filechooser, file, NULL);
+          model_update_current_folder (button, file);
+          g_object_unref (G_OBJECT (file));
+        }
+
       fs_volumes_changed_cb (priv->fs, button);
       fs_bookmarks_changed_cb (priv->fs, button);
+      reload_roots (button);
+      reload_shortcuts (button);
+
       break;
 
     case GTK_FILE_CHOOSER_PROP_FILE_SYSTEM_BACKEND:
@@ -867,6 +990,7 @@ gtk_file_chooser_button_get_property (GObject    *object,
     case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
     case GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION:
     case GTK_FILE_CHOOSER_PROP_CREATE_FOLDERS:
+    case GTK_FILE_CHOOSER_PROP_ROOT_URIS:
       g_object_get_property (G_OBJECT (priv->dialog), pspec->name, value);
       break;
 
@@ -885,6 +1009,13 @@ gtk_file_chooser_button_finalize (GObject *object)
   if (priv->old_file)
     g_object_unref (priv->old_file);
 
+  if (priv->shortcuts)
+    {
+      g_slist_foreach (priv->shortcuts, (GFunc)g_object_unref, NULL);
+      g_slist_free (priv->shortcuts);
+      priv->shortcuts = NULL;
+    }
+
   G_OBJECT_CLASS (gtk_file_chooser_button_parent_class)->finalize (object);
 }
 
@@ -1518,6 +1649,11 @@ model_get_type_position (GtkFileChooserButton *button,
 
   retval += button->priv->n_special;
 
+  if (row_type == ROW_TYPE_ROOT)
+    return retval;
+
+  retval += button->priv->n_roots;
+
   if (row_type == ROW_TYPE_VOLUME)
     return retval;
 
@@ -1750,7 +1886,9 @@ model_add_volumes (GtkFileChooserButton *button,
   GtkListStore *store;
   gint pos;
   gboolean local_only;
+  GSList *root_uris;
   GtkFileSystem *file_system;
+  GtkFileChooser *filechooser;
   GSList *l;
   
   if (!volumes)
@@ -1758,7 +1896,9 @@ model_add_volumes (GtkFileChooserButton *button,
 
   store = GTK_LIST_STORE (button->priv->model);
   pos = model_get_type_position (button, ROW_TYPE_VOLUME);
-  local_only = gtk_file_chooser_get_local_only (GTK_FILE_CHOOSER (button->priv->dialog));
+  filechooser = GTK_FILE_CHOOSER (button->priv->dialog);
+  local_only = gtk_file_chooser_get_local_only (filechooser);
+  root_uris = gtk_file_chooser_get_root_uris (filechooser);
   file_system = button->priv->fs;
 
   for (l = volumes; l; l = l->next)
@@ -1767,23 +1907,31 @@ model_add_volumes (GtkFileChooserButton *button,
       GtkTreeIter iter;
       GdkPixbuf *pixbuf;
       gchar *display_name;
+      GFile *base_file;
+      gboolean skip = FALSE;
 
       volume = l->data;
 
-      if (local_only)
-	{
-	  if (_gtk_file_system_volume_is_mounted (volume))
-	    {
-	      GFile *base_file;
+      base_file = _gtk_file_system_volume_get_root (volume);
 
-	      base_file = _gtk_file_system_volume_get_root (volume);
-	      if (base_file != NULL && !g_file_is_native (base_file))
-		{
-		  _gtk_file_system_volume_free (volume);
-		  continue;
-		}
-	    }
-	}
+      if (local_only &&
+          base_file != NULL &&
+          _gtk_file_system_volume_is_mounted (volume) &&
+          !g_file_is_native (base_file))
+        skip = TRUE;
+      else if (root_uris != NULL &&
+               (base_file == NULL ||
+                !_gtk_file_chooser_is_file_in_root (filechooser, base_file)))
+        skip = TRUE;
+
+      if (base_file != NULL)
+        g_object_unref (base_file);
+
+      if (skip)
+        {
+          _gtk_file_system_volume_free (volume);
+          continue;
+        }
 
       pixbuf = _gtk_file_system_volume_render_icon (volume,
 						    GTK_WIDGET (button),
@@ -1820,13 +1968,15 @@ model_add_bookmarks (GtkFileChooserButton *button,
   gint pos;
   gboolean local_only;
   GSList *l;
+  GtkFileChooser *filechooser;
 
   if (!bookmarks)
     return;
 
   store = GTK_LIST_STORE (button->priv->model);
   pos = model_get_type_position (button, ROW_TYPE_BOOKMARK);
-  local_only = gtk_file_chooser_get_local_only (GTK_FILE_CHOOSER (button->priv->dialog));
+  filechooser = GTK_FILE_CHOOSER (button->priv->dialog);
+  local_only = gtk_file_chooser_get_local_only (filechooser);
 
   for (l = bookmarks; l; l = l->next)
     {
@@ -1834,6 +1984,9 @@ model_add_bookmarks (GtkFileChooserButton *button,
 
       file = l->data;
 
+      if (!_gtk_file_chooser_is_file_in_root (filechooser, file))
+        continue;
+
       if (g_file_is_native (file))
 	{
 	  gtk_list_store_insert (store, &iter, pos);
@@ -2051,14 +2204,26 @@ static inline gboolean
 test_if_file_is_visible (GtkFileSystem *fs,
 			 GFile         *file,
 			 gboolean       local_only,
+			 GSList        *root_uris,
 			 gboolean       is_folder)
 {
+  char *uri;
+  gboolean result;
+
   if (!file)
     return FALSE;
 
   if (local_only && !g_file_is_native (file))
     return FALSE;
 
+  uri = g_file_get_uri (file);
+  result = (root_uris == NULL ||
+            _gtk_file_chooser_uri_has_prefix (uri, root_uris));
+  g_free (uri);
+
+  if (!result)
+    return FALSE;
+
   if (!is_folder)
     return FALSE;
 
@@ -2075,10 +2240,12 @@ filter_model_visible_func (GtkTreeModel *model,
   gchar type;
   gpointer data;
   gboolean local_only, retval, is_folder;
+  GSList *root_uris;
 
   type = ROW_TYPE_INVALID;
   data = NULL;
   local_only = gtk_file_chooser_get_local_only (GTK_FILE_CHOOSER (priv->dialog));
+  root_uris = gtk_file_chooser_get_root_uris (GTK_FILE_CHOOSER (priv->dialog));
 
   gtk_tree_model_get (model, iter,
 		      TYPE_COLUMN, &type,
@@ -2094,7 +2261,8 @@ filter_model_visible_func (GtkTreeModel *model,
     case ROW_TYPE_SPECIAL:
     case ROW_TYPE_SHORTCUT:
     case ROW_TYPE_BOOKMARK:
-      retval = test_if_file_is_visible (priv->fs, data, local_only, is_folder);
+      retval = test_if_file_is_visible (priv->fs, data, local_only, root_uris,
+										is_folder);
       break;
     case ROW_TYPE_VOLUME:
       {
@@ -2512,6 +2680,7 @@ combo_box_changed_cb (GtkComboBox *combo_box,
       switch (type)
 	{
 	case ROW_TYPE_SPECIAL:
+	case ROW_TYPE_ROOT:
 	case ROW_TYPE_SHORTCUT:
 	case ROW_TYPE_BOOKMARK:
 	case ROW_TYPE_CURRENT_FOLDER:
@@ -2920,3 +3089,5 @@ gtk_file_chooser_button_get_focus_on_click (GtkFileChooserButton *button)
 
 #define __GTK_FILE_CHOOSER_BUTTON_C__
 #include "gtkaliasdef.c"
+
+// vim: et sw=2 cinoptions=(0,t0,f1s,n-1s,{1s,>2s,^-1s
diff --git a/gtk/gtkfilechooserdefault.c b/gtk/gtkfilechooserdefault.c
index 3f634d3..335a666 100644
--- a/gtk/gtkfilechooserdefault.c
+++ b/gtk/gtkfilechooserdefault.c
@@ -244,6 +244,7 @@ typedef enum {
   SHORTCUTS_RECENT_SEPARATOR,
   SHORTCUTS_HOME,
   SHORTCUTS_DESKTOP,
+  SHORTCUTS_ROOTS,
   SHORTCUTS_VOLUMES,
   SHORTCUTS_SHORTCUTS,
   SHORTCUTS_BOOKMARKS_SEPARATOR,
@@ -359,6 +360,8 @@ static gboolean shortcuts_select_func   (GtkTreeSelection      *selection,
 					 GtkTreePath           *path,
 					 gboolean               path_currently_selected,
 					 gpointer               data);
+static void shortcuts_find_folder (GtkFileChooserDefault *impl,
+                                   GFile                 *folder);
 static gboolean shortcuts_get_selected  (GtkFileChooserDefault *impl,
 					 GtkTreeIter           *iter);
 static void shortcuts_activate_iter (GtkFileChooserDefault *impl,
@@ -367,6 +370,10 @@ static int shortcuts_get_index (GtkFileChooserDefault *impl,
 				ShortcutsIndex         where);
 static int shortcut_find_position (GtkFileChooserDefault *impl,
 				   GFile                 *file);
+static int shortcuts_get_pos_for_shortcut_folder (GtkFileChooserDefault *impl,
+                                                  int                    pos);
+static void switch_to_shortcut (GtkFileChooserDefault *impl,
+                                int                    pos);
 
 static void bookmarks_check_add_sensitivity (GtkFileChooserDefault *impl);
 
@@ -407,6 +414,8 @@ static void location_switch_to_path_bar (GtkFileChooserDefault *impl);
 
 static void stop_loading_and_clear_list_model (GtkFileChooserDefault *impl,
                                                gboolean remove_from_treeview);
+static void stop_operation (GtkFileChooserDefault *impl,
+                            OperationMode          mode);
 static void     search_stop_searching        (GtkFileChooserDefault *impl,
                                               gboolean               remove_query);
 static void     search_clear_model           (GtkFileChooserDefault *impl, 
@@ -710,7 +719,7 @@ _gtk_file_chooser_default_init (GtkFileChooserDefault *impl)
 #ifdef PROFILE_FILE_CHOOSER
   access ("MARK: *** CREATE FILE CHOOSER", F_OK);
 #endif
-  impl->local_only = TRUE;
+  impl->root_uris = g_slist_append (NULL, g_strdup ("file://"));
   impl->preview_widget_active = TRUE;
   impl->use_preview_label = TRUE;
   impl->select_multiple = FALSE;
@@ -1320,17 +1329,14 @@ shortcuts_reload_icons (GtkFileChooserDefault *impl)
 }
 
 static void 
-shortcuts_find_folder (GtkFileChooserDefault *impl,
-		       GFile                 *folder)
+shortcuts_find_pos (GtkFileChooserDefault *impl,
+		    int                    pos)
 {
   GtkTreeSelection *selection;
-  int pos;
   GtkTreePath *path;
 
   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
 
-  g_assert (folder != NULL);
-  pos = shortcut_find_position (impl, folder);
   if (pos == -1)
     {
       gtk_tree_selection_unselect_all (selection);
@@ -1342,6 +1348,17 @@ shortcuts_find_folder (GtkFileChooserDefault *impl,
   gtk_tree_path_free (path);
 }
 
+static void 
+shortcuts_find_folder (GtkFileChooserDefault *impl,
+		       GFile                 *folder)
+{
+  int pos;
+  g_assert (folder != NULL);
+  pos = shortcut_find_position (impl, folder);
+
+  shortcuts_find_pos (impl, pos);
+}
+
 /* If a shortcut corresponds to the current folder, selects it */
 static void
 shortcuts_find_current_folder (GtkFileChooserDefault *impl)
@@ -1394,6 +1411,10 @@ shortcuts_update_count (GtkFileChooserDefault *impl,
 	  impl->has_desktop = TRUE;
 	break;
 
+      case SHORTCUTS_ROOTS:
+	impl->num_roots += value;
+	break;
+
       case SHORTCUTS_VOLUMES:
 	impl->num_volumes += value;
 	break;
@@ -1757,25 +1778,52 @@ shortcuts_insert_file (GtkFileChooserDefault *impl,
 static void
 shortcuts_append_search (GtkFileChooserDefault *impl)
 {
-  GdkPixbuf *pixbuf;
-  GtkTreeIter iter;
+  gboolean old_changing_folders = impl->changing_folder;
+  impl->changing_folder = TRUE;
+  int start_row = shortcuts_get_index (impl, SHORTCUTS_SEARCH);
+  gboolean can_search = (impl->root_uris == NULL);
 
-  pixbuf = render_search_icon (impl);
+  if (impl->has_search)
+      shortcuts_remove_rows (impl, start_row, 1);
 
-  gtk_list_store_append (impl->shortcuts_model, &iter);
-  gtk_list_store_set (impl->shortcuts_model, &iter,
-		      SHORTCUTS_COL_PIXBUF, pixbuf,
-		      SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE,
-		      SHORTCUTS_COL_NAME, _("Search"),
-		      SHORTCUTS_COL_DATA, NULL,
-		      SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_SEARCH,
-		      SHORTCUTS_COL_REMOVABLE, FALSE,
-		      -1);
+  if (!can_search)
+    {
+      GSList *l;
 
-  if (pixbuf)
-    g_object_unref (pixbuf);
+      for (l = impl->root_uris; l != NULL; l = l->next)
+        {
+          if (g_str_has_prefix ((char *)l->data, "file:"))
+            {
+              can_search = TRUE;
+              break;
+            }
+        }
+    }
+
+  if (can_search)
+    {
+      GtkTreeIter iter;
+      GdkPixbuf *pixbuf = render_search_icon (impl);
+
+      gtk_list_store_insert (impl->shortcuts_model, &iter, start_row);
+      gtk_list_store_set (impl->shortcuts_model, &iter,
+                          SHORTCUTS_COL_PIXBUF, pixbuf,
+                          SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE,
+                          SHORTCUTS_COL_NAME, _("Search"),
+                          SHORTCUTS_COL_DATA, NULL,
+                          SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_SEARCH,
+                          SHORTCUTS_COL_REMOVABLE, FALSE,
+                          -1);
+
+      if (pixbuf)
+        g_object_unref (pixbuf);
+
+      impl->has_search = TRUE;
+    }
+  else
+    impl->has_search = FALSE;
 
-  impl->has_search = TRUE;
+  impl->changing_folder = old_changing_folders;
 }
 
 static void
@@ -1783,10 +1831,21 @@ shortcuts_append_recent (GtkFileChooserDefault *impl)
 {
   GdkPixbuf *pixbuf;
   GtkTreeIter iter;
+  gboolean old_changing_folders = impl->changing_folder;
+  int start_row = shortcuts_get_index (impl, SHORTCUTS_RECENT);
+
+  impl->changing_folder = TRUE;
+
+  if (impl->has_recent)
+    {
+      shortcuts_remove_rows (impl,
+        shortcuts_get_index (impl, SHORTCUTS_RECENT_SEPARATOR), 1);
+      shortcuts_remove_rows (impl, start_row, 1);
+    }
 
   pixbuf = render_recent_icon (impl);
 
-  gtk_list_store_append (impl->shortcuts_model, &iter);
+  gtk_list_store_insert (impl->shortcuts_model, &iter, start_row);
   gtk_list_store_set (impl->shortcuts_model, &iter,
                       SHORTCUTS_COL_PIXBUF, pixbuf,
                       SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE,
@@ -1795,11 +1854,13 @@ shortcuts_append_recent (GtkFileChooserDefault *impl)
                       SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_RECENT,
                       SHORTCUTS_COL_REMOVABLE, FALSE,
                       -1);
-  
+
   if (pixbuf)
     g_object_unref (pixbuf);
 
   impl->has_recent = TRUE;
+
+  impl->changing_folder = old_changing_folders;
 }
 
 /* Appends an item for the user's home directory to the shortcuts model */
@@ -1808,22 +1869,43 @@ shortcuts_append_home (GtkFileChooserDefault *impl)
 {
   const char *home_path;
   GFile *home;
+  gboolean old_changing_folders;
+  int start_row;
 
   profile_start ("start", NULL);
 
+  old_changing_folders = impl->changing_folder;
+  impl->changing_folder = TRUE;
+
+  start_row = shortcuts_get_index (impl, SHORTCUTS_HOME);
+
+  if (impl->has_home)
+    shortcuts_remove_rows (impl, start_row, 1);
+
   home_path = g_get_home_dir ();
   if (home_path == NULL)
     {
+      impl->has_home = FALSE;
+      impl->changing_folder = old_changing_folders;
       profile_end ("end - no home directory!?", NULL);
       return;
     }
 
   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, start_row, SHORTCUT_TYPE_FILE, NULL, home,
+                             NULL, FALSE, SHORTCUTS_HOME);
+      impl->has_home = TRUE;
+    }
+  else
+    impl->has_home = FALSE;
 
   g_object_unref (home);
 
+  impl->changing_folder = old_changing_folders;
+
   profile_end ("end", NULL);
 }
 
@@ -1833,22 +1915,41 @@ shortcuts_append_desktop (GtkFileChooserDefault *impl)
 {
   const char *name;
   GFile *file;
+  gboolean old_changing_folders;
+  int start_row;
 
   profile_start ("start", NULL);
 
+  old_changing_folders = impl->changing_folder;
+  impl->changing_folder = TRUE;
+
+  start_row = shortcuts_get_index (impl, SHORTCUTS_DESKTOP);
+
+  if (impl->has_desktop)
+    shortcuts_remove_rows (impl, start_row, 1);
+
   name = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP);
   /* "To disable a directory, point it to the homedir."
    * See http://freedesktop.org/wiki/Software/xdg-user-dirs
    **/
   if (!g_strcmp0 (name, g_get_home_dir ()))
     {
+      impl->has_desktop = FALSE;
+      impl->changing_folder = old_changing_folders;
       profile_end ("end", NULL);
       return;
     }
 
   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, start_row, 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.
@@ -1856,6 +1957,51 @@ shortcuts_append_desktop (GtkFileChooserDefault *impl)
 
   g_object_unref (file);
 
+  impl->changing_folder = old_changing_folders;
+
+  profile_end ("end", NULL);
+}
+
+static void
+shortcuts_append_roots (GtkFileChooserDefault *impl)
+{
+  GSList *l, *roots;
+  gboolean old_changing_folders;
+  int start_row;
+  int n = 0;
+
+  profile_start ("start", NULL);
+
+  old_changing_folders = impl->changing_folder;
+  impl->changing_folder = TRUE;
+
+  start_row = shortcuts_get_index (impl, SHORTCUTS_ROOTS);
+  shortcuts_remove_rows (impl, start_row, impl->num_roots);
+  impl->num_roots = 0;
+
+  roots = _gtk_file_chooser_get_visible_roots (GTK_FILE_CHOOSER (impl));
+
+  for (l = roots; l != NULL; l = l->next)
+    {
+      GFile *file = (GFile *)l->data;
+
+      shortcuts_insert_file (impl, start_row + n, SHORTCUT_TYPE_FILE,
+                             NULL, file, NULL, FALSE, SHORTCUTS_ROOTS);
+      n++;
+    }
+
+  g_slist_free (roots);
+
+  impl->num_roots = n;
+
+  if (impl->shortcuts_pane_filter_model)
+    gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model));
+
+  if (impl->shortcuts_combo_filter_model)
+    gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_combo_filter_model));
+
+  impl->changing_folder = old_changing_folders;
+
   profile_end ("end", NULL);
 }
 
@@ -1867,20 +2013,26 @@ shortcuts_append_bookmarks (GtkFileChooserDefault *impl,
   int start_row;
   int num_inserted;
   gchar *label;
+  gboolean local_only;
 
   profile_start ("start", NULL);
 
   start_row = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR) + 1;
   num_inserted = 0;
 
+  local_only = gtk_file_chooser_get_local_only (GTK_FILE_CHOOSER (impl));
+
   for (; bookmarks; bookmarks = bookmarks->next)
     {
       GFile *file;
 
       file = bookmarks->data;
 
-      if (impl->local_only && !g_file_is_native (file))
-	continue;
+      if (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;
@@ -1932,6 +2084,11 @@ shortcuts_get_index (GtkFileChooserDefault *impl,
 
   n += impl->has_desktop ? 1 : 0;
 
+  if (where == SHORTCUTS_ROOTS)
+    goto out;
+
+  n += impl->num_roots;
+
   if (where == SHORTCUTS_VOLUMES)
     goto out;
 
@@ -1976,6 +2133,7 @@ shortcuts_add_volumes (GtkFileChooserDefault *impl)
   GSList *list, *l;
   int n;
   gboolean old_changing_folders;
+  gboolean local_only;
 
   profile_start ("start", NULL);
 
@@ -1988,32 +2146,36 @@ shortcuts_add_volumes (GtkFileChooserDefault *impl)
 
   list = _gtk_file_system_list_volumes (impl->file_system);
 
+  local_only = gtk_file_chooser_get_local_only (GTK_FILE_CHOOSER (impl));
+
   n = 0;
 
   for (l = list; l; l = l->next)
     {
       GtkFileSystemVolume *volume;
+      gboolean skip = FALSE;
+      GFile *base_file;
 
       volume = l->data;
 
-      if (impl->local_only)
-	{
-	  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);
 
-	      base_file = _gtk_file_system_volume_get_root (volume);
-              if (base_file != NULL)
-                {
-                  base_is_native = g_file_is_native (base_file);
-                  g_object_unref (base_file);
-                }
+      if (local_only &&
+          base_file != NULL &&
+          _gtk_file_system_volume_is_mounted (volume) &&
+          !g_file_is_native (base_file))
+        skip = TRUE;
+      else if (impl->root_uris != NULL &&
+               (base_file == NULL ||
+                !_gtk_file_chooser_is_file_in_root (GTK_FILE_CHOOSER (impl),
+                                                    base_file)))
+        skip = TRUE;
 
-              if (!base_is_native)
-                continue;
-	    }
-	}
+      if (base_file != NULL)
+        g_object_unref (base_file);
+
+      if (skip)
+        continue;
 
       shortcuts_insert_file (impl, start_row + n, SHORTCUT_TYPE_VOLUME, volume, NULL, NULL, FALSE, SHORTCUTS_VOLUMES);
       n++;
@@ -2155,6 +2317,46 @@ shortcuts_add_bookmarks (GtkFileChooserDefault *impl)
   profile_end ("end", NULL);
 }
 
+/* Update the list of custom shortcut folders. */
+static void
+shortcuts_add_custom_folders (GtkFileChooserDefault *impl)
+{
+  gboolean old_changing_folders;
+  GSList *l;
+
+  profile_start ("start", NULL);
+
+  old_changing_folders = impl->changing_folder;
+  impl->changing_folder = TRUE;
+
+  if (impl->num_shortcuts > 0)
+    {
+      shortcuts_remove_rows (impl,
+                             shortcuts_get_index (impl, SHORTCUTS_SHORTCUTS),
+                             impl->num_shortcuts);
+    }
+
+  impl->num_shortcuts = 0;
+
+  for (l = impl->shortcuts; l != NULL; l = l->next)
+    {
+      GFile *file = (GFile *)l->data;
+
+      if (impl->root_uris != NULL &&
+          _gtk_file_chooser_is_file_in_root (GTK_FILE_CHOOSER (impl), file))
+        {
+          int pos = shortcuts_get_pos_for_shortcut_folder (impl,
+                                                           impl->num_shortcuts);
+          shortcuts_insert_file (impl, pos, SHORTCUT_TYPE_FILE, NULL,
+                                 file, NULL, FALSE, SHORTCUTS_SHORTCUTS);
+        }
+    }
+
+  impl->changing_folder = old_changing_folders;
+
+  profile_end ("end", NULL);
+}
+
 /* Appends a separator and a row to the shortcuts list for the current folder */
 static void
 shortcuts_add_current_folder (GtkFileChooserDefault *impl)
@@ -2251,42 +2453,47 @@ shortcuts_pane_filter_cb (GtkTreeModel *model,
 static void
 shortcuts_model_create (GtkFileChooserDefault *impl)
 {
-  /* Keep this order in sync with the SHORCUTS_COL_* enum values */
-  impl->shortcuts_model = gtk_list_store_new (SHORTCUTS_COL_NUM_COLUMNS,
-					      GDK_TYPE_PIXBUF,	/* pixbuf */
-					      G_TYPE_STRING,	/* name */
-					      G_TYPE_POINTER,	/* path or volume */
-					      G_TYPE_INT,       /* ShortcutType */
-					      G_TYPE_BOOLEAN,   /* removable */
-					      G_TYPE_BOOLEAN,   /* pixbuf cell visibility */
-					      G_TYPE_POINTER);  /* GCancellable */
-
-  if (search_is_possible (impl))
+  if (impl->shortcuts_model == NULL)
     {
-      shortcuts_append_search (impl);
+      /* Keep this order in sync with the SHORCUTS_COL_* enum values */
+      impl->shortcuts_model = gtk_list_store_new (SHORTCUTS_COL_NUM_COLUMNS,
+                              GDK_TYPE_PIXBUF,	/* pixbuf */
+                              G_TYPE_STRING,	/* name */
+                              G_TYPE_POINTER,	/* path or volume */
+                              G_TYPE_INT,       /* ShortcutType */
+                              G_TYPE_BOOLEAN,   /* removable */
+                              G_TYPE_BOOLEAN,   /* pixbuf cell visibility */
+                              G_TYPE_POINTER);  /* GCancellable */
     }
 
+  if (search_is_possible (impl))
+    shortcuts_append_search (impl);
+
   if (impl->recent_manager)
     {
       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_roots (impl);
       shortcuts_add_volumes (impl);
     }
 
-  impl->shortcuts_pane_filter_model = shortcuts_pane_model_filter_new (impl,
-							               GTK_TREE_MODEL (impl->shortcuts_model),
-								       NULL);
+  if (impl->shortcuts_pane_filter_model == NULL)
+    {
+      impl->shortcuts_pane_filter_model = shortcuts_pane_model_filter_new (impl,
+                                               GTK_TREE_MODEL (impl->shortcuts_model),
+                                               NULL);
 
-  gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model),
-					  shortcuts_pane_filter_cb,
-					  impl,
-					  NULL);
+      gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model),
+                          shortcuts_pane_filter_cb,
+                          impl,
+                          NULL);
+  }
 }
 
 /* Callback used when the "New Folder" button is clicked */
@@ -4803,7 +5010,14 @@ save_widgets_create (GtkFileChooserDefault *impl)
   impl->location_entry = _gtk_file_chooser_entry_new (TRUE);
   _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_local_only (
+     GTK_FILE_CHOOSER_ENTRY (impl->location_entry),
+     gtk_file_chooser_get_local_only (GTK_FILE_CHOOSER (impl)));
+  _gtk_file_chooser_entry_set_root_uris (
+     GTK_FILE_CHOOSER_ENTRY (impl->location_entry),
+     impl->root_uris);
+  _gtk_path_bar_set_root_uris (GTK_PATH_BAR (impl->browse_path_bar),
+                               impl->root_uris);
   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,
@@ -4958,6 +5172,12 @@ location_switch_to_filename_entry (GtkFileChooserDefault *impl)
   /* Configure the entry */
 
   _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->current_folder);
+  _gtk_file_chooser_entry_set_local_only (
+     GTK_FILE_CHOOSER_ENTRY (impl->location_entry),
+     gtk_file_chooser_get_local_only (GTK_FILE_CHOOSER (impl)));
+  _gtk_file_chooser_entry_set_root_uris (
+     GTK_FILE_CHOOSER_ENTRY (impl->location_entry),
+     impl->root_uris);
 
   /* Done */
 
@@ -5242,41 +5462,144 @@ set_extra_widget (GtkFileChooserDefault *impl,
 }
 
 static void
-set_local_only (GtkFileChooserDefault *impl,
-		gboolean               local_only)
+set_root_uris (GtkFileChooserDefault *impl,
+               GSList                *root_uris)
 {
-  if (local_only != impl->local_only)
+  GtkTreeIter iter;
+  GFile *list_selected = NULL;
+  ShortcutType shortcut_type = -1;
+  gboolean local_only;
+
+  if (root_uris == impl->root_uris)
+    return;
+
+  g_slist_foreach (impl->root_uris, (GFunc)g_free, NULL);
+  g_slist_free (impl->root_uris);
+
+  impl->root_uris = root_uris;
+
+  local_only = gtk_file_chooser_get_local_only (GTK_FILE_CHOOSER (impl));
+
+  if (impl->location_entry)
     {
-      impl->local_only = local_only;
+      _gtk_file_chooser_entry_set_root_uris (
+        GTK_FILE_CHOOSER_ENTRY (impl->location_entry),
+       impl->root_uris);
+      _gtk_file_chooser_entry_set_local_only (
+        GTK_FILE_CHOOSER_ENTRY (impl->location_entry),
+        local_only);
+    }
 
-      if (impl->location_entry)
-	_gtk_file_chooser_entry_set_local_only (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), local_only);
+  _gtk_path_bar_set_root_uris (GTK_PATH_BAR (impl->browse_path_bar),
+                               impl->root_uris);
 
-      if (impl->shortcuts_model && impl->file_system)
-	{
-	  shortcuts_add_volumes (impl);
-	  shortcuts_add_bookmarks (impl);
-	}
+  /* Attempt to preserve the sidebar selection if possible. */
+  if (shortcuts_get_selected (impl, &iter))
+    {
+      gpointer col_data = NULL;
 
-      if (local_only && impl->current_folder &&
-           !g_file_is_native (impl->current_folder))
-	{
-	  /* If we are pointing to a non-local folder, make an effort to change
-	   * back to a local folder, but it's really up to the app to not cause
-	   * such a situation, so we ignore errors.
-	   */
-	  const gchar *home = g_get_home_dir ();
-	  GFile *home_file;
+      gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model),
+                          &iter,
+                          SHORTCUTS_COL_DATA, &col_data,
+                          SHORTCUTS_COL_TYPE, &shortcut_type,
+                          -1);
 
-	  if (home == NULL)
-	    return;
+      if (col_data != NULL)
+        {
+          if (shortcut_type == SHORTCUT_TYPE_FILE)
+            list_selected = g_object_ref (col_data);
+          else if (shortcut_type == SHORTCUT_TYPE_VOLUME)
+            list_selected = col_data;
+        }
+    }
 
-	  home_file = g_file_new_for_path (home);
+  if (impl->shortcuts_model && impl->file_system)
+    {
+      /* Update all the sidebar entries to filter the root URI. */
+      shortcuts_model_create (impl);
+      shortcuts_add_bookmarks (impl);
+      shortcuts_add_custom_folders (impl);
+    }
 
-	  gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (impl), home_file, NULL);
+  if (impl->current_folder != NULL)
+    {
+      if (local_only && !g_file_is_native (impl->current_folder))
+        {
+          /* If we are pointing to a non-local folder, make an effort to
+           * change back to a local folder, but it's really up to the app
+           * to not cause such a situation, so we ignore errors.
+           */
+          const gchar *home = g_get_home_dir ();
+          GFile *home_file;
 
-	  g_object_unref (home_file);
-	}
+          if (home != NULL)
+            {
+              home_file = g_file_new_for_path (home);
+              gtk_file_chooser_set_current_folder_file (
+                GTK_FILE_CHOOSER (impl),
+                home_file, NULL);
+
+              g_object_unref (home_file);
+            }
+        }
+      else if (impl->root_uris != NULL &&
+               !_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.
+           */
+
+          /*
+           * Clear the list first, in case we can't load this path.
+           * We don't want to show files they shouldn't be able to access.
+           */
+          if (impl->operation_mode == OPERATION_MODE_BROWSE)
+            stop_operation (impl, impl->operation_mode);
+
+          gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (impl),
+                                                   (char *)impl->root_uris->data);
+        }
+      else
+        {
+          /* Re-set the current folder so we'll display it properly. */
+          gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (impl),
+                                                    impl->current_folder,
+                                                    NULL);
+        }
+    }
+
+  if (shortcut_type != -1)
+    {
+      if (list_selected != NULL)
+        {
+          shortcuts_find_folder (impl, list_selected);
+
+          if (shortcut_type == SHORTCUT_TYPE_FILE)
+            g_object_unref (list_selected);
+        }
+      else
+        {
+          int pos = -1;
+
+          /*
+           * Switch the operation mode to browse, a good default.
+           * We'll go back to Search or Recent if that's what was
+           * selected. The advantage is that we'll reload the results
+           * as well.
+           */
+          stop_operation (impl, impl->operation_mode);
+          impl->operation_mode = OPERATION_MODE_BROWSE;
+
+          if (shortcut_type == SHORTCUT_TYPE_SEARCH)
+            pos = shortcuts_get_index (impl, SHORTCUTS_SEARCH);
+          else if (shortcut_type == SHORTCUT_TYPE_RECENT)
+            pos = shortcuts_get_index (impl, SHORTCUTS_RECENT);
+
+          if (pos != -1)
+            shortcuts_find_pos (impl, pos);
+        }
     }
 }
 
@@ -5284,6 +5607,7 @@ static void
 volumes_bookmarks_changed_cb (GtkFileSystem         *file_system,
 			      GtkFileChooserDefault *impl)
 {
+  shortcuts_append_roots (impl);
   shortcuts_add_volumes (impl);
   shortcuts_add_bookmarks (impl);
 
@@ -5458,7 +5782,10 @@ gtk_file_chooser_default_set_property (GObject      *object,
       break;
 
     case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
-      set_local_only (impl, g_value_get_boolean (value));
+      set_root_uris (impl,
+                     g_value_get_boolean (value)
+                     ? g_slist_append (NULL, g_strdup ("file://"))
+                     : NULL);
       break;
 
     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
@@ -5524,6 +5851,10 @@ gtk_file_chooser_default_set_property (GObject      *object,
       }
       break;
 
+    case GTK_FILE_CHOOSER_PROP_ROOT_URIS:
+      set_root_uris (impl, (GSList *)g_value_get_pointer (value));
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -5549,8 +5880,26 @@ gtk_file_chooser_default_get_property (GObject    *object,
       break;
 
     case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
-      g_value_set_boolean (value, impl->local_only);
-      break;
+      {
+        gboolean local_only = (impl->root_uris != NULL);
+
+        if (local_only)
+          {
+            GSList *l;
+
+            for (l = impl->root_uris; l != NULL; l = l->next)
+              {
+                if (!g_str_has_prefix ((char *)l->data, "file://"))
+                  {
+                    local_only = FALSE;
+                    break;
+                  }
+              }
+          }
+
+        g_value_set_boolean (value, local_only);
+        break;
+      }
 
     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
       g_value_set_object (value, impl->preview_widget);
@@ -5584,6 +5933,10 @@ gtk_file_chooser_default_get_property (GObject    *object,
       g_value_set_boolean (value, impl->create_folders);
       break;
 
+    case GTK_FILE_CHOOSER_PROP_ROOT_URIS:
+      g_value_set_pointer (value, impl->root_uris);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -5618,6 +5971,10 @@ gtk_file_chooser_default_dispose (GObject *object)
       impl->extra_widget = NULL;
     }
 
+  g_slist_foreach (impl->root_uris, (GFunc)g_free, NULL);
+  g_slist_free (impl->root_uris);
+  impl->root_uris = NULL;
+
   pending_select_files_free (impl);
 
   /* cancel all pending operations */
@@ -5654,6 +6011,13 @@ gtk_file_chooser_default_dispose (GObject *object)
       impl->loading_shortcuts = NULL;
     }
 
+  if (impl->shortcuts)
+    {
+      g_slist_foreach (impl->shortcuts, (GFunc)g_object_unref, NULL);
+      g_slist_free (impl->shortcuts);
+      impl->shortcuts = NULL;
+    }
+
   if (impl->file_list_drag_data_received_cancellable)
     {
       g_cancellable_cancel (impl->file_list_drag_data_received_cancellable);
@@ -7066,7 +7430,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;
@@ -7203,7 +7569,8 @@ gtk_file_chooser_default_update_current_folder (GtkFileChooser    *chooser,
       break;
     }
 
-  if (impl->local_only && !g_file_is_native (file))
+  if (gtk_file_chooser_get_local_only (GTK_FILE_CHOOSER (impl)) &&
+      !g_file_is_native (file))
     {
       g_set_error_literal (error,
                            GTK_FILE_CHOOSER_ERROR,
@@ -7215,6 +7582,19 @@ gtk_file_chooser_default_update_current_folder (GtkFileChooser    *chooser,
       return FALSE;
     }
 
+  if (!_gtk_file_chooser_is_file_in_root (GTK_FILE_CHOOSER (impl), file))
+    {
+      g_set_error_literal (error,
+                           GTK_FILE_CHOOSER_ERROR,
+                           GTK_FILE_CHOOSER_ERROR_BAD_FILENAME,
+                           _("Cannot change to folder because it is not "
+                             "accessible from this file chooser"));
+
+      g_object_unref (file);
+      profile_end ("end - not accessible within root URI", NULL);
+      return FALSE;
+    }
+
   if (impl->update_current_folder_cancellable)
     g_cancellable_cancel (impl->update_current_folder_cancellable);
 
@@ -7773,7 +8153,15 @@ add_shortcut_get_info_cb (GCancellable *cancellable,
 
   pos = shortcuts_get_pos_for_shortcut_folder (data->impl, data->impl->num_shortcuts);
 
-  shortcuts_insert_file (data->impl, pos, SHORTCUT_TYPE_FILE, NULL, data->file, NULL, FALSE, SHORTCUTS_SHORTCUTS);
+  g_object_ref (data->file);
+  data->impl->shortcuts = g_slist_append (data->impl->shortcuts, data->file);
+
+  if (_gtk_file_chooser_is_file_in_root (GTK_FILE_CHOOSER (data->impl),
+                                         data->file))
+    {
+      shortcuts_insert_file (data->impl, pos, SHORTCUT_TYPE_FILE, NULL,
+                             data->file, NULL, FALSE, SHORTCUTS_SHORTCUTS);
+    }
 
 out:
   g_object_unref (data->impl);
@@ -7902,13 +8290,25 @@ gtk_file_chooser_default_remove_shortcut_folder (GtkFileChooser  *chooser,
 	{
 	  shortcuts_remove_rows (impl, pos + i, 1);
 	  impl->num_shortcuts--;
-	  return TRUE;
+	  break;
 	}
 
       if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
 	g_assert_not_reached ();
     }
 
+  for (l = impl->shortcuts; l != NULL; l = l->next)
+    {
+      GFile *shortcut = (GFile *)l->data;
+
+      if (g_file_equal (shortcut, file))
+        {
+          g_object_unref (shortcut);
+          impl->shortcuts = g_slist_remove_link (impl->shortcuts, l);
+          return TRUE;
+        }
+    }
+
  out:
 
   uri = g_file_get_uri (file);
@@ -7927,44 +8327,8 @@ static GSList *
 gtk_file_chooser_default_list_shortcut_folders (GtkFileChooser *chooser)
 {
   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
-  int pos;
-  GtkTreeIter iter;
-  int i;
-  GSList *list;
-
-  if (impl->num_shortcuts == 0)
-    return NULL;
-
-  pos = shortcuts_get_pos_for_shortcut_folder (impl, 0);
-  if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->shortcuts_model), &iter, NULL, pos))
-    g_assert_not_reached ();
-
-  list = NULL;
-
-  for (i = 0; i < impl->num_shortcuts; i++)
-    {
-      gpointer col_data;
-      ShortcutType shortcut_type;
-      GFile *shortcut;
-
-      gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
-			  SHORTCUTS_COL_DATA, &col_data,
-			  SHORTCUTS_COL_TYPE, &shortcut_type,
-			  -1);
-      g_assert (col_data != NULL);
-      g_assert (shortcut_type == SHORTCUT_TYPE_FILE);
-
-      shortcut = col_data;
-      list = g_slist_prepend (list, g_object_ref (shortcut));
-
-      if (i != impl->num_shortcuts - 1)
-	{
-	  if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
-	    g_assert_not_reached ();
-	}
-    }
 
-  return g_slist_reverse (list);
+  return impl->shortcuts;
 }
 
 /* Guesses a size based upon font sizes */
@@ -8766,7 +9130,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;
@@ -9306,7 +9671,20 @@ recent_idle_load (gpointer data)
   /* first iteration: load all the items */
   if (!load_data->items)
     {
-      load_data->items = gtk_recent_manager_get_items (impl->recent_manager);
+      for (walk = gtk_recent_manager_get_items (impl->recent_manager);
+           walk != NULL;
+           walk = walk->next)
+        {
+          GtkRecentInfo *info = walk->data;
+          const char *uri = gtk_recent_info_get_uri (info);
+
+          if (_gtk_file_chooser_is_uri_in_root (GTK_FILE_CHOOSER (impl), uri))
+            {
+              // We'll sort this later, so prepend for efficiency.
+              load_data->items = g_list_prepend(load_data->items, info);
+            }
+        }
+
       if (!load_data->items)
         return FALSE;
 
@@ -10350,3 +10728,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 0977040..e216cea 100644
--- a/gtk/gtkfilechooserentry.c
+++ b/gtk/gtkfilechooserentry.c
@@ -26,6 +26,7 @@
 #include "gtkcellrenderertext.h"
 #include "gtkentry.h"
 #include "gtkfilechooserentry.h"
+#include "gtkfilechooserprivate.h"
 #include "gtklabel.h"
 #include "gtkmain.h"
 #include "gtkwindow.h"
@@ -66,6 +67,7 @@ struct _GtkFileChooserEntry
   GtkFileChooserAction action;
 
   GtkFileSystem *file_system;
+  const GSList *root_uris;
   GFile *base_folder;
   GFile *current_folder_file;
   gchar *file_part;
@@ -206,6 +208,7 @@ _gtk_file_chooser_entry_init (GtkFileChooserEntry *chooser_entry)
   GtkCellRenderer *cell;
 
   chooser_entry->local_only = TRUE;
+  chooser_entry->root_uris = NULL;
 
   g_object_set (chooser_entry, "truncate-multiline", TRUE, NULL);
 
@@ -446,6 +449,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_uris == NULL ||
+    _gtk_file_chooser_uri_has_prefix (uri, chooser_entry->root_uris);
+  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
@@ -1324,6 +1340,9 @@ populate_completion_store (GtkFileChooserEntry *chooser_entry)
 
       file = tmp_list->data;
 
+      if (!is_file_in_root (chooser_entry, file))
+        continue;
+
       info = _gtk_folder_get_info (chooser_entry->current_folder, file);
 
       if (info)
@@ -1464,8 +1483,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_uris != 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;
@@ -2001,3 +2023,20 @@ _gtk_file_chooser_entry_get_local_only (GtkFileChooserEntry *chooser_entry)
 {
   return chooser_entry->local_only;
 }
+
+void
+_gtk_file_chooser_entry_set_root_uris (GtkFileChooserEntry *chooser_entry,
+                                       const GSList        *root_uris)
+{
+  /* This doesn't need its own copy. */
+  chooser_entry->root_uris = root_uris;
+  clear_completions (chooser_entry);
+}
+
+const GSList *
+_gtk_file_chooser_entry_get_root_uris (GtkFileChooserEntry *chooser_entry)
+{
+  return chooser_entry->root_uris;
+}
+
+// vim: et sw=2 cinoptions=(0,t0,f1s,n-1s,{1s,>2s,^-1s
diff --git a/gtk/gtkfilechooserentry.h b/gtk/gtkfilechooserentry.h
index a9c9f83..ef392f9 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_uris    (GtkFileChooserEntry *chooser_entry,
+                                                             const GSList        *root_uris);
+const GSList      *_gtk_file_chooser_entry_get_root_uris    (GtkFileChooserEntry *chooser_entry);
 
 G_END_DECLS
 
diff --git a/gtk/gtkfilechooserprivate.h b/gtk/gtkfilechooserprivate.h
index 9f02b42..c02c3ee 100644
--- a/gtk/gtkfilechooserprivate.h
+++ b/gtk/gtkfilechooserprivate.h
@@ -104,6 +104,17 @@ 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);
+GSList *       _gtk_file_chooser_get_visible_roots (GtkFileChooser *chooser);
+
+gboolean       _gtk_file_chooser_uri_has_prefix (const char   *uri,
+                                                 const GSList *prefixes);
+
 /* GtkFileChooserDialog private */
 
 struct _GtkFileChooserDialogPrivate
@@ -160,6 +171,7 @@ struct _GtkFileChooserDefault
   GtkFileChooserAction action;
 
   GtkFileSystem *file_system;
+  GSList *root_uris;
 
   /* Save mode widgets */
   GtkWidget *save_widgets;
@@ -230,6 +242,7 @@ struct _GtkFileChooserDefault
 
   /* Handles */
   GSList *loading_shortcuts;
+  GSList *shortcuts;
   GSList *reload_icon_cancellables;
   GCancellable *file_list_drag_data_received_cancellable;
   GCancellable *update_current_folder_cancellable;
@@ -250,6 +263,7 @@ struct _GtkFileChooserDefault
   GtkFileFilter *current_filter;
   GSList *filters;
 
+  int num_roots;
   int num_volumes;
   int num_shortcuts;
   int num_bookmarks;
@@ -286,7 +300,6 @@ struct _GtkFileChooserDefault
 
   /* Flags */
 
-  guint local_only : 1;
   guint preview_widget_active : 1;
   guint use_preview_label : 1;
   guint select_multiple : 1;
diff --git a/gtk/gtkfilechooserutils.c b/gtk/gtkfilechooserutils.c
index 69c71d1..f18c171 100644
--- a/gtk/gtkfilechooserutils.c
+++ b/gtk/gtkfilechooserutils.c
@@ -120,6 +120,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_URIS,
+				    "root-uris");
 }
 
 /**
diff --git a/gtk/gtkfilechooserutils.h b/gtk/gtkfilechooserutils.h
index a590ccc..770656f 100644
--- a/gtk/gtkfilechooserutils.h
+++ b/gtk/gtkfilechooserutils.h
@@ -42,7 +42,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_URIS,
+  GTK_FILE_CHOOSER_PROP_LAST                   = GTK_FILE_CHOOSER_PROP_ROOT_URIS
 } GtkFileChooserProp;
 
 void _gtk_file_chooser_install_properties (GObjectClass *klass);
diff --git a/gtk/gtkpathbar.c b/gtk/gtkpathbar.c
index a9d2da3..0e14863 100644
--- a/gtk/gtkpathbar.c
+++ b/gtk/gtkpathbar.c
@@ -25,6 +25,7 @@
 #include "gtkalignment.h"
 #include "gtkarrow.h"
 #include "gtkdnd.h"
+#include "gtkfilechooserprivate.h"
 #include "gtkimage.h"
 #include "gtkintl.h"
 #include "gtkicontheme.h"
@@ -43,6 +44,7 @@ enum {
 typedef enum {
   NORMAL_BUTTON,
   ROOT_BUTTON,
+  VIRTUAL_ROOT_BUTTON,
   HOME_BUTTON,
   DESKTOP_BUTTON
 } ButtonType;
@@ -455,7 +457,7 @@ gtk_path_bar_size_allocate (GtkWidget     *widget,
   GtkPathBar *path_bar = GTK_PATH_BAR (widget);
   GtkTextDirection direction;
   GtkAllocation child_allocation;
-  GList *list, *first_button;
+  GList *list, *first_button, *root_button;
   gint width;
   gint allocation_width;
   gint border_width;
@@ -471,7 +473,11 @@ gtk_path_bar_size_allocate (GtkWidget     *widget,
 
   /* No path is set; we don't have to allocate anything. */
   if (path_bar->button_list == NULL)
-    return;
+    {
+      gtk_widget_set_child_visible (path_bar->up_slider_button, FALSE);
+      gtk_widget_set_child_visible (path_bar->down_slider_button, FALSE);
+      return;
+    }
 
   direction = gtk_widget_get_direction (widget);
   border_width = (gint) GTK_CONTAINER (path_bar)->border_width;
@@ -489,15 +495,17 @@ gtk_path_bar_size_allocate (GtkWidget     *widget,
 
       width += child->requisition.width + path_bar->spacing;
       if (list == path_bar->fake_root)
-	break;
+        break;
     }
 
+  root_button = g_list_last (path_bar->button_list);
+
   if (width <= allocation_width)
     {
       if (path_bar->fake_root)
 	first_button = path_bar->fake_root;
       else
-	first_button = g_list_last (path_bar->button_list);
+	first_button = root_button;
     }
   else
     {
@@ -559,7 +567,8 @@ gtk_path_bar_size_allocate (GtkWidget     *widget,
   if (direction == GTK_TEXT_DIR_RTL)
     {
       child_allocation.x = allocation->x + allocation->width - border_width;
-      if (need_sliders || path_bar->fake_root)
+      if (need_sliders ||
+          (path_bar->fake_root && path_bar->fake_root != root_button))
 	{
 	  child_allocation.x -= (path_bar->spacing + path_bar->slider_width);
 	  up_slider_offset = allocation->width - border_width - path_bar->slider_width;
@@ -568,7 +577,8 @@ gtk_path_bar_size_allocate (GtkWidget     *widget,
   else
     {
       child_allocation.x = allocation->x + border_width;
-      if (need_sliders || path_bar->fake_root)
+      if (need_sliders ||
+          (path_bar->fake_root && path_bar->fake_root != root_button))
 	{
 	  up_slider_offset = border_width;
 	  child_allocation.x += (path_bar->spacing + path_bar->slider_width);
@@ -628,7 +638,8 @@ gtk_path_bar_size_allocate (GtkWidget     *widget,
       gtk_widget_set_child_visible (BUTTON_DATA (list->data)->button, FALSE);
     }
 
-  if (need_sliders || path_bar->fake_root)
+  if (need_sliders ||
+      (path_bar->fake_root && path_bar->fake_root != root_button))
     {
       child_allocation.width = path_bar->slider_width;
       child_allocation.x = up_slider_offset + allocation->x;
@@ -1213,6 +1224,13 @@ set_button_image_get_info_cb (GCancellable *cancellable,
 
   switch (data->button_data->type)
     {
+      case VIRTUAL_ROOT_BUTTON:
+        if (data->path_bar->root_icon != NULL)
+          g_object_unref (data->path_bar->root_icon);
+        else
+          data->path_bar->root_icon = pixbuf;
+        break;
+
       case HOME_BUTTON:
 	if (data->path_bar->home_icon)
 	  g_object_unref (pixbuf);
@@ -1266,6 +1284,38 @@ set_button_image (GtkPathBar *path_bar,
       gtk_image_set_from_pixbuf (GTK_IMAGE (button_data->image), path_bar->root_icon);
       break;
 
+    case VIRTUAL_ROOT_BUTTON:
+      /*
+       * This is the first button, meaning that it's the root of the
+       * file chooser. Give it a special icon.
+       *
+       * We're reusing the root_icon variable, which in this particular
+       * case will actually represent this root. This is reset every time
+       * the icon theme or root URI changes.
+       */
+      if (path_bar->root_icon != NULL)
+        {
+          gtk_image_set_from_pixbuf (GTK_IMAGE (button_data->image),
+                                     path_bar->root_icon);
+        }
+      else
+        {
+          data = g_new0 (struct SetButtonImageData, 1);
+          data->path_bar = path_bar;
+          data->button_data = button_data;
+
+          if (button_data->cancellable)
+            g_cancellable_cancel (button_data->cancellable);
+
+          button_data->cancellable =
+            _gtk_file_system_get_info (path_bar->file_system,
+                                       button_data->file,
+                                       "standard::icon",
+                                       set_button_image_get_info_cb,
+                                       data);
+        }
+      break;
+
     case HOME_BUTTON:
       if (path_bar->home_icon != NULL)
         {
@@ -1309,6 +1359,7 @@ set_button_image (GtkPathBar *path_bar,
 				   set_button_image_get_info_cb,
 				   data);
       break;
+
     default:
       break;
     }
@@ -1404,6 +1455,8 @@ static ButtonType
 find_button_type (GtkPathBar  *path_bar,
 		  GFile       *file)
 {
+  const GSList *l;
+
   if (path_bar->root_file != NULL &&
       g_file_equal (file, path_bar->root_file))
     return ROOT_BUTTON;
@@ -1414,6 +1467,21 @@ find_button_type (GtkPathBar  *path_bar,
       g_file_equal (file, path_bar->desktop_file))
     return DESKTOP_BUTTON;
 
+  for (l = path_bar->root_uris; l != NULL; l = l->next)
+    {
+      char *root_uri = (char *)l->data;
+      char *uri = g_file_get_uri (file);
+      int root_uri_len = strlen (root_uri);
+
+      if (!strcmp (uri, root_uri) ||
+          (root_uri[root_uri_len - 1] == '/' &&
+           root_uri_len == strlen (uri) + 1 &&
+           !strncmp (uri, root_uri, root_uri_len - 1)))
+        {
+          return VIRTUAL_ROOT_BUTTON;
+        }
+    }
+
  return NORMAL_BUTTON;
 }
 
@@ -1470,6 +1538,7 @@ make_directory_button (GtkPathBar  *path_bar,
       break;
     case HOME_BUTTON:
     case DESKTOP_BUTTON:
+    case VIRTUAL_ROOT_BUTTON:
       button_data->image = gtk_image_new ();
       button_data->label = gtk_label_new (NULL);
       label_alignment = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
@@ -1592,6 +1661,18 @@ struct SetFileInfo
   gboolean first_directory;
 };
 
+static gboolean
+is_file_in_root (GtkPathBar *path_bar,
+                 GFile      *file)
+{
+  char *uri = g_file_get_uri (file);
+  gboolean result = path_bar->root_uris == NULL ||
+                    _gtk_file_chooser_uri_has_prefix (uri, path_bar->root_uris);
+  g_free(uri);
+
+  return result;
+}
+
 static void
 gtk_path_bar_set_file_finish (struct SetFileInfo *info,
                               gboolean            result)
@@ -1663,6 +1744,12 @@ gtk_path_bar_get_info_callback (GCancellable *cancellable,
   display_name = g_file_info_get_display_name (info);
   is_hidden = g_file_info_get_is_hidden (info) || g_file_info_get_is_backup (info);
 
+  if (!is_file_in_root (file_info->path_bar, file_info->file))
+    {
+      gtk_path_bar_set_file_finish (file_info, TRUE);
+      return;
+    }
+
   gtk_widget_push_composite_child ();
   button_data = make_directory_button (file_info->path_bar, display_name,
                                        file_info->file,
@@ -1770,6 +1857,29 @@ _gtk_path_bar_set_file_system (GtkPathBar    *path_bar,
   path_bar->root_file = g_file_new_for_path ("/");
 }
 
+void
+_gtk_path_bar_set_root_uris   (GtkPathBar         *path_bar,
+                               const GSList       *root_uris)
+{
+  g_return_if_fail (GTK_IS_PATH_BAR (path_bar));
+
+  path_bar->root_uris = root_uris;
+
+  /*
+   * We don't know if we can even query this URI, so clear the buttons as
+   * a precaution. Otherwise the user could jump to folders outside the root
+   * URI and break things.
+   */
+  gtk_path_bar_clear_buttons (path_bar);
+
+  // Also clear the root icon, as we'll be using this for the root URI button.
+  if (path_bar->root_icon)
+    {
+      g_object_unref (path_bar->root_icon);
+      path_bar->root_icon = NULL;
+    }
+}
+
 /**
  * _gtk_path_bar_up:
  * @path_bar: a #GtkPathBar
@@ -1826,3 +1936,5 @@ _gtk_path_bar_down (GtkPathBar *path_bar)
 
 #define __GTK_PATH_BAR_C__
 #include "gtkaliasdef.c"
+
+// vim: et sw=2 cinoptions=(0,t0,f1s,n-1s,{1s,>2s,^-1s
diff --git a/gtk/gtkpathbar.h b/gtk/gtkpathbar.h
index 2006411..b27ceec 100644
--- a/gtk/gtkpathbar.h
+++ b/gtk/gtkpathbar.h
@@ -45,6 +45,8 @@ struct _GtkPathBar
   GFile *home_file;
   GFile *desktop_file;
 
+  const GSList *root_uris;
+
   GCancellable *get_info_cancellable;
 
   GdkPixbuf *root_icon;
@@ -88,6 +90,8 @@ gboolean _gtk_path_bar_set_file        (GtkPathBar         *path_bar,
 					GFile              *file,
 					gboolean            keep_trail,
 					GError            **error);
+void     _gtk_path_bar_set_root_uris   (GtkPathBar         *path_bar,
+										const GSList       *root_uris);
 void     _gtk_path_bar_up              (GtkPathBar *path_bar);
 void     _gtk_path_bar_down            (GtkPathBar *path_bar);
 
