]> Pileus Git - ~andy/gtk/commitdiff
Rework the user interface of the file chooser, as per Seth Nickell's
authorFederico Mena Quintero <federico@ximian.com>
Mon, 23 Feb 2004 19:55:49 +0000 (19:55 +0000)
committerFederico Mena Quintero <federico@src.gnome.org>
Mon, 23 Feb 2004 19:55:49 +0000 (19:55 +0000)
2004-02-23  Federico Mena Quintero  <federico@ximian.com>

Rework the user interface of the file chooser, as per
Seth Nickell's design.

* gtk/gtkfilechooserdefault.c
(gtk_file_chooser_default_class_init): Add binding signals and bindings:
"location-popup" - C-l
"up-folder"      - C-Up
"home-folder"    - C-Home
(up_folder_handler): New function; moved the code from up_button_clicked().
(up_button_clicked_cb): Call up_folder_handler().
(home_folder_handler): New function.
(location_popup_handler): New function.
(struct _GtkFileChooserDefault): Add an hpaned field.
(gtk_file_chooser_default_set_current_name): Check that we are in
Save mode.
(save_widgets_create): New function, create the widgets specific
to Save mode.
(main_paned_create): New function, create the hpaned's widgets here.
(gtk_file_chooser_default_constructor): Create the open and save
widgets, and show only one set.
(gtk_file_chooser_default_set_property): Show/hide the save widgets.
(gtk_file_chooser_default_get_paths): Only pay attention to the
entry in Save mode.
(update_chooser_entry): Update the entry only in Save mode.
(entry_activate): Removed.

ChangeLog
ChangeLog.pre-2-10
ChangeLog.pre-2-4
ChangeLog.pre-2-6
ChangeLog.pre-2-8
gtk/gtkfilechooserdefault.c

index eb3c62f68d91f2567fb35ec80776e942851ff396..ff2f10bc062c7a513b07d4852d626ca12a41835e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,31 @@
+2004-02-23  Federico Mena Quintero  <federico@ximian.com>
+
+       Rework the user interface of the file chooser, as per 
+       Seth Nickell's design.
+
+       * gtk/gtkfilechooserdefault.c
+       (gtk_file_chooser_default_class_init): Add binding signals and bindings:
+               "location-popup" - C-l
+               "up-folder"      - C-Up
+               "home-folder"    - C-Home
+       (up_folder_handler): New function; moved the code from up_button_clicked().
+       (up_button_clicked_cb): Call up_folder_handler().
+       (home_folder_handler): New function.
+       (location_popup_handler): New function.
+       (struct _GtkFileChooserDefault): Add an hpaned field.
+       (gtk_file_chooser_default_set_current_name): Check that we are in
+       Save mode.
+       (save_widgets_create): New function, create the widgets specific
+       to Save mode.
+       (main_paned_create): New function, create the hpaned's widgets here.
+       (gtk_file_chooser_default_constructor): Create the open and save
+       widgets, and show only one set.
+       (gtk_file_chooser_default_set_property): Show/hide the save widgets.
+       (gtk_file_chooser_default_get_paths): Only pay attention to the
+       entry in Save mode.
+       (update_chooser_entry): Update the entry only in Save mode.
+       (entry_activate): Removed.
+
 Mon Feb 23 11:20:34 2004  Owen Taylor  <otaylor@redhat.com>
 
        * configure.in: Remove the configure.in check for fontconfig 
index eb3c62f68d91f2567fb35ec80776e942851ff396..ff2f10bc062c7a513b07d4852d626ca12a41835e 100644 (file)
@@ -1,3 +1,31 @@
+2004-02-23  Federico Mena Quintero  <federico@ximian.com>
+
+       Rework the user interface of the file chooser, as per 
+       Seth Nickell's design.
+
+       * gtk/gtkfilechooserdefault.c
+       (gtk_file_chooser_default_class_init): Add binding signals and bindings:
+               "location-popup" - C-l
+               "up-folder"      - C-Up
+               "home-folder"    - C-Home
+       (up_folder_handler): New function; moved the code from up_button_clicked().
+       (up_button_clicked_cb): Call up_folder_handler().
+       (home_folder_handler): New function.
+       (location_popup_handler): New function.
+       (struct _GtkFileChooserDefault): Add an hpaned field.
+       (gtk_file_chooser_default_set_current_name): Check that we are in
+       Save mode.
+       (save_widgets_create): New function, create the widgets specific
+       to Save mode.
+       (main_paned_create): New function, create the hpaned's widgets here.
+       (gtk_file_chooser_default_constructor): Create the open and save
+       widgets, and show only one set.
+       (gtk_file_chooser_default_set_property): Show/hide the save widgets.
+       (gtk_file_chooser_default_get_paths): Only pay attention to the
+       entry in Save mode.
+       (update_chooser_entry): Update the entry only in Save mode.
+       (entry_activate): Removed.
+
 Mon Feb 23 11:20:34 2004  Owen Taylor  <otaylor@redhat.com>
 
        * configure.in: Remove the configure.in check for fontconfig 
index eb3c62f68d91f2567fb35ec80776e942851ff396..ff2f10bc062c7a513b07d4852d626ca12a41835e 100644 (file)
@@ -1,3 +1,31 @@
+2004-02-23  Federico Mena Quintero  <federico@ximian.com>
+
+       Rework the user interface of the file chooser, as per 
+       Seth Nickell's design.
+
+       * gtk/gtkfilechooserdefault.c
+       (gtk_file_chooser_default_class_init): Add binding signals and bindings:
+               "location-popup" - C-l
+               "up-folder"      - C-Up
+               "home-folder"    - C-Home
+       (up_folder_handler): New function; moved the code from up_button_clicked().
+       (up_button_clicked_cb): Call up_folder_handler().
+       (home_folder_handler): New function.
+       (location_popup_handler): New function.
+       (struct _GtkFileChooserDefault): Add an hpaned field.
+       (gtk_file_chooser_default_set_current_name): Check that we are in
+       Save mode.
+       (save_widgets_create): New function, create the widgets specific
+       to Save mode.
+       (main_paned_create): New function, create the hpaned's widgets here.
+       (gtk_file_chooser_default_constructor): Create the open and save
+       widgets, and show only one set.
+       (gtk_file_chooser_default_set_property): Show/hide the save widgets.
+       (gtk_file_chooser_default_get_paths): Only pay attention to the
+       entry in Save mode.
+       (update_chooser_entry): Update the entry only in Save mode.
+       (entry_activate): Removed.
+
 Mon Feb 23 11:20:34 2004  Owen Taylor  <otaylor@redhat.com>
 
        * configure.in: Remove the configure.in check for fontconfig 
index eb3c62f68d91f2567fb35ec80776e942851ff396..ff2f10bc062c7a513b07d4852d626ca12a41835e 100644 (file)
@@ -1,3 +1,31 @@
+2004-02-23  Federico Mena Quintero  <federico@ximian.com>
+
+       Rework the user interface of the file chooser, as per 
+       Seth Nickell's design.
+
+       * gtk/gtkfilechooserdefault.c
+       (gtk_file_chooser_default_class_init): Add binding signals and bindings:
+               "location-popup" - C-l
+               "up-folder"      - C-Up
+               "home-folder"    - C-Home
+       (up_folder_handler): New function; moved the code from up_button_clicked().
+       (up_button_clicked_cb): Call up_folder_handler().
+       (home_folder_handler): New function.
+       (location_popup_handler): New function.
+       (struct _GtkFileChooserDefault): Add an hpaned field.
+       (gtk_file_chooser_default_set_current_name): Check that we are in
+       Save mode.
+       (save_widgets_create): New function, create the widgets specific
+       to Save mode.
+       (main_paned_create): New function, create the hpaned's widgets here.
+       (gtk_file_chooser_default_constructor): Create the open and save
+       widgets, and show only one set.
+       (gtk_file_chooser_default_set_property): Show/hide the save widgets.
+       (gtk_file_chooser_default_get_paths): Only pay attention to the
+       entry in Save mode.
+       (update_chooser_entry): Update the entry only in Save mode.
+       (entry_activate): Removed.
+
 Mon Feb 23 11:20:34 2004  Owen Taylor  <otaylor@redhat.com>
 
        * configure.in: Remove the configure.in check for fontconfig 
index eb3c62f68d91f2567fb35ec80776e942851ff396..ff2f10bc062c7a513b07d4852d626ca12a41835e 100644 (file)
@@ -1,3 +1,31 @@
+2004-02-23  Federico Mena Quintero  <federico@ximian.com>
+
+       Rework the user interface of the file chooser, as per 
+       Seth Nickell's design.
+
+       * gtk/gtkfilechooserdefault.c
+       (gtk_file_chooser_default_class_init): Add binding signals and bindings:
+               "location-popup" - C-l
+               "up-folder"      - C-Up
+               "home-folder"    - C-Home
+       (up_folder_handler): New function; moved the code from up_button_clicked().
+       (up_button_clicked_cb): Call up_folder_handler().
+       (home_folder_handler): New function.
+       (location_popup_handler): New function.
+       (struct _GtkFileChooserDefault): Add an hpaned field.
+       (gtk_file_chooser_default_set_current_name): Check that we are in
+       Save mode.
+       (save_widgets_create): New function, create the widgets specific
+       to Save mode.
+       (main_paned_create): New function, create the hpaned's widgets here.
+       (gtk_file_chooser_default_constructor): Create the open and save
+       widgets, and show only one set.
+       (gtk_file_chooser_default_set_property): Show/hide the save widgets.
+       (gtk_file_chooser_default_get_paths): Only pay attention to the
+       entry in Save mode.
+       (update_chooser_entry): Update the entry only in Save mode.
+       (entry_activate): Removed.
+
 Mon Feb 23 11:20:34 2004  Owen Taylor  <otaylor@redhat.com>
 
        * configure.in: Remove the configure.in check for fontconfig 
index f1c2aa260dfee262e9a8ebcc01c4738c2f8f2607..33c00b91950c204be0d9c7f7a07e898c03ccab11 100644 (file)
  * Boston, MA 02111-1307, USA.
  */
 
+#define USE_PATH_BAR
+
+#include "gdk/gdkkeysyms.h"
 #include "gtkalignment.h"
+#include "gtkbindings.h"
 #include "gtkbutton.h"
 #include "gtkcellrendererpixbuf.h"
 #include "gtkcellrendererseptext.h"
 #include "gtkcellrenderertext.h"
 #include "gtkcombobox.h"
 #include "gtkentry.h"
+#include "gtkexpander.h"
 #include "gtkfilechooserdefault.h"
 #include "gtkfilechooserentry.h"
 #include "gtkfilechooserutils.h"
@@ -37,6 +42,7 @@
 #include "gtkimage.h"
 #include "gtkintl.h"
 #include "gtklabel.h"
+#include "gtkmarshalers.h"
 #include "gtkmenuitem.h"
 #include "gtkmessagedialog.h"
 #include "gtkpathbar.h"
@@ -76,14 +82,19 @@ struct _GtkFileChooserDefault
 {
   GtkVBox parent_instance;
 
+  GtkFileChooserAction action;
+
   GtkFileSystem *file_system;
+
+  /* Things common to Open and Save mode */
+
+  GtkWidget *hpaned;
+
   GtkFileSystemModel *tree_model;
   GtkTreeStore *shortcuts_model;
   GtkFileSystemModel *list_model;
   GtkTreeModelSort *sort_model;
 
-  GtkFileChooserAction action;
-
   GtkFileFilter *current_filter;
   GSList *filters;
 
@@ -95,7 +106,6 @@ struct _GtkFileChooserDefault
   int num_bookmarks;
 
   guint volumes_changed_id;
-
   guint bookmarks_changed_id;
 
   GtkFilePath *current_volume_path;
@@ -115,7 +125,6 @@ struct _GtkFileChooserDefault
   GtkWidget *remove_bookmark_button;
   GtkWidget *list_scrollwin;
   GtkWidget *list;
-  GtkWidget *entry;
   GtkWidget *preview_widget;
   GtkWidget *extra_widget;
   GtkWidget *path_bar;
@@ -123,6 +132,15 @@ struct _GtkFileChooserDefault
   GtkTreeViewColumn *list_name_column;
   GtkCellRenderer *list_name_renderer;
 
+  /* Things for Save mode */
+
+  GtkWidget *save_widgets;
+
+  GtkWidget *entry;
+  GtkWidget *save_folder_combo;
+
+  /* Flags */
+
   guint folder_mode : 1;
   guint local_only : 1;
   guint preview_widget_active : 1;
@@ -132,6 +150,16 @@ struct _GtkFileChooserDefault
   guint changing_folder : 1;
 };
 
+/* Signal IDs */
+enum {
+  LOCATION_POPUP,
+  UP_FOLDER,
+  HOME_FOLDER,
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
 /* Column numbers for the shortcuts tree.  Keep these in sync with create_shortcuts_model() */
 enum {
   SHORTCUTS_COL_PIXBUF,
@@ -220,6 +248,10 @@ static gboolean       gtk_file_chooser_default_remove_shortcut_folder (GtkFileCh
                                                                       GError           **error);
 static GSList *       gtk_file_chooser_default_list_shortcut_folders  (GtkFileChooser    *chooser);
 
+static void location_popup_handler (GtkFileChooserDefault *impl);
+static void up_folder_handler      (GtkFileChooserDefault *impl);
+static void home_folder_handler    (GtkFileChooserDefault *impl);
+
 static void set_current_filter   (GtkFileChooserDefault *impl,
                                  GtkFileFilter         *filter);
 static void check_preview_change (GtkFileChooserDefault *impl);
@@ -245,11 +277,11 @@ static void list_row_activated         (GtkTreeView           *tree_view,
                                        GtkTreePath           *path,
                                        GtkTreeViewColumn     *column,
                                        GtkFileChooserDefault *impl);
-static void entry_activate             (GtkEntry              *entry,
-                                       GtkFileChooserDefault *impl);
+
 static void path_bar_clicked           (GtkPathBar            *path_bar,
                                        const char            *file_path,
                                        GtkFileChooserDefault *impl);
+
 static void add_bookmark_button_clicked_cb    (GtkButton             *button,
                                               GtkFileChooserDefault *impl);
 static void remove_bookmark_button_clicked_cb (GtkButton             *button,
@@ -327,6 +359,7 @@ gtk_file_chooser_default_class_init (GtkFileChooserDefaultClass *class)
 {
   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
+  GtkBindingSet *binding_set;
 
   parent_class = g_type_class_peek_parent (class);
 
@@ -337,6 +370,56 @@ gtk_file_chooser_default_class_init (GtkFileChooserDefaultClass *class)
 
   widget_class->show_all = gtk_file_chooser_default_show_all;
 
+  signals[LOCATION_POPUP] =
+    _gtk_binding_signal_new ("location-popup",
+                            G_OBJECT_CLASS_TYPE (class),
+                            G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                            G_CALLBACK (location_popup_handler),
+                            NULL, NULL,
+                            _gtk_marshal_VOID__VOID,
+                            G_TYPE_NONE, 0);
+  signals[UP_FOLDER] =
+    _gtk_binding_signal_new ("up-folder",
+                            G_OBJECT_CLASS_TYPE (class),
+                            G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                            G_CALLBACK (up_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),
+                            G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                            G_CALLBACK (home_folder_handler),
+                            NULL, NULL,
+                            _gtk_marshal_VOID__VOID,
+                            G_TYPE_NONE, 0);
+
+  binding_set = gtk_binding_set_by_class (class);
+
+  gtk_binding_entry_add_signal (binding_set,
+                               GDK_l, GDK_CONTROL_MASK,
+                               "location-popup",
+                               0);
+
+  gtk_binding_entry_add_signal (binding_set,
+                               GDK_Up, GDK_CONTROL_MASK,
+                               "up-folder",
+                               0);
+  gtk_binding_entry_add_signal (binding_set,
+                               GDK_KP_Up, GDK_CONTROL_MASK,
+                               "up-folder",
+                               0);
+
+  gtk_binding_entry_add_signal (binding_set,
+                               GDK_Home, GDK_CONTROL_MASK,
+                               "up-folder",
+                               0);
+  gtk_binding_entry_add_signal (binding_set,
+                               GDK_KP_Home, GDK_CONTROL_MASK,
+                               "up-folder",
+                               0);
+
   _gtk_file_chooser_install_properties (gobject_class);
 }
 
@@ -409,19 +492,14 @@ gtk_file_chooser_default_finalize (GObject *object)
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
-/* Shows an error dialog */
+/* Shows an error dialog set as transient for the specified window */
 static void
-error_message (GtkFileChooserDefault *impl,
-              const char            *msg)
+error_message_with_parent (GtkWindow  *parent,
+                          const char *msg)
 {
-  GtkWidget *toplevel;
   GtkWidget *dialog;
 
-  toplevel = gtk_widget_get_toplevel (GTK_WIDGET (impl));
-  if (!GTK_WIDGET_TOPLEVEL (toplevel))
-    toplevel = NULL;
-
-  dialog = gtk_message_dialog_new (GTK_WINDOW (toplevel),
+  dialog = gtk_message_dialog_new (parent,
                                   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
                                   GTK_MESSAGE_ERROR,
                                   GTK_BUTTONS_CLOSE,
@@ -431,6 +509,21 @@ error_message (GtkFileChooserDefault *impl,
   gtk_widget_destroy (dialog);
 }
 
+/* Shows an error dialog for the file chooser */
+static void
+error_message (GtkFileChooserDefault *impl,
+              const char            *msg)
+{
+  GtkWidget *toplevel;
+
+  toplevel = gtk_widget_get_toplevel (GTK_WIDGET (impl));
+  if (!GTK_WIDGET_TOPLEVEL (toplevel))
+    toplevel = NULL;
+
+  error_message_with_parent (toplevel ? GTK_WINDOW (toplevel) : NULL,
+                            msg);
+}
+
 /* Shows a simple error dialog relative to a path.  Frees the GError as well. */
 static void
 error_dialog (GtkFileChooserDefault *impl,
@@ -1523,59 +1616,106 @@ create_filename_entry_and_filter_combo (GtkFileChooserDefault *impl)
   hbox = gtk_hbox_new (FALSE, 12);
   gtk_widget_show (hbox);
 
-  /* Label and entry */
+  /* Filter combo */
 
-  widget = gtk_label_new_with_mnemonic (_("_Filename:"));
+  widget = filter_create (impl);
   gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
+
+  return hbox;
+}
+
+/* Callback used when the "Browse for more folders" expander is toggled */
+static void
+expander_activate_cb (GtkExpander           *expander,
+                     GtkFileChooserDefault *impl)
+{
+  gboolean active;
+
+  active = gtk_expander_get_expanded (expander);
+
+  if (active)
+    gtk_widget_show (impl->hpaned);
+  else
+    gtk_widget_hide (impl->hpaned);
+}
+
+/* Creates the widgets specific to Save mode */
+static GtkWidget *
+save_widgets_create (GtkFileChooserDefault *impl)
+{
+  GtkWidget *table;
+  GtkWidget *widget;
+  GtkWidget *alignment;
+
+  table = gtk_table_new (3, 2, FALSE);
+  gtk_table_set_row_spacings (GTK_TABLE (table), 12);
+  gtk_table_set_col_spacings (GTK_TABLE (table), 12);
+
+  /* Name entry */
+
+  widget = gtk_label_new_with_mnemonic (_("_Name:"));
+  gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
+  gtk_table_attach (GTK_TABLE (table), widget,
+                   0, 1, 0, 1,
+                   GTK_FILL, GTK_FILL,
+                   0, 0);
   gtk_widget_show (widget);
 
   impl->entry = _gtk_file_chooser_entry_new ();
   gtk_entry_set_activates_default (GTK_ENTRY (impl->entry), TRUE);
-  g_signal_connect (impl->entry, "activate",
-                   G_CALLBACK (entry_activate), impl);
-  _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (impl->entry),
-                                          impl->file_system);
+  _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (impl->entry), impl->file_system);
+  _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (impl->entry), impl->current_folder);
 
-  gtk_box_pack_start (GTK_BOX (hbox), impl->entry, TRUE, TRUE, 0);
+  gtk_table_attach (GTK_TABLE (table), impl->entry,
+                   1, 2, 0, 1,
+                   GTK_EXPAND | GTK_FILL, 0,
+                   0, 0);
   gtk_widget_show (impl->entry);
-
   gtk_label_set_mnemonic_widget (GTK_LABEL (widget), impl->entry);
 
-  /* Filter combo */
+  /* Folder combo */
 
-  widget = filter_create (impl);
-  gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
+  widget = gtk_label_new_with_mnemonic (_("Save in _Folder:"));
+  gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
+  gtk_table_attach (GTK_TABLE (table), widget,
+                   0, 1, 1, 2,
+                   GTK_FILL, GTK_FILL,
+                   0, 0);
+  gtk_widget_show (widget);
 
-  return hbox;
+  /* FIXME: create the combo */
+
+  /* Expander */
+
+  alignment = gtk_alignment_new (0.0, 0.5, 1.0, 1.0);
+  gtk_table_attach (GTK_TABLE (table), alignment,
+                   0, 2, 2, 3,
+                   GTK_FILL, GTK_FILL,
+                   0, 0);
+  gtk_widget_show (alignment);
+
+  widget = gtk_expander_new_with_mnemonic (_("_Browse for other folders"));
+  gtk_container_add (GTK_CONTAINER (alignment), widget);
+  gtk_widget_show (widget);
+  g_signal_connect (widget, "activate",
+                   G_CALLBACK (expander_activate_cb),
+                   impl);
+
+  return table;  
 }
 
-static GObject*
-gtk_file_chooser_default_constructor (GType                  type,
-                                     guint                  n_construct_properties,
-                                     GObjectConstructParam *construct_params)
+/* Creates the main hpaned with the widgets shared by Open and Save mode */
+static GtkWidget *
+main_paned_create (GtkFileChooserDefault *impl)
 {
-  GtkFileChooserDefault *impl;
-  GObject *object;
   GtkWidget *hpaned;
   GtkWidget *widget;
-  GList *focus_chain;
   GtkWidget *entry_widget;
 
-  object = parent_class->constructor (type,
-                                     n_construct_properties,
-                                     construct_params);
-  impl = GTK_FILE_CHOOSER_DEFAULT (object);
-
-  g_assert (impl->file_system);
-
-  gtk_widget_push_composite_child ();
-
   /* Paned widget */
 
   hpaned = gtk_hpaned_new ();
-  gtk_box_pack_start (GTK_BOX (impl), hpaned, TRUE, TRUE, 0);
   gtk_paned_set_position (GTK_PANED (hpaned), 200); /* FIXME: this sucks */
-  gtk_widget_show (hpaned);
 
   /* Shortcuts pane */
 
@@ -1592,12 +1732,43 @@ gtk_file_chooser_default_constructor (GType                  type,
   entry_widget = create_filename_entry_and_filter_combo (impl);
   gtk_box_pack_start (GTK_BOX (impl), entry_widget, FALSE, FALSE, 0);
 
-  /* Make the entry the first widget in the focus chain
-   */
-  focus_chain = g_list_append (NULL, entry_widget);
-  focus_chain = g_list_append (focus_chain, hpaned);
-  gtk_container_set_focus_chain (GTK_CONTAINER (impl), focus_chain);
-  g_list_free (focus_chain);
+  return hpaned;
+}
+
+static GObject*
+gtk_file_chooser_default_constructor (GType                  type,
+                                     guint                  n_construct_properties,
+                                     GObjectConstructParam *construct_params)
+{
+  GtkFileChooserDefault *impl;
+  GObject *object;
+  GtkWidget *show_widget;
+
+  object = parent_class->constructor (type,
+                                     n_construct_properties,
+                                     construct_params);
+  impl = GTK_FILE_CHOOSER_DEFAULT (object);
+
+  g_assert (impl->file_system);
+
+  gtk_widget_push_composite_child ();
+
+  /* Widgets for Save mode */
+
+  impl->save_widgets = save_widgets_create (impl);
+  gtk_box_pack_start (GTK_BOX (impl), impl->save_widgets, FALSE, FALSE, 0);
+
+  /* Widgets for Open and Save mode */
+
+  impl->hpaned = main_paned_create (impl);
+  gtk_box_pack_start (GTK_BOX (impl), impl->hpaned, TRUE, TRUE, 0);
+
+  if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN)
+    show_widget = impl->hpaned;
+  else
+    show_widget = impl->save_widgets;
+
+  gtk_widget_show (show_widget);
 
   gtk_widget_pop_composite_child ();
 
@@ -1723,6 +1894,8 @@ gtk_file_chooser_default_set_property (GObject      *object,
       impl->action = g_value_get_enum (value);
       if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
        {
+         gtk_widget_hide (impl->hpaned);
+         gtk_widget_show (impl->save_widgets);
          gtk_widget_show (impl->new_folder_button);
 
          if (impl->select_multiple)
@@ -1734,6 +1907,9 @@ gtk_file_chooser_default_set_property (GObject      *object,
        }
       else
        {
+         gtk_widget_hide (impl->save_widgets);
+         gtk_widget_show (impl->hpaned);
+
          if (impl->folder_mode)
            gtk_widget_show (impl->new_folder_button);
          else
@@ -2112,18 +2288,17 @@ set_tree_model (GtkFileChooserDefault *impl, const GtkFilePath *path)
 static void
 update_chooser_entry (GtkFileChooserDefault *impl)
 {
-  GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->list));
+  GtkTreeSelection *selection;
   const GtkFileInfo *info;
   GtkTreeIter iter;
   GtkTreeIter child_iter;
 
-  /* FIXME #132255: Fixing this for multiple selection involves getting the full
-   * selection and diffing to find out what the most recently selected file is;
-   * there is logic in GtkFileSelection that probably can be copied;
-   * check_preview_change() is similar.
-   */
-  if (impl->select_multiple
-      || !gtk_tree_selection_get_selected (selection, NULL, &iter))
+  if (impl->action != GTK_FILE_CHOOSER_ACTION_SAVE)
+    return;
+
+  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->list));
+
+  if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
     return;
 
   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
@@ -2162,7 +2337,7 @@ gtk_file_chooser_default_set_current_folder (GtkFileChooser    *chooser,
       impl->changing_folder = FALSE;
     }
 
-  /* Notify the location entry */
+  /* Notify the save entry */
 
   _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (impl->entry), impl->current_folder);
 
@@ -2196,6 +2371,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);
+
   _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->entry), name);
 }
 
@@ -2331,33 +2508,37 @@ static GSList *
 gtk_file_chooser_default_get_paths (GtkFileChooser *chooser)
 {
   GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
-  GtkFileChooserEntry *chooser_entry;
-  const GtkFilePath *folder_path;
-  const gchar *file_part;
   struct get_paths_closure info;
 
   info.impl = impl;
   info.result = NULL;
   info.path_from_entry = NULL;
 
-  chooser_entry = GTK_FILE_CHOOSER_ENTRY (impl->entry);
-  folder_path = _gtk_file_chooser_entry_get_current_folder (chooser_entry);
-  file_part = _gtk_file_chooser_entry_get_file_part (chooser_entry);
-
-  if (file_part != NULL && file_part[0] != '\0')
+  if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
     {
-      GtkFilePath *selected;
-      GError *error = NULL;
+      GtkFileChooserEntry *chooser_entry;
+      const GtkFilePath *folder_path;
+      const gchar *file_part;
 
-      selected = gtk_file_system_make_path (impl->file_system, folder_path, file_part, &error);
+      chooser_entry = GTK_FILE_CHOOSER_ENTRY (impl->entry);
+      folder_path = _gtk_file_chooser_entry_get_current_folder (chooser_entry);
+      file_part = _gtk_file_chooser_entry_get_file_part (chooser_entry);
 
-      if (!selected)
+      if (file_part != NULL && file_part[0] != '\0')
        {
-         error_building_filename_dialog (impl, folder_path, file_part, error);
-         return NULL;
-       }
+         GtkFilePath *selected;
+         GError *error = NULL;
+
+         selected = gtk_file_system_make_path (impl->file_system, folder_path, file_part, &error);
+
+         if (!selected)
+           {
+             error_building_filename_dialog (impl, folder_path, file_part, error);
+             return NULL;
+           }
 
-      info.path_from_entry = selected;
+         info.path_from_entry = selected;
+       }
     }
 
   if (!info.path_from_entry || impl->select_multiple)
@@ -2706,7 +2887,7 @@ check_preview_change (GtkFileChooserDefault *impl)
   /* FIXME #132255: Fixing preview for multiple selection involves getting the
    * full selection and diffing to find out what the most recently selected file
    * is; there is logic in GtkFileSelection that probably can be
-   * copied. update_chooser_entry() is similar.
+   * copied.
    */
   if (impl->sort_model && !impl->select_multiple)
     {
@@ -2714,7 +2895,7 @@ check_preview_change (GtkFileChooserDefault *impl)
       GtkTreeIter iter;
 
       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->list));
-      if (gtk_tree_selection_get_selected  (selection, NULL, &iter))
+      if (gtk_tree_selection_get_selected (selection, NULL, &iter))
        {
          GtkTreeIter child_iter;
 
@@ -2918,100 +3099,6 @@ list_row_activated (GtkTreeView           *tree_view,
   g_signal_emit_by_name (impl, "file-activated");
 }
 
-static void
-entry_activate (GtkEntry              *entry,
-               GtkFileChooserDefault *impl)
-{
-  GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (entry);
-  const GtkFilePath *folder_path = _gtk_file_chooser_entry_get_current_folder (chooser_entry);
-  const gchar *file_part = _gtk_file_chooser_entry_get_file_part (chooser_entry);
-  GtkFilePath *new_folder = NULL;
-
-  if (!folder_path)
-    return; /* The entry got a nonexistent path */
-
-  if (file_part[0] == '\0')
-    {
-      if (gtk_file_path_compare (impl->current_folder, folder_path) != 0)
-       new_folder = gtk_file_path_copy (folder_path);
-      else
-       return;
-    }
-  else
-    {
-      GtkFileFolder *folder = NULL;
-      GtkFilePath *subfolder_path = NULL;
-      GtkFileInfo *info = NULL;
-      GError *error;
-
-      /* If the file part is non-empty, we need to figure out if it
-       * refers to a folder within folder. We could optimize the case
-       * here where the folder is already loaded for one of our tree models.
-       */
-
-      error = NULL;
-      folder = gtk_file_system_get_folder (impl->file_system, folder_path, GTK_FILE_INFO_IS_FOLDER, &error);
-
-      if (!folder)
-       {
-         error_getting_info_dialog (impl, folder_path, error);
-         return;
-       }
-
-      error = NULL;
-      subfolder_path = gtk_file_system_make_path (impl->file_system, folder_path, file_part, &error);
-
-      if (!subfolder_path)
-       {
-         char *msg;
-
-         msg = g_strdup_printf (_("Could not build file name from '%s' and '%s':\n%s"),
-                                gtk_file_path_get_string (folder_path),
-                                file_part,
-                                error->message);
-         error_message (impl, msg);
-         g_free (msg);
-         g_object_unref (folder);
-         return;
-       }
-
-      error = NULL;
-      info = gtk_file_folder_get_info (folder, subfolder_path, &error);
-
-      if (!info)
-       {
-         if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
-           {
-             g_object_unref (folder);
-             gtk_file_path_free (subfolder_path);
-             return;
-           }
-         error_getting_info_dialog (impl, subfolder_path, error);
-         g_object_unref (folder);
-         gtk_file_path_free (subfolder_path);
-         return;
-       }
-
-      if (gtk_file_info_get_is_folder (info))
-       new_folder = gtk_file_path_copy (subfolder_path);
-
-      g_object_unref (folder);
-      gtk_file_path_free (subfolder_path);
-      gtk_file_info_free (info);
-    }
-
-  if (new_folder)
-    {
-      g_signal_stop_emission_by_name (entry, "activate");
-
-      _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), new_folder);
-      _gtk_file_chooser_entry_set_file_part (chooser_entry, "");
-
-      gtk_file_path_free (new_folder);
-    }
-}
-
-
 static void
 path_bar_clicked (GtkPathBar            *path_bar,
                  const char            *file_path,
@@ -3082,23 +3169,6 @@ list_icon_data_func (GtkTreeViewColumn *tree_column,
 
   if (pixbuf)
     g_object_unref (pixbuf);
-
-#if 0
-  const GtkFileInfo *info = get_list_file_info (impl, iter);
-
-  if (info)
-    {
-      GtkWidget *widget = GTK_TREE_VIEW_COLUMN (tree_column)->tree_view;
-      GdkPixbuf *pixbuf = gtk_file_info_render_icon (info, widget, ICON_SIZE);
-
-      g_object_set (cell,
-                   "pixbuf", pixbuf,
-                   NULL);
-
-      if (pixbuf)
-       g_object_unref (pixbuf);
-    }
-#endif
 }
 
 /* Sets a cellrenderer's text, making it bold if the GtkFileInfo is a folder */
@@ -3228,3 +3298,186 @@ _gtk_file_chooser_default_new (const char *file_system)
                        "file-system-backend", file_system,
                        NULL);
 }
+
+static GtkWidget *
+location_entry_create (GtkFileChooserDefault *impl)
+{
+  GtkWidget *entry;
+
+  entry = _gtk_file_chooser_entry_new ();
+  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);
+
+  return GTK_WIDGET (entry);
+}
+
+static void
+update_from_entry (GtkFileChooserDefault *impl,
+                  GtkWindow             *parent,
+                  GtkFileChooserEntry   *chooser_entry)
+{
+  const GtkFilePath *folder_path;
+  const char *file_part;
+
+  folder_path = _gtk_file_chooser_entry_get_current_folder (chooser_entry);
+  file_part = _gtk_file_chooser_entry_get_file_part (chooser_entry);
+
+  if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN && !folder_path)
+    {
+      error_message_with_parent (parent,
+                                _("Cannot change to the folder you specified as it is an invalid path."));
+      return;
+    }
+
+  if (file_part[0] == '\0')
+    {
+      _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), folder_path);
+      return;
+    }
+  else
+    {
+      GtkFileFolder *folder = NULL;
+      GtkFilePath *subfolder_path = NULL;
+      GtkFileInfo *info = NULL;
+      GError *error;
+
+      /* If the file part is non-empty, we need to figure out if it refers to a
+       * folder within folder. We could optimize the case here where the folder
+       * is already loaded for one of our tree models.
+       */
+
+      error = NULL;
+      folder = gtk_file_system_get_folder (impl->file_system, folder_path, GTK_FILE_INFO_IS_FOLDER, &error);
+
+      if (!folder)
+       {
+         error_getting_info_dialog (impl, folder_path, error);
+         return;
+       }
+
+      error = NULL;
+      subfolder_path = gtk_file_system_make_path (impl->file_system, folder_path, file_part, &error);
+
+      if (!subfolder_path)
+       {
+         char *msg;
+
+         msg = g_strdup_printf (_("Could not build file name from '%s' and '%s':\n%s"),
+                                gtk_file_path_get_string (folder_path),
+                                file_part,
+                                error->message);
+         error_message (impl, msg);
+         g_free (msg);
+         g_object_unref (folder);
+         return;
+       }
+
+      error = NULL;
+      info = gtk_file_folder_get_info (folder, subfolder_path, &error);
+
+      if (!info)
+       {
+#if 0
+         if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
+           {
+             g_object_unref (folder);
+             gtk_file_path_free (subfolder_path);
+             return;
+           }
+#endif
+         error_getting_info_dialog (impl, subfolder_path, error);
+         g_object_unref (folder);
+         gtk_file_path_free (subfolder_path);
+         return;
+       }
+
+      if (gtk_file_info_get_is_folder (info))
+       _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), subfolder_path);
+      else
+       _gtk_file_chooser_select_path (GTK_FILE_CHOOSER (impl), subfolder_path);
+
+      g_object_unref (folder);
+      gtk_file_path_free (subfolder_path);
+      gtk_file_info_free (info);
+    }
+}
+
+static void
+location_popup_handler (GtkFileChooserDefault *impl)
+{
+  GtkWidget *dialog;
+  GtkWidget *toplevel;
+  GtkWidget *hbox;
+  GtkWidget *label;
+  GtkWidget *entry;
+
+  /* Create dialog */
+
+  toplevel = gtk_widget_get_toplevel (GTK_WIDGET (impl));
+  if (!GTK_WIDGET_TOPLEVEL (toplevel))
+    toplevel = NULL;
+
+  dialog = gtk_dialog_new_with_buttons (_("Open Location"),
+                                       GTK_WINDOW (toplevel),
+                                       GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
+                                       GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+                                       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+                                       NULL);
+  gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
+
+  hbox = gtk_hbox_new (FALSE, 12);
+  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox, FALSE, FALSE, 0);
+
+  label = gtk_label_new_with_mnemonic (_("_Location:"));
+  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+
+  entry = location_entry_create (impl);
+  gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
+  gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
+
+  /* Run */
+
+  gtk_widget_show_all (dialog);
+  if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
+    update_from_entry (impl, GTK_WINDOW (dialog), GTK_FILE_CHOOSER_ENTRY (entry));
+
+  gtk_widget_destroy (dialog);
+}
+
+/* Handler for the "up-folder" keybinding signal */
+static void
+up_folder_handler (GtkFileChooserDefault *impl)
+{
+  GtkFilePath *parent_path;
+  GError *error;
+
+  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 */
+       {
+         _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (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 "home-folder" keybinding signal */
+static void
+home_folder_handler (GtkFileChooserDefault *impl)
+{
+  const char *home;
+
+  /* Should we pull this information from impl->has_home and the shortcuts data
+   * instead?  Sounds like a bit of overkill...
+   */
+
+  home = g_get_home_dir ();
+  gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (impl), home);
+}