]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkfilechooserdefault.c
When using gtk_dialog_run() for modal dialogs, make sure to inherit the
[~andy/gtk] / gtk / gtkfilechooserdefault.c
index 715232d0d2adadf7bb9f1c09108e061aec3c74ba..786a2519526f10b513f055210f787e54c6d7b6b7 100644 (file)
 #include <string.h>
 #include <time.h>
 
+\f
+
+/* Profiling stuff */
+
+#define PROFILE_FILE_CHOOSER
+#ifdef PROFILE_FILE_CHOOSER
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef G_OS_WIN32
+#include <io.h>
+#endif
+
+#ifndef F_OK 
+#define F_OK 0
+#endif
+
+#define PROFILE_INDENT 4
+static int profile_indent;
+
+static void
+profile_add_indent (int indent)
+{
+  profile_indent += indent;
+  if (profile_indent < 0)
+    g_error ("You screwed up your indentation");
+}
+
+void
+_gtk_file_chooser_profile_log (const char *func, int indent, const char *msg1, const char *msg2)
+{
+  char *str;
+
+  if (indent < 0)
+    profile_add_indent (indent);
+
+  if (profile_indent == 0)
+    str = g_strdup_printf ("MARK: %s %s %s", func, msg1 ? msg1 : "", msg2 ? msg2 : "");
+  else
+    str = g_strdup_printf ("MARK: %*c %s %s %s", profile_indent - 1, ' ', func, msg1 ? msg1 : "", msg2 ? msg2 : "");
+
+  access (str, F_OK);
+  g_free (str);
+
+  if (indent > 0)
+    profile_add_indent (indent);
+}
+
+#define profile_start(x, y) _gtk_file_chooser_profile_log (G_STRFUNC, PROFILE_INDENT, x, y)
+#define profile_end(x, y) _gtk_file_chooser_profile_log (G_STRFUNC, -PROFILE_INDENT, x, y)
+#define profile_msg(x, y) _gtk_file_chooser_profile_log (NULL, 0, x, y)
+#else
+#define profile_start(x, y)
+#define profile_end(x, y)
+#define profile_msg(x, y)
+#endif
+
+\f
+
 typedef struct _GtkFileChooserDefaultClass GtkFileChooserDefaultClass;
 
 #define GTK_FILE_CHOOSER_DEFAULT_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_CHOOSER_DEFAULT, GtkFileChooserDefaultClass))
@@ -194,6 +254,7 @@ struct _GtkFileChooserDefault
   guint use_preview_label : 1;
   guint select_multiple : 1;
   guint show_hidden : 1;
+  guint do_overwrite_confirmation : 1;
   guint list_sort_ascending : 1;
   guint changing_folder : 1;
   guint shortcuts_current_folder_active : 1;
@@ -264,6 +325,15 @@ static const GtkTargetEntry file_list_source_targets[] = {
 static const int num_file_list_source_targets = (sizeof (file_list_source_targets)
                                                 / sizeof (file_list_source_targets[0]));
 
+/* Target types for dropping into the file list */
+static const GtkTargetEntry file_list_dest_targets[] = {
+  { "text/uri-list", 0, TEXT_URI_LIST }
+};
+
+static const int num_file_list_dest_targets = (sizeof (file_list_dest_targets)
+                                              / sizeof (file_list_dest_targets[0]));
+
+
 /* Interesting places in the shortcuts bar */
 typedef enum {
   SHORTCUTS_HOME,
@@ -313,6 +383,10 @@ static void     gtk_file_chooser_default_screen_changed (GtkWidget             *
 static gboolean       gtk_file_chooser_default_set_current_folder         (GtkFileChooser    *chooser,
                                                                            const GtkFilePath *path,
                                                                            GError           **error);
+static gboolean       gtk_file_chooser_default_update_current_folder      (GtkFileChooser    *chooser,
+                                                                           const GtkFilePath *path,
+                                                                           gboolean           keep_trail,
+                                                                           GError           **error);
 static GtkFilePath *  gtk_file_chooser_default_get_current_folder         (GtkFileChooser    *chooser);
 static void           gtk_file_chooser_default_set_current_name           (GtkFileChooser    *chooser,
                                                                            const gchar       *name);
@@ -441,6 +515,7 @@ static const GtkFileInfo *get_list_file_info (GtkFileChooserDefault *impl,
                                              GtkTreeIter           *iter);
 
 static void load_remove_timer (GtkFileChooserDefault *impl);
+static void browse_files_center_selected_row (GtkFileChooserDefault *impl);
 
 static GObjectClass *parent_class;
 
@@ -661,6 +736,8 @@ gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface *iface)
 static void
 gtk_file_chooser_default_init (GtkFileChooserDefault *impl)
 {
+  profile_start ("start", NULL);
+
   impl->local_only = TRUE;
   impl->preview_widget_active = TRUE;
   impl->use_preview_label = TRUE;
@@ -670,12 +747,13 @@ gtk_file_chooser_default_init (GtkFileChooserDefault *impl)
   impl->load_state = LOAD_EMPTY;
   impl->pending_select_paths = NULL;
 
-  gtk_widget_set_redraw_on_allocate (GTK_WIDGET (impl), TRUE);
   gtk_box_set_spacing (GTK_BOX (impl), 12);
 
   impl->tooltips = gtk_tooltips_new ();
   g_object_ref (impl->tooltips);
   gtk_object_sink (GTK_OBJECT (impl->tooltips));
+
+  profile_end ("end", NULL);
 }
 
 /* Frees the data columns for the specified iter in the shortcuts model*/
@@ -858,6 +936,10 @@ error_message_with_parent (GtkWindow  *parent,
                                   msg);
   gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
                                            "%s", detail);
+
+  if (parent->group)
+    gtk_window_group_add_window (parent->group, GTK_WINDOW (dialog));
+
   gtk_dialog_run (GTK_DIALOG (dialog));
   gtk_widget_destroy (dialog);
 }
@@ -952,6 +1034,21 @@ error_creating_folder_dialog (GtkFileChooserDefault *impl,
                path, error);
 }
 
+/* Shows an error about not being able to create a folder because a file with
+ * the same name is already there.
+ */
+static void
+error_creating_folder_over_existing_file_dialog (GtkFileChooserDefault *impl,
+                                                const GtkFilePath     *path,
+                                                GError                *error)
+{
+  error_dialog (impl,
+               _("The folder could not be created, as a file with the same name "
+                 "already exists.  Try using a different name for the folder, "
+                 "or rename the file first."),
+               path, error);
+}
+
 /* Shows an error dialog about not being able to create a filename */
 static void
 error_building_filename_dialog (GtkFileChooserDefault *impl,
@@ -982,6 +1079,10 @@ change_folder_and_display_error (GtkFileChooserDefault *impl,
   gboolean result;
   GtkFilePath *path_copy;
 
+  g_return_val_if_fail (path != NULL, FALSE);
+
+  profile_start ("start", (char *) path);
+
   /* We copy the path because of this case:
    *
    * list_row_activated()
@@ -994,13 +1095,15 @@ change_folder_and_display_error (GtkFileChooserDefault *impl,
   path_copy = gtk_file_path_copy (path);
 
   error = NULL;
-  result = _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), path_copy, &error);
+  result = gtk_file_chooser_default_update_current_folder (GTK_FILE_CHOOSER (impl), path_copy, TRUE, &error);
 
   if (!result)
     error_changing_folder_dialog (impl, path_copy, error);
 
   gtk_file_path_free (path_copy);
 
+  profile_end ("end", (char *) path);
+
   return result;
 }
 
@@ -1065,8 +1168,10 @@ shortcuts_reload_icons (GtkFileChooserDefault *impl)
 {
   GtkTreeIter iter;
 
+  profile_start ("start", NULL);
+
   if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
-    return;
+    goto out;
 
   do {
     gpointer data;
@@ -1106,6 +1211,10 @@ shortcuts_reload_icons (GtkFileChooserDefault *impl)
          g_object_unref (pixbuf);
       }
   } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model),&iter));
+
+ out:
+
+  profile_end ("end", NULL);
 }
 
 static void 
@@ -1150,6 +1259,8 @@ get_file_info (GtkFileSystem      *file_system,
   GtkFileInfo *info;
   GError *tmp = NULL;
 
+  profile_start ("start", (char *) path);
+
   parent_path = NULL;
   info = NULL;
 
@@ -1181,6 +1292,8 @@ get_file_info (GtkFileSystem      *file_system,
       g_error_free (tmp);
     }
 
+  profile_end ("end", (char *) path);
+
   return info;
 }
 
@@ -1192,11 +1305,18 @@ check_is_folder (GtkFileSystem      *file_system,
 {
   GtkFileFolder *folder;
 
+  profile_start ("start", (char *) path);
+
   folder = gtk_file_system_get_folder (file_system, path, 0, error);
   if (!folder)
-    return FALSE;
+    {
+      profile_end ("end - is not folder", (char *) path);
+      return FALSE;
+    }
 
   g_object_unref (folder);
+
+  profile_end ("end", (char *) path);
   return TRUE;
 }
 
@@ -1218,6 +1338,8 @@ shortcuts_insert_path (GtkFileChooserDefault *impl,
   gpointer data;
   GtkTreeIter iter;
 
+  profile_start ("start", is_volume ? "volume" : (char *) path);
+
   if (is_volume)
     {
       data = volume;
@@ -1228,7 +1350,10 @@ shortcuts_insert_path (GtkFileChooserDefault *impl,
   else
     {
       if (!check_is_folder (impl->file_system, path, error))
-       return FALSE;
+       {
+         profile_end ("end - is not folder", NULL);
+         return FALSE;
+       }
 
       if (label)
        label_copy = g_strdup (label);
@@ -1237,7 +1362,10 @@ shortcuts_insert_path (GtkFileChooserDefault *impl,
          GtkFileInfo *info = get_file_info (impl->file_system, path, TRUE, error);
 
          if (!info)
-           return FALSE;
+           {
+             profile_end ("end - could not get info", (char *) path);
+             return FALSE;
+           }
 
          label_copy = g_strdup (gtk_file_info_get_display_name (info));
          gtk_file_info_free (info);
@@ -1267,6 +1395,8 @@ shortcuts_insert_path (GtkFileChooserDefault *impl,
   if (pixbuf)
     g_object_unref (pixbuf);
 
+  profile_end ("end", NULL);
+
   return TRUE;
 }
 
@@ -1278,9 +1408,14 @@ shortcuts_append_home (GtkFileChooserDefault *impl)
   GtkFilePath *home_path;
   GError *error;
 
+  profile_start ("start", NULL);
+
   home = g_get_home_dir ();
   if (home == NULL)
-    return;
+    {
+      profile_end ("end - no home directory!?", NULL);
+      return;
+    }
 
   home_path = gtk_file_system_filename_to_path (impl->file_system, home);
 
@@ -1290,6 +1425,8 @@ shortcuts_append_home (GtkFileChooserDefault *impl)
     error_getting_info_dialog (impl, home_path, error);
 
   gtk_file_path_free (home_path);
+
+  profile_end ("end", NULL);
 }
 
 /* Appends the ~/Desktop directory to the shortcuts model */
@@ -1299,12 +1436,17 @@ shortcuts_append_desktop (GtkFileChooserDefault *impl)
   char *name;
   GtkFilePath *path;
 
+  profile_start ("start", NULL);
+
 #ifdef G_OS_WIN32
   name = _gtk_file_system_win32_get_desktop ();
 #else
   const char *home = g_get_home_dir ();
   if (home == NULL)
-    return;
+    {
+      profile_end ("end - no home directory!?", NULL);
+      return;
+    }
 
   name = g_build_filename (home, "Desktop", NULL);
 #endif
@@ -1318,6 +1460,8 @@ shortcuts_append_desktop (GtkFileChooserDefault *impl)
    */
 
   gtk_file_path_free (path);
+
+  profile_end ("end", NULL);
 }
 
 /* Appends a list of GtkFilePath to the shortcuts model; returns how many were inserted */
@@ -1329,6 +1473,8 @@ shortcuts_append_paths (GtkFileChooserDefault *impl,
   int num_inserted;
   gchar *label;
 
+  profile_start ("start", NULL);
+
   /* As there is no separator now, we want to start there.
    */
   start_row = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR);
@@ -1355,6 +1501,8 @@ shortcuts_append_paths (GtkFileChooserDefault *impl,
       g_free (label);
     }
 
+  profile_end ("end", NULL);
+
   return num_inserted;
 }
 
@@ -1446,6 +1594,8 @@ shortcuts_add_volumes (GtkFileChooserDefault *impl)
   int n;
   gboolean old_changing_folders;
 
+  profile_start ("start", NULL);
+
   old_changing_folders = impl->changing_folder;
   impl->changing_folder = TRUE;
 
@@ -1489,6 +1639,8 @@ shortcuts_add_volumes (GtkFileChooserDefault *impl)
     gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model));
 
   impl->changing_folder = old_changing_folders;
+
+  profile_end ("end", NULL);
 }
 
 /* Inserts a separator node in the shortcuts list */
@@ -1521,6 +1673,8 @@ shortcuts_add_bookmarks (GtkFileChooserDefault *impl)
   GtkFilePath *combo_selected = NULL;
   gboolean is_volume;
   gpointer col_data;
+
+  profile_start ("start", NULL);
         
   old_changing_folders = impl->changing_folder;
   impl->changing_folder = TRUE;
@@ -1584,6 +1738,8 @@ shortcuts_add_bookmarks (GtkFileChooserDefault *impl)
     }
   
   impl->changing_folder = old_changing_folders;
+
+  profile_end ("end", NULL);
 }
 
 /* Appends a separator and a row to the shortcuts list for the current folder */
@@ -1843,6 +1999,8 @@ static GtkWidget *
 filter_create (GtkFileChooserDefault *impl)
 {
   impl->filter_combo = gtk_combo_box_new_text ();
+  gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (impl->filter_combo), FALSE);
+
   g_signal_connect (impl->filter_combo, "changed",
                    G_CALLBACK (filter_combo_changed), impl);
 
@@ -1858,29 +2016,15 @@ button_new (GtkFileChooserDefault *impl,
            GCallback   callback)
 {
   GtkWidget *button;
-  GtkWidget *hbox;
-  GtkWidget *widget;
-  GtkWidget *align;
-
-  button = gtk_button_new ();
-  hbox = gtk_hbox_new (FALSE, 2);
-  align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
-
-  gtk_container_add (GTK_CONTAINER (button), align);
-  gtk_container_add (GTK_CONTAINER (align), hbox);
-  widget = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON);
-
-  gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
+  GtkWidget *image;
 
-  widget = gtk_label_new_with_mnemonic (text);
-  gtk_label_set_mnemonic_widget (GTK_LABEL (widget), GTK_WIDGET (button));
-  gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
+  button = gtk_button_new_with_mnemonic (text);
+  image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON);
+  gtk_button_set_image (GTK_BUTTON (button), image);
 
   gtk_widget_set_sensitive (button, sensitive);
   g_signal_connect (button, "clicked", callback, impl);
 
-  gtk_widget_show_all (align);
-
   if (show)
     gtk_widget_show (button);
 
@@ -1922,8 +2066,8 @@ shortcut_find_position (GtkFileChooserDefault *impl,
              volume = col_data;
              base_path = gtk_file_system_volume_get_base_path (impl->file_system, volume);
 
-             exists = strcmp (gtk_file_path_get_string (path),
-                              gtk_file_path_get_string (base_path)) == 0;
+             exists = base_path && strcmp (gtk_file_path_get_string (path),
+                                           gtk_file_path_get_string (base_path)) == 0;
              g_free (base_path);
 
              if (exists)
@@ -1954,6 +2098,8 @@ shortcuts_add_bookmark_from_path (GtkFileChooserDefault *impl,
 {
   GError *error;
 
+  g_return_val_if_fail (path != NULL, FALSE);
   if (shortcut_find_position (impl, path) != -1)
     return FALSE;
 
@@ -2049,7 +2195,6 @@ remove_selected_bookmarks (GtkFileChooserDefault *impl)
 {
   GtkTreeIter iter;
   gpointer col_data;
-  gboolean is_volume;
   GtkFilePath *path;
   gboolean removable;
   GError *error;
@@ -2059,11 +2204,9 @@ remove_selected_bookmarks (GtkFileChooserDefault *impl)
 
   gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter,
                      SHORTCUTS_COL_DATA, &col_data,
-                     SHORTCUTS_COL_IS_VOLUME, &is_volume,
                      SHORTCUTS_COL_REMOVABLE, &removable,
                      -1);
   g_assert (col_data != NULL);
-  g_assert (!is_volume);
 
   if (!removable)
     return;
@@ -3211,11 +3354,14 @@ trap_activate_cb (GtkWidget   *widget,
                  gpointer     data)
 {
   GtkFileChooserDefault *impl;
+  int modifiers;
 
   impl = (GtkFileChooserDefault *) data;
+
+  modifiers = gtk_accelerator_get_default_mod_mask ();
   
   if (event->keyval == GDK_slash &&
-      ! (event->state & (~GDK_SHIFT_MASK & gtk_accelerator_get_default_mod_mask ())))
+      ! (event->state & (~GDK_SHIFT_MASK & modifiers)))
     {
       location_popup_handler (impl, "/");
       return TRUE;
@@ -3225,6 +3371,7 @@ trap_activate_cb (GtkWidget   *widget,
        || event->keyval == GDK_ISO_Enter
        || event->keyval == GDK_KP_Enter
        || event->keyval == GDK_space)
+      && ((event->state && modifiers) == 0)
       && !(impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
           impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER))
     {
@@ -3285,6 +3432,127 @@ show_hidden_toggled_cb (GtkCheckMenuItem      *item,
                NULL);
 }
 
+/* Shows an error dialog about not being able to select a dragged file */
+static void
+error_selecting_dragged_file_dialog (GtkFileChooserDefault *impl,
+                                    const GtkFilePath     *path,
+                                    GError                *error)
+{
+  error_dialog (impl,
+               _("Could not select file"),
+               path, error);
+}
+
+static void
+file_list_drag_data_received_cb (GtkWidget          *widget,
+                                GdkDragContext     *context,
+                                gint                x,
+                                gint                y,
+                                GtkSelectionData   *selection_data,
+                                guint               info,
+                                guint               time_,
+                                gpointer            data)
+{
+  GtkFileChooserDefault *impl;
+  GtkFileChooser *chooser;
+  gchar **uris;
+  char *uri;
+  GtkFilePath *path;
+  GError *error = NULL;
+  gint i;
+  impl = GTK_FILE_CHOOSER_DEFAULT (data);
+  chooser = GTK_FILE_CHOOSER (data);
+  
+  /* Parse the text/uri-list string, navigate to the first one */
+  uris = g_uri_list_extract_uris (selection_data->data);
+  if (uris[0]) 
+    {
+      uri = uris[0];
+      path = gtk_file_system_uri_to_path (impl->file_system, uri);
+      
+      if (path)
+       {
+         if ((impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
+              impl->action == GTK_FILE_CHOOSER_ACTION_SAVE) &&
+             uris[1] == 0 &&
+             check_is_folder (impl->file_system, path, NULL))
+           change_folder_and_display_error (impl, path);
+          else
+            {
+              gtk_file_chooser_default_unselect_all (chooser);
+              gtk_file_chooser_default_select_path (chooser, path, &error);
+              if (error)
+               error_selecting_dragged_file_dialog (impl, path, error);
+             else
+               browse_files_center_selected_row (impl);
+            }
+
+         gtk_file_path_free (path);
+       }
+      else
+       {
+         g_set_error (&error,
+                      GTK_FILE_CHOOSER_ERROR,
+                      GTK_FILE_CHOOSER_ERROR_BAD_FILENAME,
+                      _("Could not select file '%s' "
+                        "because it is an invalid path name."),
+                      uri);
+         error_selecting_dragged_file_dialog (impl, NULL, error);
+       }
+
+      
+      if (impl->select_multiple)
+       {
+         for (i = 1; uris[i]; i++)
+           {
+             uri = uris[i];
+             path = gtk_file_system_uri_to_path (impl->file_system, uri);
+             
+             if (path)
+               {
+                 gtk_file_chooser_default_select_path (chooser, path, &error);
+                 if (error)
+                   error_selecting_dragged_file_dialog (impl, path, error);
+
+                 gtk_file_path_free (path);
+               }
+           }
+       }
+    }
+
+  g_strfreev (uris);
+
+  g_signal_stop_emission_by_name (widget, "drag-data-received");
+}
+
+/* Don't do anything with the drag_drop signal */
+static gboolean
+file_list_drag_drop_cb (GtkWidget             *widget,
+                       GdkDragContext        *context,
+                       gint                   x,
+                       gint                   y,
+                       guint                  time_,
+                       GtkFileChooserDefault *impl)
+{
+  g_signal_stop_emission_by_name (widget, "drag-drop");
+  return TRUE;
+}
+
+/* Disable the normal tree drag motion handler, it makes it look like you're
+   dropping the dragged item onto a tree item */
+static gboolean
+file_list_drag_motion_cb (GtkWidget             *widget,
+                          GdkDragContext        *context,
+                          gint                   x,
+                          gint                   y,
+                          guint                  time_,
+                          GtkFileChooserDefault *impl)
+{
+  g_signal_stop_emission_by_name (widget, "drag-motion");
+  return TRUE;
+}
+
 /* Constructs the popup menu for the file list if needed */
 static void
 file_list_build_popup_menu (GtkFileChooserDefault *impl)
@@ -3449,6 +3717,13 @@ create_file_list (GtkFileChooserDefault *impl)
 
   gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (impl->browse_files_tree_view), TRUE);
   gtk_container_add (GTK_CONTAINER (swin), impl->browse_files_tree_view);
+
+  gtk_drag_dest_set (impl->browse_files_tree_view,
+                     GTK_DEST_DEFAULT_ALL,
+                     file_list_dest_targets,
+                     num_file_list_dest_targets,
+                     GDK_ACTION_COPY | GDK_ACTION_MOVE);
+  
   g_signal_connect (impl->browse_files_tree_view, "row-activated",
                    G_CALLBACK (list_row_activated), impl);
   g_signal_connect (impl->browse_files_tree_view, "key-press-event",
@@ -3458,6 +3733,13 @@ create_file_list (GtkFileChooserDefault *impl)
   g_signal_connect (impl->browse_files_tree_view, "button-press-event",
                    G_CALLBACK (list_button_press_event_cb), impl);
 
+  g_signal_connect (impl->browse_files_tree_view, "drag-data-received",
+                    G_CALLBACK (file_list_drag_data_received_cb), impl);
+  g_signal_connect (impl->browse_files_tree_view, "drag-drop",
+                    G_CALLBACK (file_list_drag_drop_cb), impl);
+  g_signal_connect (impl->browse_files_tree_view, "drag-motion",
+                    G_CALLBACK (file_list_drag_motion_cb), impl);
+
   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
   gtk_tree_selection_set_select_function (selection,
                                          list_select_func,
@@ -3931,6 +4213,8 @@ static void
 set_file_system_backend (GtkFileChooserDefault *impl,
                         const char *backend)
 {
+  profile_start ("start for backend", backend ? backend : "default");
+
   if (impl->file_system)
     {
       g_signal_handler_disconnect (impl->file_system, impl->volumes_changed_id);
@@ -3976,6 +4260,8 @@ set_file_system_backend (GtkFileChooserDefault *impl,
                                                     G_CALLBACK (bookmarks_changed_cb),
                                                     impl);
     }
+
+  profile_end ("end", NULL);
 }
 
 /* This function is basically a do_all function.
@@ -4058,9 +4344,12 @@ gtk_file_chooser_default_set_property (GObject      *object,
          {
            gtk_file_chooser_default_unselect_all (GTK_FILE_CHOOSER (impl));
            
-           if (action == GTK_FILE_CHOOSER_ACTION_SAVE && impl->select_multiple)
+           if ((action == GTK_FILE_CHOOSER_ACTION_SAVE || action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
+               && impl->select_multiple)
              {
-               g_warning ("Multiple selection mode is not allowed in Save mode");
+               g_warning ("Tried to change the file chooser action to SAVE or CREATE_FOLDER, but "
+                          "this is not allowed in multiple selection mode.  Resetting the file chooser "
+                          "to single selection mode.");
                set_select_multiple (impl, FALSE, TRUE);
              }
            impl->action = action;
@@ -4072,41 +4361,53 @@ gtk_file_chooser_default_set_property (GObject      *object,
                                              action);
       }
       break;
+
     case GTK_FILE_CHOOSER_PROP_FILE_SYSTEM_BACKEND:
       set_file_system_backend (impl, g_value_get_string (value));
       break;
+
     case GTK_FILE_CHOOSER_PROP_FILTER:
       set_current_filter (impl, g_value_get_object (value));
       break;
+
     case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
       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));
       break;
+
     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
       impl->preview_widget_active = g_value_get_boolean (value);
       update_preview_widget_visibility (impl);
       break;
+
     case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
       impl->use_preview_label = g_value_get_boolean (value);
       update_preview_widget_visibility (impl);
       break;
+
     case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
       set_extra_widget (impl, g_value_get_object (value));
       break;
+
     case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
       {
        gboolean select_multiple = g_value_get_boolean (value);
-       if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE && select_multiple)
+       if ((impl->action == GTK_FILE_CHOOSER_ACTION_SAVE || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
+           && select_multiple)
          {
-           g_warning ("Multiple selection mode is not allowed in Save mode");
+           g_warning ("Tried to set the file chooser to multiple selection mode, but this is "
+                      "not allowed in SAVE or CREATE_FOLDER modes.  Ignoring the change and "
+                      "leaving the file chooser in single selection mode.");
            return;
          }
 
        set_select_multiple (impl, select_multiple, FALSE);
       }
       break;
+
     case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
       {
        gboolean show_hidden = g_value_get_boolean (value);
@@ -4119,6 +4420,14 @@ gtk_file_chooser_default_set_property (GObject      *object,
          }
       }
       break;
+
+    case GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION:
+      {
+       gboolean do_overwrite_confirmation = g_value_get_boolean (value);
+       impl->do_overwrite_confirmation = do_overwrite_confirmation;
+      }
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -4138,30 +4447,43 @@ gtk_file_chooser_default_get_property (GObject    *object,
     case GTK_FILE_CHOOSER_PROP_ACTION:
       g_value_set_enum (value, impl->action);
       break;
+
     case GTK_FILE_CHOOSER_PROP_FILTER:
       g_value_set_object (value, impl->current_filter);
       break;
+
     case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
       g_value_set_boolean (value, impl->local_only);
       break;
+
     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
       g_value_set_object (value, impl->preview_widget);
       break;
+
     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
       g_value_set_boolean (value, impl->preview_widget_active);
       break;
+
     case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
       g_value_set_boolean (value, impl->use_preview_label);
       break;
+
     case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
       g_value_set_object (value, impl->extra_widget);
       break;
+
     case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
       g_value_set_boolean (value, impl->select_multiple);
       break;
+
     case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
       g_value_set_boolean (value, impl->show_hidden);
       break;
+
+    case GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION:
+      g_value_set_boolean (value, impl->do_overwrite_confirmation);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -4317,6 +4639,8 @@ gtk_file_chooser_default_style_set (GtkWidget *widget,
 {
   GtkFileChooserDefault *impl;
 
+  profile_start ("start", NULL);
+
   impl = GTK_FILE_CHOOSER_DEFAULT (widget);
 
   if (GTK_WIDGET_CLASS (parent_class)->style_set)
@@ -4326,6 +4650,8 @@ gtk_file_chooser_default_style_set (GtkWidget *widget,
     change_icon_theme (impl);
 
   g_signal_emit_by_name (widget, "default-size-changed");
+
+  profile_end ("end", NULL);
 }
 
 static void
@@ -4398,6 +4724,8 @@ gtk_file_chooser_default_map (GtkWidget *widget)
 {
   GtkFileChooserDefault *impl;
 
+  profile_start ("start", NULL);
+
   impl = GTK_FILE_CHOOSER_DEFAULT (widget);
 
   GTK_WIDGET_CLASS (parent_class)->map (widget);
@@ -4409,6 +4737,8 @@ gtk_file_chooser_default_map (GtkWidget *widget)
     }
 
   bookmarks_changed_cb (impl->file_system, impl);
+
+  profile_end ("end", NULL);
 }
 
 static gboolean
@@ -4587,6 +4917,8 @@ load_timeout_cb (gpointer data)
 {
   GtkFileChooserDefault *impl;
 
+  profile_start ("start", NULL);
+
   GDK_THREADS_ENTER ();
 
   impl = GTK_FILE_CHOOSER_DEFAULT (data);
@@ -4601,6 +4933,8 @@ load_timeout_cb (gpointer data)
 
   GDK_THREADS_LEAVE ();
 
+  profile_end ("end", NULL);
+
   return FALSE;
 }
 
@@ -4697,12 +5031,20 @@ show_and_select_paths (GtkFileChooserDefault *impl,
   gboolean have_hidden;
   gboolean have_filtered;
 
+  profile_start ("start", NULL);
+
   if (!only_one_path && !paths)
-    return TRUE;
+    {
+      profile_end ("end", NULL);
+      return TRUE;
+    }
 
   folder = gtk_file_system_get_folder (impl->file_system, parent_path, GTK_FILE_INFO_IS_HIDDEN, error);
   if (!folder)
-    return FALSE;
+    {
+      profile_end ("end", NULL);
+      return FALSE;
+    }
 
   success = FALSE;
   have_hidden = FALSE;
@@ -4755,7 +5097,10 @@ show_and_select_paths (GtkFileChooserDefault *impl,
   g_object_unref (folder);
 
   if (!success)
-    return FALSE;
+    {
+      profile_end ("end", NULL);
+      return FALSE;
+    }
 
   if (have_hidden)
     g_object_set (impl, "show-hidden", TRUE, NULL);
@@ -4778,6 +5123,7 @@ show_and_select_paths (GtkFileChooserDefault *impl,
        }
     }
 
+  profile_end ("end", NULL);
   return TRUE;
 }
 
@@ -4823,6 +5169,8 @@ static void
 browse_files_model_finished_loading_cb (GtkFileSystemModel    *model,
                                        GtkFileChooserDefault *impl)
 {
+  profile_start ("start", NULL);
+
   if (impl->load_state == LOAD_PRELOAD)
     {
       load_remove_timer (impl);
@@ -4837,6 +5185,7 @@ browse_files_model_finished_loading_cb (GtkFileSystemModel    *model,
       /* We can't g_assert_not_reached(), as something other than us may have
        *  initiated a folder reload.  See #165556.
        */
+      profile_end ("end", NULL);
       return;
     }
 
@@ -4846,6 +5195,8 @@ browse_files_model_finished_loading_cb (GtkFileSystemModel    *model,
 
   pending_select_paths_process (impl);
   set_busy_cursor (impl, FALSE);
+
+  profile_end ("end", NULL);
 }
 
 /* Gets rid of the old list model and creates a new one for the current folder */
@@ -4855,6 +5206,8 @@ set_list_model (GtkFileChooserDefault *impl,
 {
   g_assert (impl->current_folder != NULL);
 
+  profile_start ("start", NULL);
+
   load_remove_timer (impl); /* This changes the state to LOAD_EMPTY */
 
   if (impl->browse_files_model)
@@ -4879,6 +5232,7 @@ set_list_model (GtkFileChooserDefault *impl,
   if (!impl->browse_files_model)
     {
       set_busy_cursor (impl, FALSE);
+      profile_end ("end", NULL);
       return FALSE;
     }
 
@@ -4891,6 +5245,8 @@ set_list_model (GtkFileChooserDefault *impl,
 
   install_list_model_filter (impl);
 
+  profile_end ("end", NULL);
+
   return TRUE;
 }
 
@@ -4901,15 +5257,19 @@ update_chooser_entry (GtkFileChooserDefault *impl)
   const GtkFileInfo *info;
   GtkTreeIter iter;
   GtkTreeIter child_iter;
+  gboolean change_entry;
 
-  if (impl->action != GTK_FILE_CHOOSER_ACTION_SAVE)
+  if (!(impl->action == GTK_FILE_CHOOSER_ACTION_SAVE || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER))
     return;
 
   g_assert (!impl->select_multiple);
   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
 
   if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
-    return;
+    {
+      _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry), "");
+      return;
+    }
 
   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
                                                  &child_iter,
@@ -4917,7 +5277,12 @@ update_chooser_entry (GtkFileChooserDefault *impl)
 
   info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
 
-  if (!gtk_file_info_get_is_folder (info))
+  if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
+    change_entry = !gtk_file_info_get_is_folder (info); /* We don't want the name to change when clicking on a folder... */
+  else
+    change_entry = TRUE;                                /* ... unless we are in CREATE_FOLDER mode */
+
+  if (change_entry)
     _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry),
                                           gtk_file_info_get_display_name (info));
 }
@@ -4926,10 +5291,21 @@ static gboolean
 gtk_file_chooser_default_set_current_folder (GtkFileChooser    *chooser,
                                             const GtkFilePath *path,
                                             GError           **error)
+{
+  return gtk_file_chooser_default_update_current_folder (chooser, path, FALSE, error);
+}
+
+static gboolean
+gtk_file_chooser_default_update_current_folder (GtkFileChooser    *chooser,
+                                               const GtkFilePath *path,
+                                               gboolean           keep_trail,
+                                               GError           **error)
 {
   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
   gboolean result;
 
+  profile_start ("start", (char *) path);
+
   g_assert (path != NULL);
 
   if (impl->local_only &&
@@ -4940,15 +5316,22 @@ gtk_file_chooser_default_set_current_folder (GtkFileChooser    *chooser,
                   GTK_FILE_CHOOSER_ERROR_BAD_FILENAME,
                   _("Cannot change to folder because it is not local"));
 
+      profile_end ("end - not local", (char *) path);
       return FALSE;
     }
 
   /* Test validity of path here.  */
   if (!check_is_folder (impl->file_system, path, error))
-    return FALSE;
+    {
+      profile_end ("end - not a folder", (char *) path);
+      return FALSE;
+    }
 
-  if (!_gtk_path_bar_set_path (GTK_PATH_BAR (impl->browse_path_bar), path, error))
-    return FALSE;
+  if (!_gtk_path_bar_set_path (GTK_PATH_BAR (impl->browse_path_bar), path, keep_trail, error))
+    {
+      profile_end ("end - could not set path bar", (char *) path);
+      return FALSE;
+    }
 
   if (impl->current_folder != path)
     {
@@ -4991,6 +5374,7 @@ gtk_file_chooser_default_set_current_folder (GtkFileChooser    *chooser,
 
   g_signal_emit_by_name (impl, "selection-changed", 0);
 
+  profile_end ("end", NULL);
   return result;
 }
 
@@ -5011,6 +5395,7 @@ gtk_file_chooser_default_set_current_name (GtkFileChooser *chooser,
   g_return_if_fail (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
                    || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
 
+  pending_select_paths_free (impl);
   _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry), name);
 }
 
@@ -5149,13 +5534,24 @@ gtk_file_chooser_default_unselect_all (GtkFileChooser *chooser)
   GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
 
   gtk_tree_selection_unselect_all (selection);
+  pending_select_paths_free (impl);
 }
 
-/* Checks whether the filename entry for the Save modes contains a valid filename */
-static GtkFilePath *
+/* Checks whether the filename entry for the Save modes contains a well-formed filename.
+ *
+ * is_well_formed_ret - whether what the user typed passes gkt_file_system_make_path()
+ *
+ * is_empty_ret - whether the file entry is totally empty
+ *
+ * is_file_part_empty_ret - whether the file part is empty (will be if user types "foobar/", and
+ *                          the path will be "$cwd/foobar")
+ */
+static void
 check_save_entry (GtkFileChooserDefault *impl,
-                 gboolean              *is_valid,
-                 gboolean              *is_empty)
+                 GtkFilePath          **path_ret,
+                 gboolean              *is_well_formed_ret,
+                 gboolean              *is_empty_ret,
+                 gboolean              *is_file_part_empty_ret)
 {
   GtkFileChooserEntry *chooser_entry;
   const GtkFilePath *current_folder;
@@ -5168,17 +5564,31 @@ check_save_entry (GtkFileChooserDefault *impl,
 
   chooser_entry = GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry);
 
+  if (strlen (gtk_entry_get_text (GTK_ENTRY (chooser_entry))) == 0)
+    {
+      *path_ret = NULL;
+      *is_well_formed_ret = TRUE;
+      *is_empty_ret = TRUE;
+      *is_file_part_empty_ret = TRUE;
+
+      return;
+    }
+
+  *is_empty_ret = FALSE;
+
   current_folder = _gtk_file_chooser_entry_get_current_folder (chooser_entry);
   file_part = _gtk_file_chooser_entry_get_file_part (chooser_entry);
 
   if (!file_part || file_part[0] == '\0')
     {
-      *is_valid = FALSE;
-      *is_empty = TRUE;
-      return NULL;
+      *path_ret = gtk_file_path_copy (current_folder);
+      *is_well_formed_ret = TRUE;
+      *is_file_part_empty_ret = TRUE;
+
+      return;
     }
 
-  *is_empty = FALSE;
+  *is_file_part_empty_ret = FALSE;
 
   error = NULL;
   path = gtk_file_system_make_path (impl->file_system, current_folder, file_part, &error);
@@ -5186,12 +5596,14 @@ check_save_entry (GtkFileChooserDefault *impl,
   if (!path)
     {
       error_building_filename_dialog (impl, current_folder, file_part, error);
-      *is_valid = FALSE;
-      return NULL;
+      *path_ret = NULL;
+      *is_well_formed_ret = FALSE;
+
+      return;
     }
 
-  *is_valid = TRUE;
-  return path;
+  *path_ret = path;
+  *is_well_formed_ret = TRUE;
 }
 
 struct get_paths_closure {
@@ -5237,11 +5649,21 @@ gtk_file_chooser_default_get_paths (GtkFileChooser *chooser)
   if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
       || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
     {
-      gboolean is_valid, is_empty;
+      gboolean is_well_formed, is_empty, is_file_part_empty;
 
-      info.path_from_entry = check_save_entry (impl, &is_valid, &is_empty);
-      if (!is_valid && !is_empty)
+      check_save_entry (impl, &info.path_from_entry, &is_well_formed, &is_empty, &is_file_part_empty);
+
+      if (!is_well_formed)
        return NULL;
+
+      if (!is_empty)
+       {
+         if (is_file_part_empty && impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
+           {
+             gtk_file_path_free (info.path_from_entry);
+             return NULL;
+           }
+       }
     }
 
   if (!info.path_from_entry || impl->select_multiple)
@@ -5629,6 +6051,198 @@ switch_to_selected_folder (GtkFileChooserDefault *impl)
   change_folder_and_display_error (impl, closure.path);
 }
 
+/* Gets the GtkFileInfo for the selected row in the file list; assumes single
+ * selection mode.
+ */
+static const GtkFileInfo *
+get_selected_file_info_from_file_list (GtkFileChooserDefault *impl,
+                                      gboolean              *had_selection)
+{
+  GtkTreeSelection *selection;
+  GtkTreeIter iter, child_iter;
+  const GtkFileInfo *info;
+
+  g_assert (!impl->select_multiple);
+  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
+  if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
+    {
+      *had_selection = FALSE;
+      return NULL;
+    }
+
+  *had_selection = TRUE;
+
+  gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
+                                                 &child_iter,
+                                                 &iter);
+
+  info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
+  return info;
+}
+
+/* Gets the display name of the selected file in the file list; assumes single
+ * selection mode and that something is selected.
+ */
+static const gchar *
+get_display_name_from_file_list (GtkFileChooserDefault *impl)
+{
+  const GtkFileInfo *info;
+  gboolean had_selection;
+
+  info = get_selected_file_info_from_file_list (impl, &had_selection);
+  g_assert (had_selection);
+  g_assert (info != NULL);
+
+  return gtk_file_info_get_display_name (info);
+}
+
+static void
+add_custom_button_to_dialog (GtkDialog   *dialog,
+                            const gchar *mnemonic_label,
+                            const gchar *stock_id,
+                            gint         response_id)
+{
+  GtkWidget *button;
+
+  button = gtk_button_new_with_mnemonic (mnemonic_label);
+  GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
+  gtk_button_set_image (GTK_BUTTON (button),
+                       gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON));
+  gtk_widget_show (button);
+
+  gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button, response_id);
+}
+
+/* Presents an overwrite confirmation dialog; returns whether we should accept
+ * the filename.
+ */
+static gboolean
+confirm_dialog_should_accept_filename (GtkFileChooserDefault *impl,
+                                      const gchar           *file_part,
+                                      const gchar           *folder_display_name)
+{
+  GtkWindow *toplevel;
+  GtkWidget *dialog;
+  int response;
+
+  toplevel = get_toplevel (GTK_WIDGET (impl));
+
+  dialog = gtk_message_dialog_new (toplevel,
+                                  GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+                                  GTK_MESSAGE_QUESTION,
+                                  GTK_BUTTONS_NONE,
+                                  _("A file named \"%s\" already exists.  Do you want to replace it?"),
+                                  file_part);
+  gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+                                           _("The file already exists in \"%s\".  Replacing it will "
+                                             "overwrite its contents."),
+                                           folder_display_name);
+
+  gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+  add_custom_button_to_dialog (GTK_DIALOG (dialog), _("_Replace"), GTK_STOCK_SAVE_AS, GTK_RESPONSE_ACCEPT);
+  gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
+  
+  if (toplevel->group)
+    gtk_window_group_add_window (toplevel->group, GTK_WINDOW (dialog));
+
+  response = gtk_dialog_run (GTK_DIALOG (dialog));
+
+  gtk_widget_destroy (dialog);
+
+  return (response == GTK_RESPONSE_ACCEPT);
+}
+
+static char *
+get_display_name_for_folder (GtkFileChooserDefault *impl,
+                            const GtkFilePath     *path)
+{
+  char *display_name;
+  GtkFilePath *parent_path;
+  GtkFileFolder *parent_folder;
+  GtkFileInfo *info;
+
+  display_name = NULL;
+  parent_path = NULL;
+  parent_folder = NULL;
+  info = NULL;
+
+  if (!gtk_file_system_get_parent (impl->file_system, path, &parent_path, NULL))
+    goto out;
+
+  parent_folder = gtk_file_system_get_folder (impl->file_system,
+                                             parent_path ? parent_path : path,
+                                             GTK_FILE_INFO_DISPLAY_NAME,
+                                             NULL);
+  if (!parent_folder)
+    goto out;
+
+  info = gtk_file_folder_get_info (parent_folder, parent_path ? path : NULL, NULL);
+  if (!info)
+    goto out;
+
+  display_name = g_strdup (gtk_file_info_get_display_name (info));
+
+ out:
+
+  if (parent_path)
+    gtk_file_path_free (parent_path);
+
+  if (parent_folder)
+    g_object_unref (parent_folder);
+
+  if (info)
+    gtk_file_info_free (info);
+
+  return display_name;
+}
+
+/* Does overwrite confirmation if appropriate, and returns whether the dialog
+ * should respond.  Can get the file part from the file list or the save entry.
+ */
+static gboolean
+should_respond_after_confirm_overwrite (GtkFileChooserDefault *impl,
+                                       const gchar           *file_part,
+                                       const GtkFilePath     *parent_path)
+{
+  GtkFileChooserConfirmation conf;
+
+  if (!impl->do_overwrite_confirmation)
+    return TRUE;
+
+  conf = GTK_FILE_CHOOSER_CONFIRMATION_CONFIRM;
+
+  g_signal_emit_by_name (impl, "confirm-overwrite", &conf);
+
+  switch (conf)
+    {
+    case GTK_FILE_CHOOSER_CONFIRMATION_CONFIRM:
+      {
+       char *parent_display_name;
+       gboolean retval;
+
+       g_assert (file_part != NULL);
+
+       parent_display_name = get_display_name_for_folder (impl, parent_path);
+       if (!parent_display_name)
+         return TRUE; /* Huh?  Did the folder disappear?  Let the caller deal with it */
+
+       retval = confirm_dialog_should_accept_filename (impl, file_part, parent_display_name);
+       g_free (parent_display_name);
+       return retval;
+      }
+
+    case GTK_FILE_CHOOSER_CONFIRMATION_ACCEPT_FILENAME:
+      return TRUE;
+
+    case GTK_FILE_CHOOSER_CONFIRMATION_SELECT_AGAIN:
+      return FALSE;
+
+    default:
+      g_assert_not_reached ();
+      return FALSE;
+    }
+}
+
 /* Implementation for GtkFileChooserEmbed::should_respond() */
 static gboolean
 gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed)
@@ -5646,49 +6260,88 @@ gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed)
 
   if (current_focus == impl->browse_files_tree_view)
     {
+      /* The following array encodes what we do based on the impl->action and the
+       * number of files selected.
+       */
+      typedef enum {
+       NOOP,                   /* Do nothing (don't respond) */
+       RESPOND,                /* Respond immediately */
+       RESPOND_OR_SWITCH,      /* Respond immediately if the selected item is a file; switch to it if it is a folder */
+       ALL_FILES,              /* Respond only if everything selected is a file */
+       ALL_FOLDERS,            /* Respond only if everything selected is a folder */
+       SAVE_ENTRY,             /* Go to the code for handling the save entry */
+       NOT_REACHED             /* Sanity check */
+      } ActionToTake;
+      static const ActionToTake what_to_do[4][3] = {
+       /*                                0 selected            1 selected              many selected */
+       /* ACTION_OPEN */               { NOOP,                 RESPOND_OR_SWITCH,      ALL_FILES   },
+       /* ACTION_SAVE */               { SAVE_ENTRY,           RESPOND_OR_SWITCH,      NOT_REACHED },
+       /* ACTION_SELECT_FOLDER */      { RESPOND,              ALL_FOLDERS,            ALL_FOLDERS },
+       /* ACTION_CREATE_FOLDER */      { SAVE_ENTRY,           ALL_FOLDERS,            NOT_REACHED }
+      };
+
       int num_selected;
       gboolean all_files, all_folders;
+      int k;
+      ActionToTake action;
 
     file_list:
 
+      g_assert (impl->action >= GTK_FILE_CHOOSER_ACTION_OPEN && impl->action <= GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
+
       selection_check (impl, &num_selected, &all_files, &all_folders);
 
-      if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
+      if (num_selected > 2)
+       k = 2;
+      else
+       k = num_selected;
+
+      action = what_to_do [impl->action] [k];
+
+      switch (action)
        {
-         if (num_selected != 1)         
-           return TRUE; /* zero means current folder; more than one means use the whole selection */    
-         else if (current_focus != impl->browse_files_tree_view)        
+       case NOOP:
+         return FALSE;
+
+       case RESPOND:
+         return TRUE;
+
+       case RESPOND_OR_SWITCH:
+         g_assert (num_selected == 1);
+
+         if (all_folders)
            {
-             /* a single folder is selected and a button was clicked */
-             switch_to_selected_folder (impl);          
-             return TRUE;
+             switch_to_selected_folder (impl);
+             return FALSE;
            }
-       }
-
-      if (num_selected == 0)
-       {
-         if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
-             || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
-           goto save_entry; /* it makes sense to use the typed name */
+         else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
+           return should_respond_after_confirm_overwrite (impl,
+                                                          get_display_name_from_file_list (impl),
+                                                          impl->current_folder);
          else
-           return FALSE;
-       }
+           return TRUE;
 
-      if (num_selected == 1 && all_folders)
-       {
-         switch_to_selected_folder (impl);
-         return FALSE;
+       case ALL_FILES:
+         return all_files;
+
+       case ALL_FOLDERS:
+         return all_folders;
+
+       case SAVE_ENTRY:
+         goto save_entry;
+
+       default:
+         g_assert_not_reached ();
        }
-      else
-       return all_files;
     }
   else if (current_focus == impl->save_file_name_entry)
     {
       GtkFilePath *path;
-      gboolean is_valid, is_empty;
+      gboolean is_well_formed, is_empty, is_file_part_empty;
       gboolean is_folder;
       gboolean retval;
-      GtkFileChooserEntry *entry;  
+      GtkFileChooserEntry *entry;
+      GError *error;
 
     save_entry:
 
@@ -5696,34 +6349,93 @@ gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed)
                || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
 
       entry = GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry);
-      path = check_save_entry (impl, &is_valid, &is_empty);
+      check_save_entry (impl, &path, &is_well_formed, &is_empty, &is_file_part_empty);
 
-      if (!is_empty && !is_valid)
+      if (is_empty || !is_well_formed)
        return FALSE;
 
-      if (is_empty)
-       path = gtk_file_path_copy (_gtk_file_chooser_entry_get_current_folder (entry));
-      
-      is_folder = check_is_folder (impl->file_system, path, NULL);
+      g_assert (path != NULL);
+
+      error = NULL;
+      is_folder = check_is_folder (impl->file_system, path, &error);
       if (is_folder)
        {
-         _gtk_file_chooser_entry_set_file_part (entry, "");
-         change_folder_and_display_error (impl, path);
-         retval = FALSE;
+         if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
+           {
+             _gtk_file_chooser_entry_set_file_part (entry, "");
+             change_folder_and_display_error (impl, path);
+             retval = FALSE;
+           }
+         else /* GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER */
+           {
+             /* The folder already exists, so we do not need to create it.
+              * Just respond to terminate the dialog.
+              */
+             retval = TRUE;
+           }
        }
       else
        {
-         /* check that everything up to the last component exists */
-         gtk_file_path_free (path);
-         path = gtk_file_path_copy (_gtk_file_chooser_entry_get_current_folder (entry));
-         is_folder = check_is_folder (impl->file_system, path, NULL);
-         if (!is_folder)
+         gboolean file_exists_and_is_not_folder;
+
+         file_exists_and_is_not_folder = g_error_matches (error, GTK_FILE_SYSTEM_ERROR, GTK_FILE_SYSTEM_ERROR_NOT_FOLDER);
+
+         if (impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER && file_exists_and_is_not_folder)
            {
-             change_folder_and_display_error (impl, path);
+             /* Oops, the user typed the name of an existing path which is not a folder */
+             error_creating_folder_over_existing_file_dialog (impl, path, error);
+             error = NULL; /* as it will be freed below for the general case */
              retval = FALSE;
            }
          else
-           retval = TRUE;
+           {
+             GtkFilePath *parent_path;
+             gboolean parent_is_folder;
+
+             /* check that everything up to the last component exists */
+
+             parent_path = gtk_file_path_copy (_gtk_file_chooser_entry_get_current_folder (entry));
+             parent_is_folder = check_is_folder (impl->file_system, parent_path, NULL);
+             if (parent_is_folder)
+               {
+                 if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
+                   {
+                     if (file_exists_and_is_not_folder)
+                       {
+                         const char *file_part;
+
+                         file_part = _gtk_file_chooser_entry_get_file_part (GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry));
+                         retval = should_respond_after_confirm_overwrite (impl, file_part, parent_path);
+                       }
+                     else
+                       retval = TRUE;
+                   }
+                 else /* GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER */
+                   {
+                     GError *create_error;
+
+                     create_error = NULL;
+                     if (gtk_file_system_create_folder (impl->file_system, path, &create_error))
+                       retval = TRUE;
+                     else
+                       {
+                         error_creating_folder_dialog (impl, path, create_error);
+                         retval = FALSE;
+                       }
+                   }
+               }
+             else
+               {
+                 /* This will display an error, which is what we want */
+                 change_folder_and_display_error (impl, parent_path);
+                 retval = FALSE;
+               }
+
+             gtk_file_path_free (parent_path);
+           }
+
+         if (error != NULL)
+           g_error_free (error);
        }
 
       gtk_file_path_free (path);
@@ -6064,24 +6776,19 @@ list_selection_changed (GtkTreeSelection      *selection,
   /* See if we are in the new folder editable row for Save mode */
   if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
     {
-      GtkTreeSelection *selection;
-      GtkTreeIter iter, child_iter;
       const GtkFileInfo *info;
+      gboolean had_selection;
 
-      g_assert (!impl->select_multiple);
-      selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
-      if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
-       return;
+      info = get_selected_file_info_from_file_list (impl, &had_selection);
+      if (!had_selection)
+       goto out; /* normal processing */
 
-      gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
-                                                     &child_iter,
-                                                     &iter);
-
-      info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
       if (!info)
        return; /* We are on the editable row for New Folder */
     }
 
+ out:
+
   update_chooser_entry (impl);
   check_preview_change (impl);
   bookmarks_check_add_sensitivity (impl);
@@ -6164,6 +6871,8 @@ list_icon_data_func (GtkTreeViewColumn *tree_column,
   GdkPixbuf *pixbuf;
   const GtkFileInfo *info; 
   gboolean sensitive = TRUE;
+
+  profile_start ("start", NULL);
   
   info = get_list_file_info (impl, iter);
 
@@ -6195,6 +6904,8 @@ list_icon_data_func (GtkTreeViewColumn *tree_column,
     
   if (pixbuf)
     g_object_unref (pixbuf);
+
+  profile_end ("end", NULL);
 }
 
 static void
@@ -6303,28 +7014,34 @@ list_mtime_data_func (GtkTreeViewColumn *tree_column,
     }
 
   time_mtime = gtk_file_info_get_modification_time (info);
-  g_date_set_time (&mtime, (GTime) time_mtime);
 
-  time_now = (GTime ) time (NULL);
-  g_date_set_time (&now, (GTime) time_now);
-
-  days_diff = g_date_get_julian (&now) - g_date_get_julian (&mtime);
-
-  if (days_diff == 0)
-    strcpy (buf, _("Today"));
-  else if (days_diff == 1)
-    strcpy (buf, _("Yesterday"));
+  if (time_mtime == 0)
+    strcpy (buf, _("Unknown"));
   else
     {
-      char *format;
+      g_date_set_time (&mtime, (GTime) time_mtime);
+
+      time_now = (GTime ) time (NULL);
+      g_date_set_time (&now, (GTime) time_now);
 
-      if (days_diff > 1 && days_diff < 7)
-       format = "%A"; /* Days from last week */
+      days_diff = g_date_get_julian (&now) - g_date_get_julian (&mtime);
+
+      if (days_diff == 0)
+       strcpy (buf, _("Today"));
+      else if (days_diff == 1)
+       strcpy (buf, _("Yesterday"));
       else
-       format = "%x"; /* Any other date */
+       {
+         char *format;
+
+         if (days_diff > 1 && days_diff < 7)
+           format = "%A"; /* Days from last week */
+         else
+           format = "%x"; /* Any other date */
 
-      if (g_date_strftime (buf, sizeof (buf), format, &mtime) == 0)
-       strcpy (buf, _("Unknown"));
+         if (g_date_strftime (buf, sizeof (buf), format, &mtime) == 0)
+           strcpy (buf, _("Unknown"));
+       }
     }
 
   if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
@@ -6527,6 +7244,9 @@ location_popup_handler (GtkFileChooserDefault *impl,
                                        GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                                        accept_stock, GTK_RESPONSE_ACCEPT,
                                        NULL);
+  if (toplevel->group)
+    gtk_window_group_add_window (toplevel->group, GTK_WINDOW (dialog));
+  
   gtk_window_set_default_size (GTK_WINDOW (dialog), 300, -1);
   gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
   gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 2);