summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog137
-rw-r--r--ChangeLog.pre-2-10137
-rw-r--r--gtk/Makefile.am2
-rw-r--r--gtk/gtkfilechooserdefault.c829
-rw-r--r--gtk/gtkfilechooserprivate.h13
-rw-r--r--gtk/gtkfilechoosersettings.c535
-rw-r--r--gtk/gtkfilechoosersettings.h75
-rw-r--r--gtk/gtkpathbar.c2
-rw-r--r--tests/autotestfilechooser.c8
9 files changed, 1388 insertions, 350 deletions
diff --git a/ChangeLog b/ChangeLog
index 034e7e2ff..1a6e2421b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,140 @@
+2006-05-03 Federico Mena Quintero <federico@novell.com>
+
+ Merged the federico-filename-entry branch, to fix bug #136541.
+ Combined ChangeLogs:
+
+ 2006-04-17 Federico Mena Quintero <federico@novell.com>
+
+ * gtk/gtkfilechooserdefault.c (pending_select_paths_process):
+ Oops, we *do* need to check that we are in OPEN mode before
+ selecting the first row in the file list. See
+ https://bugzilla.novell.com/show_bug.cgi?id=166906
+ (gtk_file_chooser_default_get_paths): If we are in the case for
+ the file list, and the list has no selected rows, jump to the case
+ for the filename entry. This is so that
+
+ 1. The user types a filename in the SAVE filename entry
+ ("foo.txt").
+
+ 2. He then double-clicks on a folder ("bar") in the file
+ list.
+
+ will yield the expected "bar/foo.txt" selection.
+
+ 2006-03-29 Federico Mena Quintero <federico@novell.com>
+
+ * gtk/gtkpathbar.c (gtk_path_bar_init): Reduce the inter-button
+ spacing to 0.
+
+ * gtk/gtkfilechooserdefault.c (browse_widgets_create): Make the
+ location label bold.
+
+ 2006-03-29 Federico Mena Quintero <federico@novell.com>
+
+ * gtk/gtkfilechooserdefault.c (location_mode_set): Just change the
+ location_mode field if we are in SAVE/CREATE_FOLDER modes.
+ (gtk_file_chooser_default_get_paths): Get the path based on the
+ currently focused widget, or the last-focused widget. This is
+ what we should have been doing in the beginning, but it worked out
+ fine because we didn't have the possibility of a filename entry in
+ OPEN mode.
+ (gtk_file_chooser_default_should_respond): Handle the case where
+ the last focused widget is the location_entry.
+
+ 2006-03-28 Federico Mena Quintero <federico@novell.com>
+
+ * gtk/gtkfilechoosersettings.[ch]: New files with a simple
+ framework for saving/loading settings from the file chooser in
+ $XDG_CONFIG_HOME/gtk-2.0/gtkfilechooser.
+
+ * gtk/gtkfilechooserdefault.c (gtk_file_chooser_default_unmap):
+ Save the current settings.
+ (settings_save): New helper function. We save the location_mode
+ and show_hidden flags.
+ (gtk_file_chooser_default_map): Load the settings.
+ (settings_load): New helper function.
+
+ * gtk/gtkfilechooserentry.c
+ (_gtk_file_chooser_entry_set_file_part): Oops, don't modify
+ in_change. Our handlers are what set the file_part, so they
+ *must* be run when we modify the text.
+
+ 2006-03-27 Federico Mena Quintero <federico@novell.com>
+
+ * gtk/gtkfilechooserprivate.h (struct _GtkFileChooserDefault):
+ Removed the save_file_name_entry. We'll make this be the same as
+ the location_entry widget.
+ (struct _GtkFileChooserDefault): Leave only location_button,
+ location_entry_box, location_label, location_entry. We'll use a
+ single toggle button for the location entry, which will appear
+ below the path bar.
+ (struct _GtkFileChooserDefault): Added a
+ processing_pending_selections flag.
+
+ * gtk/gtkfilechooserdefault.c (save_widgets_create): Destroy the
+ old location_entry if necessary, and hide the location toggle
+ widgets.
+ (update_chooser_entry): In multiple selection mode, just clear the
+ location_entry.
+ (check_save_entry): Allow running in OPEN or SELECT_FOLDER modes
+ if we are in LOCATION_MODE_FILENAME_ENTRY.
+ (gtk_file_chooser_default_should_respond): Switch to a folder if
+ the location_entry contains a folder name in OPEN and SAVE mode,
+ not just SAVE mode. If the entry doesn't contain a folder name,
+ but is otherwise well-formed, and we are in OPEN mode, return that
+ we should respond with that filename.
+ (gtk_file_chooser_default_initial_focus): Focus the location_entry
+ if appropriate.
+ (browse_widgets_create): Create the location_entry_box and the
+ location_label here.
+ (update_appearance): Call location_mode_set() when switching back
+ to OPEN/SELECT_FOLDER mode. Hide the location_button when
+ switching to SAVE/CREATE_FOLDER mode.
+ (pending_select_paths_process): Turn the
+ processing_pending_selections flag on and off around changes to
+ the current selection. Don't special-case OPEN mode anymore,
+ since the new flag will take care of things in
+ update_chooser_entry().
+ (update_chooser_entry): Don't do anything if
+ processing_pending_selections is TRUE. This keeps the entry from
+ being polluted when changing folders.
+ (location_popup_handler): In OPEN/SELECT_FOLDER modes, toggle
+ between the path bar and the entry. In SAVE/CREATE_FOLDER modes, simply focus the
+ location_entry.
+ (update_from_entry): Removed.
+ (location_entry_create): Removed.
+ (open_location_cb): Removed.
+ (file_list_build_popup_menu): Don't add an "Open _Location" menu item.
+ (location_entry_set_initial_text): Don't do anything if
+ current_folder is NULL.
+
+ * gtk/gtkfilechooserentry.c
+ (_gtk_file_chooser_entry_set_file_part): Turn in_change on and off
+ around the call to gtk_entry_set_text(). This makes completion
+ not happen when the caller has explicitly set a name.
+
+ 2006-03-24 Federico Mena Quintero <federico@novell.com>
+
+ * gtk/gtkfilechooserprivate.h (struct _GtkFileChooserDefault):
+ Added fields location_mode_box, location_pathbar_radio,
+ location_filename_radio, location_widget_box, location_label,
+ location_entry. The radio buttons will switch between the pathbar
+ and the location entry; the other boxes are for layout purposes.
+ (enum LocationMode): New enum.
+ (struct _GtkFileChooserDefault): Added a location_mode field.
+
+ * gtk/gtkfilechooserdefault.c (browse_widgets_create): Create the
+ location radio buttons to switch between the pathbar and the
+ location entry. Pack the browse_path_bar in the new
+ location_widget_box instead of a generic hbox.
+ (location_buttons_create): New function.
+ (gtk_file_chooser_default_init): Initialize impl->location_mode.
+ (location_switch_to_path_bar): New function.
+ (location_switch_to_filename_entry): New function.
+
+ * gtk/gtkfilechooserbutton.c (model_add_special): The display_name
+ should not be const.
+
2006-05-03 Matthias Clasen <mclasen@redhat.com>
* gtk/gtkprintunixdialog.c: Replace the xpm collate icons
diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10
index 034e7e2ff..1a6e2421b 100644
--- a/ChangeLog.pre-2-10
+++ b/ChangeLog.pre-2-10
@@ -1,3 +1,140 @@
+2006-05-03 Federico Mena Quintero <federico@novell.com>
+
+ Merged the federico-filename-entry branch, to fix bug #136541.
+ Combined ChangeLogs:
+
+ 2006-04-17 Federico Mena Quintero <federico@novell.com>
+
+ * gtk/gtkfilechooserdefault.c (pending_select_paths_process):
+ Oops, we *do* need to check that we are in OPEN mode before
+ selecting the first row in the file list. See
+ https://bugzilla.novell.com/show_bug.cgi?id=166906
+ (gtk_file_chooser_default_get_paths): If we are in the case for
+ the file list, and the list has no selected rows, jump to the case
+ for the filename entry. This is so that
+
+ 1. The user types a filename in the SAVE filename entry
+ ("foo.txt").
+
+ 2. He then double-clicks on a folder ("bar") in the file
+ list.
+
+ will yield the expected "bar/foo.txt" selection.
+
+ 2006-03-29 Federico Mena Quintero <federico@novell.com>
+
+ * gtk/gtkpathbar.c (gtk_path_bar_init): Reduce the inter-button
+ spacing to 0.
+
+ * gtk/gtkfilechooserdefault.c (browse_widgets_create): Make the
+ location label bold.
+
+ 2006-03-29 Federico Mena Quintero <federico@novell.com>
+
+ * gtk/gtkfilechooserdefault.c (location_mode_set): Just change the
+ location_mode field if we are in SAVE/CREATE_FOLDER modes.
+ (gtk_file_chooser_default_get_paths): Get the path based on the
+ currently focused widget, or the last-focused widget. This is
+ what we should have been doing in the beginning, but it worked out
+ fine because we didn't have the possibility of a filename entry in
+ OPEN mode.
+ (gtk_file_chooser_default_should_respond): Handle the case where
+ the last focused widget is the location_entry.
+
+ 2006-03-28 Federico Mena Quintero <federico@novell.com>
+
+ * gtk/gtkfilechoosersettings.[ch]: New files with a simple
+ framework for saving/loading settings from the file chooser in
+ $XDG_CONFIG_HOME/gtk-2.0/gtkfilechooser.
+
+ * gtk/gtkfilechooserdefault.c (gtk_file_chooser_default_unmap):
+ Save the current settings.
+ (settings_save): New helper function. We save the location_mode
+ and show_hidden flags.
+ (gtk_file_chooser_default_map): Load the settings.
+ (settings_load): New helper function.
+
+ * gtk/gtkfilechooserentry.c
+ (_gtk_file_chooser_entry_set_file_part): Oops, don't modify
+ in_change. Our handlers are what set the file_part, so they
+ *must* be run when we modify the text.
+
+ 2006-03-27 Federico Mena Quintero <federico@novell.com>
+
+ * gtk/gtkfilechooserprivate.h (struct _GtkFileChooserDefault):
+ Removed the save_file_name_entry. We'll make this be the same as
+ the location_entry widget.
+ (struct _GtkFileChooserDefault): Leave only location_button,
+ location_entry_box, location_label, location_entry. We'll use a
+ single toggle button for the location entry, which will appear
+ below the path bar.
+ (struct _GtkFileChooserDefault): Added a
+ processing_pending_selections flag.
+
+ * gtk/gtkfilechooserdefault.c (save_widgets_create): Destroy the
+ old location_entry if necessary, and hide the location toggle
+ widgets.
+ (update_chooser_entry): In multiple selection mode, just clear the
+ location_entry.
+ (check_save_entry): Allow running in OPEN or SELECT_FOLDER modes
+ if we are in LOCATION_MODE_FILENAME_ENTRY.
+ (gtk_file_chooser_default_should_respond): Switch to a folder if
+ the location_entry contains a folder name in OPEN and SAVE mode,
+ not just SAVE mode. If the entry doesn't contain a folder name,
+ but is otherwise well-formed, and we are in OPEN mode, return that
+ we should respond with that filename.
+ (gtk_file_chooser_default_initial_focus): Focus the location_entry
+ if appropriate.
+ (browse_widgets_create): Create the location_entry_box and the
+ location_label here.
+ (update_appearance): Call location_mode_set() when switching back
+ to OPEN/SELECT_FOLDER mode. Hide the location_button when
+ switching to SAVE/CREATE_FOLDER mode.
+ (pending_select_paths_process): Turn the
+ processing_pending_selections flag on and off around changes to
+ the current selection. Don't special-case OPEN mode anymore,
+ since the new flag will take care of things in
+ update_chooser_entry().
+ (update_chooser_entry): Don't do anything if
+ processing_pending_selections is TRUE. This keeps the entry from
+ being polluted when changing folders.
+ (location_popup_handler): In OPEN/SELECT_FOLDER modes, toggle
+ between the path bar and the entry. In SAVE/CREATE_FOLDER modes, simply focus the
+ location_entry.
+ (update_from_entry): Removed.
+ (location_entry_create): Removed.
+ (open_location_cb): Removed.
+ (file_list_build_popup_menu): Don't add an "Open _Location" menu item.
+ (location_entry_set_initial_text): Don't do anything if
+ current_folder is NULL.
+
+ * gtk/gtkfilechooserentry.c
+ (_gtk_file_chooser_entry_set_file_part): Turn in_change on and off
+ around the call to gtk_entry_set_text(). This makes completion
+ not happen when the caller has explicitly set a name.
+
+ 2006-03-24 Federico Mena Quintero <federico@novell.com>
+
+ * gtk/gtkfilechooserprivate.h (struct _GtkFileChooserDefault):
+ Added fields location_mode_box, location_pathbar_radio,
+ location_filename_radio, location_widget_box, location_label,
+ location_entry. The radio buttons will switch between the pathbar
+ and the location entry; the other boxes are for layout purposes.
+ (enum LocationMode): New enum.
+ (struct _GtkFileChooserDefault): Added a location_mode field.
+
+ * gtk/gtkfilechooserdefault.c (browse_widgets_create): Create the
+ location radio buttons to switch between the pathbar and the
+ location entry. Pack the browse_path_bar in the new
+ location_widget_box instead of a generic hbox.
+ (location_buttons_create): New function.
+ (gtk_file_chooser_default_init): Initialize impl->location_mode.
+ (location_switch_to_path_bar): New function.
+ (location_switch_to_filename_entry): New function.
+
+ * gtk/gtkfilechooserbutton.c (model_add_special): The display_name
+ should not be const.
+
2006-05-03 Matthias Clasen <mclasen@redhat.com>
* gtk/gtkprintunixdialog.c: Replace the xpm collate icons
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 91fa48c46..6b1439ec0 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -329,6 +329,7 @@ gtk_private_h_sources = \
gtkfilechooserembed.h \
gtkfilechooserentry.h \
gtkfilechooserprivate.h \
+ gtkfilechoosersettings.h \
gtkfilechooserutils.h \
gtkfilesystemmodel.h \
gtkfilesystemunix.h \
@@ -414,6 +415,7 @@ gtk_c_sources = \
gtkfilechooserdialog.c \
gtkfilechooserembed.c \
gtkfilechooserentry.c \
+ gtkfilechoosersettings.c \
gtkfilechooserutils.c \
gtkfilechooserwidget.c \
gtkfilefilter.c \
diff --git a/gtk/gtkfilechooserdefault.c b/gtk/gtkfilechooserdefault.c
index 3d46c1cc1..a8b199fe6 100644
--- a/gtk/gtkfilechooserdefault.c
+++ b/gtk/gtkfilechooserdefault.c
@@ -36,6 +36,7 @@
#include "gtkfilechooserdefault.h"
#include "gtkfilechooserembed.h"
#include "gtkfilechooserentry.h"
+#include "gtkfilechoosersettings.h"
#include "gtkfilechooserutils.h"
#include "gtkfilechooser.h"
#include "gtkfilesystemmodel.h"
@@ -53,6 +54,7 @@
#include "gtkmessagedialog.h"
#include "gtkpathbar.h"
#include "gtkprivate.h"
+#include "gtkradiobutton.h"
#include "gtkscrolledwindow.h"
#include "gtkseparatormenuitem.h"
#include "gtksizegroup.h"
@@ -410,6 +412,10 @@ static const GtkFileInfo *get_list_file_info (GtkFileChooserDefault *impl,
static void load_remove_timer (GtkFileChooserDefault *impl);
static void browse_files_center_selected_row (GtkFileChooserDefault *impl);
+static void location_button_toggled_cb (GtkToggleButton *toggle,
+ GtkFileChooserDefault *impl);
+static void location_switch_to_path_bar (GtkFileChooserDefault *impl);
+
@@ -527,7 +533,8 @@ _gtk_file_chooser_default_class_init (GtkFileChooserDefaultClass *class)
GDK_l, GDK_CONTROL_MASK,
"location-popup",
1, G_TYPE_STRING, "");
-
+ /* FMQ: remove this? */
+#if 0
gtk_binding_entry_add_signal (binding_set,
GDK_slash, 0,
"location-popup",
@@ -543,6 +550,7 @@ _gtk_file_chooser_default_class_init (GtkFileChooserDefaultClass *class)
"location-popup",
1, G_TYPE_STRING, "~");
#endif
+#endif
gtk_binding_entry_add_signal (binding_set,
GDK_Up, GDK_MOD1_MASK,
@@ -640,6 +648,7 @@ _gtk_file_chooser_default_init (GtkFileChooserDefault *impl)
impl->load_state = LOAD_EMPTY;
impl->reload_state = RELOAD_EMPTY;
impl->pending_select_paths = NULL;
+ impl->location_mode = LOCATION_MODE_PATH_BAR;
gtk_box_set_spacing (GTK_BOX (impl), 12);
@@ -3133,6 +3142,8 @@ tree_view_keybinding_cb (GtkWidget *tree_view,
GdkEventKey *event,
GtkFileChooserDefault *impl)
{
+ /* FMQ: remove this? */
+#if 0
if ((event->keyval == GDK_slash
|| event->keyval == GDK_KP_Divide
#ifdef G_OS_UNIX
@@ -3143,7 +3154,7 @@ tree_view_keybinding_cb (GtkWidget *tree_view,
location_popup_handler (impl, event->string);
return TRUE;
}
-
+#endif
return FALSE;
}
@@ -3514,7 +3525,9 @@ trap_activate_cb (GtkWidget *widget,
impl = (GtkFileChooserDefault *) data;
modifiers = gtk_accelerator_get_default_mod_mask ();
-
+
+ /* FMQ: remove this? */
+#if 0
if ((event->keyval == GDK_slash
|| event->keyval == GDK_KP_Divide
#ifdef G_OS_UNIX
@@ -3525,6 +3538,7 @@ trap_activate_cb (GtkWidget *widget,
location_popup_handler (impl, event->string);
return TRUE;
}
+#endif
if ((event->keyval == GDK_Return
|| event->keyval == GDK_ISO_Enter
@@ -3573,14 +3587,6 @@ add_to_shortcuts_cb (GtkMenuItem *item,
bookmarks_add_selected_folder (impl);
}
-/* Callback used when the "Open Location" menu item is activated */
-static void
-open_location_cb (GtkMenuItem *item,
- GtkFileChooserDefault *impl)
-{
- location_popup_handler (impl, "");
-}
-
/* Callback used when the "Show Hidden Files" menu item is toggled */
static void
show_hidden_toggled_cb (GtkCheckMenuItem *item,
@@ -3802,14 +3808,6 @@ file_list_build_popup_menu (GtkFileChooserDefault *impl)
gtk_widget_show (item);
gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
- item = gtk_image_menu_item_new_with_mnemonic (_("Open _Location"));
- gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
- gtk_image_new_from_stock (GTK_STOCK_OPEN, GTK_ICON_SIZE_MENU));
- g_signal_connect (item, "activate",
- G_CALLBACK (open_location_cb), impl);
- gtk_widget_show (item);
- gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
-
item = gtk_separator_menu_item_new ();
gtk_widget_show (item);
gtk_menu_shell_append (GTK_MENU_SHELL (impl->browse_files_popup_menu), item);
@@ -4024,7 +4022,7 @@ create_file_list (GtkFileChooserDefault *impl)
gtk_tree_view_column_set_title (column, _("Size"));
renderer = gtk_cell_renderer_text_new ();
- gtk_tree_view_column_pack_start (column, renderer, TRUE);
+ gtk_tree_view_column_pack_start (column, renderer, TRUE); /* bug: it doesn't expand */
gtk_tree_view_column_set_cell_data_func (column, renderer,
list_size_data_func, impl, NULL);
gtk_tree_view_column_set_sort_column_id (column, FILE_LIST_COL_SIZE);
@@ -4202,6 +4200,8 @@ save_widgets_create (GtkFileChooserDefault *impl)
if (impl->save_widgets != NULL)
return;
+ location_switch_to_path_bar (impl);
+
vbox = gtk_vbox_new (FALSE, 12);
table = gtk_table_new (2, 2, FALSE);
@@ -4210,7 +4210,7 @@ save_widgets_create (GtkFileChooserDefault *impl)
gtk_table_set_row_spacings (GTK_TABLE (table), 12);
gtk_table_set_col_spacings (GTK_TABLE (table), 12);
- /* Name entry */
+ /* Label */
widget = gtk_label_new_with_mnemonic (_("_Name:"));
gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
@@ -4220,17 +4220,19 @@ save_widgets_create (GtkFileChooserDefault *impl)
0, 0);
gtk_widget_show (widget);
- impl->save_file_name_entry = _gtk_file_chooser_entry_new (TRUE);
- _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry),
+ /* Location entry */
+
+ impl->location_entry = _gtk_file_chooser_entry_new (TRUE);
+ _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (impl->location_entry),
impl->file_system);
- gtk_entry_set_width_chars (GTK_ENTRY (impl->save_file_name_entry), 45);
- gtk_entry_set_activates_default (GTK_ENTRY (impl->save_file_name_entry), TRUE);
- gtk_table_attach (GTK_TABLE (table), impl->save_file_name_entry,
+ gtk_entry_set_width_chars (GTK_ENTRY (impl->location_entry), 45);
+ gtk_entry_set_activates_default (GTK_ENTRY (impl->location_entry), TRUE);
+ gtk_table_attach (GTK_TABLE (table), impl->location_entry,
1, 2, 0, 1,
GTK_EXPAND | GTK_FILL, 0,
0, 0);
- gtk_widget_show (impl->save_file_name_entry);
- gtk_label_set_mnemonic_widget (GTK_LABEL (widget), impl->save_file_name_entry);
+ gtk_widget_show (impl->location_entry);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (widget), impl->location_entry);
/* Folder combo */
impl->save_folder_label = gtk_label_new (NULL);
@@ -4274,12 +4276,226 @@ save_widgets_destroy (GtkFileChooserDefault *impl)
gtk_widget_destroy (impl->save_widgets);
impl->save_widgets = NULL;
- impl->save_file_name_entry = NULL;
+ impl->location_entry = NULL;
impl->save_folder_label = NULL;
impl->save_folder_combo = NULL;
impl->save_expander = NULL;
}
+/* Turns on the path bar widget. Can be called even if we are already in that
+ * mode.
+ */
+static void
+location_switch_to_path_bar (GtkFileChooserDefault *impl)
+{
+ if (impl->location_entry)
+ {
+ gtk_widget_destroy (impl->location_entry);
+ impl->location_entry = NULL;
+ }
+
+ gtk_widget_hide (impl->location_entry_box);
+}
+
+/* Sets the full path of the current folder as the text in the location entry. */
+static void
+location_entry_set_initial_text (GtkFileChooserDefault *impl)
+{
+ char *text;
+
+ if (!impl->current_folder)
+ return;
+
+ if (gtk_file_system_path_is_local (impl->file_system, impl->current_folder))
+ {
+ char *filename;
+
+ filename = gtk_file_system_path_to_filename (impl->file_system, impl->current_folder);
+ if (filename)
+ {
+ text = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
+ g_free (filename);
+ }
+ else
+ text = NULL;
+ }
+ else
+ text = gtk_file_system_path_to_uri (impl->file_system, impl->current_folder);
+
+ if (text)
+ {
+ gboolean need_slash;
+ int len;
+
+ len = strlen (text);
+ need_slash = (text[len - 1] != G_DIR_SEPARATOR);
+
+ if (need_slash)
+ {
+ char *slash_text;
+
+ slash_text = g_new (char, len + 2);
+ strcpy (slash_text, text);
+ slash_text[len] = G_DIR_SEPARATOR;
+ slash_text[len + 1] = 0;
+
+ g_free (text);
+ text = slash_text;
+ }
+
+ _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), text);
+ g_free (text);
+ }
+}
+
+/* Turns on the location entry. Can be called even if we are already in that
+ * mode.
+ */
+static void
+location_switch_to_filename_entry (GtkFileChooserDefault *impl)
+{
+ if (impl->location_entry)
+ gtk_widget_destroy (impl->location_entry);
+
+ /* Box */
+
+ gtk_widget_show (impl->location_entry_box);
+
+ /* Entry */
+
+ impl->location_entry = _gtk_file_chooser_entry_new (TRUE);
+ _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (impl->location_entry),
+ impl->file_system);
+ gtk_entry_set_activates_default (GTK_ENTRY (impl->location_entry), TRUE);
+ _gtk_file_chooser_entry_set_action (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->action);
+
+ gtk_box_pack_start (GTK_BOX (impl->location_entry_box), impl->location_entry, TRUE, TRUE, 0);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (impl->location_label), impl->location_entry);
+
+ /* Configure the entry */
+
+ _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->current_folder);
+ location_entry_set_initial_text (impl);
+
+ /* Done */
+
+ gtk_widget_show (impl->location_entry);
+ gtk_widget_grab_focus (impl->location_entry);
+}
+
+/* Sets a new location mode. set_buttons determines whether the toggle button
+ * for the mode will also be changed.
+ */
+static void
+location_mode_set (GtkFileChooserDefault *impl,
+ LocationMode new_mode,
+ gboolean set_button)
+{
+ if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
+ || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
+ {
+ GtkWindow *toplevel;
+ GtkWidget *current_focus;
+ gboolean button_active;
+ gboolean switch_to_file_list;
+
+ switch (new_mode)
+ {
+ case LOCATION_MODE_PATH_BAR:
+ button_active = FALSE;
+
+ /* The location_entry will disappear when we switch to path bar mode. So,
+ * we'll focus the file list in that case, to avoid having a window with
+ * no focused widget.
+ */
+ toplevel = get_toplevel (GTK_WIDGET (impl));
+ switch_to_file_list = FALSE;
+ if (toplevel)
+ {
+ current_focus = gtk_window_get_focus (toplevel);
+ if (!current_focus || current_focus == impl->location_entry)
+ switch_to_file_list = TRUE;
+ }
+
+ location_switch_to_path_bar (impl);
+
+ if (switch_to_file_list)
+ gtk_widget_grab_focus (impl->browse_files_tree_view);
+
+ break;
+
+ case LOCATION_MODE_FILENAME_ENTRY:
+ button_active = TRUE;
+ location_switch_to_filename_entry (impl);
+ break;
+
+ default:
+ g_assert_not_reached ();
+ return;
+ }
+
+ if (set_button)
+ {
+ g_signal_handlers_block_by_func (impl->location_button,
+ G_CALLBACK (location_button_toggled_cb), impl);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (impl->location_button), button_active);
+
+ g_signal_handlers_unblock_by_func (impl->location_button,
+ G_CALLBACK (location_button_toggled_cb), impl);
+ }
+ }
+
+ impl->location_mode = new_mode;
+}
+
+/* Callback used when one of the location mode buttons is toggled */
+static void
+location_button_toggled_cb (GtkToggleButton *toggle,
+ GtkFileChooserDefault *impl)
+{
+ gboolean is_active;
+ LocationMode new_mode;
+
+ is_active = gtk_toggle_button_get_active (toggle);
+
+ if (is_active)
+ {
+ g_assert (impl->location_mode == LOCATION_MODE_PATH_BAR);
+ new_mode = LOCATION_MODE_FILENAME_ENTRY;
+ }
+ else
+ {
+ g_assert (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY);
+ new_mode = LOCATION_MODE_PATH_BAR;
+ }
+
+ location_mode_set (impl, new_mode, FALSE);
+}
+
+/* Creates a toggle button for the location entry. */
+static void
+location_button_create (GtkFileChooserDefault *impl)
+{
+ GtkWidget *image;
+ const char *str;
+
+ image = gtk_image_new_from_stock (GTK_STOCK_EDIT, GTK_ICON_SIZE_BUTTON);
+ gtk_widget_show (image);
+
+ impl->location_button = g_object_new (GTK_TYPE_TOGGLE_BUTTON,
+ "image", image,
+ NULL);
+
+ g_signal_connect (impl->location_button, "toggled",
+ G_CALLBACK (location_button_toggled_cb), impl);
+
+ str = _("Type a file name");
+
+ gtk_tooltips_set_tip (impl->tooltips, impl->location_button, str, NULL);
+ atk_object_set_name (gtk_widget_get_accessible (impl->location_button), str);
+}
+
/* Creates the main hpaned with the widgets shared by Open and Save mode */
static GtkWidget *
browse_widgets_create (GtkFileChooserDefault *impl)
@@ -4294,11 +4510,16 @@ browse_widgets_create (GtkFileChooserDefault *impl)
size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
vbox = gtk_vbox_new (FALSE, 12);
- /* The path bar and 'Create Folder' button */
+ /* Location widgets */
hbox = gtk_hbox_new (FALSE, 12);
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
gtk_widget_show (hbox);
+ location_button_create (impl);
+ gtk_box_pack_start (GTK_BOX (hbox), impl->location_button, FALSE, FALSE, 0);
+
+ /* Path bar */
+
impl->browse_path_bar = create_path_bar (impl);
g_signal_connect (impl->browse_path_bar, "path-clicked", G_CALLBACK (path_bar_clicked), impl);
gtk_widget_show_all (impl->browse_path_bar);
@@ -4310,6 +4531,16 @@ browse_widgets_create (GtkFileChooserDefault *impl)
G_CALLBACK (new_folder_button_clicked), impl);
gtk_box_pack_end (GTK_BOX (hbox), impl->browse_new_folder_button, FALSE, FALSE, 0);
+ /* Box for the location label and entry */
+
+ impl->location_entry_box = gtk_hbox_new (FALSE, 12);
+ gtk_box_pack_start (GTK_BOX (vbox), impl->location_entry_box, FALSE, FALSE, 0);
+
+ impl->location_label = gtk_label_new_with_mnemonic ("<b>_Location:</b>");
+ gtk_label_set_use_markup (GTK_LABEL (impl->location_label), TRUE);
+ gtk_widget_show (impl->location_label);
+ gtk_box_pack_start (GTK_BOX (impl->location_entry_box), impl->location_label, FALSE, FALSE, 0);
+
/* Paned widget */
hpaned = gtk_hpaned_new ();
gtk_widget_show (hpaned);
@@ -4539,6 +4770,7 @@ update_appearance (GtkFileChooserDefault *impl)
{
const char *text;
+ gtk_widget_hide (impl->location_button);
save_widgets_create (impl);
if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
@@ -4548,8 +4780,6 @@ update_appearance (GtkFileChooserDefault *impl)
gtk_label_set_text_with_mnemonic (GTK_LABEL (impl->save_folder_label), text);
- _gtk_file_chooser_entry_set_action (GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry), impl->action);
-
if (gtk_expander_get_expanded (GTK_EXPANDER (impl->save_expander)))
{
gtk_widget_set_sensitive (impl->save_folder_label, FALSE);
@@ -4575,10 +4805,15 @@ update_appearance (GtkFileChooserDefault *impl)
else if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
{
+ gtk_widget_show (impl->location_button);
save_widgets_destroy (impl);
gtk_widget_show (impl->browse_widgets);
+ location_mode_set (impl, impl->location_mode, TRUE);
}
+ if (impl->location_entry)
+ _gtk_file_chooser_entry_set_action (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->action);
+
if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN)
gtk_widget_hide (impl->browse_new_folder_button);
else
@@ -5089,6 +5324,40 @@ get_is_file_filtered (GtkFileChooserDefault *impl,
return !result;
}
+static void
+settings_load (GtkFileChooserDefault *impl)
+{
+ GtkFileChooserSettings *settings;
+ LocationMode location_mode;
+ gboolean show_hidden;
+
+ settings = _gtk_file_chooser_settings_new ();
+
+ location_mode = _gtk_file_chooser_settings_get_location_mode (settings);
+ show_hidden = _gtk_file_chooser_settings_get_show_hidden (settings);
+
+ g_object_unref (settings);
+
+ location_mode_set (impl, location_mode, TRUE);
+ gtk_file_chooser_set_show_hidden (GTK_FILE_CHOOSER (impl), show_hidden);
+}
+
+static void
+settings_save (GtkFileChooserDefault *impl)
+{
+ GtkFileChooserSettings *settings;
+
+ settings = _gtk_file_chooser_settings_new ();
+
+ _gtk_file_chooser_settings_set_location_mode (settings, impl->location_mode);
+ _gtk_file_chooser_settings_set_show_hidden (settings, gtk_file_chooser_get_show_hidden (GTK_FILE_CHOOSER (impl)));
+
+ /* NULL GError */
+ _gtk_file_chooser_settings_save (settings, NULL);
+
+ g_object_unref (settings);
+}
+
/* GtkWidget::map method */
static void
gtk_file_chooser_default_map (GtkWidget *widget)
@@ -5130,6 +5399,8 @@ gtk_file_chooser_default_map (GtkWidget *widget)
bookmarks_changed_cb (impl->file_system, impl);
+ settings_load (impl);
+
profile_end ("end", NULL);
}
@@ -5141,6 +5412,8 @@ gtk_file_chooser_default_unmap (GtkWidget *widget)
impl = GTK_FILE_CHOOSER_DEFAULT (widget);
+ settings_save (impl);
+
GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->unmap (widget);
impl->reload_state = RELOAD_WAS_UNMAPPED;
@@ -5575,6 +5848,8 @@ pending_select_paths_process (GtkFileChooserDefault *impl)
g_assert (impl->browse_files_model != NULL);
g_assert (impl->sort_model != NULL);
+ impl->processing_pending_selections = TRUE;
+
if (impl->pending_select_paths)
{
/* NULL GError */
@@ -5601,6 +5876,8 @@ pending_select_paths_process (GtkFileChooserDefault *impl)
browse_files_select_first_row (impl);
}
+ impl->processing_pending_selections = FALSE;
+
g_assert (impl->pending_select_paths == NULL);
}
@@ -5693,22 +5970,53 @@ set_list_model (GtkFileChooserDefault *impl,
return TRUE;
}
+struct update_chooser_entry_selected_foreach_closure {
+ int num_selected;
+ GtkTreeIter first_selected_iter;
+};
+
+static void
+update_chooser_entry_selected_foreach (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ struct update_chooser_entry_selected_foreach_closure *closure;
+
+ closure = data;
+ closure->num_selected++;
+
+ if (closure->num_selected == 1)
+ closure->first_selected_iter = *iter;
+}
+
static void
update_chooser_entry (GtkFileChooserDefault *impl)
{
GtkTreeSelection *selection;
- const GtkFileInfo *info;
- GtkTreeIter iter;
- GtkTreeIter child_iter;
- gboolean change_entry;
+ struct update_chooser_entry_selected_foreach_closure closure;
+ const char *file_part;
- if (!(impl->action == GTK_FILE_CHOOSER_ACTION_SAVE || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER))
+ if (impl->processing_pending_selections)
return;
- g_assert (!impl->select_multiple);
+ if (!(impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
+ || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER
+ || ((impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
+ || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
+ && impl->location_mode == LOCATION_MODE_FILENAME_ENTRY)))
+ return;
+
+ g_assert (impl->location_entry != NULL);
+
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
- if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
+ closure.num_selected = 0;
+ gtk_tree_selection_selected_foreach (selection, update_chooser_entry_selected_foreach, &closure);
+
+ file_part = NULL;
+
+ if (closure.num_selected == 0)
{
/* If nothing is selected, we only reset the file name entry if we are in
* CREATE_FOLDER mode. In SAVE mode, nothing will be selected when the
@@ -5717,24 +6025,38 @@ update_chooser_entry (GtkFileChooserDefault *impl)
* for a folder name. See http://bugzilla.gnome.org/show_bug.cgi?id=308332
*/
if (impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
- _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry), "");
- return;
+ file_part = "";
}
+ else if (closure.num_selected == 1)
+ {
+ GtkTreeIter child_iter;
+ const GtkFileInfo *info;
+ gboolean change_entry;
- gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
- &child_iter,
- &iter);
+ gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
+ &child_iter,
+ &closure.first_selected_iter);
- info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
+ info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
- 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... */
+ if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
+ || impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
+ || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
+ 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)
+ file_part = gtk_file_info_get_display_name (info);
+ }
else
- change_entry = TRUE; /* ... unless we are in CREATE_FOLDER mode */
+ {
+ /* Multiple selection, so just clear the entry. */
+ file_part = "";
+ }
- 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));
+ if (file_part)
+ _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), file_part);
}
static gboolean
@@ -5808,8 +6130,8 @@ update_current_folder_get_info_cb (GtkFileSystemHandle *handle,
/* Set the folder on the save entry */
- if (impl->save_file_name_entry)
- _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry),
+ if (impl->location_entry)
+ _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (impl->location_entry),
impl->current_folder);
/* Create a new list model. This is slightly evil; we store the result value
@@ -5914,7 +6236,7 @@ gtk_file_chooser_default_set_current_name (GtkFileChooser *chooser,
|| 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);
+ _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), name);
}
static void
@@ -6083,9 +6405,12 @@ check_save_entry (GtkFileChooserDefault *impl,
GError *error;
g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
- || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
+ || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER
+ || ((impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
+ || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
+ && impl->location_mode == LOCATION_MODE_FILENAME_ENTRY));
- chooser_entry = GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry);
+ chooser_entry = GTK_FILE_CHOOSER_ENTRY (impl->location_entry);
if (strlen (gtk_entry_get_text (GTK_ENTRY (chooser_entry))) == 0)
{
@@ -6168,16 +6493,45 @@ gtk_file_chooser_default_get_paths (GtkFileChooser *chooser)
{
GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
struct get_paths_closure info;
+ GtkWindow *toplevel;
+ GtkWidget *current_focus;
info.impl = impl;
info.result = NULL;
info.path_from_entry = NULL;
- if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
- || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
+ toplevel = get_toplevel (GTK_WIDGET (impl));
+ if (toplevel)
+ current_focus = gtk_window_get_focus (toplevel);
+ else
+ current_focus = NULL;
+
+ if (current_focus == impl->browse_files_tree_view)
+ {
+ GtkTreeSelection *selection;
+
+ file_list:
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
+ gtk_tree_selection_selected_foreach (selection, get_paths_foreach, &info);
+
+ /* If there is no selection in the file list, we probably have this situation:
+ *
+ * 1. The user typed a filename in the SAVE filename entry ("foo.txt").
+ * 2. He then double-clicked on a folder ("bar") in the file list
+ *
+ * So we want the selection to be "bar/foo.txt". Jump to the case for the
+ * filename entry to see if that is the case.
+ */
+ if (info.result == NULL && impl->location_entry)
+ goto file_entry;
+ }
+ else if (impl->location_entry && current_focus == impl->location_entry)
{
gboolean is_well_formed, is_empty, is_file_part_empty, is_folder;
+ file_entry:
+
check_save_entry (impl, &info.path_from_entry, &is_well_formed, &is_empty, &is_file_part_empty, &is_folder);
if (!is_well_formed)
@@ -6191,19 +6545,23 @@ gtk_file_chooser_default_get_paths (GtkFileChooser *chooser)
return NULL;
}
}
- }
- if (!info.path_from_entry || impl->select_multiple)
+ info.result = g_slist_prepend (info.result, info.path_from_entry);
+ }
+ else if (impl->toplevel_last_focus_widget == impl->browse_files_tree_view)
+ goto file_list;
+ else if (impl->location_entry && impl->toplevel_last_focus_widget == impl->location_entry)
+ goto file_entry;
+ else
{
- GtkTreeSelection *selection;
-
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
- gtk_tree_selection_selected_foreach (selection, get_paths_foreach, &info);
+ /* The focus is on a dialog's action area button or something else */
+ if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
+ || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
+ goto file_entry;
+ else
+ goto file_list;
}
- if (info.path_from_entry)
- info.result = g_slist_prepend (info.result, info.path_from_entry);
-
/* If there's no folder selected, and we're in SELECT_FOLDER mode, then we
* fall back to the current directory */
if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER &&
@@ -6938,7 +7296,7 @@ save_entry_get_info_cb (GtkFileSystemHandle *handle,
gboolean retval;
const char *file_part;
- file_part = _gtk_file_chooser_entry_get_file_part (GTK_FILE_CHOOSER_ENTRY (data->impl->save_file_name_entry));
+ file_part = _gtk_file_chooser_entry_get_file_part (GTK_FILE_CHOOSER_ENTRY (data->impl->location_entry));
retval = should_respond_after_confirm_overwrite (data->impl, file_part, data->parent_path);
if (retval)
@@ -7066,7 +7424,7 @@ gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed)
g_assert_not_reached ();
}
}
- else if ((impl->save_file_name_entry != NULL) && (current_focus == impl->save_file_name_entry))
+ else if ((impl->location_entry != NULL) && (current_focus == impl->location_entry))
{
GtkFilePath *path;
gboolean is_well_formed, is_empty, is_file_part_empty;
@@ -7078,9 +7436,12 @@ gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed)
save_entry:
g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
- || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
+ || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER
+ || ((impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
+ || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
+ && impl->location_mode == LOCATION_MODE_FILENAME_ENTRY));
- entry = GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry);
+ entry = GTK_FILE_CHOOSER_ENTRY (impl->location_entry);
check_save_entry (impl, &path, &is_well_formed, &is_empty, &is_file_part_empty, &is_folder);
if (is_empty || !is_well_formed)
@@ -7091,19 +7452,26 @@ gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed)
error = NULL;
if (is_folder)
{
- if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
+ if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
+ || 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 */
+ else if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
+ || 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
+ {
+ g_assert_not_reached ();
+ retval = FALSE;
+ }
}
else
{
@@ -7111,7 +7479,9 @@ gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed)
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)
+ if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN)
+ retval = TRUE; /* user typed a filename; we are done */
+ else if (impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER && file_exists_and_is_not_folder)
{
/* 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);
@@ -7179,6 +7549,13 @@ gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed)
*/
goto file_list;
}
+ else if (impl->location_entry && impl->toplevel_last_focus_widget == impl->location_entry)
+ {
+ /* The focus is on a dialog's action area button, *and* the widget that
+ * was focused immediately before it is the location entry.
+ */
+ goto save_entry;
+ }
else
/* The focus is on a dialog's action area button or something else */
if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
@@ -7202,16 +7579,22 @@ gtk_file_chooser_default_initial_focus (GtkFileChooserEmbed *chooser_embed)
if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
|| impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
- widget = impl->browse_files_tree_view;
+ {
+ if (impl->location_mode == LOCATION_MODE_PATH_BAR)
+ widget = impl->browse_files_tree_view;
+ else
+ widget = impl->location_entry;
+ }
else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
|| impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
- widget = impl->save_file_name_entry;
+ widget = impl->location_entry;
else
{
g_assert_not_reached ();
widget = NULL;
}
+ g_assert (widget != NULL);
gtk_widget_grab_focus (widget);
}
@@ -7765,21 +8148,25 @@ list_size_data_func (GtkTreeViewColumn *tree_column,
if (!info || gtk_file_info_get_is_folder (info))
{
- g_object_set (cell,"sensitive", sensitive, NULL);
+ g_object_set (cell,
+ "text", NULL,
+ "sensitive", sensitive,
+ NULL);
return;
}
size = gtk_file_info_get_size (info);
-
+#if 0
if (size < (gint64)1024)
str = g_strdup_printf (ngettext ("%d byte", "%d bytes", (gint)size), (gint)size);
else if (size < (gint64)1024*1024)
- str = g_strdup_printf (_("%.1f K"), size / (1024.));
+ str = g_strdup_printf (_("%.1f KB"), size / (1024.));
else if (size < (gint64)1024*1024*1024)
- str = g_strdup_printf (_("%.1f M"), size / (1024.*1024.));
+ str = g_strdup_printf (_("%.1f MB"), size / (1024.*1024.));
else
- str = g_strdup_printf (_("%.1f G"), size / (1024.*1024.*1024.));
-
+ str = g_strdup_printf (_("%.1f GB"), size / (1024.*1024.*1024.));
+#endif
+ str = g_strdup_printf ("%" G_GINT64_FORMAT, size);
if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
sensitive = FALSE;
@@ -7787,6 +8174,7 @@ list_size_data_func (GtkTreeViewColumn *tree_column,
g_object_set (cell,
"text", str,
"sensitive", sensitive,
+ "alignment", PANGO_ALIGN_RIGHT,
NULL);
g_free (str);
@@ -7870,281 +8258,32 @@ _gtk_file_chooser_default_new (const char *file_system)
NULL);
}
-static GtkWidget *
-location_entry_create (GtkFileChooserDefault *impl,
- const gchar *path)
-{
- GtkWidget *entry;
-
- entry = _gtk_file_chooser_entry_new (TRUE);
- /* Pick a good width for the entry */
- gtk_entry_set_width_chars (GTK_ENTRY (entry), 30);
- gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
- _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (entry), impl->file_system);
- _gtk_file_chooser_entry_set_action (GTK_FILE_CHOOSER_ENTRY (entry), impl->action);
- _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (entry), impl->current_folder);
- if (path[0])
- gtk_entry_set_text (GTK_ENTRY (entry), path);
- else
- {
- if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
- || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
- _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (entry), "");
- else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
- || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
- _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (entry),
- gtk_entry_get_text (GTK_ENTRY (impl->save_file_name_entry)));
- else
- g_assert_not_reached ();
- }
-
- return GTK_WIDGET (entry);
-}
-
-struct UpdateFromEntryData
-{
- GtkFileChooserDefault *impl;
- GtkFilePath *subfolder_path;
- GtkFilePath *folder_path;
- char *file_part;
-};
-
-static void
-update_from_entry_get_info_cb (GtkFileSystemHandle *handle,
- const GtkFileInfo *file_info,
- const GError *error,
- gpointer user_data)
-{
- gboolean cancelled = handle->cancelled;
- struct UpdateFromEntryData *data = user_data;
-
- if (handle != data->impl->update_from_entry_handle)
- goto out;
-
- data->impl->update_from_entry_handle = NULL;
-
- if (cancelled)
- goto out;
-
- if (!file_info)
- {
- if (data->impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
- || data->impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
- {
- if (!change_folder_and_display_error (data->impl, data->folder_path))
- goto out;
-
- gtk_file_chooser_default_set_current_name (GTK_FILE_CHOOSER (data->impl), data->file_part);
- }
- else
- {
- GError *err = g_error_copy (error);
-
- error_getting_info_dialog (data->impl, data->subfolder_path, err);
- }
-
- goto out;
- }
-
- if (gtk_file_info_get_is_folder (file_info))
- change_folder_and_display_error (data->impl, data->subfolder_path);
- else
- {
- gboolean result;
- GError *select_error = NULL;
-
- result = _gtk_file_chooser_select_path (GTK_FILE_CHOOSER (data->impl), data->subfolder_path, &select_error);
-
- if (!result)
- error_dialog (data->impl, _("Could not select item"),
- data->subfolder_path, select_error);
- }
-
-out:
- g_object_unref (data->impl);
- gtk_file_path_free (data->subfolder_path);
- gtk_file_path_free (data->folder_path);
- g_free (data->file_part);
- g_free (data);
-
- g_object_unref (handle);
-}
-
-static gboolean
-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 folder"),
- _("The folder you specified is an invalid path."));
- return FALSE;
- }
-
- if (file_part[0] == '\0')
- return change_folder_and_display_error (impl, folder_path);
- else
- {
- GtkFilePath *subfolder_path = NULL;
- GError *error = NULL;
- gboolean result = FALSE;
- struct UpdateFromEntryData *data;
-
- /* 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.
- */
-
- subfolder_path = gtk_file_system_make_path (impl->file_system, folder_path, file_part, &error);
-
- if (!subfolder_path)
- {
- char *msg;
- char *uri;
-
- uri = gtk_file_system_path_to_uri (impl->file_system, folder_path);
- msg = g_strdup_printf (_("Could not build file name from '%s' and '%s'"),
- uri, file_part);
- error_message (impl, msg, error->message);
-
- g_free (uri);
- g_free (msg);
- gtk_file_path_free (subfolder_path);
-
- return result;
- }
-
- data = g_new0 (struct UpdateFromEntryData, 1);
- data->impl = g_object_ref (impl);
- data->folder_path = gtk_file_path_copy (folder_path);
- data->subfolder_path = subfolder_path;
- data->file_part = g_strdup (file_part);
-
- if (impl->update_from_entry_handle)
- gtk_file_system_cancel_operation (impl->update_from_entry_handle);
-
- impl->update_from_entry_handle =
- gtk_file_system_get_info (impl->file_system, subfolder_path,
- GTK_FILE_INFO_IS_FOLDER,
- update_from_entry_get_info_cb, data);
-
- return TRUE;
- }
-
- g_assert_not_reached ();
-}
-
static void
location_popup_handler (GtkFileChooserDefault *impl,
const gchar *path)
{
- GtkWidget *dialog;
- GtkWindow *toplevel;
- GtkWidget *hbox;
- GtkWidget *label;
- GtkWidget *entry;
- gboolean refocus;
- const char *title;
- const char *accept_stock;
-
- /* Create dialog */
-
- toplevel = get_toplevel (GTK_WIDGET (impl));
-
if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
|| impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
{
- title = _("Open Location");
- accept_stock = GTK_STOCK_OPEN;
- }
- else
- {
- g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
- || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
- title = _("Save in Location");
- accept_stock = GTK_STOCK_SAVE;
- }
-
- dialog = gtk_dialog_new_with_buttons (title,
- toplevel,
- GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
- 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);
- gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
-
- gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
- GTK_RESPONSE_ACCEPT,
- GTK_RESPONSE_CANCEL,
- -1);
-
- hbox = gtk_hbox_new (FALSE, 12);
- gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox, FALSE, FALSE, 0);
- gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
-
- label = gtk_label_new_with_mnemonic (_("_Location:"));
- gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
-
- entry = location_entry_create (impl, path);
-
- gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
- gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
+ LocationMode new_mode;
- /* Run */
-
- gtk_widget_show_all (dialog);
- /* If the dialog is brought up by typing the first characters
- * of a path, unselect the text in the entry, so that you can
- * just type on without erasing the initial part.
- */
- if (path[0])
- gtk_editable_select_region (GTK_EDITABLE (entry), -1, -1);
-
- refocus = TRUE;
-
- if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
- {
- if (update_from_entry (impl, GTK_WINDOW (dialog), GTK_FILE_CHOOSER_ENTRY (entry)))
+ if (impl->location_mode == LOCATION_MODE_PATH_BAR)
+ new_mode = LOCATION_MODE_FILENAME_ENTRY;
+ else if (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY)
+ new_mode = LOCATION_MODE_PATH_BAR;
+ else
{
- if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
- || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
- {
- gtk_widget_grab_focus (impl->browse_files_tree_view);
- }
- else
- {
- g_assert (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
- || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
- gtk_widget_grab_focus (impl->save_file_name_entry);
- }
- refocus = FALSE;
+ g_assert_not_reached ();
+ return;
}
- }
-
- if (refocus)
- {
- GtkWindow *toplevel;
- toplevel = get_toplevel (GTK_WIDGET (impl));
- if (toplevel && toplevel->focus_widget)
- gtk_widget_grab_focus (toplevel->focus_widget);
+ location_mode_set (impl, new_mode, TRUE);
}
-
- gtk_widget_destroy (dialog);
+ else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
+ || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
+ gtk_widget_grab_focus (impl->location_entry);
+ else
+ g_assert_not_reached ();
}
/* Handler for the "up-folder" keybinding signal */
diff --git a/gtk/gtkfilechooserprivate.h b/gtk/gtkfilechooserprivate.h
index 34ad17b40..abfcddc9c 100644
--- a/gtk/gtkfilechooserprivate.h
+++ b/gtk/gtkfilechooserprivate.h
@@ -142,6 +142,11 @@ typedef enum {
RELOAD_WAS_UNMAPPED /* We had a folder but got unmapped; reload is needed */
} ReloadState;
+typedef enum {
+ LOCATION_MODE_PATH_BAR,
+ LOCATION_MODE_FILENAME_ENTRY
+} LocationMode;
+
struct _GtkFileChooserDefault
{
GtkVBox parent_instance;
@@ -153,7 +158,6 @@ struct _GtkFileChooserDefault
/* Save mode widgets */
GtkWidget *save_widgets;
- GtkWidget *save_file_name_entry;
GtkWidget *save_folder_label;
GtkWidget *save_folder_combo;
GtkWidget *save_expander;
@@ -183,6 +187,12 @@ struct _GtkFileChooserDefault
GtkWidget *extra_align;
GtkWidget *extra_widget;
+ GtkWidget *location_button;
+ GtkWidget *location_entry_box;
+ GtkWidget *location_label;
+ GtkWidget *location_entry;
+ LocationMode location_mode;
+
GtkListStore *shortcuts_model;
GtkTreeModel *shortcuts_filter_model;
@@ -253,6 +263,7 @@ struct _GtkFileChooserDefault
guint list_sort_ascending : 1;
guint changing_folder : 1;
guint shortcuts_current_folder_active : 1;
+ guint processing_pending_selections : 1;
#if 0
guint shortcuts_drag_outside : 1;
diff --git a/gtk/gtkfilechoosersettings.c b/gtk/gtkfilechoosersettings.c
new file mode 100644
index 000000000..861ca3f43
--- /dev/null
+++ b/gtk/gtkfilechoosersettings.c
@@ -0,0 +1,535 @@
+/* GTK - The GIMP Toolkit
+ * gtkfilechoosersettings.c: Internal settings for the GtkFileChooser widget
+ * Copyright (C) 2006, Novell, Inc.
+ *
+ * Authors: Federico Mena-Quintero <federico@novell.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* TODO:
+ *
+ * - Persist these:
+ * - hpaned position
+ * - browse_for_other_folders?
+ *
+ * - Do we want lockdown?
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <string.h>
+#include <glib/gi18n-lib.h>
+#include "gtkfilechoosersettings.h"
+
+/* Increment this every time you change the configuration format */
+#define CONFIG_VERSION 0
+
+#define ELEMENT_TOPLEVEL "gtkfilechooser"
+#define ELEMENT_LOCATION "location"
+#define ELEMENT_SHOW_HIDDEN "show_hidden"
+#define ATTRIBUTE_VERSION "version"
+#define ATTRIBUTE_MODE "mode"
+#define ATTRIBUTE_VALUE "value"
+#define MODE_PATH_BAR "path-bar"
+#define MODE_FILENAME_ENTRY "filename-entry"
+#define VALUE_TRUE "true"
+#define VALUE_FALSE "false"
+
+#define EQ(a, b) (g_ascii_strcasecmp ((a), (b)) == 0)
+
+static char *
+get_config_dirname (void)
+{
+ return g_build_filename (g_get_user_config_dir (), "gtk-2.0", NULL);
+}
+
+static char *
+get_config_filename (void)
+{
+ return g_build_filename (g_get_user_config_dir (), "gtk-2.0", "gtkfilechooser", NULL);
+}
+
+static void
+set_defaults (GtkFileChooserSettings *settings)
+{
+ settings->location_mode = LOCATION_MODE_PATH_BAR;
+ settings->show_hidden = FALSE;
+}
+
+typedef enum {
+ STATE_START,
+ STATE_END,
+ STATE_ERROR,
+ STATE_IN_TOPLEVEL,
+ STATE_IN_LOCATION,
+ STATE_IN_SHOW_HIDDEN
+} State;
+
+struct parse_state {
+ GtkFileChooserSettings *settings;
+ int version;
+ State state;
+};
+
+static const char *
+get_attribute_value (const char **attribute_names,
+ const char **attribute_values,
+ const char *attribute)
+{
+ const char **name;
+ const char **value;
+
+ name = attribute_names;
+ value = attribute_values;
+
+ while (*name)
+ {
+ if (EQ (*name, attribute))
+ return *value;
+
+ name++;
+ value++;
+ }
+
+ return NULL;
+}
+
+static void
+set_missing_attribute_error (struct parse_state *state,
+ int line,
+ int col,
+ const char *attribute,
+ GError **error)
+{
+ state->state = STATE_ERROR;
+ g_set_error (error,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ _("Line %d, column %d: missing attribute \"%s\""),
+ line,
+ col,
+ attribute);
+}
+
+static void
+set_unexpected_element_error (struct parse_state *state,
+ int line,
+ int col,
+ const char *element,
+ GError **error)
+{
+ state->state = STATE_ERROR;
+ g_set_error (error,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+ _("Line %d, column %d: unexpected element \"%s\""),
+ line,
+ col,
+ element);
+}
+
+static void
+set_unexpected_element_end_error (struct parse_state *state,
+ int line,
+ int col,
+ const char *expected_element,
+ const char *unexpected_element,
+ GError **error)
+{
+ state->state = STATE_ERROR;
+ g_set_error (error,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+ _("Line %d, column %d: expected end of element \"%s\", but got element for \"%s\" instead"),
+ line,
+ col,
+ expected_element,
+ unexpected_element);
+}
+
+
+static void
+parse_start_element_cb (GMarkupParseContext *context,
+ const char *element_name,
+ const char **attribute_names,
+ const char **attribute_values,
+ gpointer data,
+ GError **error)
+{
+ struct parse_state *state;
+ int line, col;
+
+ state = data;
+ g_markup_parse_context_get_position (context, &line, &col);
+
+ switch (state->state)
+ {
+ case STATE_START:
+ if (EQ (element_name, ELEMENT_TOPLEVEL))
+ {
+ const char *version_str;
+
+ state->state = STATE_IN_TOPLEVEL;
+
+ version_str = get_attribute_value (attribute_names, attribute_values, ATTRIBUTE_VERSION);
+ if (!version_str)
+ state->version = -1;
+ else
+ if (sscanf (version_str, "%d", &state->version) != 1 || state->version < 0)
+ state->version = -1;
+ }
+ else
+ {
+ state->state = STATE_ERROR;
+ g_set_error (error,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+ _("Line %d, column %d: expected \"%s\" at the toplevel, but found \"%s\" instead"),
+ line,
+ col,
+ ELEMENT_TOPLEVEL,
+ element_name);
+ }
+ break;
+
+ case STATE_END:
+ g_assert_not_reached ();
+ break;
+
+ case STATE_ERROR:
+ g_assert_not_reached ();
+ break;
+
+ case STATE_IN_TOPLEVEL:
+ if (EQ (element_name, ELEMENT_LOCATION))
+ {
+ const char *location_mode_str;
+
+ state->state = STATE_IN_LOCATION;
+
+ location_mode_str = get_attribute_value (attribute_names, attribute_values, ATTRIBUTE_MODE);
+ if (!location_mode_str)
+ set_missing_attribute_error (state, line, col, ATTRIBUTE_MODE, error);
+ else if (EQ (location_mode_str, MODE_PATH_BAR))
+ state->settings->location_mode = LOCATION_MODE_PATH_BAR;
+ else if (EQ (location_mode_str, MODE_FILENAME_ENTRY))
+ state->settings->location_mode = LOCATION_MODE_FILENAME_ENTRY;
+ else
+ {
+ state->state = STATE_ERROR;
+ g_set_error (error,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ _("Line %d, column %d: expected \"%s\" or \"%s\", but found \"%s\" instead"),
+ line,
+ col,
+ MODE_PATH_BAR,
+ MODE_FILENAME_ENTRY,
+ location_mode_str);
+ }
+ }
+ else if (EQ (element_name, ELEMENT_SHOW_HIDDEN))
+ {
+ const char *value_str;
+
+ state->state = STATE_IN_SHOW_HIDDEN;
+
+ value_str = get_attribute_value (attribute_names, attribute_values, ATTRIBUTE_VALUE);
+
+ if (!value_str)
+ set_missing_attribute_error (state, line, col, ATTRIBUTE_VALUE, error);
+ else if (EQ (value_str, VALUE_TRUE))
+ state->settings->show_hidden = TRUE;
+ else if (EQ (value_str, VALUE_FALSE))
+ state->settings->show_hidden = FALSE;
+ else
+ {
+ state->state = STATE_ERROR;
+ g_set_error (error,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ _("Line %d, column %d: expected \"%s\" or \"%s\", but found \"%s\" instead"),
+ line,
+ col,
+ VALUE_FALSE,
+ VALUE_TRUE,
+ value_str);
+ }
+ }
+ else
+ set_unexpected_element_error (state, line, col, element_name, error);
+
+ break;
+
+ case STATE_IN_LOCATION:
+ case STATE_IN_SHOW_HIDDEN:
+ set_unexpected_element_error (state, line, col, element_name, error);
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+parse_end_element_cb (GMarkupParseContext *context,
+ const char *element_name,
+ gpointer data,
+ GError **error)
+{
+ struct parse_state *state;
+ int line, col;
+
+ state = data;
+ g_markup_parse_context_get_position (context, &line, &col);
+
+ switch (state->state)
+ {
+ case STATE_START:
+ g_assert_not_reached ();
+ break;
+
+ case STATE_END:
+ g_assert_not_reached ();
+ break;
+
+ case STATE_ERROR:
+ g_assert_not_reached ();
+ break;
+
+ case STATE_IN_TOPLEVEL:
+ if (EQ (element_name, ELEMENT_TOPLEVEL))
+ state->state = STATE_END;
+ else
+ set_unexpected_element_end_error (state, line, col, ELEMENT_TOPLEVEL, element_name, error);
+
+ break;
+
+ case STATE_IN_LOCATION:
+ if (EQ (element_name, ELEMENT_LOCATION))
+ state->state = STATE_IN_TOPLEVEL;
+ else
+ set_unexpected_element_end_error (state, line, col, ELEMENT_LOCATION, element_name, error);
+
+ break;
+
+ case STATE_IN_SHOW_HIDDEN:
+ if (EQ (element_name, ELEMENT_SHOW_HIDDEN))
+ state->state = STATE_IN_TOPLEVEL;
+ else
+ set_unexpected_element_end_error (state, line, col, ELEMENT_SHOW_HIDDEN, element_name, error);
+
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static gboolean
+parse_config (GtkFileChooserSettings *settings,
+ const char *contents,
+ GError **error)
+{
+ GMarkupParser parser = { 0, };
+ GMarkupParseContext *context;
+ struct parse_state state;
+ gboolean retval;
+
+ parser.start_element = parse_start_element_cb;
+ parser.end_element = parse_end_element_cb;
+
+ state.settings = settings;
+ state.version = -1;
+ state.state = STATE_START;
+
+ context = g_markup_parse_context_new (&parser,
+ 0,
+ &state,
+ NULL);
+
+ retval = g_markup_parse_context_parse (context, contents, -1, error);
+ g_markup_parse_context_free (context);
+
+ return retval;
+}
+
+static gboolean
+read_config (GtkFileChooserSettings *settings,
+ GError **error)
+{
+ char *filename;
+ char *contents;
+ gsize contents_len;
+ gboolean success;
+
+ filename = get_config_filename ();
+
+ success = g_file_get_contents (filename, &contents, &contents_len, error);
+ g_free (filename);
+
+ if (!success)
+ {
+ set_defaults (settings);
+ return FALSE;
+ }
+
+ success = parse_config (settings, contents, error);
+
+ g_free (contents);
+
+ return success;
+}
+
+static void
+ensure_settings_read (GtkFileChooserSettings *settings)
+{
+ if (settings->settings_read)
+ return;
+
+ /* NULL GError */
+ read_config (settings, NULL);
+
+ settings->settings_read = TRUE;
+}
+
+G_DEFINE_TYPE (GtkFileChooserSettings,
+ _gtk_file_chooser_settings,
+ G_TYPE_OBJECT);
+
+static void
+_gtk_file_chooser_settings_class_init (GtkFileChooserSettingsClass *class)
+{
+}
+
+static void
+_gtk_file_chooser_settings_init (GtkFileChooserSettings *settings)
+{
+}
+
+GtkFileChooserSettings *
+_gtk_file_chooser_settings_new (void)
+{
+ return g_object_new (GTK_FILE_CHOOSER_SETTINGS_TYPE, NULL);
+}
+
+LocationMode
+_gtk_file_chooser_settings_get_location_mode (GtkFileChooserSettings *settings)
+{
+ ensure_settings_read (settings);
+ return settings->location_mode;
+}
+
+void
+_gtk_file_chooser_settings_set_location_mode (GtkFileChooserSettings *settings,
+ LocationMode location_mode)
+{
+ settings->location_mode = location_mode;
+}
+
+gboolean
+_gtk_file_chooser_settings_get_show_hidden (GtkFileChooserSettings *settings)
+{
+ ensure_settings_read (settings);
+ return settings->show_hidden;
+}
+
+void
+_gtk_file_chooser_settings_set_show_hidden (GtkFileChooserSettings *settings,
+ gboolean show_hidden)
+{
+ settings->show_hidden = show_hidden ? TRUE : FALSE;
+}
+
+static char *
+settings_to_markup (GtkFileChooserSettings *settings)
+{
+ const char *location_mode_str;
+ const char *show_hidden_str;
+
+ if (settings->location_mode == LOCATION_MODE_PATH_BAR)
+ location_mode_str = MODE_PATH_BAR;
+ else if (settings->location_mode == LOCATION_MODE_FILENAME_ENTRY)
+ location_mode_str = MODE_FILENAME_ENTRY;
+ else
+ {
+ g_assert_not_reached ();
+ return NULL;
+ }
+
+ show_hidden_str = settings->show_hidden ? VALUE_TRUE : VALUE_FALSE;
+
+ return g_strdup_printf
+ ("<" ELEMENT_TOPLEVEL ">\n" /* <gtkfilechooser> */
+ " <" ELEMENT_LOCATION " " ATTRIBUTE_MODE "=\"%s\"/>\n" /* <location mode="path-bar"/> */
+ " <" ELEMENT_SHOW_HIDDEN " " ATTRIBUTE_VALUE "=\"%s\"/>\n" /* <show_hidden value="false"/> */
+ "</" ELEMENT_TOPLEVEL ">\n", /* </gtkfilechooser> */
+ location_mode_str,
+ show_hidden_str);
+}
+
+gboolean
+_gtk_file_chooser_settings_save (GtkFileChooserSettings *settings,
+ GError **error)
+{
+ char *contents;
+ char *filename;
+ char *dirname;
+ gboolean retval;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ contents = settings_to_markup (settings);
+
+ filename = get_config_filename ();
+ dirname = NULL;
+
+ retval = FALSE;
+
+ if (!g_file_set_contents (filename, contents, -1, NULL))
+ {
+ char *dirname;
+ int saved_errno;
+
+ /* Directory is not there? */
+
+ dirname = get_config_dirname ();
+ if (g_mkdir_with_parents (dirname, 0700) != 0) /* 0700 per the XDG basedir spec */
+ {
+ saved_errno = errno;
+ g_set_error (error,
+ G_FILE_ERROR,
+ g_file_error_from_errno (saved_errno),
+ _("Could not create directory: %s"),
+ dirname);
+ goto out;
+ }
+
+ if (!g_file_set_contents (filename, contents, -1, error))
+ goto out;
+ }
+
+ retval = TRUE;
+
+ out:
+
+ g_free (contents);
+ g_free (dirname);
+ g_free (filename);
+
+ return retval;
+}
diff --git a/gtk/gtkfilechoosersettings.h b/gtk/gtkfilechoosersettings.h
new file mode 100644
index 000000000..47f6abba6
--- /dev/null
+++ b/gtk/gtkfilechoosersettings.h
@@ -0,0 +1,75 @@
+/* GTK - The GIMP Toolkit
+ * gtkfilechoosersettings.h: Internal settings for the GtkFileChooser widget
+ * Copyright (C) 2006, Novell, Inc.
+ *
+ * Authors: Federico Mena-Quintero <federico@novell.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_FILE_CHOOSER_SETTINGS_H__
+#define __GTK_FILE_CHOOSER_SETTINGS_H__
+
+#include <glib-object.h>
+#include "gtkfilechooserprivate.h"
+
+G_BEGIN_DECLS
+
+#define GTK_FILE_CHOOSER_SETTINGS_TYPE (_gtk_file_chooser_settings_get_type ())
+
+typedef struct _GtkFileChooserSettings GtkFileChooserSettings;
+typedef struct _GtkFileChooserSettingsClass GtkFileChooserSettingsClass;
+
+struct _GtkFileChooserSettings
+{
+ GObject object;
+
+ LocationMode location_mode;
+
+ guint settings_read : 1;
+
+ guint show_hidden : 1;
+};
+
+struct _GtkFileChooserSettingsClass
+{
+ GObjectClass parent_class;
+};
+
+GType _gtk_file_chooser_settings_get_type (void) G_GNUC_CONST;
+
+GtkFileChooserSettings *_gtk_file_chooser_settings_new (void);
+
+LocationMode _gtk_file_chooser_settings_get_location_mode (GtkFileChooserSettings *settings);
+void _gtk_file_chooser_settings_set_location_mode (GtkFileChooserSettings *settings,
+ LocationMode location_mode);
+
+gboolean _gtk_file_chooser_settings_get_show_hidden (GtkFileChooserSettings *settings);
+void _gtk_file_chooser_settings_set_show_hidden (GtkFileChooserSettings *settings,
+ gboolean show_hidden);
+
+gboolean _gtk_file_chooser_settings_save (GtkFileChooserSettings *settings,
+ GError **error);
+
+/* FIXME: persist these options:
+ *
+ * - paned width
+ * - show_hidden
+ */
+
+G_END_DECLS
+
+#endif
diff --git a/gtk/gtkpathbar.c b/gtk/gtkpathbar.c
index 2688c1ee3..1e2cac417 100644
--- a/gtk/gtkpathbar.c
+++ b/gtk/gtkpathbar.c
@@ -143,7 +143,7 @@ gtk_path_bar_init (GtkPathBar *path_bar)
path_bar->set_path_handle = NULL;
- path_bar->spacing = 3;
+ path_bar->spacing = 0;
path_bar->up_slider_button = get_slider_button (path_bar, GTK_ARROW_LEFT);
path_bar->down_slider_button = get_slider_button (path_bar, GTK_ARROW_RIGHT);
path_bar->icon_size = FALLBACK_ICON_SIZE;
diff --git a/tests/autotestfilechooser.c b/tests/autotestfilechooser.c
index d169042ef..bf654bccb 100644
--- a/tests/autotestfilechooser.c
+++ b/tests/autotestfilechooser.c
@@ -147,7 +147,9 @@ test_widgets_for_current_action (GtkFileChooserDialog *dialog,
if (has_action (open_actions, G_N_ELEMENTS (open_actions), impl->action))
{
passed = passed && (impl->save_widgets == NULL
- && impl->save_file_name_entry == NULL
+ && (impl->location_mode == LOCATION_MODE_PATH_BAR
+ ? impl->location_entry == NULL
+ : impl->location_entry != NULL)
&& impl->save_folder_label == NULL
&& impl->save_folder_combo == NULL
&& impl->save_expander == NULL
@@ -157,10 +159,10 @@ test_widgets_for_current_action (GtkFileChooserDialog *dialog,
{
/* FIXME: we can't use GTK_IS_FILE_CHOOSER_ENTRY() because it uses
* _gtk_file_chooser_entry_get_type(), which is a non-exported symbol.
- * So, we just test impl->save_file_name_entry for being non-NULL
+ * So, we just test impl->location_entry for being non-NULL
*/
passed = passed && (GTK_IS_CONTAINER (impl->save_widgets) && GTK_WIDGET_DRAWABLE (impl->save_widgets)
- && impl->save_file_name_entry != NULL && GTK_WIDGET_DRAWABLE (impl->save_file_name_entry)
+ && impl->location_entry != NULL && GTK_WIDGET_DRAWABLE (impl->location_entry)
&& GTK_IS_LABEL (impl->save_folder_label) && GTK_WIDGET_DRAWABLE (impl->save_folder_label)
&& GTK_IS_COMBO_BOX (impl->save_folder_combo) && GTK_WIDGET_DRAWABLE (impl->save_folder_combo)
&& GTK_IS_EXPANDER (impl->save_expander) && GTK_WIDGET_DRAWABLE (impl->save_expander)