diff --git a/ChangeLog b/ChangeLog index a69ffc2a7f..ee73f1e880 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,37 @@ +2005-06-10 Federico Mena Quintero + + Merged from gtk-2-6: + + Fixes #162358: + + * gtk/gtkfilechooserdefault.c (update_chooser_entry): Don't return + immediately if we are in CREATE_FOLDER mode, so that we can fill + the entry with the newly-selected folder. + (gtk_file_chooser_default_set_property): Warn against turning on + multiple selection for CREATE_FOLDER mode, or about setting that + action while multiple selection is on. + (update_chooser_entry): Change the entry's contents as well if we + are in CREATE_FOLDER mode. If nothing is selected, clear the + chooser entry. + (trap_activate_cb): Don't trap enter/space if modifiers are + pressed. This lets one use Ctrl-space to toggle rows in multiple + selection mode. + (gtk_file_chooser_default_should_respond): Clean up the if-chain + mess of special cases by using an array to determine what to do. + Also, for the save-entry case in CREATE_FOLDER mode, actually fix + the bug where the file chooser would switch to an existing folder + rather than confirming with it, and create the folder ourselves. + (error_creating_folder_over_existing_file_dialog): New function. + + * gtk/gtkfilechooserentry.c (check_completion_callback): Only + insert the common prefix if we are in an "open" mode. Use a + helper function. + (append_common_prefix): New helper function; code moved over from + check_completion_callback(). + (find_common_prefix): New helper function. + (gtk_file_chooser_entry_focus): Append the common prefix if the + user requests it explicitly. + 2005-06-10 Matthias Clasen * gtk/gtktreeviewcolumn.c (gtk_tree_view_column_set_expand): diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index a69ffc2a7f..ee73f1e880 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -1,3 +1,37 @@ +2005-06-10 Federico Mena Quintero + + Merged from gtk-2-6: + + Fixes #162358: + + * gtk/gtkfilechooserdefault.c (update_chooser_entry): Don't return + immediately if we are in CREATE_FOLDER mode, so that we can fill + the entry with the newly-selected folder. + (gtk_file_chooser_default_set_property): Warn against turning on + multiple selection for CREATE_FOLDER mode, or about setting that + action while multiple selection is on. + (update_chooser_entry): Change the entry's contents as well if we + are in CREATE_FOLDER mode. If nothing is selected, clear the + chooser entry. + (trap_activate_cb): Don't trap enter/space if modifiers are + pressed. This lets one use Ctrl-space to toggle rows in multiple + selection mode. + (gtk_file_chooser_default_should_respond): Clean up the if-chain + mess of special cases by using an array to determine what to do. + Also, for the save-entry case in CREATE_FOLDER mode, actually fix + the bug where the file chooser would switch to an existing folder + rather than confirming with it, and create the folder ourselves. + (error_creating_folder_over_existing_file_dialog): New function. + + * gtk/gtkfilechooserentry.c (check_completion_callback): Only + insert the common prefix if we are in an "open" mode. Use a + helper function. + (append_common_prefix): New helper function; code moved over from + check_completion_callback(). + (find_common_prefix): New helper function. + (gtk_file_chooser_entry_focus): Append the common prefix if the + user requests it explicitly. + 2005-06-10 Matthias Clasen * gtk/gtktreeviewcolumn.c (gtk_tree_view_column_set_expand): diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8 index a69ffc2a7f..ee73f1e880 100644 --- a/ChangeLog.pre-2-8 +++ b/ChangeLog.pre-2-8 @@ -1,3 +1,37 @@ +2005-06-10 Federico Mena Quintero + + Merged from gtk-2-6: + + Fixes #162358: + + * gtk/gtkfilechooserdefault.c (update_chooser_entry): Don't return + immediately if we are in CREATE_FOLDER mode, so that we can fill + the entry with the newly-selected folder. + (gtk_file_chooser_default_set_property): Warn against turning on + multiple selection for CREATE_FOLDER mode, or about setting that + action while multiple selection is on. + (update_chooser_entry): Change the entry's contents as well if we + are in CREATE_FOLDER mode. If nothing is selected, clear the + chooser entry. + (trap_activate_cb): Don't trap enter/space if modifiers are + pressed. This lets one use Ctrl-space to toggle rows in multiple + selection mode. + (gtk_file_chooser_default_should_respond): Clean up the if-chain + mess of special cases by using an array to determine what to do. + Also, for the save-entry case in CREATE_FOLDER mode, actually fix + the bug where the file chooser would switch to an existing folder + rather than confirming with it, and create the folder ourselves. + (error_creating_folder_over_existing_file_dialog): New function. + + * gtk/gtkfilechooserentry.c (check_completion_callback): Only + insert the common prefix if we are in an "open" mode. Use a + helper function. + (append_common_prefix): New helper function; code moved over from + check_completion_callback(). + (find_common_prefix): New helper function. + (gtk_file_chooser_entry_focus): Append the common prefix if the + user requests it explicitly. + 2005-06-10 Matthias Clasen * gtk/gtktreeviewcolumn.c (gtk_tree_view_column_set_expand): diff --git a/gtk/gtkfilechooserdefault.c b/gtk/gtkfilechooserdefault.c index 25571eec22..4b0df6c1fa 100644 --- a/gtk/gtkfilechooserdefault.c +++ b/gtk/gtkfilechooserdefault.c @@ -951,6 +951,21 @@ error_creating_folder_dialog (GtkFileChooserDefault *impl, path, error); } +/* Shows an error about not being able to create a folder because a file with + * the same name is already there. + */ +static void +error_creating_folder_over_existing_file_dialog (GtkFileChooserDefault *impl, + const GtkFilePath *path, + GError *error) +{ + error_dialog (impl, + _("The folder could not be created, as a file with the same name " + "already exists. Try using a different name for the folder, " + "or rename the file first."), + path, error); +} + /* Shows an error dialog about not being able to create a filename */ static void error_building_filename_dialog (GtkFileChooserDefault *impl, @@ -3209,11 +3224,14 @@ trap_activate_cb (GtkWidget *widget, gpointer data) { GtkFileChooserDefault *impl; + int modifiers; impl = (GtkFileChooserDefault *) data; + + modifiers = gtk_accelerator_get_default_mod_mask (); if (event->keyval == GDK_slash && - ! (event->state & (~GDK_SHIFT_MASK & gtk_accelerator_get_default_mod_mask ()))) + ! (event->state & (~GDK_SHIFT_MASK & modifiers))) { location_popup_handler (impl, "/"); return TRUE; @@ -3223,6 +3241,7 @@ trap_activate_cb (GtkWidget *widget, || event->keyval == GDK_ISO_Enter || event->keyval == GDK_KP_Enter || event->keyval == GDK_space) + && ((event->state && modifiers) == 0) && !(impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)) { @@ -4056,9 +4075,12 @@ gtk_file_chooser_default_set_property (GObject *object, { gtk_file_chooser_default_unselect_all (GTK_FILE_CHOOSER (impl)); - if (action == GTK_FILE_CHOOSER_ACTION_SAVE && impl->select_multiple) + if ((action == GTK_FILE_CHOOSER_ACTION_SAVE || action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) + && impl->select_multiple) { - g_warning ("Multiple selection mode is not allowed in Save mode"); + g_warning ("Tried to change the file chooser action to SAVE or CREATE_FOLDER, but " + "this is not allowed in multiple selection mode. Resetting the file chooser " + "to single selection mode."); set_select_multiple (impl, FALSE, TRUE); } impl->action = action; @@ -4096,9 +4118,12 @@ gtk_file_chooser_default_set_property (GObject *object, case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE: { gboolean select_multiple = g_value_get_boolean (value); - if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE && select_multiple) + if ((impl->action == GTK_FILE_CHOOSER_ACTION_SAVE || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) + && select_multiple) { - g_warning ("Multiple selection mode is not allowed in Save mode"); + g_warning ("Tried to set the file chooser to multiple selection mode, but this is " + "not allowed in SAVE or CREATE_FOLDER modes. Ignoring the change and " + "leaving the file chooser in single selection mode."); return; } @@ -4899,15 +4924,19 @@ update_chooser_entry (GtkFileChooserDefault *impl) const GtkFileInfo *info; GtkTreeIter iter; GtkTreeIter child_iter; + gboolean change_entry; - if (impl->action != GTK_FILE_CHOOSER_ACTION_SAVE) + if (!(impl->action == GTK_FILE_CHOOSER_ACTION_SAVE || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)) return; g_assert (!impl->select_multiple); selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view)); if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) - return; + { + _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry), ""); + return; + } gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, @@ -4915,7 +4944,12 @@ update_chooser_entry (GtkFileChooserDefault *impl) info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter); - if (!gtk_file_info_get_is_folder (info)) + if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE) + change_entry = !gtk_file_info_get_is_folder (info); /* We don't want the name to change when clicking on a folder... */ + else + change_entry = TRUE; /* ... unless we are in CREATE_FOLDER mode */ + + if (change_entry) _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry), gtk_file_info_get_display_name (info)); } @@ -5644,41 +5678,75 @@ gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed) if (current_focus == impl->browse_files_tree_view) { + /* The following array encodes what we do based on the impl->action and the + * number of files selected. + */ + typedef enum { + NOOP, /* Do nothing (don't respond) */ + RESPOND, /* Respond immediately */ + RESPOND_OR_SWITCH, /* Respond immediately if the selected item is a file; switch to it if it is a folder */ + ALL_FILES, /* Respond only if everything selected is a file */ + ALL_FOLDERS, /* Respond only if everything selected is a folder */ + SAVE_ENTRY, /* Go to the code for handling the save entry */ + NOT_REACHED /* Sanity check */ + } ActionToTake; + static const ActionToTake what_to_do[4][3] = { + /* 0 selected 1 selected many selected */ + /* ACTION_OPEN */ { NOOP, RESPOND_OR_SWITCH, ALL_FILES }, + /* ACTION_SAVE */ { SAVE_ENTRY, RESPOND_OR_SWITCH, NOT_REACHED }, + /* ACTION_SELECT_FOLDER */ { RESPOND, ALL_FOLDERS, ALL_FOLDERS }, + /* ACTION_CREATE_FOLDER */ { SAVE_ENTRY, ALL_FOLDERS, NOT_REACHED } + }; + int num_selected; gboolean all_files, all_folders; + int k; + ActionToTake action; file_list: + g_assert (impl->action >= GTK_FILE_CHOOSER_ACTION_OPEN && impl->action <= GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER); + selection_check (impl, &num_selected, &all_files, &all_folders); - if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) - { - if (num_selected != 1) - return TRUE; /* zero means current folder; more than one means use the whole selection */ - else if (current_focus != impl->browse_files_tree_view) - { - /* a single folder is selected and a button was clicked */ - switch_to_selected_folder (impl); - return TRUE; - } - } - - if (num_selected == 0) - { - if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE - || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) - goto save_entry; /* it makes sense to use the typed name */ - else - return FALSE; - } - - if (num_selected == 1 && all_folders) - { - switch_to_selected_folder (impl); - return FALSE; - } + if (num_selected > 2) + k = 2; else - return all_files; + k = num_selected; + + action = what_to_do [impl->action] [k]; + + switch (action) + { + case NOOP: + return FALSE; + + case RESPOND: + return TRUE; + + case RESPOND_OR_SWITCH: + g_assert (num_selected == 1); + + if (all_folders) + { + switch_to_selected_folder (impl); + return FALSE; + } + else + return TRUE; + + case ALL_FILES: + return all_files; + + case ALL_FOLDERS: + return all_folders; + + case SAVE_ENTRY: + goto save_entry; + + default: + g_assert_not_reached (); + } } else if (current_focus == impl->save_file_name_entry) { @@ -5686,7 +5754,8 @@ gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed) gboolean is_valid, is_empty; gboolean is_folder; gboolean retval; - GtkFileChooserEntry *entry; + GtkFileChooserEntry *entry; + GError *error; save_entry: @@ -5696,32 +5765,67 @@ gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed) entry = GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry); path = check_save_entry (impl, &is_valid, &is_empty); - if (!is_empty && !is_valid) + if (!is_valid) return FALSE; - if (is_empty) - path = gtk_file_path_copy (_gtk_file_chooser_entry_get_current_folder (entry)); - - is_folder = check_is_folder (impl->file_system, path, NULL); + g_assert (!is_empty); + + error = NULL; + is_folder = check_is_folder (impl->file_system, path, &error); if (is_folder) { - _gtk_file_chooser_entry_set_file_part (entry, ""); - change_folder_and_display_error (impl, path); - retval = FALSE; - } - else - { - /* check that everything up to the last component exists */ - gtk_file_path_free (path); - path = gtk_file_path_copy (_gtk_file_chooser_entry_get_current_folder (entry)); - is_folder = check_is_folder (impl->file_system, path, NULL); - if (!is_folder) + if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE) { + _gtk_file_chooser_entry_set_file_part (entry, ""); change_folder_and_display_error (impl, path); retval = FALSE; } + else /* GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER */ + { + retval = TRUE; + } + } + else + { + if (impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER + && g_error_matches (error, GTK_FILE_SYSTEM_ERROR, GTK_FILE_SYSTEM_ERROR_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); + retval = FALSE; + } else - retval = TRUE; + { + GtkFilePath *parent_path; + + /* check that everything up to the last component exists */ + + parent_path = gtk_file_path_copy (_gtk_file_chooser_entry_get_current_folder (entry)); + is_folder = check_is_folder (impl->file_system, parent_path, NULL); + if (!is_folder) + { + change_folder_and_display_error (impl, parent_path); + retval = FALSE; + } + else + { + GError *create_error; + + create_error = NULL; + if (gtk_file_system_create_folder (impl->file_system, path, &create_error)) + retval = TRUE; + else + { + error_creating_folder_dialog (impl, path, create_error); + retval = FALSE; + } + } + + gtk_file_path_free (parent_path); + } + + if (error != NULL) + g_error_free (error); } gtk_file_path_free (path); diff --git a/gtk/gtkfilechooserentry.c b/gtk/gtkfilechooserentry.c index 97b0f2327f..4967832b90 100644 --- a/gtk/gtkfilechooserentry.c +++ b/gtk/gtkfilechooserentry.c @@ -384,28 +384,25 @@ maybe_append_separator_to_path (GtkFileChooserEntry *chooser_entry, return display_name; } -static gboolean -check_completion_callback (GtkFileChooserEntry *chooser_entry) +/* Determines if the completion model has entries with a common prefix relative + * to the current contents of the entry. Also, if there's one and only one such + * path, stores it in unique_path_ret. + */ +static void +find_common_prefix (GtkFileChooserEntry *chooser_entry, + gchar **common_prefix_ret, + GtkFilePath **unique_path_ret) { GtkTreeIter iter; - gchar *common_prefix = NULL; - GtkFilePath *unique_path = NULL; gboolean valid; - GDK_THREADS_ENTER (); - - g_assert (chooser_entry->file_part); - - chooser_entry->check_completion_idle = NULL; - - if (strcmp (chooser_entry->file_part, "") == 0) - goto done; + *common_prefix_ret = NULL; + *unique_path_ret = NULL; if (chooser_entry->completion_store == NULL) - goto done; + return; - valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (chooser_entry->completion_store), - &iter); + valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (chooser_entry->completion_store), &iter); while (valid) { @@ -420,14 +417,14 @@ check_completion_callback (GtkFileChooserEntry *chooser_entry) if (g_str_has_prefix (display_name, chooser_entry->file_part)) { - if (!common_prefix) + if (!*common_prefix_ret) { - common_prefix = g_strdup (display_name); - unique_path = gtk_file_path_copy (path); + *common_prefix_ret = g_strdup (display_name); + *unique_path_ret = gtk_file_path_copy (path); } else { - gchar *p = common_prefix; + gchar *p = *common_prefix_ret; const gchar *q = display_name; while (*p && *p == *q) @@ -438,16 +435,26 @@ check_completion_callback (GtkFileChooserEntry *chooser_entry) *p = '\0'; - gtk_file_path_free (unique_path); - unique_path = NULL; + gtk_file_path_free (*unique_path_ret); + *unique_path_ret = NULL; } } g_free (display_name); gtk_file_path_free (path); - valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (chooser_entry->completion_store), - &iter); + valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (chooser_entry->completion_store), &iter); } +} + +/* Finds a common prefix based on the contents of the entry and mandatorily appends it */ +static void +append_common_prefix (GtkFileChooserEntry *chooser_entry, + gboolean highlight) +{ + gchar *common_prefix; + GtkFilePath *unique_path; + + find_common_prefix (chooser_entry, &common_prefix, &unique_path); if (unique_path) { @@ -457,19 +464,6 @@ check_completion_callback (GtkFileChooserEntry *chooser_entry) gtk_file_path_free (unique_path); } - switch (chooser_entry->action) - { - case GTK_FILE_CHOOSER_ACTION_SAVE: - case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER: - if (common_prefix && !g_str_has_suffix (common_prefix, "/")) - { - g_free (common_prefix); - common_prefix = NULL; - } - break; - default: ; - } - if (common_prefix) { gint file_part_len; @@ -489,16 +483,40 @@ check_completion_callback (GtkFileChooserEntry *chooser_entry) gtk_editable_insert_text (GTK_EDITABLE (chooser_entry), common_prefix, -1, &pos); - gtk_editable_select_region (GTK_EDITABLE (chooser_entry), - chooser_entry->file_part_pos + file_part_len, - chooser_entry->file_part_pos + common_prefix_len); chooser_entry->in_change = FALSE; - chooser_entry->has_completion = TRUE; + if (highlight) + { + gtk_editable_select_region (GTK_EDITABLE (chooser_entry), + chooser_entry->file_part_pos + file_part_len, + chooser_entry->file_part_pos + common_prefix_len); + chooser_entry->has_completion = TRUE; + } } - + g_free (common_prefix); } +} + +static gboolean +check_completion_callback (GtkFileChooserEntry *chooser_entry) +{ + GDK_THREADS_ENTER (); + + g_assert (chooser_entry->file_part); + + chooser_entry->check_completion_idle = NULL; + + if (strcmp (chooser_entry->file_part, "") == 0) + goto done; + + /* We only insert the common prefix without requiring the user to hit Tab in + * the "open" modes. For "save" modes, the user must hit Tab to cause the prefix + * to be inserted. That happens in gtk_file_chooser_entry_focus(). + */ + if (chooser_entry->action == GTK_FILE_CHOOSER_ACTION_OPEN + || chooser_entry->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) + append_common_prefix (chooser_entry, TRUE); done: @@ -660,13 +678,21 @@ static gboolean gtk_file_chooser_entry_focus (GtkWidget *widget, GtkDirectionType direction) { - GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (widget); + GtkFileChooserEntry *chooser_entry; + GtkEditable *editable; + GtkEntry *entry; GdkModifierType state; - gboolean control_pressed = FALSE; + gboolean control_pressed; + + chooser_entry = GTK_FILE_CHOOSER_ENTRY (widget); + editable = GTK_EDITABLE (widget); + entry = GTK_ENTRY (widget); if (!chooser_entry->eat_tabs) return GTK_WIDGET_CLASS (parent_class)->focus (widget, direction); + control_pressed = FALSE; + if (gtk_get_current_event_state (&state)) { if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK) @@ -681,13 +707,16 @@ gtk_file_chooser_entry_focus (GtkWidget *widget, { gint pos = 0; - if (chooser_entry->has_completion) - gtk_editable_set_position (GTK_EDITABLE (widget), - GTK_ENTRY (widget)->text_length); + if (!chooser_entry->has_completion + && gtk_editable_get_position (editable) == entry->text_length) + append_common_prefix (chooser_entry, FALSE); + + gtk_editable_set_position (editable, entry->text_length); + /* Trigger the completion window to pop up again by a * zero-length insertion, a bit of a hack. */ - gtk_editable_insert_text (GTK_EDITABLE (widget), "", -1, &pos); + gtk_editable_insert_text (editable, "", -1, &pos); return TRUE; }