]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkfilechooserdefault.c
Free volumes not actually put into the shortcut list.
[~andy/gtk] / gtk / gtkfilechooserdefault.c
index 6005ff4dc585bf2d36f55283b65db360222b145e..8987297667fd8402043ffd0074b524b2b8bfa938 100644 (file)
@@ -39,6 +39,7 @@
 #include "gtkframe.h"
 #include "gtkhbox.h"
 #include "gtkhpaned.h"
+#include "gtkiconfactory.h"
 #include "gtkicontheme.h"
 #include "gtkimage.h"
 #include "gtkintl.h"
@@ -52,6 +53,8 @@
 #include "gtksizegroup.h"
 #include "gtkstock.h"
 #include "gtktable.h"
+#include "gtktreednd.h"
+#include "gtktreeprivate.h"
 #include "gtktreeview.h"
 #include "gtktreemodelsort.h"
 #include "gtktreeselection.h"
@@ -100,7 +103,6 @@ struct _GtkFileChooserDefault
   /* The file browsing widgets */
   GtkWidget *browse_widgets;
   GtkWidget *browse_shortcuts_tree_view;
-  GtkWidget *browse_shortcuts_swin;
   GtkWidget *browse_shortcuts_add_button;
   GtkWidget *browse_shortcuts_remove_button;
   GtkWidget *browse_files_tree_view;
@@ -142,6 +144,14 @@ struct _GtkFileChooserDefault
   GtkTreeViewColumn *list_name_column;
   GtkCellRenderer *list_name_renderer;
 
+  guint settings_signal_id;
+  int icon_size;
+
+#if 0
+  GdkDragContext *shortcuts_drag_context;
+  GSource *shortcuts_drag_outside_idle;
+#endif
+
   /* Flags */
 
   guint local_only : 1;
@@ -153,12 +163,17 @@ struct _GtkFileChooserDefault
   guint changing_folder : 1;
   guint shortcuts_current_folder_active : 1;
   guint shortcuts_current_folder_is_volume : 1;
+
+#if 0
+  guint shortcuts_drag_outside : 1;
+#endif
 };
 
 /* Signal IDs */
 enum {
   LOCATION_POPUP,
   UP_FOLDER,
+  DOWN_FOLDER,
   HOME_FOLDER,
   LAST_SIGNAL
 };
@@ -185,15 +200,34 @@ enum {
 
 /* Identifiers for target types */
 enum {
+  GTK_TREE_MODEL_ROW,
   TEXT_URI_LIST
 };
 
-/* Target types for DnD in the shortcuts list */
-static GtkTargetEntry shortcuts_targets[] = {
+/* Target types for dragging from the shortcuts list */
+static GtkTargetEntry shortcuts_source_targets[] = {
+  { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, GTK_TREE_MODEL_ROW }
+};
+
+static const int num_shortcuts_source_targets = (sizeof (shortcuts_source_targets)
+                                                / sizeof (shortcuts_source_targets[0]));
+
+/* Target types for dropping into the shortcuts list */
+static GtkTargetEntry shortcuts_dest_targets[] = {
+  { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, GTK_TREE_MODEL_ROW },
+  { "text/uri-list", 0, TEXT_URI_LIST }
+};
+
+static const int num_shortcuts_dest_targets = (sizeof (shortcuts_dest_targets)
+                                              / sizeof (shortcuts_dest_targets[0]));
+
+/* Target types for DnD from the file list */
+static GtkTargetEntry file_list_source_targets[] = {
   { "text/uri-list", 0, TEXT_URI_LIST }
 };
 
-static const int num_shortcuts_targets = sizeof (shortcuts_targets) / sizeof (shortcuts_targets[0]);
+static const int num_file_list_source_targets = (sizeof (file_list_source_targets)
+                                                / sizeof (file_list_source_targets[0]));
 
 /* Interesting places in the shortcuts bar */
 typedef enum {
@@ -207,9 +241,9 @@ typedef enum {
   SHORTCUTS_CURRENT_FOLDER
 } ShortcutsIndex;
 
-/* Standard icon size */
-/* FIXME: maybe this should correspond to the font size in the tree views... */
-#define ICON_SIZE 20
+/* Icon size for if we can't get it from the theme */
+#define FALLBACK_ICON_SIZE 20
+
 #define PREVIEW_HBOX_SPACING 12
 #define NUM_LINES 40
 #define NUM_CHARS 60
@@ -274,9 +308,11 @@ static void           gtk_file_chooser_default_get_resizable_hints    (GtkFileCh
                                                                       gboolean            *resize_horizontally,
                                                                       gboolean            *resize_vertically);
 static gboolean       gtk_file_chooser_default_should_respond         (GtkFileChooserEmbed *chooser_embed);
+static void           gtk_file_chooser_default_initial_focus          (GtkFileChooserEmbed *chooser_embed);
 
 static void location_popup_handler (GtkFileChooserDefault *impl);
 static void up_folder_handler      (GtkFileChooserDefault *impl);
+static void down_folder_handler    (GtkFileChooserDefault *impl);
 static void home_folder_handler    (GtkFileChooserDefault *impl);
 static void update_appearance      (GtkFileChooserDefault *impl);
 
@@ -343,6 +379,37 @@ static void list_mtime_data_func (GtkTreeViewColumn *tree_column,
 
 static GObjectClass *parent_class;
 
+\f
+
+/* Drag and drop interface declarations */
+
+typedef struct {
+  GtkTreeModelFilter parent;
+
+  GtkFileChooserDefault *impl;
+} ShortcutsModelFilter;
+
+typedef struct {
+  GtkTreeModelFilterClass parent_class;
+} ShortcutsModelFilterClass;
+
+#define SHORTCUTS_MODEL_FILTER_TYPE (shortcuts_model_filter_get_type ())
+#define SHORTCUTS_MODEL_FILTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHORTCUTS_MODEL_FILTER_TYPE, ShortcutsModelFilter))
+
+static void shortcuts_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (ShortcutsModelFilter,
+                        shortcuts_model_filter,
+                        GTK_TYPE_TREE_MODEL_FILTER,
+                        G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
+                                               shortcuts_model_filter_drag_source_iface_init));
+
+static GtkTreeModel *shortcuts_model_filter_new (GtkFileChooserDefault *impl,
+                                                GtkTreeModel          *child_model,
+                                                GtkTreePath           *root);
+
+\f
+
 GType
 _gtk_file_chooser_default_get_type (void)
 {
@@ -426,6 +493,14 @@ gtk_file_chooser_default_class_init (GtkFileChooserDefaultClass *class)
                             NULL, NULL,
                             _gtk_marshal_VOID__VOID,
                             G_TYPE_NONE, 0);
+  signals[DOWN_FOLDER] =
+    _gtk_binding_signal_new ("down-folder",
+                            G_OBJECT_CLASS_TYPE (class),
+                            G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                            G_CALLBACK (down_folder_handler),
+                            NULL, NULL,
+                            _gtk_marshal_VOID__VOID,
+                            G_TYPE_NONE, 0);
   signals[HOME_FOLDER] =
     _gtk_binding_signal_new ("home-folder",
                             G_OBJECT_CLASS_TYPE (class),
@@ -451,6 +526,15 @@ gtk_file_chooser_default_class_init (GtkFileChooserDefaultClass *class)
                                "up-folder",
                                0);
 
+  gtk_binding_entry_add_signal (binding_set,
+                               GDK_Down, GDK_MOD1_MASK,
+                               "down-folder",
+                               0);
+  gtk_binding_entry_add_signal (binding_set,
+                               GDK_KP_Down, GDK_MOD1_MASK,
+                               "down-folder",
+                               0);
+
   gtk_binding_entry_add_signal (binding_set,
                                GDK_Home, GDK_MOD1_MASK,
                                "home-folder",
@@ -496,6 +580,7 @@ gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface *iface)
   iface->get_default_size = gtk_file_chooser_default_get_default_size;
   iface->get_resizable_hints = gtk_file_chooser_default_get_resizable_hints;
   iface->should_respond = gtk_file_chooser_default_should_respond;
+  iface->initial_focus = gtk_file_chooser_default_initial_focus;
 }
 static void
 gtk_file_chooser_default_init (GtkFileChooserDefault *impl)
@@ -505,6 +590,7 @@ gtk_file_chooser_default_init (GtkFileChooserDefault *impl)
   impl->use_preview_label = TRUE;
   impl->select_multiple = FALSE;
   impl->show_hidden = FALSE;
+  impl->icon_size = FALLBACK_ICON_SIZE;
 
   gtk_widget_set_redraw_on_allocate (GTK_WIDGET (impl), TRUE);
   gtk_box_set_spacing (GTK_BOX (impl), 12);
@@ -571,7 +657,7 @@ error_message_with_parent (GtkWindow  *parent,
   dialog = gtk_message_dialog_new (parent,
                                   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
                                   GTK_MESSAGE_ERROR,
-                                  GTK_BUTTONS_CLOSE,
+                                  GTK_BUTTONS_OK,
                                   "%s",
                                   msg);
   gtk_dialog_run (GTK_DIALOG (dialog));
@@ -600,14 +686,19 @@ error_dialog (GtkFileChooserDefault *impl,
              const GtkFilePath     *path,
              GError                *error)
 {
-  char *text;
+  g_return_if_fail (path != NULL);
 
-  text = g_strdup_printf (msg,
-                         gtk_file_path_get_string (path),
-                         error->message);
-  error_message (impl, text);
-  g_free (text);
-  g_error_free (error);
+  if (error)
+    {
+      char *uri = gtk_file_system_path_to_uri (impl->file_system, path);
+      char *text = g_strdup_printf (msg,
+                                   uri,
+                                   error->message);
+      error_message (impl, text);
+      g_free (text);
+      g_free (uri);
+      g_error_free (error);
+    }
 }
 
 /* Displays an error message about not being able to get information for a file.
@@ -737,42 +828,128 @@ set_preview_widget (GtkFileChooserDefault *impl,
   update_preview_widget_visibility (impl);
 }
 
-/* Clears the selection in the shortcuts tree */
+/* Re-reads all the icons for the shortcuts, used when the theme changes */
+static void
+shortcuts_reload_icons (GtkFileChooserDefault *impl)
+{
+  GtkTreeIter iter;
+  int i;
+  int bookmarks_separator_idx;
+  int current_folder_separator_idx;
+  int volumes_idx;
+
+  if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
+    return;
+
+  bookmarks_separator_idx = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR);
+  current_folder_separator_idx = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR);
+  volumes_idx = shortcuts_get_index (impl, SHORTCUTS_VOLUMES);
+
+  i = 0;
+
+  do {
+    gpointer data;
+    gboolean pixbuf_visible;
+    GdkPixbuf *pixbuf;
+
+    gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
+                       SHORTCUTS_COL_PATH, &data,
+                       SHORTCUTS_COL_PIXBUF_VISIBLE, &pixbuf_visible,
+                       -1);
+
+    if (!pixbuf_visible || !data)
+      goto next_iter;
+
+    if (i >= volumes_idx && i < volumes_idx + impl->num_volumes)
+      {
+       GtkFileSystemVolume *volume;
+
+       volume = data;
+       pixbuf = gtk_file_system_volume_render_icon (impl->file_system, volume, GTK_WIDGET (impl),
+                                                    impl->icon_size, NULL);
+      }
+    else
+      {
+       const GtkFilePath *path;
+
+       path = data;
+       pixbuf = gtk_file_system_render_icon (impl->file_system, path, GTK_WIDGET (impl),
+                                             impl->icon_size, NULL);
+      }
+
+    gtk_list_store_set (impl->shortcuts_model, &iter,
+                       SHORTCUTS_COL_PIXBUF, pixbuf,
+                       -1);
+
+  next_iter:
+    i++;
+  } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model),&iter));
+}
+
+/* If a shortcut corresponds to the current folder, selects it */
 static void
-shortcuts_unselect_all (GtkFileChooserDefault *impl)
+shortcuts_find_current_folder (GtkFileChooserDefault *impl)
 {
   GtkTreeSelection *selection;
+  int pos;
+  GtkTreePath *path;
 
   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
-  gtk_tree_selection_unselect_all (selection);
+
+  pos = shortcut_find_position (impl, impl->current_folder);
+  if (pos == -1)
+    {
+      gtk_tree_selection_unselect_all (selection);
+      return;
+    }
+
+  path = gtk_tree_path_new_from_indices (pos, -1);
+  gtk_tree_selection_select_path (selection, path);
+  gtk_tree_path_free (path);
+}
+
+/* Returns whether a path is a folder */
+static gboolean
+check_is_folder (GtkFileSystem *file_system, const GtkFilePath *path, GError **error)
+{
+  GtkFileFolder *folder;
+
+  folder = gtk_file_system_get_folder (file_system, path,
+                                      GTK_FILE_INFO_DISPLAY_NAME,
+                                      error);
+  if (!folder)
+    return FALSE;
+
+  g_object_unref (folder);
+  return TRUE;
 }
 
 /* Convenience function to get the display name and icon info for a path */
 static GtkFileInfo *
-get_file_info (GtkFileSystem *file_system, const GtkFilePath *path, GError **error)
+get_file_info (GtkFileSystem *file_system, const GtkFilePath *path, gboolean name_only, GError **error)
 {
   GtkFilePath *parent_path;
   GtkFileFolder *parent_folder;
   GtkFileInfo *info;
 
+  info = NULL;
+
   if (!gtk_file_system_get_parent (file_system, path, &parent_path, error))
     return NULL;
 
-  parent_folder = gtk_file_system_get_folder (file_system, parent_path,
+  parent_folder = gtk_file_system_get_folder (file_system, parent_path ? parent_path : path,
                                              GTK_FILE_INFO_DISPLAY_NAME
-#if 0
-                                             | GTK_FILE_INFO_ICON
-#endif
-                                             | GTK_FILE_INFO_IS_FOLDER,
+                                             | (name_only ? 0 : GTK_FILE_INFO_IS_FOLDER),
                                              error);
-  gtk_file_path_free (parent_path);
-
   if (!parent_folder)
-    return NULL;
+    goto out;
 
-  info = gtk_file_folder_get_info (parent_folder, path, error);
+  info = gtk_file_folder_get_info (parent_folder, parent_path ? path : NULL, error);
   g_object_unref (parent_folder);
 
+ out:
+
+  gtk_file_path_free (parent_path);
   return info;
 }
 
@@ -798,30 +975,30 @@ shortcuts_insert_path (GtkFileChooserDefault *impl,
     {
       data = volume;
       label_copy = gtk_file_system_volume_get_display_name (impl->file_system, volume);
-      pixbuf = gtk_file_system_volume_render_icon (impl->file_system,
-                                                  volume,
-                                                  GTK_WIDGET (impl),
-                                                  ICON_SIZE,
-                                                  NULL);
+      pixbuf = gtk_file_system_volume_render_icon (impl->file_system, volume, GTK_WIDGET (impl),
+                                                  impl->icon_size, NULL);
     }
   else
     {
-      GtkFileInfo *info;
-
-      info = get_file_info (impl->file_system, path, error);
-      if (!info)
+      if (!check_is_folder (impl->file_system, path, error))
        return FALSE;
 
-      data = gtk_file_path_copy (path);
-
       if (label)
        label_copy = g_strdup (label);
       else
-       label_copy = g_strdup (gtk_file_info_get_display_name (info));
+       {
+         GtkFileInfo *info = get_file_info (impl->file_system, path, TRUE, error);
+
+         if (!info)
+           return FALSE;
 
-      pixbuf = gtk_file_system_render_icon (impl->file_system, path, GTK_WIDGET (impl), ICON_SIZE, NULL);
+         label_copy = g_strdup (gtk_file_info_get_display_name (info));
+         gtk_file_info_free (info);
+       }
 
-      gtk_file_info_free (info);
+      data = gtk_file_path_copy (path);
+      pixbuf = gtk_file_system_render_icon (impl->file_system, path, GTK_WIDGET (impl),
+                                           impl->icon_size, NULL);
     }
 
   if (pos == -1)
@@ -904,6 +1081,10 @@ shortcuts_append_paths (GtkFileChooserDefault *impl,
       path = paths->data;
       error = NULL;
 
+      if (impl->local_only &&
+         !gtk_file_system_path_is_local (impl->file_system, path))
+       continue;
+
       /* NULL GError, but we don't really want to show error boxes here */
       if (shortcuts_insert_path (impl, start_row + num_inserted, FALSE, NULL, path, NULL, TRUE, NULL))
        num_inserted++;
@@ -1017,6 +1198,10 @@ shortcuts_add_volumes (GtkFileChooserDefault *impl)
   int start_row;
   GSList *list, *l;
   int n;
+  gboolean old_changing_folders;
+
+  old_changing_folders = impl->changing_folder;
+  impl->changing_folder = TRUE;
 
   start_row = shortcuts_get_index (impl, SHORTCUTS_VOLUMES);
   shortcuts_remove_rows (impl, start_row, impl->num_volumes, volume_remove_cb);
@@ -1031,8 +1216,24 @@ shortcuts_add_volumes (GtkFileChooserDefault *impl)
       GtkFileSystemVolume *volume;
 
       volume = l->data;
-      shortcuts_insert_path (impl, start_row + n, TRUE, volume, NULL, NULL, FALSE, NULL);
-      n++;
+
+      if (impl->local_only)
+       {
+         GtkFilePath *base_path = gtk_file_system_volume_get_base_path (impl->file_system, volume);
+         gboolean is_local = gtk_file_system_path_is_local (impl->file_system, base_path);
+         gtk_file_path_free (base_path);
+
+         if (!is_local)
+           {
+             gtk_file_system_volume_free (impl->file_system, volume);
+             continue;
+           }
+       }
+
+      if (shortcuts_insert_path (impl, start_row + n, TRUE, volume, NULL, NULL, FALSE, NULL))
+       n++;
+      else
+       gtk_file_system_volume_free (impl->file_system, volume);
     }
 
   impl->num_volumes = n;
@@ -1040,6 +1241,8 @@ shortcuts_add_volumes (GtkFileChooserDefault *impl)
 
   if (impl->shortcuts_filter_model)
     gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model));
+
+  impl->changing_folder = old_changing_folders;
 }
 
 /* Used from shortcuts_remove_rows() */
@@ -1076,6 +1279,10 @@ static void
 shortcuts_add_bookmarks (GtkFileChooserDefault *impl)
 {
   GSList *bookmarks;
+  gboolean old_changing_folders;
+
+  old_changing_folders = impl->changing_folder;
+  impl->changing_folder = TRUE;
 
   if (impl->num_bookmarks > 0)
     {
@@ -1096,6 +1303,8 @@ shortcuts_add_bookmarks (GtkFileChooserDefault *impl)
     }
   if (impl->shortcuts_filter_model)
     gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model));
+
+  impl->changing_folder = old_changing_folders;
 }
 
 /* Appends a separator and a row to the shortcuts list for the current folder */
@@ -1225,7 +1434,10 @@ shortcuts_model_create (GtkFileChooserDefault *impl)
       shortcuts_add_bookmarks (impl);
     }
 
-  impl->shortcuts_filter_model = gtk_tree_model_filter_new (GTK_TREE_MODEL (impl->shortcuts_model), NULL);
+  impl->shortcuts_filter_model = shortcuts_model_filter_new (impl,
+                                                            GTK_TREE_MODEL (impl->shortcuts_model),
+                                                            NULL);
+
   gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model),
                                          shortcuts_filter_cb,
                                          impl,
@@ -1413,39 +1625,35 @@ shortcut_find_position (GtkFileChooserDefault *impl,
 }
 
 /* Tries to add a bookmark from a path name */
-static void
+static gboolean
 shortcuts_add_bookmark_from_path (GtkFileChooserDefault *impl,
-                                 const GtkFilePath     *path)
+                                 const GtkFilePath     *path,
+                                 int                    pos)
 {
-  GtkFileInfo *info;
   GError *error;
 
   if (shortcut_find_position (impl, path) != -1)
-    return;
+    return FALSE;
 
+  /* FIXME: this check really belongs in gtk_file_system_insert_bookmark.  */
   error = NULL;
-  info = get_file_info (impl->file_system, path, &error);
-
-  if (!info)
-    error_getting_info_dialog (impl, path, error);
-  else if (!gtk_file_info_get_is_folder (info))
+  if (!check_is_folder (impl->file_system, path, &error))
     {
-      char *msg;
-      char *uri;
-
-      uri = gtk_file_system_path_to_uri (impl->file_system, path);
-      msg = g_strdup_printf (_("Could not add bookmark for %s because it is not a folder."),
-                            uri);
-      error_message (impl, msg);
-      g_free (uri);
-      g_free (msg);
+      error_dialog (impl,
+                   _("Could not add bookmark for %s because it is not a folder."),
+                   path,
+                   error);
+      return FALSE;
     }
-  else
+
+  error = NULL;
+  if (!gtk_file_system_insert_bookmark (impl->file_system, path, pos, &error))
     {
-      error = NULL;
-      if (!gtk_file_system_insert_bookmark (impl->file_system, path, -1, &error))
-       error_could_not_add_bookmark_dialog (impl, path, error);
+      error_could_not_add_bookmark_dialog (impl, path, error);
+      return FALSE;
     }
+
+  return TRUE;
 }
 
 static void
@@ -1460,12 +1668,12 @@ add_bookmark_foreach_cb (GtkTreeModel *model,
   const GtkFilePath *file_path;
 
   impl = (GtkFileChooserDefault *) data;
-  
+
   fs_model = impl->browse_files_model;
   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, iter);
 
   file_path = _gtk_file_system_model_get_path (GTK_FILE_SYSTEM_MODEL (fs_model), &child_iter);
-  shortcuts_add_bookmark_from_path (impl, file_path);
+  shortcuts_add_bookmark_from_path (impl, file_path, -1);
 }
 
 /* Callback used when the "Add bookmark" button is clicked */
@@ -1478,7 +1686,7 @@ add_bookmark_button_clicked_cb (GtkButton *button,
   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
 
   if (gtk_tree_selection_count_selected_rows (selection) == 0)
-    shortcuts_add_bookmark_from_path (impl, impl->current_folder);
+    shortcuts_add_bookmark_from_path (impl, impl->current_folder, -1);
   else
     gtk_tree_selection_selected_foreach (selection,
                                         add_bookmark_foreach_cb,
@@ -1651,6 +1859,378 @@ bookmarks_check_remove_sensitivity (GtkFileChooserDefault *impl)
   gtk_widget_set_sensitive (impl->browse_shortcuts_remove_button, removable);
 }
 
+/* GtkWidget::drag-begin handler for the shortcuts list. */
+static void
+shortcuts_drag_begin_cb (GtkWidget             *widget,
+                        GdkDragContext        *context,
+                        GtkFileChooserDefault *impl)
+{
+#if 0
+  impl->shortcuts_drag_context = g_object_ref (context);
+#endif
+}
+
+#if 0
+/* Removes the idle handler for outside drags */
+static void
+shortcuts_cancel_drag_outside_idle (GtkFileChooserDefault *impl)
+{
+  if (!impl->shortcuts_drag_outside_idle)
+    return;
+
+  g_source_destroy (impl->shortcuts_drag_outside_idle);
+  impl->shortcuts_drag_outside_idle = NULL;
+}
+#endif
+
+/* GtkWidget::drag-end handler for the shortcuts list. */
+static void
+shortcuts_drag_end_cb (GtkWidget             *widget,
+                      GdkDragContext        *context,
+                      GtkFileChooserDefault *impl)
+{
+#if 0
+  g_object_unref (impl->shortcuts_drag_context);
+
+  shortcuts_cancel_drag_outside_idle (impl);
+
+  if (!impl->shortcuts_drag_outside)
+    return;
+
+  gtk_button_clicked (GTK_BUTTON (impl->browse_shortcuts_remove_button));
+
+  impl->shortcuts_drag_outside = FALSE;
+#endif
+}
+
+/* GtkWidget::drag-data-delete handler for the shortcuts list. */
+static void
+shortcuts_drag_data_delete_cb (GtkWidget             *widget,
+                              GdkDragContext        *context,
+                              GtkFileChooserDefault *impl)
+{
+  g_signal_stop_emission_by_name (widget, "drag-data-delete");
+}
+
+#if 0
+/* Creates a suitable drag cursor to indicate that the selected bookmark will be
+ * deleted or not.
+ */
+static void
+shortcuts_drag_set_delete_cursor (GtkFileChooserDefault *impl,
+                                 gboolean               delete)
+{
+  GtkTreeView *tree_view;
+  GtkTreeSelection *selection;
+  GtkTreeIter iter, child_iter;
+  GtkTreePath *path;
+  GdkPixmap *row_pixmap;
+  GdkBitmap *mask;
+  int row_pixmap_y;
+  int cell_y;
+
+  tree_view = GTK_TREE_VIEW (impl->browse_shortcuts_tree_view);
+
+  /* Find the selected path and get its drag pixmap */
+
+  selection = gtk_tree_view_get_selection (tree_view);
+  if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
+    g_assert_not_reached ();
+
+  gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model),
+                                                   &child_iter,
+                                                   &iter);
+
+  path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &child_iter);
+
+  row_pixmap = gtk_tree_view_create_row_drag_icon (tree_view, path);
+  gtk_tree_path_free (path);
+
+  mask = NULL;
+  row_pixmap_y = 0;
+
+  if (delete)
+    {
+      GdkPixbuf *pixbuf;
+
+      pixbuf = gtk_widget_render_icon (impl->browse_shortcuts_tree_view,
+                                      GTK_STOCK_DELETE,
+                                      GTK_ICON_SIZE_DND,
+                                      NULL);
+      if (pixbuf)
+       {
+         GdkPixmap *composite;
+         int row_pixmap_width, row_pixmap_height;
+         int pixbuf_width, pixbuf_height;
+         int composite_width, composite_height;
+         int pixbuf_x, pixbuf_y;
+         GdkGC *gc, *mask_gc;
+         GdkColor color;
+         GdkBitmap *pixbuf_mask;
+
+         /* Create pixmap and mask for composite image */
+
+         gdk_drawable_get_size (row_pixmap, &row_pixmap_width, &row_pixmap_height);
+         pixbuf_width = gdk_pixbuf_get_width (pixbuf);
+         pixbuf_height = gdk_pixbuf_get_height (pixbuf);
+
+         composite_width = MAX (row_pixmap_width, pixbuf_width);
+         composite_height = MAX (row_pixmap_height, pixbuf_height);
+
+         row_pixmap_y = (composite_height - row_pixmap_height) / 2;
+
+         if (gtk_widget_get_direction (impl->browse_shortcuts_tree_view) == GTK_TEXT_DIR_RTL)
+           pixbuf_x = 0;
+         else
+           pixbuf_x = composite_width - pixbuf_width;
+
+         pixbuf_y = (composite_height - pixbuf_height) / 2;
+
+         composite = gdk_pixmap_new (row_pixmap, composite_width, composite_height, -1);
+         gc = gdk_gc_new (composite);
+
+         mask = gdk_pixmap_new (row_pixmap, composite_width, composite_height, 1);
+         mask_gc = gdk_gc_new (mask);
+         color.pixel = 0;
+         gdk_gc_set_foreground (mask_gc, &color);
+         gdk_draw_rectangle (mask, mask_gc, TRUE, 0, 0, composite_width, composite_height);
+
+         color.red = 0xffff;
+         color.green = 0xffff;
+         color.blue = 0xffff;
+         gdk_gc_set_rgb_fg_color (gc, &color);
+         gdk_draw_rectangle (composite, gc, TRUE, 0, 0, composite_width, composite_height);
+
+         /* Composite the row pixmap and the pixbuf */
+
+         gdk_pixbuf_render_pixmap_and_mask_for_colormap
+           (pixbuf,
+            gtk_widget_get_colormap (impl->browse_shortcuts_tree_view),
+            NULL, &pixbuf_mask, 128);
+         gdk_draw_drawable (mask, mask_gc, pixbuf_mask,
+                            0, 0,
+                            pixbuf_x, pixbuf_y,
+                            pixbuf_width, pixbuf_height);
+         g_object_unref (pixbuf_mask);
+
+         gdk_draw_drawable (composite, gc, row_pixmap,
+                            0, 0,
+                            0, row_pixmap_y,
+                            row_pixmap_width, row_pixmap_height);
+         color.pixel = 1;
+         gdk_gc_set_foreground (mask_gc, &color);
+         gdk_draw_rectangle (mask, mask_gc, TRUE, 0, row_pixmap_y, row_pixmap_width, row_pixmap_height);
+
+         gdk_draw_pixbuf (composite, gc, pixbuf,
+                          0, 0,
+                          pixbuf_x, pixbuf_y,
+                          pixbuf_width, pixbuf_height,
+                          GDK_RGB_DITHER_MAX,
+                          0, 0);
+
+         g_object_unref (pixbuf);
+         g_object_unref (row_pixmap);
+
+         row_pixmap = composite;
+       }
+    }
+
+  /* The hotspot offsets here are copied from gtk_tree_view_drag_begin(), ugh */
+
+  gtk_tree_view_get_path_at_pos (tree_view,
+                                 tree_view->priv->press_start_x,
+                                 tree_view->priv->press_start_y,
+                                 NULL,
+                                 NULL,
+                                 NULL,
+                                 &cell_y);
+
+  gtk_drag_set_icon_pixmap (impl->shortcuts_drag_context,
+                           gdk_drawable_get_colormap (row_pixmap),
+                           row_pixmap,
+                           mask,
+                           tree_view->priv->press_start_x + 1,
+                           row_pixmap_y + cell_y + 1);
+
+  g_object_unref (row_pixmap);
+  if (mask)
+    g_object_unref (mask);
+}
+
+/* We set the delete cursor and the shortcuts_drag_outside flag in an idle
+ * handler so that we can tell apart the drag_leave event that comes right
+ * before a drag_drop, from a normal drag_leave.  We don't want to set the
+ * cursor nor the flag in the latter case.
+ */
+static gboolean
+shortcuts_drag_outside_idle_cb (GtkFileChooserDefault *impl)
+{
+  shortcuts_drag_set_delete_cursor (impl, TRUE);
+  impl->shortcuts_drag_outside = TRUE;
+
+  shortcuts_cancel_drag_outside_idle (impl);
+  return FALSE;
+}
+#endif
+
+/* GtkWidget::drag-leave handler for the shortcuts list.  We unhighlight the
+ * drop position.
+ */
+static void
+shortcuts_drag_leave_cb (GtkWidget             *widget,
+                        GdkDragContext        *context,
+                        guint                  time_,
+                        GtkFileChooserDefault *impl)
+{
+#if 0
+  if (gtk_drag_get_source_widget (context) == widget && !impl->shortcuts_drag_outside_idle)
+    {
+      impl->shortcuts_drag_outside_idle = g_idle_source_new ();
+      g_source_set_closure (impl->shortcuts_drag_outside_idle,
+                           g_cclosure_new_object (G_CALLBACK (shortcuts_drag_outside_idle_cb),
+                                                  G_OBJECT (impl)));
+      g_source_attach (impl->shortcuts_drag_outside_idle, NULL);
+    }
+#endif
+
+  gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
+                                  NULL,
+                                  GTK_TREE_VIEW_DROP_BEFORE);
+
+  g_signal_stop_emission_by_name (widget, "drag-leave");
+}
+
+/* Computes the appropriate row and position for dropping */
+static void
+shortcuts_compute_drop_position (GtkFileChooserDefault   *impl,
+                                int                      x,
+                                int                      y,
+                                GtkTreePath            **path,
+                                GtkTreeViewDropPosition *pos)
+{
+  GtkTreeView *tree_view;
+  GtkTreeViewColumn *column;
+  int cell_y;
+  GdkRectangle cell;
+  int row;
+  int bookmarks_index;
+
+  tree_view = GTK_TREE_VIEW (impl->browse_shortcuts_tree_view);
+
+  bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
+
+  if (!gtk_tree_view_get_path_at_pos (tree_view,
+                                      x,
+                                     y - TREE_VIEW_HEADER_HEIGHT (tree_view),
+                                      path,
+                                      &column,
+                                      NULL,
+                                      &cell_y))
+    {
+      row = bookmarks_index + impl->num_bookmarks - 1;
+      *path = gtk_tree_path_new_from_indices (row, -1);
+      *pos = GTK_TREE_VIEW_DROP_AFTER;
+      return;
+    }
+
+  row = *gtk_tree_path_get_indices (*path);
+  gtk_tree_view_get_background_area (tree_view, *path, column, &cell);
+  gtk_tree_path_free (*path);
+
+  if (row < bookmarks_index)
+    {
+      row = bookmarks_index;
+      *pos = GTK_TREE_VIEW_DROP_BEFORE;
+    }
+  else if (row > bookmarks_index + impl->num_bookmarks - 1)
+    {
+      row = bookmarks_index + impl->num_bookmarks - 1;
+      *pos = GTK_TREE_VIEW_DROP_AFTER;
+    }
+  else
+    {
+      if (cell_y < cell.height / 2)
+       *pos = GTK_TREE_VIEW_DROP_BEFORE;
+      else
+       *pos = GTK_TREE_VIEW_DROP_AFTER;
+    }
+
+  *path = gtk_tree_path_new_from_indices (row, -1);
+}
+
+/* GtkWidget::drag-motion handler for the shortcuts list.  We basically
+ * implement the destination side of DnD by hand, due to limitations in
+ * GtkTreeView's DnD API.
+ */
+static gboolean
+shortcuts_drag_motion_cb (GtkWidget             *widget,
+                         GdkDragContext        *context,
+                         gint                   x,
+                         gint                   y,
+                         guint                  time_,
+                         GtkFileChooserDefault *impl)
+{
+  GtkTreePath *path;
+  GtkTreeViewDropPosition pos;
+  GdkDragAction action;
+
+#if 0
+  if (gtk_drag_get_source_widget (context) == widget)
+    {
+      shortcuts_cancel_drag_outside_idle (impl);
+
+      if (impl->shortcuts_drag_outside)
+       {
+         shortcuts_drag_set_delete_cursor (impl, FALSE);
+         impl->shortcuts_drag_outside = FALSE;
+       }
+    }
+#endif
+
+  if (context->suggested_action == GDK_ACTION_COPY || (context->actions & GDK_ACTION_COPY) != 0)
+    action = GDK_ACTION_COPY;
+  else if (context->suggested_action == GDK_ACTION_MOVE || (context->actions & GDK_ACTION_MOVE) != 0)
+    action = GDK_ACTION_MOVE;
+  else
+    {
+      action = 0;
+      goto out;
+    }
+
+  shortcuts_compute_drop_position (impl, x, y, &path, &pos);
+  gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), path, pos);
+  gtk_tree_path_free (path);
+
+ out:
+
+  g_signal_stop_emission_by_name (widget, "drag-motion");
+
+  if (action != 0)
+    {
+      gdk_drag_status (context, action, time_);
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+
+/* GtkWidget::drag-drop handler for the shortcuts list. */
+static gboolean
+shortcuts_drag_drop_cb (GtkWidget             *widget,
+                       GdkDragContext        *context,
+                       gint                   x,
+                       gint                   y,
+                       guint                  time_,
+                       GtkFileChooserDefault *impl)
+{
+#if 0
+  shortcuts_cancel_drag_outside_idle (impl);
+#endif
+
+  g_signal_stop_emission_by_name (widget, "drag-drop");
+  return TRUE;
+}
+
 /* Converts raw selection data from text/uri-list to a list of strings */
 static GSList *
 split_uris (const char *data)
@@ -1658,24 +2238,119 @@ split_uris (const char *data)
   GSList *uris;
   const char *p, *start;
 
-  uris = NULL;
+  uris = NULL;
+
+  start = data;
+
+  for (p = start; *p != 0; p++)
+    if (*p == '\r' && *(p + 1) == '\n')
+      {
+       char *name;
+
+       name = g_strndup (start, p - start);
+       uris = g_slist_prepend (uris, name);
+
+       start = p + 2;
+       p = start;
+      }
+
+  uris = g_slist_reverse (uris);
+  return uris;
+}
+
+/* Parses a "text/uri-list" string and inserts its URIs as bookmarks */
+static void
+shortcuts_drop_uris (GtkFileChooserDefault *impl,
+                    const char            *data,
+                    int                    position)
+{
+  GSList *uris, *l;
+
+  uris = split_uris (data);
+
+  for (l = uris; l; l = l->next)
+    {
+      char *uri;
+      GtkFilePath *path;
+
+      uri = l->data;
+      path = gtk_file_system_uri_to_path (impl->file_system, uri);
+
+      if (path)
+       {
+         if (shortcuts_add_bookmark_from_path (impl, path, position))
+           position++;
+
+         gtk_file_path_free (path);
+       }
+      else
+       {
+         char *msg;
+
+         msg = g_strdup_printf (_("Could not add a bookmark for %s because it is an invalid path name."),
+                                uri);
+         error_message (impl, msg);
+         g_free (msg);
+       }
+
+      g_free (uri);
+    }
+
+  g_slist_free (uris);
+}
+
+/* Reorders the selected bookmark to the specified position */
+static void
+shortcuts_reorder (GtkFileChooserDefault *impl,
+                  int                    new_position)
+{
+  GtkTreeSelection *selection;
+  GtkTreeIter iter, child_iter;
+  GtkTreePath *path;
+  int old_position;
+  int bookmarks_index;
+  const GtkFilePath *file_path;
+  GtkFilePath *file_path_copy;
+  GError *error;
+
+  /* Get the selected path */
+
+  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
+  if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
+    g_assert_not_reached ();
+
+  gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model),
+                                                   &child_iter,
+                                                   &iter);
+
+  path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &child_iter);
+  old_position = *gtk_tree_path_get_indices (path);
+  gtk_tree_path_free (path);
 
-  start = data;
+  bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
+  old_position -= bookmarks_index;
+  g_assert (old_position >= 0 && old_position < impl->num_bookmarks);
 
-  for (p = start; *p != 0; p++)
-    if (*p == '\r' && *(p + 1) == '\n')
-      {
-       char *name;
+  gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &child_iter,
+                     SHORTCUTS_COL_PATH, &file_path,
+                     -1);
+  file_path_copy = gtk_file_path_copy (file_path); /* removal below will free file_path, so we need a copy */
 
-       name = g_strndup (start, p - start);
-       uris = g_slist_prepend (uris, name);
+  /* Remove the path from the old position and insert it in the new one */
 
-       start = p + 2;
-       p = start;
-      }
+  if (new_position > old_position)
+    new_position--;
 
-  uris = g_slist_reverse (uris);
-  return uris;
+  if (old_position == new_position)
+    return;
+
+  error = NULL;
+  if (gtk_file_system_remove_bookmark (impl->file_system, file_path_copy, &error))
+    shortcuts_add_bookmark_from_path (impl, file_path_copy, new_position);
+  else
+    error_could_not_add_bookmark_dialog (impl, file_path_copy, error);
+
+  gtk_file_path_free (file_path_copy);
 }
 
 /* Callback used when we get the drag data for the bookmarks list.  We add the
@@ -1692,39 +2367,33 @@ shortcuts_drag_data_received_cb (GtkWidget          *widget,
                                 gpointer            data)
 {
   GtkFileChooserDefault *impl;
-  GSList *uris, *l;
+  GtkTreePath *tree_path;
+  GtkTreeViewDropPosition tree_pos;
+  int position;
+  int bookmarks_index;
 
   impl = GTK_FILE_CHOOSER_DEFAULT (data);
 
-  uris = split_uris (selection_data->data);
+  /* Compute position */
 
-  for (l = uris; l; l = l->next)
-    {
-      char *uri;
-      GtkFilePath *path;
+  bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
 
-      uri = l->data;
-      path = gtk_file_system_uri_to_path (impl->file_system, uri);
+  shortcuts_compute_drop_position (impl, x, y, &tree_path, &tree_pos);
+  position = *gtk_tree_path_get_indices (tree_path);
+  gtk_tree_path_free (tree_path);
 
-      if (path)
-       {
-         shortcuts_add_bookmark_from_path (impl, path);
-         gtk_file_path_free (path);
-       }
-      else
-       {
-         char *msg;
+  if (tree_pos == GTK_TREE_VIEW_DROP_AFTER)
+    position++;
 
-         msg = g_strdup_printf (_("Could not add a bookmark for %s because it is an invalid path name."),
-                                uri);
-         error_message (impl, msg);
-         g_free (msg);
-       }
+  g_assert (position >= bookmarks_index);
+  position -= bookmarks_index;
 
-      g_free (uri);
-    }
+  if (selection_data->target == gdk_atom_intern ("text/uri-list", FALSE))
+    shortcuts_drop_uris (impl, selection_data->data, position);
+  else if (selection_data->target == gdk_atom_intern ("GTK_TREE_MODEL_ROW", FALSE))
+    shortcuts_reorder (impl, position);
 
-  g_slist_free (uris);
+  g_signal_stop_emission_by_name (widget, "drag-data-received");
 }
 
 /* Callback used when the selection in the shortcuts tree changes */
@@ -1739,18 +2408,19 @@ shortcuts_selection_changed_cb (GtkTreeSelection      *selection,
 static GtkWidget *
 shortcuts_list_create (GtkFileChooserDefault *impl)
 {
+  GtkWidget *swin;
   GtkTreeSelection *selection;
   GtkTreeViewColumn *column;
   GtkCellRenderer *renderer;
 
   /* Scrolled window */
 
-  impl->browse_shortcuts_swin = gtk_scrolled_window_new (NULL, NULL);
-  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (impl->browse_shortcuts_swin),
+  swin = gtk_scrolled_window_new (NULL, NULL);
+  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin),
                                  GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
-  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (impl->browse_shortcuts_swin),
+  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swin),
                                       GTK_SHADOW_IN);
-  gtk_widget_show (impl->browse_shortcuts_swin);
+  gtk_widget_show (swin);
 
   /* Tree */
 
@@ -1759,11 +2429,17 @@ shortcuts_list_create (GtkFileChooserDefault *impl)
 
   gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), impl->shortcuts_filter_model);
 
+  gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view),
+                                         GDK_BUTTON1_MASK,
+                                         shortcuts_source_targets,
+                                         num_shortcuts_source_targets,
+                                         GDK_ACTION_MOVE);
+
   gtk_drag_dest_set (impl->browse_shortcuts_tree_view,
                     GTK_DEST_DEFAULT_ALL,
-                    shortcuts_targets,
-                    num_shortcuts_targets,
-                    GDK_ACTION_COPY);
+                    shortcuts_dest_targets,
+                    num_shortcuts_dest_targets,
+                    GDK_ACTION_COPY | GDK_ACTION_MOVE);
 
   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view));
   gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
@@ -1777,10 +2453,23 @@ shortcuts_list_create (GtkFileChooserDefault *impl)
   g_signal_connect (impl->browse_shortcuts_tree_view, "row-activated",
                    G_CALLBACK (shortcuts_row_activated_cb), impl);
 
+  g_signal_connect (impl->browse_shortcuts_tree_view, "drag-begin",
+                   G_CALLBACK (shortcuts_drag_begin_cb), impl);
+  g_signal_connect (impl->browse_shortcuts_tree_view, "drag-end",
+                   G_CALLBACK (shortcuts_drag_end_cb), impl);
+  g_signal_connect (impl->browse_shortcuts_tree_view, "drag-data-delete",
+                   G_CALLBACK (shortcuts_drag_data_delete_cb), impl);
+
+  g_signal_connect (impl->browse_shortcuts_tree_view, "drag-leave",
+                   G_CALLBACK (shortcuts_drag_leave_cb), impl);
+  g_signal_connect (impl->browse_shortcuts_tree_view, "drag-motion",
+                   G_CALLBACK (shortcuts_drag_motion_cb), impl);
+  g_signal_connect (impl->browse_shortcuts_tree_view, "drag-drop",
+                   G_CALLBACK (shortcuts_drag_drop_cb), impl);
   g_signal_connect (impl->browse_shortcuts_tree_view, "drag-data-received",
                    G_CALLBACK (shortcuts_drag_data_received_cb), impl);
 
-  gtk_container_add (GTK_CONTAINER (impl->browse_shortcuts_swin), impl->browse_shortcuts_tree_view);
+  gtk_container_add (GTK_CONTAINER (swin), impl->browse_shortcuts_tree_view);
   gtk_widget_show (impl->browse_shortcuts_tree_view);
 
   /* Column */
@@ -1803,7 +2492,7 @@ shortcuts_list_create (GtkFileChooserDefault *impl)
 
   gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), column);
 
-  return impl->browse_shortcuts_swin;
+  return swin;
 }
 
 /* Creates the widgets for the shortcuts/bookmarks pane */
@@ -1875,7 +2564,7 @@ trap_activate_cb (GtkWidget   *widget,
          GtkWindow *window;
 
          window = GTK_WINDOW (toplevel);
-      
+
          if (window &&
              widget != window->default_widget &&
              !(widget == window->focus_widget &&
@@ -1918,8 +2607,8 @@ create_file_list (GtkFileChooserDefault *impl)
   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
   gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (impl->browse_files_tree_view),
                                          GDK_BUTTON1_MASK,
-                                         shortcuts_targets,
-                                         num_shortcuts_targets,
+                                         file_list_source_targets,
+                                         num_file_list_source_targets,
                                          GDK_ACTION_COPY);
 
   g_signal_connect (selection, "changed",
@@ -1993,6 +2682,17 @@ create_filename_entry_and_filter_combo (GtkFileChooserDefault *impl)
   return hbox;
 }
 
+static GtkWidget *
+create_path_bar (GtkFileChooserDefault *impl)
+{
+  GtkWidget *path_bar;
+
+  path_bar = g_object_new (GTK_TYPE_PATH_BAR, NULL);
+  _gtk_path_bar_set_file_system (GTK_PATH_BAR (path_bar), impl->file_system);
+
+  return path_bar;
+}
+
 /* Creates the widgets for the files/folders pane */
 static GtkWidget *
 file_pane_create (GtkFileChooserDefault *impl,
@@ -2008,7 +2708,7 @@ file_pane_create (GtkFileChooserDefault *impl,
   /* The path bar and 'Create Folder' button */
   hbox = gtk_hbox_new (FALSE, 12);
   gtk_widget_show (hbox);
-  impl->browse_path_bar = g_object_new (GTK_TYPE_PATH_BAR, NULL);
+  impl->browse_path_bar = create_path_bar (impl);
   g_signal_connect (impl->browse_path_bar, "path-clicked", G_CALLBACK (path_bar_clicked), impl);
   gtk_widget_show_all (impl->browse_path_bar);
   gtk_box_pack_start (GTK_BOX (hbox), impl->browse_path_bar, TRUE, TRUE, 0);
@@ -2140,7 +2840,7 @@ save_widgets_create (GtkFileChooserDefault *impl)
   gtk_label_set_mnemonic_widget (GTK_LABEL (widget), impl->save_file_name_entry);
 
   /* Folder combo */
-  impl->save_folder_label = gtk_label_new_with_mnemonic (_("Save in _Folder:"));
+  impl->save_folder_label = gtk_label_new (NULL);
   gtk_misc_set_alignment (GTK_MISC (impl->save_folder_label), 0.0, 0.5);
   gtk_table_attach (GTK_TABLE (table), impl->save_folder_label,
                    0, 1, 1, 2,
@@ -2197,6 +2897,8 @@ browse_widgets_create (GtkFileChooserDefault *impl)
   widget = file_pane_create (impl, size_group);
   gtk_paned_pack2 (GTK_PANED (hpaned), widget, TRUE, FALSE);
 
+  g_object_unref (size_group);
+
   /* Alignment to hold custom widget */
   impl->browse_extra_align = gtk_alignment_new (0.0, .5, 1.0, 1.0);
   gtk_box_pack_start (GTK_BOX (vbox), impl->browse_extra_align, FALSE, FALSE, 0);
@@ -2257,6 +2959,37 @@ set_extra_widget (GtkFileChooserDefault *impl,
   impl->extra_widget = extra_widget;
 }
 
+static void
+set_local_only (GtkFileChooserDefault *impl,
+               gboolean               local_only)
+{
+  if (local_only != impl->local_only)
+    {
+      impl->local_only = local_only;
+
+      if (impl->shortcuts_model && impl->file_system)
+       {
+         shortcuts_add_volumes (impl);
+         shortcuts_add_bookmarks (impl);
+       }
+
+      if (local_only &&
+         !gtk_file_system_path_is_local (impl->file_system, 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 ();
+         GtkFilePath *home_path = gtk_file_system_filename_to_path (impl->file_system, home);
+
+         _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), home_path, NULL);
+
+         gtk_file_path_free (home_path);
+       }
+    }
+}
+
 static void
 volumes_changed_cb (GtkFileSystem         *file_system,
                    GtkFileChooserDefault *impl)
@@ -2363,8 +3096,17 @@ update_appearance (GtkFileChooserDefault *impl)
   if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
       impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
     {
+      const char *text;
+
       gtk_widget_show (impl->save_widgets);
 
+      if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
+       text = _("Save in _folder:");
+      else
+       text = _("Create in _folder:");
+
+      gtk_label_set_text_with_mnemonic (GTK_LABEL (impl->save_folder_label), text);
+
       if (gtk_expander_get_expanded (GTK_EXPANDER (impl->save_expander)))
        {
          gtk_widget_set_sensitive (impl->save_folder_label, FALSE);
@@ -2416,7 +3158,8 @@ update_appearance (GtkFileChooserDefault *impl)
       GtkWidget *align;
       GtkWidget *unused_align;
 
-      if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
+      if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
+         || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
        {
          align = impl->save_extra_align;
          unused_align = impl->browse_extra_align;
@@ -2497,7 +3240,7 @@ gtk_file_chooser_default_set_property (GObject      *object,
       set_current_filter (impl, g_value_get_object (value));
       break;
     case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
-      impl->local_only = g_value_get_boolean (value);
+      set_local_only (impl, g_value_get_boolean (value));
       break;
     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
       set_preview_widget (impl, g_value_get_object (value));
@@ -2586,6 +3329,21 @@ gtk_file_chooser_default_get_property (GObject    *object,
     }
 }
 
+/* Removes the settings signal handler.  It's safe to call multiple times */
+static void
+remove_settings_signal (GtkFileChooserDefault *impl,
+                       GdkScreen             *screen)
+{
+  if (impl->settings_signal_id)
+    {
+      GtkSettings *settings;
+
+      settings = gtk_settings_get_for_screen (screen);
+      g_signal_handler_disconnect (settings,
+                                  impl->settings_signal_id);
+      impl->settings_signal_id = 0;
+    }
+}
 
 static void
 gtk_file_chooser_default_dispose (GObject *object)
@@ -2597,6 +3355,9 @@ gtk_file_chooser_default_dispose (GObject *object)
       g_object_unref (impl->extra_widget);
       impl->extra_widget = NULL;
     }
+
+  remove_settings_signal (impl, gtk_widget_get_screen (GTK_WIDGET (impl)));
+
   G_OBJECT_CLASS (parent_class)->dispose (object);
 }
 
@@ -2615,13 +3376,73 @@ gtk_file_chooser_default_show_all (GtkWidget *widget)
     gtk_widget_show_all (impl->extra_widget);
 }
 
+/* Changes the icons wherever it is needed */
+static void
+change_icon_theme (GtkFileChooserDefault *impl)
+{
+  GtkSettings *settings;
+  gint width, height;
+
+  settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
+
+  if (gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_SMALL_TOOLBAR, &width, &height))
+    impl->icon_size = MAX (width, height);
+  else
+    impl->icon_size = FALLBACK_ICON_SIZE;
+
+  shortcuts_reload_icons (impl);
+  gtk_widget_queue_resize (impl->browse_files_tree_view);
+}
+
+/* Callback used when a GtkSettings value changes */
+static void
+settings_notify_cb (GObject               *object,
+                   GParamSpec            *pspec,
+                   GtkFileChooserDefault *impl)
+{
+  const char *name;
+
+  name = g_param_spec_get_name (pspec);
+
+  if (strcmp (name, "gtk-icon-theme-name") == 0
+      || strcmp (name, "gtk-icon-sizes") == 0)
+    change_icon_theme (impl);
+}
+
+/* Installs a signal handler for GtkSettings so that we can monitor changes in
+ * the icon theme.
+ */
+static void
+check_icon_theme (GtkFileChooserDefault *impl)
+{
+  GtkSettings *settings;
+
+  if (impl->settings_signal_id)
+    return;
+
+  if (gtk_widget_has_screen (GTK_WIDGET (impl)))
+    {
+      settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
+      impl->settings_signal_id = g_signal_connect (settings, "notify",
+                                                  G_CALLBACK (settings_notify_cb), impl);
+
+      change_icon_theme (impl);
+    }
+}
+
 static void
 gtk_file_chooser_default_style_set      (GtkWidget *widget,
                                         GtkStyle  *previous_style)
 {
+  GtkFileChooserDefault *impl;
+
+  impl = GTK_FILE_CHOOSER_DEFAULT (widget);
+
   if (GTK_WIDGET_CLASS (parent_class)->style_set)
     GTK_WIDGET_CLASS (parent_class)->style_set (widget, previous_style);
 
+  check_icon_theme (impl);
+
   g_signal_emit_by_name (widget, "default-size-changed");
 }
 
@@ -2629,9 +3450,16 @@ static void
 gtk_file_chooser_default_screen_changed (GtkWidget *widget,
                                         GdkScreen *previous_screen)
 {
+  GtkFileChooserDefault *impl;
+
+  impl = GTK_FILE_CHOOSER_DEFAULT (widget);
+
   if (GTK_WIDGET_CLASS (parent_class)->screen_changed)
     GTK_WIDGET_CLASS (parent_class)->screen_changed (widget, previous_screen);
 
+  remove_settings_signal (impl, previous_screen);
+  check_icon_theme (impl);
+
   g_signal_emit_by_name (widget, "default-size-changed");
 }
 
@@ -2855,15 +3683,25 @@ gtk_file_chooser_default_set_current_folder (GtkFileChooser    *chooser,
                                             GError           **error)
 {
   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
-  GError *err;
 
-  err = NULL;
-  if (!_gtk_path_bar_set_path (GTK_PATH_BAR (impl->browse_path_bar), path, impl->file_system, &err))
+  /* Test validity of path here.  */
+  if (!check_is_folder (impl->file_system, path, error))
+    return FALSE;
+
+  if (impl->local_only &&
+      !gtk_file_system_path_is_local (impl->file_system, path))
     {
-      g_propagate_error (error, err);
+      g_set_error (error,
+                  GTK_FILE_SYSTEM_ERROR,
+                  GTK_FILE_SYSTEM_ERROR_FAILED,
+                  _("Can't change to folder because it isn't local"));
+
       return FALSE;
     }
 
+  if (!_gtk_path_bar_set_path (GTK_PATH_BAR (impl->browse_path_bar), path, error))
+    return FALSE;
+
   if (impl->current_folder != path)
     {
       if (impl->current_folder)
@@ -2872,7 +3710,7 @@ gtk_file_chooser_default_set_current_folder (GtkFileChooser    *chooser,
       impl->current_folder = gtk_file_path_copy (path);
     }
 
-  /* Update the widgets that may trigger a folder chnage themselves */
+  /* Update the widgets that may trigger a folder change themselves.  */
 
   if (!impl->changing_folder)
     {
@@ -2888,7 +3726,7 @@ gtk_file_chooser_default_set_current_folder (GtkFileChooser    *chooser,
 
   /* Refresh controls */
 
-  shortcuts_unselect_all (impl);
+  shortcuts_find_current_folder (impl);
 
   g_signal_emit_by_name (impl, "current-folder-changed", 0);
 
@@ -2914,7 +3752,8 @@ gtk_file_chooser_default_set_current_name (GtkFileChooser *chooser,
 {
   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
 
-  g_return_if_fail (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE);
+  g_return_if_fail (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
+                   || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
 
   gtk_entry_set_text (GTK_ENTRY (impl->save_file_name_entry), name);
 }
@@ -3069,6 +3908,8 @@ get_paths_foreach (GtkTreeModel *model,
   gtk_tree_model_sort_convert_iter_to_child_iter (info->impl->sort_model, &sel_iter, iter);
 
   file_path = _gtk_file_system_model_get_path (GTK_FILE_SYSTEM_MODEL (fs_model), &sel_iter);
+  if (!file_path)
+    return; /* We are on the editable row */
 
   if (!info->path_from_entry
       || gtk_file_path_compare (info->path_from_entry, file_path) != 0)
@@ -3239,6 +4080,10 @@ gtk_file_chooser_default_add_shortcut_folder (GtkFileChooser    *chooser,
   gboolean result;
   int pos;
 
+  /* Test validity of path here.  */
+  if (!check_is_folder (impl->file_system, path, error))
+    return FALSE;
+
   pos = shortcuts_get_pos_for_shortcut_folder (impl, impl->num_shortcuts);
 
   result = shortcuts_insert_path (impl, pos, FALSE, NULL, path, NULL, FALSE, error);
@@ -3539,6 +4384,38 @@ gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed)
   return FALSE;
 }
 
+/* Implementation for GtkFileChooserEmbed::initial_focus() */
+static void
+gtk_file_chooser_default_initial_focus (GtkFileChooserEmbed *chooser_embed)
+{
+  GtkFileChooserDefault *impl;
+  GtkWidget *widget;
+
+  impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed);
+
+  if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
+      || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
+    {
+      GtkTreePath *path;
+
+      path = gtk_tree_path_new_from_indices (0, -1);
+      gtk_tree_view_set_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view), path, NULL, FALSE);
+      gtk_tree_path_free (path);
+
+      widget = impl->browse_files_tree_view;
+    }
+  else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
+          || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
+    widget = impl->save_file_name_entry;
+  else
+    {
+      g_assert_not_reached ();
+      widget = NULL;
+    }
+
+  gtk_widget_grab_focus (widget);
+}
+
 static void
 set_current_filter (GtkFileChooserDefault *impl,
                    GtkFileFilter         *filter)
@@ -3858,7 +4735,8 @@ list_icon_data_func (GtkTreeViewColumn *tree_column,
     return;
 
   /* FIXME: NULL GError */
-  pixbuf = gtk_file_system_render_icon (impl->file_system, path, GTK_WIDGET (impl), ICON_SIZE, NULL);
+  pixbuf = gtk_file_system_render_icon (impl->file_system, path, GTK_WIDGET (impl),
+                                       impl->icon_size, NULL);
   g_object_set (cell,
                "pixbuf", pixbuf,
                NULL);
@@ -3970,8 +4848,7 @@ list_mtime_data_func (GtkTreeViewColumn *tree_column,
       if (days_diff > 1 && days_diff < 7)
        format = "%A"; /* Days from last week */
       else
-       /* FIXME: Get the right format for the locale */
-       format = _("%d/%b/%Y"); /* Any other date */
+       format = "%x"; /* Any other date */
 
       if (g_date_strftime (buf, sizeof (buf), format, &mtime) == 0)
        strcpy (buf, _("Unknown"));
@@ -3997,7 +4874,7 @@ location_entry_create (GtkFileChooserDefault *impl)
 
   entry = _gtk_file_chooser_entry_new ();
   /* Pick a good width for the entry */
-  gtk_entry_set_width_chars (GTK_ENTRY (entry), 25);
+  gtk_entry_set_width_chars (GTK_ENTRY (entry), 30);
   gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
   _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (entry), impl->file_system);
   _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (entry), impl->current_folder);
@@ -4005,7 +4882,7 @@ location_entry_create (GtkFileChooserDefault *impl)
   return GTK_WIDGET (entry);
 }
 
-static void
+static gboolean
 update_from_entry (GtkFileChooserDefault *impl,
                   GtkWindow             *parent,
                   GtkFileChooserEntry   *chooser_entry)
@@ -4020,20 +4897,20 @@ update_from_entry (GtkFileChooserDefault *impl,
     {
       error_message_with_parent (parent,
                                 _("Cannot change to the folder you specified as it is an invalid path."));
-      return;
+      return FALSE;
     }
 
   if (file_part[0] == '\0')
-    {
-      change_folder_and_display_error (impl, folder_path);
-      return;
-    }
+    return change_folder_and_display_error (impl, folder_path);
   else
     {
       GtkFileFolder *folder = NULL;
       GtkFilePath *subfolder_path = NULL;
       GtkFileInfo *info = NULL;
       GError *error;
+      gboolean result;
+
+      result = FALSE;
 
       /* If the file part is non-empty, we need to figure out if it refers to a
        * folder within folder. We could optimize the case here where the folder
@@ -4046,7 +4923,7 @@ update_from_entry (GtkFileChooserDefault *impl,
       if (!folder)
        {
          error_getting_info_dialog (impl, folder_path, error);
-         return;
+         goto out;
        }
 
       error = NULL;
@@ -4064,8 +4941,7 @@ update_from_entry (GtkFileChooserDefault *impl,
          error_message (impl, msg);
          g_free (uri);
          g_free (msg);
-         g_object_unref (folder);
-         return;
+         goto out;
        }
 
       error = NULL;
@@ -4073,39 +4949,48 @@ update_from_entry (GtkFileChooserDefault *impl,
 
       if (!info)
        {
-#if 0
-         if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
+         if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
+             || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
            {
-             g_object_unref (folder);
-             gtk_file_path_free (subfolder_path);
-             return;
+             if (!change_folder_and_display_error (impl, folder_path))
+               goto out;
+
+             gtk_file_chooser_default_set_current_name (GTK_FILE_CHOOSER (impl), file_part);
            }
-#endif
-         error_getting_info_dialog (impl, subfolder_path, error);
-         g_object_unref (folder);
-         gtk_file_path_free (subfolder_path);
-         return;
+         else
+           error_getting_info_dialog (impl, subfolder_path, error);
+
+         goto out;
        }
 
       if (gtk_file_info_get_is_folder (info))
-       change_folder_and_display_error (impl, subfolder_path);
+       result = change_folder_and_display_error (impl, subfolder_path);
       else
        {
          GError *error;
 
          error = NULL;
-         if (!_gtk_file_chooser_select_path (GTK_FILE_CHOOSER (impl), subfolder_path, &error))
-           {
-             error_dialog (impl,
-                           _("Could not select %s:\n%s"),
-                           subfolder_path, error);
-           }
+         result = _gtk_file_chooser_select_path (GTK_FILE_CHOOSER (impl), subfolder_path, &error);
+         if (!result)
+           error_dialog (impl,
+                         _("Could not select %s:\n%s"),
+                         subfolder_path, error);
        }
 
-      g_object_unref (folder);
+    out:
+
+      if (folder)
+       g_object_unref (folder);
+
       gtk_file_path_free (subfolder_path);
-      gtk_file_info_free (info);
+
+      if (info)
+       gtk_file_info_free (info);
+
+      return result;
     }
+
+  g_assert_not_reached ();
 }
 
 static void
@@ -4116,6 +5001,8 @@ location_popup_handler (GtkFileChooserDefault *impl)
   GtkWidget *hbox;
   GtkWidget *label;
   GtkWidget *entry;
+  gboolean refocus;
+  char *title;
 
   /* Create dialog */
 
@@ -4123,7 +5010,19 @@ location_popup_handler (GtkFileChooserDefault *impl)
   if (!GTK_WIDGET_TOPLEVEL (toplevel))
     toplevel = NULL;
 
-  dialog = gtk_dialog_new_with_buttons (_("Open Location"),
+  if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
+      || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
+    {
+      title = _("Open Location");
+    }
+  else
+    {
+      g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
+               || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
+      title = ""; /* FIXME: #137272, fix for 2.4.1 */
+    }
+
+  dialog = gtk_dialog_new_with_buttons (title,
                                        GTK_WINDOW (toplevel),
                                        GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
                                        GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
@@ -4148,8 +5047,36 @@ location_popup_handler (GtkFileChooserDefault *impl)
   /* Run */
 
   gtk_widget_show_all (dialog);
+
+  refocus = TRUE;
+
   if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
-    update_from_entry (impl, GTK_WINDOW (dialog), GTK_FILE_CHOOSER_ENTRY (entry));
+    {
+      if (update_from_entry (impl, GTK_WINDOW (dialog), GTK_FILE_CHOOSER_ENTRY (entry)))
+       {
+         if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
+             || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
+           {
+             gtk_widget_grab_focus (impl->browse_files_tree_view);
+           }
+         else
+           {
+             g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
+                       || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
+             gtk_widget_grab_focus (impl->save_file_name_entry);
+           }
+         refocus = FALSE;
+       }
+    }
+
+  if (refocus)
+    {
+      GtkWidget *toplevel;
+
+      toplevel = gtk_widget_get_toplevel (GTK_WIDGET (impl));
+      if (GTK_WIDGET_TOPLEVEL (toplevel) && GTK_WINDOW (toplevel)->focus_widget)
+       gtk_widget_grab_focus (GTK_WINDOW (toplevel)->focus_widget);
+    }
 
   gtk_widget_destroy (dialog);
 }
@@ -4158,24 +5085,14 @@ location_popup_handler (GtkFileChooserDefault *impl)
 static void
 up_folder_handler (GtkFileChooserDefault *impl)
 {
-  GtkFilePath *parent_path;
-  GError *error;
+  _gtk_path_bar_up (GTK_PATH_BAR (impl->browse_path_bar));
+}
 
-  error = NULL;
-  if (gtk_file_system_get_parent (impl->file_system, impl->current_folder, &parent_path, &error))
-    {
-      if (parent_path) /* If we were on a root, parent_path will be NULL */
-       {
-         change_folder_and_display_error (impl, parent_path);
-         gtk_file_path_free (parent_path);
-       }
-    }
-  else
-    {
-      error_dialog (impl,
-                   _("Could not go to the parent folder of %s:\n%s"),
-                   impl->current_folder, error);
-    }
+/* Handler for the "down-folder" keybinding signal */
+static void
+down_folder_handler (GtkFileChooserDefault *impl)
+{
+  _gtk_path_bar_down (GTK_PATH_BAR (impl->browse_path_bar));
 }
 
 /* Handler for the "home-folder" keybinding signal */
@@ -4198,3 +5115,85 @@ home_folder_handler (GtkFileChooserDefault *impl)
 
   change_folder_and_display_error (impl, path);
 }
+
+\f
+
+/* Drag and drop interfaces */
+
+static void
+shortcuts_model_filter_class_init (ShortcutsModelFilterClass *class)
+{
+}
+
+static void
+shortcuts_model_filter_init (ShortcutsModelFilter *model)
+{
+  model->impl = NULL;
+}
+
+/* GtkTreeDragSource::row_draggable implementation for the shortcuts filter model */
+static gboolean
+shortcuts_model_filter_row_draggable (GtkTreeDragSource *drag_source,
+                                     GtkTreePath       *path)
+{
+  ShortcutsModelFilter *model;
+  int pos;
+  int bookmarks_pos;
+
+  model = SHORTCUTS_MODEL_FILTER (drag_source);
+
+  pos = *gtk_tree_path_get_indices (path);
+  bookmarks_pos = shortcuts_get_index (model->impl, SHORTCUTS_BOOKMARKS);
+
+  return (pos >= bookmarks_pos && pos < bookmarks_pos + model->impl->num_bookmarks);
+}
+
+/* GtkTreeDragSource::drag_data_get implementation for the shortcuts filter model */
+static gboolean
+shortcuts_model_filter_drag_data_get (GtkTreeDragSource *drag_source,
+                                     GtkTreePath       *path,
+                                     GtkSelectionData  *selection_data)
+{
+  ShortcutsModelFilter *model;
+
+  model = SHORTCUTS_MODEL_FILTER (drag_source);
+
+  /* FIXME */
+
+  return FALSE;
+}
+
+/* Fill the GtkTreeDragSourceIface vtable */
+static void
+shortcuts_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface)
+{
+  iface->row_draggable = shortcuts_model_filter_row_draggable;
+  iface->drag_data_get = shortcuts_model_filter_drag_data_get;
+}
+
+#if 0
+/* Fill the GtkTreeDragDestIface vtable */
+static void
+shortcuts_model_filter_drag_dest_iface_init (GtkTreeDragDestIface *iface)
+{
+  iface->drag_data_received = shortcuts_model_filter_drag_data_received;
+  iface->row_drop_possible = shortcuts_model_filter_row_drop_possible;
+}
+#endif
+
+static GtkTreeModel *
+shortcuts_model_filter_new (GtkFileChooserDefault *impl,
+                           GtkTreeModel          *child_model,
+                           GtkTreePath           *root)
+{
+  ShortcutsModelFilter *model;
+
+  model = g_object_new (SHORTCUTS_MODEL_FILTER_TYPE,
+                       "child_model", child_model,
+                       "virtual_root", root,
+                       NULL);
+
+  model->impl = impl;
+
+  return GTK_TREE_MODEL (model);
+}