X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkfilechooserbutton.c;h=05c6a8b8e9a2038aa6f90773eba666a23e1afd60;hb=HEAD;hp=2abc11304f267ee6578acbf23528d0a1034d2448;hpb=bf0d8402f5382fadd32a9748d00a4bcd715ea07a;p=~andy%2Fgtk diff --git a/gtk/gtkfilechooserbutton.c b/gtk/gtkfilechooserbutton.c index 2abc11304..05c6a8b8e 100644 --- a/gtk/gtkfilechooserbutton.c +++ b/gtk/gtkfilechooserbutton.c @@ -1,7 +1,7 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 2 -*- */ +/* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */ /* GTK+: gtkfilechooserbutton.c - * + * * Copyright (c) 2004 James M. Cape * * This library is free software; you can redistribute it and/or @@ -15,9 +15,7 @@ * Library General Public License for more details. * * You should have received a copy of the GNU Library 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. + * License along with this library. If not, see . */ #include "config.h" @@ -56,6 +54,7 @@ #include "gtktypebuiltins.h" #include "gtkprivate.h" +#include "gtksettings.h" /** @@ -104,7 +103,7 @@ #define DEFAULT_TITLE N_("Select a File") #define DESKTOP_DISPLAY_NAME N_("Desktop") -#define FALLBACK_DISPLAY_NAME N_("(None)") +#define FALLBACK_DISPLAY_NAME N_("(None)") /* this string is used in gtk+/gtk/tests/filechooser.c - change it there if you change it here */ #define FALLBACK_ICON_NAME "stock_unknown" #define FALLBACK_ICON_SIZE 16 @@ -155,6 +154,7 @@ typedef enum ROW_TYPE_CURRENT_FOLDER, ROW_TYPE_OTHER_SEPARATOR, ROW_TYPE_OTHER, + ROW_TYPE_EMPTY_SELECTION, ROW_TYPE_INVALID = -1 } @@ -179,12 +179,10 @@ struct _GtkFileChooserButtonPrivate GtkTreeModel *filter_model; GtkFileSystem *fs; - GFile *old_file; + GFile *selection_while_inactive; + GFile *current_folder_while_inactive; gulong combo_box_changed_id; - gulong dialog_file_activated_id; - gulong dialog_folder_changed_id; - gulong dialog_selection_changed_id; gulong fs_volumes_changed_id; gulong fs_bookmarks_changed_id; @@ -206,10 +204,10 @@ struct _GtkFileChooserButtonPrivate /* Used for hiding/showing the dialog when the button is hidden */ guint active : 1; - /* Used to track whether we need to set a default current folder on ::map() */ - guint folder_has_been_set : 1; - guint focus_on_click : 1; + + /* Whether the next async callback from GIO should emit the "selection-changed" signal */ + guint is_changing_selection : 1; }; @@ -230,6 +228,17 @@ enum /* GtkFileChooserIface Functions */ static void gtk_file_chooser_button_file_chooser_iface_init (GtkFileChooserIface *iface); +static gboolean gtk_file_chooser_button_set_current_folder (GtkFileChooser *chooser, + GFile *file, + GError **error); +static GFile *gtk_file_chooser_button_get_current_folder (GtkFileChooser *chooser); +static gboolean gtk_file_chooser_button_select_file (GtkFileChooser *chooser, + GFile *file, + GError **error); +static void gtk_file_chooser_button_unselect_file (GtkFileChooser *chooser, + GFile *file); +static void gtk_file_chooser_button_unselect_all (GtkFileChooser *chooser); +static GSList *gtk_file_chooser_button_get_files (GtkFileChooser *chooser); static gboolean gtk_file_chooser_button_add_shortcut_folder (GtkFileChooser *chooser, GFile *file, GError **error); @@ -280,8 +289,9 @@ static gint model_get_type_position (GtkFileChooserButton *button, RowType row_type); static void model_free_row_data (GtkFileChooserButton *button, GtkTreeIter *iter); -static inline void model_add_special (GtkFileChooserButton *button); -static inline void model_add_other (GtkFileChooserButton *button); +static void model_add_special (GtkFileChooserButton *button); +static void model_add_other (GtkFileChooserButton *button); +static void model_add_empty_selection (GtkFileChooserButton *button); static void model_add_volumes (GtkFileChooserButton *button, GSList *volumes); static void model_add_bookmarks (GtkFileChooserButton *button, @@ -316,18 +326,15 @@ static void fs_bookmarks_changed_cb (GtkFileSystem *fs, static void combo_box_changed_cb (GtkComboBox *combo_box, gpointer user_data); +static void combo_box_notify_popup_shown_cb (GObject *object, + GParamSpec *pspec, + gpointer user_data); static void button_clicked_cb (GtkButton *real_button, gpointer user_data); static void dialog_update_preview_cb (GtkFileChooser *dialog, gpointer user_data); -static void dialog_selection_changed_cb (GtkFileChooser *dialog, - gpointer user_data); -static void dialog_file_activated_cb (GtkFileChooser *dialog, - gpointer user_data); -static void dialog_current_folder_changed_cb (GtkFileChooser *dialog, - gpointer user_data); static void dialog_notify_cb (GObject *dialog, GParamSpec *pspec, gpointer user_data); @@ -399,7 +406,7 @@ gtk_file_chooser_button_class_init (GtkFileChooserButtonClass * class) /** * GtkFileChooserButton:dialog: - * + * * Instance of the #GtkFileChooserDialog associated with the button. * * Since: 2.6 @@ -414,7 +421,7 @@ gtk_file_chooser_button_class_init (GtkFileChooserButtonClass * class) /** * GtkFileChooserButton:focus-on-click: - * + * * Whether the #GtkFileChooserButton button grabs focus when it is clicked * with the mouse. * @@ -427,10 +434,10 @@ gtk_file_chooser_button_class_init (GtkFileChooserButtonClass * class) P_("Whether the button grabs focus when it is clicked with the mouse"), TRUE, GTK_PARAM_READWRITE)); - + /** * GtkFileChooserButton:title: - * + * * Title to put on the #GtkFileChooserDialog associated with the button. * * Since: 2.6 @@ -444,7 +451,7 @@ gtk_file_chooser_button_class_init (GtkFileChooserButtonClass * class) /** * GtkFileChooserButton:width-chars: - * + * * The width of the entry and label inside the button, in characters. * * Since: 2.6 @@ -479,9 +486,10 @@ gtk_file_chooser_button_init (GtkFileChooserButton *button) /* Button */ priv->button = gtk_button_new (); - g_signal_connect (priv->button, "clicked", G_CALLBACK (button_clicked_cb), - button); - gtk_container_add (GTK_CONTAINER (button), priv->button); + g_signal_connect (priv->button, "clicked", + G_CALLBACK (button_clicked_cb), button); + gtk_box_pack_start (GTK_BOX (button), priv->button, TRUE, TRUE, 0); + gtk_widget_set_halign (priv->button, GTK_ALIGN_FILL); gtk_widget_show (priv->button); box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4); @@ -504,8 +512,7 @@ gtk_file_chooser_button_init (GtkFileChooserButton *button) gtk_box_pack_start (GTK_BOX (box), sep, FALSE, FALSE, 0); gtk_widget_show (sep); - image = gtk_image_new_from_stock (GTK_STOCK_OPEN, - GTK_ICON_SIZE_MENU); + image = gtk_image_new_from_stock (GTK_STOCK_OPEN, GTK_ICON_SIZE_MENU); gtk_box_pack_start (GTK_BOX (box), image, FALSE, FALSE, 0); gtk_widget_show (image); @@ -513,18 +520,22 @@ gtk_file_chooser_button_init (GtkFileChooserButton *button) /* Keep in sync with columns enum, line 88 */ priv->model = GTK_TREE_MODEL (gtk_list_store_new (NUM_COLUMNS, - GDK_TYPE_PIXBUF, /* Icon */ - G_TYPE_STRING, /* Display Name */ - G_TYPE_CHAR, /* Row Type */ - G_TYPE_POINTER /* Volume || Path */, - G_TYPE_BOOLEAN /* Is Folder? */, - G_TYPE_POINTER /* cancellable */)); + GDK_TYPE_PIXBUF, /* ICON_COLUMN */ + G_TYPE_STRING, /* DISPLAY_NAME_COLUMN */ + G_TYPE_CHAR, /* TYPE_COLUMN */ + G_TYPE_POINTER /* DATA_COLUMN (Volume || Path) */, + G_TYPE_BOOLEAN /* IS_FOLDER_COLUMN */, + G_TYPE_POINTER /* CANCELLABLE_COLUMN */)); priv->combo_box = gtk_combo_box_new (); - priv->combo_box_changed_id = - g_signal_connect (priv->combo_box, "changed", - G_CALLBACK (combo_box_changed_cb), button); - gtk_container_add (GTK_CONTAINER (button), priv->combo_box); + priv->combo_box_changed_id = g_signal_connect (priv->combo_box, "changed", + G_CALLBACK (combo_box_changed_cb), button); + + g_signal_connect (priv->combo_box, "notify::popup-shown", + G_CALLBACK (combo_box_notify_popup_shown_cb), button); + + gtk_box_pack_start (GTK_BOX (button), priv->combo_box, TRUE, TRUE, 0); + gtk_widget_set_halign (priv->combo_box, GTK_ALIGN_FILL); priv->icon_cell = gtk_cell_renderer_pixbuf_new (); gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->combo_box), @@ -564,10 +575,168 @@ gtk_file_chooser_button_file_chooser_iface_init (GtkFileChooserIface *iface) { _gtk_file_chooser_delegate_iface_init (iface); + iface->set_current_folder = gtk_file_chooser_button_set_current_folder; + iface->get_current_folder = gtk_file_chooser_button_get_current_folder; + iface->select_file = gtk_file_chooser_button_select_file; + iface->unselect_file = gtk_file_chooser_button_unselect_file; + iface->unselect_all = gtk_file_chooser_button_unselect_all; + iface->get_files = gtk_file_chooser_button_get_files; iface->add_shortcut_folder = gtk_file_chooser_button_add_shortcut_folder; iface->remove_shortcut_folder = gtk_file_chooser_button_remove_shortcut_folder; } +static void +emit_selection_changed_if_changing_selection (GtkFileChooserButton *button) +{ + GtkFileChooserButtonPrivate *priv = button->priv; + + if (priv->is_changing_selection) + { + priv->is_changing_selection = FALSE; + g_signal_emit_by_name (button, "selection-changed"); + } +} + +static gboolean +gtk_file_chooser_button_set_current_folder (GtkFileChooser *chooser, + GFile *file, + GError **error) +{ + GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser); + GtkFileChooserButtonPrivate *priv = button->priv; + + if (priv->current_folder_while_inactive) + g_object_unref (priv->current_folder_while_inactive); + + priv->current_folder_while_inactive = g_object_ref (file); + + update_combo_box (button); + + g_signal_emit_by_name (button, "current-folder-changed"); + + if (priv->active) + gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (priv->dialog), file, NULL); + + return TRUE; +} + +static GFile * +gtk_file_chooser_button_get_current_folder (GtkFileChooser *chooser) +{ + GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser); + GtkFileChooserButtonPrivate *priv = button->priv; + + if (priv->current_folder_while_inactive) + return g_object_ref (priv->current_folder_while_inactive); + else + return NULL; +} + +static gboolean +gtk_file_chooser_button_select_file (GtkFileChooser *chooser, + GFile *file, + GError **error) +{ + GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser); + GtkFileChooserButtonPrivate *priv = button->priv; + + if (priv->selection_while_inactive) + g_object_unref (priv->selection_while_inactive); + + priv->selection_while_inactive = g_object_ref (file); + + priv->is_changing_selection = TRUE; + + update_label_and_image (button); + update_combo_box (button); + + if (priv->active) + gtk_file_chooser_select_file (GTK_FILE_CHOOSER (priv->dialog), file, NULL); + + return TRUE; +} + +static void +unselect_current_file (GtkFileChooserButton *button) +{ + GtkFileChooserButtonPrivate *priv = button->priv; + + if (priv->selection_while_inactive) + { + g_object_unref (priv->selection_while_inactive); + priv->selection_while_inactive = NULL; + } + + priv->is_changing_selection = TRUE; + + update_label_and_image (button); + update_combo_box (button); +} + +static void +gtk_file_chooser_button_unselect_file (GtkFileChooser *chooser, + GFile *file) +{ + GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser); + GtkFileChooserButtonPrivate *priv = button->priv; + + if (g_file_equal (priv->selection_while_inactive, file)) + unselect_current_file (button); + + if (priv->active) + gtk_file_chooser_unselect_file (GTK_FILE_CHOOSER (priv->dialog), file); +} + +static void +gtk_file_chooser_button_unselect_all (GtkFileChooser *chooser) +{ + GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser); + GtkFileChooserButtonPrivate *priv = button->priv; + + unselect_current_file (button); + + if (priv->active) + gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (priv->dialog)); +} + +static GFile * +get_selected_file (GtkFileChooserButton *button) +{ + GtkFileChooserButtonPrivate *priv = button->priv; + GFile *retval; + + retval = NULL; + + if (priv->selection_while_inactive) + retval = priv->selection_while_inactive; + else if (gtk_file_chooser_get_action (GTK_FILE_CHOOSER (priv->dialog)) == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) + { + /* If there is no "real" selection in SELECT_FOLDER mode, then we'll just return + * the current folder, since that is what GtkFileChooserDefault would do. + */ + if (priv->current_folder_while_inactive) + retval = priv->current_folder_while_inactive; + } + + if (retval) + return g_object_ref (retval); + else + return NULL; +} + +static GSList * +gtk_file_chooser_button_get_files (GtkFileChooser *chooser) +{ + GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser); + GFile *file; + + file = get_selected_file (button); + if (file) + return g_slist_prepend (NULL, file); + else + return NULL; +} + static gboolean gtk_file_chooser_button_add_shortcut_folder (GtkFileChooser *chooser, GFile *file, @@ -672,7 +841,6 @@ gtk_file_chooser_button_constructor (GType type, GtkFileChooserButton *button; GtkFileChooserButtonPrivate *priv; GSList *list; - char *current_folder; object = G_OBJECT_CLASS (gtk_file_chooser_button_parent_class)->constructor (type, n_params, @@ -704,13 +872,6 @@ gtk_file_chooser_button_constructor (GType type, gtk_file_chooser_button_set_title (button, _(DEFAULT_TITLE)); } - current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (priv->dialog)); - if (current_folder != NULL) - { - priv->folder_has_been_set = TRUE; - g_free (current_folder); - } - g_signal_connect (priv->dialog, "delete-event", G_CALLBACK (dialog_delete_event_cb), object); g_signal_connect (priv->dialog, "response", @@ -719,15 +880,7 @@ gtk_file_chooser_button_constructor (GType type, /* This is used, instead of the standard delegate, to ensure that signals are only * delegated when the OK button is pressed. */ g_object_set_qdata (object, GTK_FILE_CHOOSER_DELEGATE_QUARK, priv->dialog); - priv->dialog_folder_changed_id = - g_signal_connect (priv->dialog, "current-folder-changed", - G_CALLBACK (dialog_current_folder_changed_cb), object); - priv->dialog_file_activated_id = - g_signal_connect (priv->dialog, "file-activated", - G_CALLBACK (dialog_file_activated_cb), object); - priv->dialog_selection_changed_id = - g_signal_connect (priv->dialog, "selection-changed", - G_CALLBACK (dialog_selection_changed_cb), object); + g_signal_connect (priv->dialog, "update-preview", G_CALLBACK (dialog_update_preview_cb), object); g_signal_connect (priv->dialog, "notify", @@ -751,6 +904,8 @@ gtk_file_chooser_button_constructor (GType type, model_add_other (button); + model_add_empty_selection (button); + priv->filter_model = gtk_tree_model_filter_new (priv->model, NULL); gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (priv->filter_model), filter_model_visible_func, @@ -764,7 +919,7 @@ gtk_file_chooser_button_constructor (GType type, /* set up the action for a user-provided dialog, this also updates * the label, image and combobox */ - g_object_set (object, + g_object_set (object, "action", gtk_file_chooser_get_action (GTK_FILE_CHOOSER (priv->dialog)), NULL); @@ -775,6 +930,9 @@ gtk_file_chooser_button_constructor (GType type, g_signal_connect (priv->fs, "bookmarks-changed", G_CALLBACK (fs_bookmarks_changed_cb), object); + update_label_and_image (button); + update_combo_box (button); + return object; } @@ -914,8 +1072,11 @@ gtk_file_chooser_button_finalize (GObject *object) GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (object); GtkFileChooserButtonPrivate *priv = button->priv; - if (priv->old_file) - g_object_unref (priv->old_file); + if (priv->selection_while_inactive) + g_object_unref (priv->selection_while_inactive); + + if (priv->current_folder_while_inactive) + g_object_unref (priv->current_folder_while_inactive); G_OBJECT_CLASS (gtk_file_chooser_button_parent_class)->finalize (object); } @@ -1096,7 +1257,7 @@ gtk_file_chooser_button_drag_data_received (GtkWidget *widget, struct DndSelectFolderData *info; uris = gtk_selection_data_get_uris (data); - + if (uris == NULL) break; @@ -1171,20 +1332,6 @@ gtk_file_chooser_button_hide (GtkWidget *widget) static void gtk_file_chooser_button_map (GtkWidget *widget) { - GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (widget); - GtkFileChooserButtonPrivate *priv = button->priv; - - if (!priv->folder_has_been_set) - { - char *current_working_dir; - - current_working_dir = g_get_current_dir (); - gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (widget), current_working_dir); - g_free (current_working_dir); - - priv->folder_has_been_set = TRUE; - } - GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->map (widget); } @@ -1248,7 +1395,7 @@ change_icon_theme_get_info_cb (GCancellable *cancellable, width = MAX (width, gdk_pixbuf_get_width (pixbuf)); path = gtk_tree_row_reference_get_path (data->row_ref); - if (path) + if (path) { gtk_tree_model_get_iter (data->button->priv->model, &iter, path); gtk_tree_path_free (path); @@ -1328,8 +1475,8 @@ change_icon_theme (GtkFileChooserButton *button) { GtkTreePath *path; GCancellable *cancellable; - struct ChangeIconThemeData *info; - + struct ChangeIconThemeData *info; + info = g_new0 (struct ChangeIconThemeData, 1); info->button = g_object_ref (button); path = gtk_tree_model_get_path (priv->model, &iter); @@ -1576,6 +1723,11 @@ model_get_type_position (GtkFileChooserButton *button, if (row_type == ROW_TYPE_OTHER) return retval; + retval++; + + if (row_type == ROW_TYPE_EMPTY_SELECTION) + return retval; + g_assert_not_reached (); return -1; } @@ -1673,7 +1825,7 @@ model_add_special_get_info_cb (GCancellable *cancellable, DISPLAY_NAME_COLUMN, g_file_info_get_display_name (info), -1); g_free (name); - + out: g_object_unref (data->button); gtk_tree_row_reference_free (data->row_ref); @@ -1683,7 +1835,7 @@ out: g_object_unref (model_cancellable); } -static inline void +static void model_add_special (GtkFileChooserButton *button) { const gchar *homedir; @@ -1804,7 +1956,7 @@ model_add_volumes (GtkFileChooserButton *button, base_file = _gtk_file_system_volume_get_root (volume); if (base_file != NULL) { - if (!g_file_is_native (base_file)) + if (!_gtk_file_has_native_path (base_file)) { g_object_unref (base_file); continue; @@ -1864,7 +2016,7 @@ model_add_bookmarks (GtkFileChooserButton *button, file = l->data; - if (g_file_is_native (file)) + if (_gtk_file_has_native_path (file)) { gtk_list_store_insert (store, &iter, pos); gtk_list_store_set (store, &iter, @@ -1895,7 +2047,7 @@ model_add_bookmarks (GtkFileChooserButton *button, label = _gtk_file_chooser_label_for_file (file); icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (button))); - pixbuf = gtk_icon_theme_load_icon (icon_theme, "folder-remote", + pixbuf = gtk_icon_theme_load_icon (icon_theme, "folder-remote", button->priv->icon_size, 0, NULL); gtk_list_store_insert (store, &iter, pos); @@ -1915,7 +2067,7 @@ model_add_bookmarks (GtkFileChooserButton *button, pos++; } - if (button->priv->n_bookmarks > 0 && + if (button->priv->n_bookmarks > 0 && !button->priv->has_bookmark_separator) { pos = model_get_type_position (button, ROW_TYPE_BOOKMARK_SEPARATOR); @@ -2000,10 +2152,10 @@ model_update_current_folder (GtkFileChooserButton *button, icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (button))); if (g_file_is_native (file)) - pixbuf = gtk_icon_theme_load_icon (icon_theme, "folder", + pixbuf = gtk_icon_theme_load_icon (icon_theme, "folder", button->priv->icon_size, 0, NULL); else - pixbuf = gtk_icon_theme_load_icon (icon_theme, "folder-remote", + pixbuf = gtk_icon_theme_load_icon (icon_theme, "folder-remote", button->priv->icon_size, 0, NULL); gtk_list_store_set (store, &iter, @@ -2019,13 +2171,13 @@ model_update_current_folder (GtkFileChooserButton *button, } } -static inline void +static void model_add_other (GtkFileChooserButton *button) { GtkListStore *store; GtkTreeIter iter; gint pos; - + store = GTK_LIST_STORE (button->priv->model); pos = model_get_type_position (button, ROW_TYPE_OTHER_SEPARATOR); @@ -2043,13 +2195,33 @@ model_add_other (GtkFileChooserButton *button) gtk_list_store_insert (store, &iter, pos); gtk_list_store_set (store, &iter, ICON_COLUMN, NULL, - DISPLAY_NAME_COLUMN, _("Other..."), + DISPLAY_NAME_COLUMN, _("Other…"), TYPE_COLUMN, ROW_TYPE_OTHER, DATA_COLUMN, NULL, IS_FOLDER_COLUMN, FALSE, -1); } +static void +model_add_empty_selection (GtkFileChooserButton *button) +{ + GtkListStore *store; + GtkTreeIter iter; + gint pos; + + store = GTK_LIST_STORE (button->priv->model); + pos = model_get_type_position (button, ROW_TYPE_EMPTY_SELECTION); + + gtk_list_store_insert (store, &iter, pos); + gtk_list_store_set (store, &iter, + ICON_COLUMN, NULL, + DISPLAY_NAME_COLUMN, _(FALLBACK_DISPLAY_NAME), + TYPE_COLUMN, ROW_TYPE_EMPTY_SELECTION, + DATA_COLUMN, NULL, + IS_FOLDER_COLUMN, FALSE, + -1); +} + static void model_remove_rows (GtkFileChooserButton *button, gint pos, @@ -2077,7 +2249,7 @@ model_remove_rows (GtkFileChooserButton *button, } /* Filter Model */ -static inline gboolean +static gboolean test_if_file_is_visible (GtkFileSystem *fs, GFile *file, gboolean local_only, @@ -2086,7 +2258,7 @@ test_if_file_is_visible (GtkFileSystem *fs, if (!file) return FALSE; - if (local_only && !g_file_is_native (file)) + if (local_only && !_gtk_file_has_native_path (file)) return FALSE; if (!is_folder) @@ -2139,7 +2311,7 @@ filter_model_visible_func (GtkTreeModel *model, if (base_file) { - if (!g_file_is_native (base_file)) + if (!_gtk_file_has_native_path (base_file)) retval = FALSE; g_object_unref (base_file); } @@ -2149,6 +2321,34 @@ filter_model_visible_func (GtkTreeModel *model, } } break; + case ROW_TYPE_EMPTY_SELECTION: + { + gboolean popup_shown; + + g_object_get (priv->combo_box, + "popup-shown", &popup_shown, + NULL); + + if (popup_shown) + retval = FALSE; + else + { + GFile *selected; + + /* When the combo box is not popped up... */ + + selected = get_selected_file (button); + if (selected) + retval = FALSE; /* ... nonempty selection means the ROW_TYPE_EMPTY_SELECTION is *not* visible... */ + else + retval = TRUE; /* ... and empty selection means the ROW_TYPE_EMPTY_SELECTION *is* visible */ + + if (selected) + g_object_unref (selected); + } + + break; + } default: retval = TRUE; break; @@ -2190,22 +2390,37 @@ combo_box_row_separator_func (GtkTreeModel *model, return (type == ROW_TYPE_BOOKMARK_SEPARATOR || type == ROW_TYPE_CURRENT_FOLDER_SEPARATOR || type == ROW_TYPE_OTHER_SEPARATOR); -} +} + +static void +select_combo_box_row_no_notify (GtkFileChooserButton *button, int pos) +{ + GtkFileChooserButtonPrivate *priv = button->priv; + GtkTreeIter iter, filter_iter; + + gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, pos); + gtk_tree_model_filter_convert_child_iter_to_iter (GTK_TREE_MODEL_FILTER (priv->filter_model), + &filter_iter, &iter); + + g_signal_handler_block (priv->combo_box, priv->combo_box_changed_id); + gtk_combo_box_set_active_iter (GTK_COMBO_BOX (priv->combo_box), &filter_iter); + g_signal_handler_unblock (priv->combo_box, priv->combo_box_changed_id); +} static void update_combo_box (GtkFileChooserButton *button) { GtkFileChooserButtonPrivate *priv = button->priv; - GSList *files; + GFile *file; GtkTreeIter iter; gboolean row_found; - gtk_tree_model_get_iter_first (priv->filter_model, &iter); - - files = gtk_file_chooser_get_files (GTK_FILE_CHOOSER (priv->dialog)); + file = get_selected_file (button); row_found = FALSE; + gtk_tree_model_get_iter_first (priv->filter_model, &iter); + do { gchar type; @@ -2218,16 +2433,14 @@ update_combo_box (GtkFileChooserButton *button) TYPE_COLUMN, &type, DATA_COLUMN, &data, -1); - + switch (type) { case ROW_TYPE_SPECIAL: case ROW_TYPE_SHORTCUT: case ROW_TYPE_BOOKMARK: case ROW_TYPE_CURRENT_FOLDER: - row_found = (files && - files->data && - g_file_equal (data, files->data)); + row_found = (file && g_file_equal (data, file)); break; case ROW_TYPE_VOLUME: { @@ -2236,9 +2449,7 @@ update_combo_box (GtkFileChooserButton *button) base_file = _gtk_file_system_volume_get_root (data); if (base_file) { - row_found = (files && - files->data && - g_file_equal (base_file, files->data)); + row_found = (file && g_file_equal (base_file, file)); g_object_unref (base_file); } } @@ -2259,28 +2470,30 @@ update_combo_box (GtkFileChooserButton *button) } while (!row_found && gtk_tree_model_iter_next (priv->filter_model, &iter)); - /* If it hasn't been found already, update & select the current-folder row. */ - if (!row_found && files && files->data) + if (!row_found) { - GtkTreeIter filter_iter; gint pos; - - model_update_current_folder (button, files->data); - gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model)); - pos = model_get_type_position (button, ROW_TYPE_CURRENT_FOLDER); - gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, pos); + /* If it hasn't been found already, update & select the current-folder row. */ + if (file) + { + model_update_current_folder (button, file); + pos = model_get_type_position (button, ROW_TYPE_CURRENT_FOLDER); + } + else + { + /* No selection; switch to that row */ + + pos = model_get_type_position (button, ROW_TYPE_EMPTY_SELECTION); + } - gtk_tree_model_filter_convert_child_iter_to_iter (GTK_TREE_MODEL_FILTER (priv->filter_model), - &filter_iter, &iter); + gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model)); - g_signal_handler_block (priv->combo_box, priv->combo_box_changed_id); - gtk_combo_box_set_active_iter (GTK_COMBO_BOX (priv->combo_box), &filter_iter); - g_signal_handler_unblock (priv->combo_box, priv->combo_box_changed_id); + select_combo_box_row_no_notify (button, pos); } - g_slist_foreach (files, (GFunc) g_object_unref, NULL); - g_slist_free (files); + if (file) + g_object_unref (file); } /* Button */ @@ -2317,6 +2530,8 @@ update_label_get_info_cb (GCancellable *cancellable, g_object_unref (pixbuf); out: + emit_selection_changed_if_changing_selection (button); + g_object_unref (button); g_object_unref (cancellable); } @@ -2326,10 +2541,13 @@ update_label_and_image (GtkFileChooserButton *button) { GtkFileChooserButtonPrivate *priv = button->priv; gchar *label_text; - GSList *files; + GFile *file; + gboolean done_changing_selection; + + file = get_selected_file (button); - files = gtk_file_chooser_get_files (GTK_FILE_CHOOSER (priv->dialog)); label_text = NULL; + done_changing_selection = FALSE; if (priv->update_button_cancellable) { @@ -2337,13 +2555,10 @@ update_label_and_image (GtkFileChooserButton *button) priv->update_button_cancellable = NULL; } - if (files && files->data) + if (file) { - GFile *file; GtkFileSystemVolume *volume = NULL; - file = files->data; - volume = _gtk_file_system_get_volume_for_file (priv->fs, file); if (volume) { @@ -2370,7 +2585,10 @@ update_label_and_image (GtkFileChooserButton *button) _gtk_file_system_volume_unref (volume); if (label_text) - goto out; + { + done_changing_selection = TRUE; + goto out; + } } if (g_file_is_native (file)) @@ -2392,11 +2610,20 @@ update_label_and_image (GtkFileChooserButton *button) gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), pixbuf); if (pixbuf) g_object_unref (pixbuf); + + done_changing_selection = TRUE; } } + else + { + /* We know the selection is empty */ + done_changing_selection = TRUE; + } + out: - g_slist_foreach (files, (GFunc) g_object_unref, NULL); - g_slist_free (files); + + if (file) + g_object_unref (file); if (label_text) { @@ -2408,6 +2635,9 @@ out: gtk_label_set_text (GTK_LABEL (priv->label), _(FALLBACK_DISPLAY_NAME)); gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), NULL); } + + if (done_changing_selection) + emit_selection_changed_if_changing_selection (button); } @@ -2465,6 +2695,35 @@ fs_bookmarks_changed_cb (GtkFileSystem *fs, update_combo_box (user_data); } +static void +save_inactive_state (GtkFileChooserButton *button) +{ + GtkFileChooserButtonPrivate *priv = button->priv; + + if (priv->current_folder_while_inactive) + g_object_unref (priv->current_folder_while_inactive); + + if (priv->selection_while_inactive) + g_object_unref (priv->selection_while_inactive); + + priv->current_folder_while_inactive = gtk_file_chooser_get_current_folder_file (GTK_FILE_CHOOSER (priv->dialog)); + priv->selection_while_inactive = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (priv->dialog)); +} + +static void +restore_inactive_state (GtkFileChooserButton *button) +{ + GtkFileChooserButtonPrivate *priv = button->priv; + + if (priv->current_folder_while_inactive) + gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (priv->dialog), priv->current_folder_while_inactive, NULL); + + if (priv->selection_while_inactive) + gtk_file_chooser_select_file (GTK_FILE_CHOOSER (priv->dialog), priv->selection_while_inactive, NULL); + else + gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (priv->dialog)); +} + /* Dialog */ static void open_dialog (GtkFileChooserButton *button) @@ -2484,7 +2743,7 @@ open_dialog (GtkFileChooserButton *button) if (GTK_WINDOW (toplevel) != gtk_window_get_transient_for (GTK_WINDOW (priv->dialog))) gtk_window_set_transient_for (GTK_WINDOW (priv->dialog), GTK_WINDOW (toplevel)); - + gtk_window_set_modal (GTK_WINDOW (priv->dialog), gtk_window_get_modal (GTK_WINDOW (toplevel))); } @@ -2492,24 +2751,7 @@ open_dialog (GtkFileChooserButton *button) if (!priv->active) { - GSList *files; - - g_signal_handler_block (priv->dialog, - priv->dialog_folder_changed_id); - g_signal_handler_block (priv->dialog, - priv->dialog_file_activated_id); - g_signal_handler_block (priv->dialog, - priv->dialog_selection_changed_id); - files = gtk_file_chooser_get_files (GTK_FILE_CHOOSER (priv->dialog)); - if (files) - { - if (files->data) - priv->old_file = g_object_ref (files->data); - - g_slist_foreach (files, (GFunc) g_object_unref, NULL); - g_slist_free (files); - } - + restore_inactive_state (button); priv->active = TRUE; } @@ -2545,21 +2787,17 @@ combo_box_changed_cb (GtkComboBox *combo_box, case ROW_TYPE_SHORTCUT: case ROW_TYPE_BOOKMARK: case ROW_TYPE_CURRENT_FOLDER: - gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (priv->dialog)); if (data) - gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (priv->dialog), - data, NULL); + gtk_file_chooser_button_select_file (GTK_FILE_CHOOSER (button), data, NULL); break; case ROW_TYPE_VOLUME: { GFile *base_file; - gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (priv->dialog)); base_file = _gtk_file_system_volume_get_root (data); if (base_file) { - gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (priv->dialog), - base_file, NULL); + gtk_file_chooser_button_select_file (GTK_FILE_CHOOSER (button), base_file, NULL); g_object_unref (base_file); } } @@ -2573,43 +2811,60 @@ combo_box_changed_cb (GtkComboBox *combo_box, } } -/* Button */ -static void -button_clicked_cb (GtkButton *real_button, - gpointer user_data) -{ - open_dialog (user_data); -} - -/* Dialog */ +/* Calback for the "notify::popup-shown" signal on the combo box. + * When the combo is popped up, we don't want the ROW_TYPE_EMPTY_SELECTION to be visible + * at all; otherwise we would be showing a "(None)" item in the combo box's popup. + * + * However, when the combo box is *not* popped up, we want the empty-selection row + * to be visible depending on the selection. + * + * Since all that is done through the filter_model_visible_func(), this means + * that we need to refilter the model when the combo box pops up - hence the + * present signal handler. + */ static void -dialog_current_folder_changed_cb (GtkFileChooser *dialog, - gpointer user_data) +combo_box_notify_popup_shown_cb (GObject *object, + GParamSpec *pspec, + gpointer user_data) { GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data); GtkFileChooserButtonPrivate *priv = button->priv; + gboolean popup_shown; - priv->folder_has_been_set = TRUE; + g_object_get (priv->combo_box, + "popup-shown", &popup_shown, + NULL); - g_signal_emit_by_name (button, "current-folder-changed"); -} + /* Indicate that the ROW_TYPE_EMPTY_SELECTION will change visibility... */ + gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model)); -static void -dialog_file_activated_cb (GtkFileChooser *dialog, - gpointer user_data) -{ - g_signal_emit_by_name (user_data, "file-activated"); + /* If the combo box popup got dismissed, go back to showing the ROW_TYPE_EMPTY_SELECTION if needed */ + if (!popup_shown) + { + GFile *selected = get_selected_file (button); + + if (!selected) + { + int pos; + + pos = model_get_type_position (button, ROW_TYPE_EMPTY_SELECTION); + select_combo_box_row_no_notify (button, pos); + } + else + g_object_unref (selected); + } } +/* Button */ static void -dialog_selection_changed_cb (GtkFileChooser *dialog, - gpointer user_data) +button_clicked_cb (GtkButton *real_button, + gpointer user_data) { - update_label_and_image (user_data); - update_combo_box (user_data); - g_signal_emit_by_name (user_data, "selection-changed"); + open_dialog (user_data); } +/* Dialog */ + static void dialog_update_preview_cb (GtkFileChooser *dialog, gpointer user_data) @@ -2649,7 +2904,7 @@ dialog_notify_cb (GObject *dialog, /* If the path isn't local but we're in local-only mode now, remove * the custom-folder row */ - if (data && g_file_is_native (G_FILE (data)) && + if (data && _gtk_file_has_native_path (G_FILE (data)) && gtk_file_chooser_get_local_only (GTK_FILE_CHOOSER (priv->dialog))) { pos--; @@ -2683,55 +2938,28 @@ dialog_response_cb (GtkDialog *dialog, if (response == GTK_RESPONSE_ACCEPT || response == GTK_RESPONSE_OK) { - g_signal_emit_by_name (user_data, "current-folder-changed"); - g_signal_emit_by_name (user_data, "selection-changed"); - } - else if (priv->old_file) - { - switch (gtk_file_chooser_get_action (GTK_FILE_CHOOSER (dialog))) - { - case GTK_FILE_CHOOSER_ACTION_OPEN: - gtk_file_chooser_select_file (GTK_FILE_CHOOSER (dialog), priv->old_file, - NULL); - break; - case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER: - gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (dialog), - priv->old_file, NULL); - break; - default: - g_assert_not_reached (); - break; - } + save_inactive_state (button); + + g_signal_emit_by_name (button, "current-folder-changed"); + g_signal_emit_by_name (button, "selection-changed"); } else - gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (dialog)); - - if (priv->old_file) { - g_object_unref (priv->old_file); - priv->old_file = NULL; + restore_inactive_state (button); } - update_label_and_image (user_data); - update_combo_box (user_data); - if (priv->active) - { - g_signal_handler_unblock (priv->dialog, - priv->dialog_folder_changed_id); - g_signal_handler_unblock (priv->dialog, - priv->dialog_file_activated_id); - g_signal_handler_unblock (priv->dialog, - priv->dialog_selection_changed_id); - priv->active = FALSE; - } + priv->active = FALSE; + + update_label_and_image (button); + update_combo_box (button); gtk_widget_set_sensitive (priv->combo_box, TRUE); gtk_widget_hide (priv->dialog); if (response == GTK_RESPONSE_ACCEPT || response == GTK_RESPONSE_OK) - g_signal_emit (user_data, file_chooser_button_signals[FILE_SET], 0); + g_signal_emit (button, file_chooser_button_signals[FILE_SET], 0); } @@ -2743,11 +2971,11 @@ dialog_response_cb (GtkDialog *dialog, * gtk_file_chooser_button_new: * @title: the title of the browse dialog. * @action: the open mode for the widget. - * + * * Creates a new file-selecting button widget. - * + * * Returns: a new button widget. - * + * * Since: 2.6 **/ GtkWidget * @@ -2796,9 +3024,9 @@ gtk_file_chooser_button_new_with_dialog (GtkWidget *dialog) * gtk_file_chooser_button_set_title: * @button: the button widget to modify. * @title: the new browse dialog title. - * + * * Modifies the @title of the browse dialog used by @button. - * + * * Since: 2.6 **/ void @@ -2814,12 +3042,12 @@ gtk_file_chooser_button_set_title (GtkFileChooserButton *button, /** * gtk_file_chooser_button_get_title: * @button: the button widget to examine. - * + * * Retrieves the title of the browse dialog used by @button. The returned value * should not be modified or freed. - * + * * Returns: a pointer to the browse dialog's title. - * + * * Since: 2.6 **/ const gchar * @@ -2833,11 +3061,11 @@ gtk_file_chooser_button_get_title (GtkFileChooserButton *button) /** * gtk_file_chooser_button_get_width_chars: * @button: the button widget to examine. - * + * * Retrieves the width in characters of the @button widget's entry and/or label. - * + * * Returns: an integer width (in characters) that the button will use to size itself. - * + * * Since: 2.6 **/ gint @@ -2852,9 +3080,9 @@ gtk_file_chooser_button_get_width_chars (GtkFileChooserButton *button) * gtk_file_chooser_button_set_width_chars: * @button: the button widget to examine. * @n_chars: the new width, in characters. - * + * * Sets the width (in characters) that @button will use to @n_chars. - * + * * Since: 2.6 **/ void @@ -2871,7 +3099,7 @@ gtk_file_chooser_button_set_width_chars (GtkFileChooserButton *button, * gtk_file_chooser_button_set_focus_on_click: * @button: a #GtkFileChooserButton * @focus_on_click: whether the button grabs focus when clicked with the mouse - * + * * Sets whether the button will grab focus when it is clicked with the mouse. * Making mouse clicks not grab focus is useful in places like toolbars where * you don't want the keyboard focus removed from the main area of the @@ -2896,7 +3124,7 @@ gtk_file_chooser_button_set_focus_on_click (GtkFileChooserButton *button, priv->focus_on_click = focus_on_click; gtk_button_set_focus_on_click (GTK_BUTTON (priv->button), focus_on_click); gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (priv->combo_box), focus_on_click); - + g_object_notify (G_OBJECT (button), "focus-on-click"); } } @@ -2904,7 +3132,7 @@ gtk_file_chooser_button_set_focus_on_click (GtkFileChooserButton *button, /** * gtk_file_chooser_button_get_focus_on_click: * @button: a #GtkFileChooserButton - * + * * Returns whether the button grabs focus when it is clicked with the mouse. * See gtk_file_chooser_button_set_focus_on_click(). * @@ -2917,6 +3145,6 @@ gboolean gtk_file_chooser_button_get_focus_on_click (GtkFileChooserButton *button) { g_return_val_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button), FALSE); - + return button->priv->focus_on_click; }