]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkfilechooserdefault.c
Free volumes not actually put into the shortcut list.
[~andy/gtk] / gtk / gtkfilechooserdefault.c
index dc12977fa1da93f5d9e3f8b403d2f02aad7cdc71..8987297667fd8402043ffd0074b524b2b8bfa938 100644 (file)
@@ -53,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"
@@ -145,6 +147,11 @@ struct _GtkFileChooserDefault
   guint settings_signal_id;
   int icon_size;
 
+#if 0
+  GdkDragContext *shortcuts_drag_context;
+  GSource *shortcuts_drag_outside_idle;
+#endif
+
   /* Flags */
 
   guint local_only : 1;
@@ -156,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
 };
@@ -188,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_targets = sizeof (shortcuts_targets) / sizeof (shortcuts_targets[0]);
+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_file_list_source_targets = (sizeof (file_list_source_targets)
+                                                / sizeof (file_list_source_targets[0]));
 
 /* Interesting places in the shortcuts bar */
 typedef enum {
@@ -281,6 +312,7 @@ static void           gtk_file_chooser_default_initial_focus          (GtkFileCh
 
 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);
 
@@ -347,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)
 {
@@ -430,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),
@@ -455,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",
@@ -577,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));
@@ -606,17 +686,19 @@ error_dialog (GtkFileChooserDefault *impl,
              const GtkFilePath     *path,
              GError                *error)
 {
-  char *uri;
-  char *text;
+  g_return_if_fail (path != NULL);
 
-  uri = gtk_file_system_path_to_uri (impl->file_system, path);
-  text = g_strdup_printf (msg,
-                        uri,
-                        error->message);
-  error_message (impl, text);
-  g_free (text);
-  g_free (uri);
-  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.
@@ -804,14 +886,42 @@ shortcuts_reload_icons (GtkFileChooserDefault *impl)
   } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model),&iter));
 }
 
-/* Clears the selection in the shortcuts tree */
+/* 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 */
@@ -822,30 +932,24 @@ get_file_info (GtkFileSystem *file_system, const GtkFilePath *path, gboolean nam
   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_path : path,
                                              GTK_FILE_INFO_DISPLAY_NAME
-#if 0
-                                             | GTK_FILE_INFO_ICON
-#endif
                                              | (name_only ? 0 : GTK_FILE_INFO_IS_FOLDER),
                                              error);
-  gtk_file_path_free (parent_path);
+  if (!parent_folder)
+    goto out;
 
-  if (parent_folder)
-    {
-      info = gtk_file_folder_get_info (parent_folder, path, error);
-      g_object_unref (parent_folder);
-    }
-  else
-    {
-      info = NULL;
-      /* Name-only should not fail.  */
-      g_return_val_if_fail (!name_only, NULL);
-    }
+  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;
 }
 
@@ -867,10 +971,6 @@ shortcuts_insert_path (GtkFileChooserDefault *impl,
   gpointer data;
   GtkTreeIter iter;
 
-  /* Note: currently this function cannot fail.  If you ever change
-   * it so it can, go check callers and their callers.
-   */
-
   if (is_volume)
     {
       data = volume;
@@ -880,11 +980,18 @@ shortcuts_insert_path (GtkFileChooserDefault *impl,
     }
   else
     {
+      if (!check_is_folder (impl->file_system, path, error))
+       return FALSE;
+
       if (label)
        label_copy = g_strdup (label);
       else
        {
          GtkFileInfo *info = get_file_info (impl->file_system, path, TRUE, error);
+
+         if (!info)
+           return FALSE;
+
          label_copy = g_strdup (gtk_file_info_get_display_name (info));
          gtk_file_info_free (info);
        }
@@ -974,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++;
@@ -1087,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);
@@ -1101,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;
@@ -1110,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() */
@@ -1146,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)
     {
@@ -1166,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 */
@@ -1295,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,
@@ -1483,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, FALSE, &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
@@ -1535,7 +1673,7 @@ add_bookmark_foreach_cb (GtkTreeModel *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 */
@@ -1548,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,
@@ -1721,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)
@@ -1748,25 +2258,15 @@ split_uris (const char *data)
   return uris;
 }
 
-/* Callback used when we get the drag data for the bookmarks list.  We add the
- * received URIs as bookmarks if they are folders.
- */
+/* Parses a "text/uri-list" string and inserts its URIs as bookmarks */
 static void
-shortcuts_drag_data_received_cb (GtkWidget          *widget,
-                                GdkDragContext     *context,
-                                gint                x,
-                                gint                y,
-                                GtkSelectionData   *selection_data,
-                                guint               info,
-                                guint               time_,
-                                gpointer            data)
+shortcuts_drop_uris (GtkFileChooserDefault *impl,
+                    const char            *data,
+                    int                    position)
 {
-  GtkFileChooserDefault *impl;
   GSList *uris, *l;
 
-  impl = GTK_FILE_CHOOSER_DEFAULT (data);
-
-  uris = split_uris (selection_data->data);
+  uris = split_uris (data);
 
   for (l = uris; l; l = l->next)
     {
@@ -1778,7 +2278,9 @@ shortcuts_drag_data_received_cb (GtkWidget          *widget,
 
       if (path)
        {
-         shortcuts_add_bookmark_from_path (impl, path);
+         if (shortcuts_add_bookmark_from_path (impl, path, position))
+           position++;
+
          gtk_file_path_free (path);
        }
       else
@@ -1795,6 +2297,101 @@ shortcuts_drag_data_received_cb (GtkWidget          *widget,
     }
 
   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);
+
+  bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
+  old_position -= bookmarks_index;
+  g_assert (old_position >= 0 && old_position < impl->num_bookmarks);
+
+  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 */
+
+  /* Remove the path from the old position and insert it in the new one */
+
+  if (new_position > old_position)
+    new_position--;
+
+  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
+ * received URIs as bookmarks if they are folders.
+ */
+static void
+shortcuts_drag_data_received_cb (GtkWidget          *widget,
+                                GdkDragContext     *context,
+                                gint                x,
+                                gint                y,
+                                GtkSelectionData   *selection_data,
+                                guint               info,
+                                guint               time_,
+                                gpointer            data)
+{
+  GtkFileChooserDefault *impl;
+  GtkTreePath *tree_path;
+  GtkTreeViewDropPosition tree_pos;
+  int position;
+  int bookmarks_index;
+
+  impl = GTK_FILE_CHOOSER_DEFAULT (data);
+
+  /* Compute position */
+
+  bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS);
+
+  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 (tree_pos == GTK_TREE_VIEW_DROP_AFTER)
+    position++;
+
+  g_assert (position >= bookmarks_index);
+  position -= bookmarks_index;
+
+  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_signal_stop_emission_by_name (widget, "drag-data-received");
 }
@@ -1832,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);
@@ -1850,6 +2453,19 @@ 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);
 
@@ -1991,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",
@@ -2281,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);
@@ -2341,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)
@@ -2499,8 +3148,7 @@ update_appearance (GtkFileChooserDefault *impl)
        _gtk_file_system_model_set_show_files (impl->browse_files_model, TRUE);
     }
 
-  if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
-      || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
+  if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN)
     gtk_widget_hide (impl->browse_new_folder_button);
   else
     gtk_widget_show (impl->browse_new_folder_button);
@@ -2592,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));
@@ -2681,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)
@@ -2693,14 +3356,7 @@ gtk_file_chooser_default_dispose (GObject *object)
       impl->extra_widget = NULL;
     }
 
-  if (impl->settings_signal_id)
-    {
-      GtkSettings *settings;
-
-      settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
-      g_signal_handler_disconnect (settings, impl->settings_signal_id);
-      impl->settings_signal_id = 0;
-    }
+  remove_settings_signal (impl, gtk_widget_get_screen (GTK_WIDGET (impl)));
 
   G_OBJECT_CLASS (parent_class)->dispose (object);
 }
@@ -2764,11 +3420,14 @@ check_icon_theme (GtkFileChooserDefault *impl)
   if (impl->settings_signal_id)
     return;
 
-  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);
+  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);
+      change_icon_theme (impl);
+    }
 }
 
 static void
@@ -2798,6 +3457,7 @@ gtk_file_chooser_default_screen_changed (GtkWidget *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");
@@ -3023,13 +3683,21 @@ gtk_file_chooser_default_set_current_folder (GtkFileChooser    *chooser,
                                             GError           **error)
 {
   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
-  GtkFileInfo *info;
 
   /* Test validity of path here.  */
-  info = get_file_info (impl->file_system, path, FALSE, error);
-  if (!info)
+  if (!check_is_folder (impl->file_system, path, error))
     return FALSE;
-  gtk_file_info_free (info);
+
+  if (impl->local_only &&
+      !gtk_file_system_path_is_local (impl->file_system, path))
+    {
+      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;
@@ -3058,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);
 
@@ -3084,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);
 }
@@ -3239,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)
@@ -3407,14 +4078,11 @@ gtk_file_chooser_default_add_shortcut_folder (GtkFileChooser    *chooser,
 {
   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
   gboolean result;
-  GtkFileInfo *info;
   int pos;
 
   /* Test validity of path here.  */
-  info = get_file_info (impl->file_system, path, FALSE, error);
-  if (!info)
+  if (!check_is_folder (impl->file_system, path, error))
     return FALSE;
-  gtk_file_info_free (info);
 
   pos = shortcuts_get_pos_for_shortcut_folder (impl, impl->num_shortcuts);
 
@@ -4180,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"));
@@ -4207,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);
@@ -4282,11 +4949,17 @@ update_from_entry (GtkFileChooserDefault *impl,
 
       if (!info)
        {
-#if 0
-         if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
-           return;
-#endif
-         error_getting_info_dialog (impl, subfolder_path, error);
+         if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
+             || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
+           {
+             if (!change_folder_and_display_error (impl, folder_path))
+               goto out;
+
+             gtk_file_chooser_default_set_current_name (GTK_FILE_CHOOSER (impl), file_part);
+           }
+         else
+           error_getting_info_dialog (impl, subfolder_path, error);
+
          goto out;
        }
 
@@ -4329,6 +5002,7 @@ location_popup_handler (GtkFileChooserDefault *impl)
   GtkWidget *label;
   GtkWidget *entry;
   gboolean refocus;
+  char *title;
 
   /* Create dialog */
 
@@ -4336,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,
@@ -4368,7 +5054,17 @@ location_popup_handler (GtkFileChooserDefault *impl)
     {
       if (update_from_entry (impl, GTK_WINDOW (dialog), GTK_FILE_CHOOSER_ENTRY (entry)))
        {
-         gtk_widget_grab_focus (impl->browse_files_tree_view);
+         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;
        }
     }
@@ -4389,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 */
@@ -4429,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);
+}