diff --git a/gtk/gtkfilechooserbutton.c b/gtk/gtkfilechooserbutton.c index baff52329f..1df73bc4b2 100644 --- a/gtk/gtkfilechooserbutton.c +++ b/gtk/gtkfilechooserbutton.c @@ -44,6 +44,7 @@ #include "gtktreemodelfilter.h" #include "gtkseparator.h" #include "gtkfilechooserdialog.h" +#include "gtkfilechoosernative.h" #include "gtkfilechooserprivate.h" #include "gtkfilechooserutils.h" #include "gtkmarshalers.h" @@ -167,7 +168,9 @@ RowType; struct _GtkFileChooserButtonPrivate { - GtkWidget *dialog; + GtkFileChooser *chooser; /* Points to either dialog or native, depending on which is set */ + GtkWidget *dialog; /* Set if you explicitly enable */ + GtkFileChooserNative *native; /* Otherwise this is set */ GtkWidget *button; GtkWidget *image; GtkWidget *label; @@ -328,9 +331,9 @@ static void combo_box_notify_popup_shown_cb (GObject *object, static void button_clicked_cb (GtkButton *real_button, gpointer user_data); -static void dialog_update_preview_cb (GtkFileChooser *dialog, +static void chooser_update_preview_cb (GtkFileChooser *dialog, gpointer user_data); -static void dialog_notify_cb (GObject *dialog, +static void chooser_notify_cb (GObject *dialog, GParamSpec *pspec, gpointer user_data); static gboolean dialog_delete_event_cb (GtkWidget *dialog, @@ -339,6 +342,9 @@ static gboolean dialog_delete_event_cb (GtkWidget *dialog, static void dialog_response_cb (GtkDialog *dialog, gint response, gpointer user_data); +static void native_response_cb (GtkFileChooserNative *native, + gint response, + gpointer user_data); static guint file_chooser_button_signals[LAST_SIGNAL] = { 0 }; @@ -542,7 +548,7 @@ gtk_file_chooser_button_set_current_folder (GtkFileChooser *chooser, 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); + gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (priv->chooser), file, NULL); return TRUE; } @@ -578,7 +584,7 @@ gtk_file_chooser_button_select_file (GtkFileChooser *chooser, update_combo_box (button); if (priv->active) - gtk_file_chooser_select_file (GTK_FILE_CHOOSER (priv->dialog), file, NULL); + gtk_file_chooser_select_file (GTK_FILE_CHOOSER (priv->chooser), file, NULL); return TRUE; } @@ -611,7 +617,7 @@ gtk_file_chooser_button_unselect_file (GtkFileChooser *chooser, unselect_current_file (button); if (priv->active) - gtk_file_chooser_unselect_file (GTK_FILE_CHOOSER (priv->dialog), file); + gtk_file_chooser_unselect_file (GTK_FILE_CHOOSER (priv->chooser), file); } static void @@ -623,7 +629,7 @@ gtk_file_chooser_button_unselect_all (GtkFileChooser *chooser) unselect_current_file (button); if (priv->active) - gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (priv->dialog)); + gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (priv->chooser)); } static GFile * @@ -636,7 +642,7 @@ get_selected_file (GtkFileChooserButton *button) 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) + else if (priv->chooser && gtk_file_chooser_get_action (priv->chooser) == 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 GtkFileChooserWidget would do. @@ -770,46 +776,42 @@ gtk_file_chooser_button_constructed (GObject *object) if (!priv->dialog) { - priv->dialog = gtk_file_chooser_dialog_new (NULL, NULL, + priv->native = gtk_file_chooser_native_new (NULL, + NULL, GTK_FILE_CHOOSER_ACTION_OPEN, - _("_Cancel"), GTK_RESPONSE_CANCEL, - _("_Open"), GTK_RESPONSE_ACCEPT, + NULL, NULL); - - gtk_dialog_set_default_response (GTK_DIALOG (priv->dialog), - GTK_RESPONSE_ACCEPT); -G_GNUC_BEGIN_IGNORE_DEPRECATIONS - gtk_dialog_set_alternative_button_order (GTK_DIALOG (priv->dialog), - GTK_RESPONSE_ACCEPT, - GTK_RESPONSE_CANCEL, - -1); -G_GNUC_END_IGNORE_DEPRECATIONS - + priv->chooser = GTK_FILE_CHOOSER (priv->native); gtk_file_chooser_button_set_title (button, _(DEFAULT_TITLE)); + + g_signal_connect (priv->native, "response", + G_CALLBACK (native_response_cb), object); } - else if (!gtk_window_get_title (GTK_WINDOW (priv->dialog))) + else /* dialog set */ { - gtk_file_chooser_button_set_title (button, _(DEFAULT_TITLE)); + priv->chooser = GTK_FILE_CHOOSER (priv->dialog); + + if (!gtk_window_get_title (GTK_WINDOW (priv->dialog))) + gtk_file_chooser_button_set_title (button, _(DEFAULT_TITLE)); + + g_signal_connect (priv->dialog, "delete-event", + G_CALLBACK (dialog_delete_event_cb), object); + g_signal_connect (priv->dialog, "response", + G_CALLBACK (dialog_response_cb), object); + + g_object_add_weak_pointer (G_OBJECT (priv->dialog), + (gpointer) (&priv->dialog)); } - g_signal_connect (priv->dialog, "delete-event", - G_CALLBACK (dialog_delete_event_cb), object); - g_signal_connect (priv->dialog, "response", - G_CALLBACK (dialog_response_cb), object); + g_signal_connect (priv->chooser, "notify", + G_CALLBACK (chooser_notify_cb), object); /* 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); - - g_signal_connect (priv->dialog, "update-preview", - G_CALLBACK (dialog_update_preview_cb), object); - g_signal_connect (priv->dialog, "notify", - G_CALLBACK (dialog_notify_cb), object); - g_object_add_weak_pointer (G_OBJECT (priv->dialog), - (gpointer) (&priv->dialog)); + g_object_set_qdata (object, GTK_FILE_CHOOSER_DELEGATE_QUARK, priv->chooser); priv->fs = - g_object_ref (_gtk_file_chooser_get_file_system (GTK_FILE_CHOOSER (priv->dialog))); + g_object_ref (_gtk_file_chooser_get_file_system (priv->chooser)); model_add_special (button); @@ -839,7 +841,7 @@ G_GNUC_END_IGNORE_DEPRECATIONS * the label, image and combobox */ g_object_set (object, - "action", gtk_file_chooser_get_action (GTK_FILE_CHOOSER (priv->dialog)), + "action", gtk_file_chooser_get_action (GTK_FILE_CHOOSER (priv->chooser)), NULL); priv->fs_volumes_changed_id = @@ -888,7 +890,7 @@ gtk_file_chooser_button_set_property (GObject *object, break; } - g_object_set_property (G_OBJECT (priv->dialog), pspec->name, value); + g_object_set_property (G_OBJECT (priv->chooser), pspec->name, value); update_label_and_image (GTK_FILE_CHOOSER_BUTTON (object)); update_combo_box (GTK_FILE_CHOOSER_BUTTON (object)); @@ -917,11 +919,11 @@ gtk_file_chooser_button_set_property (GObject *object, case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN: case GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION: case GTK_FILE_CHOOSER_PROP_CREATE_FOLDERS: - g_object_set_property (G_OBJECT (priv->dialog), pspec->name, value); + g_object_set_property (G_OBJECT (priv->chooser), pspec->name, value); break; case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY: - g_object_set_property (G_OBJECT (priv->dialog), pspec->name, value); + g_object_set_property (G_OBJECT (priv->chooser), pspec->name, value); fs_volumes_changed_cb (priv->fs, button); bookmarks_changed_cb (button); break; @@ -964,7 +966,7 @@ gtk_file_chooser_button_get_property (GObject *object, case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN: case GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION: case GTK_FILE_CHOOSER_PROP_CREATE_FOLDERS: - g_object_get_property (G_OBJECT (priv->dialog), pspec->name, value); + g_object_get_property (G_OBJECT (priv->chooser), pspec->name, value); break; default: @@ -1006,6 +1008,10 @@ gtk_file_chooser_button_destroy (GtkWidget *widget) priv->dialog = NULL; } + g_clear_object (&priv->native); + + priv->chooser = NULL; + if (priv->model && gtk_tree_model_get_iter_first (priv->model, &iter)) { do @@ -1173,7 +1179,7 @@ gtk_file_chooser_button_drag_data_received (GtkWidget *widget, info->uris = uris; info->selected = FALSE; info->file_system = priv->fs; - g_object_get (priv->dialog, "action", &info->action, NULL); + g_object_get (priv->chooser, "action", &info->action, NULL); info->file = g_file_new_for_uri (info->uris[info->i]); @@ -1190,7 +1196,7 @@ gtk_file_chooser_button_drag_data_received (GtkWidget *widget, case TEXT_PLAIN: text = (char*) gtk_selection_data_get_text (data); file = g_file_new_for_uri (text); - gtk_file_chooser_select_file (GTK_FILE_CHOOSER (priv->dialog), file, NULL); + gtk_file_chooser_select_file (GTK_FILE_CHOOSER (priv->chooser), file, NULL); g_object_unref (file); g_free (text); g_signal_emit (button, file_chooser_button_signals[FILE_SET], 0); @@ -1228,7 +1234,10 @@ gtk_file_chooser_button_hide (GtkWidget *widget) GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (widget); GtkFileChooserButtonPrivate *priv = button->priv; - gtk_widget_hide (priv->dialog); + if (priv->dialog) + gtk_widget_hide (priv->dialog); + else + gtk_native_dialog_hide (GTK_NATIVE_DIALOG (priv->native)); if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->hide) GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->hide (widget); @@ -1247,7 +1256,7 @@ gtk_file_chooser_button_mnemonic_activate (GtkWidget *widget, GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (widget); GtkFileChooserButtonPrivate *priv = button->priv; - switch (gtk_file_chooser_get_action (GTK_FILE_CHOOSER (priv->dialog))) + switch (gtk_file_chooser_get_action (GTK_FILE_CHOOSER (priv->chooser))) { case GTK_FILE_CHOOSER_ACTION_OPEN: gtk_widget_grab_focus (priv->button); @@ -1840,7 +1849,7 @@ model_add_volumes (GtkFileChooserButton *button, store = GTK_LIST_STORE (button->priv->model); pos = model_get_type_position (button, ROW_TYPE_VOLUME); - local_only = gtk_file_chooser_get_local_only (GTK_FILE_CHOOSER (button->priv->dialog)); + local_only = gtk_file_chooser_get_local_only (GTK_FILE_CHOOSER (button->priv->chooser)); for (l = volumes; l; l = l->next) { @@ -1912,7 +1921,7 @@ model_add_bookmarks (GtkFileChooserButton *button, store = GTK_LIST_STORE (button->priv->model); pos = model_get_type_position (button, ROW_TYPE_BOOKMARK); - local_only = gtk_file_chooser_get_local_only (GTK_FILE_CHOOSER (button->priv->dialog)); + local_only = gtk_file_chooser_get_local_only (GTK_FILE_CHOOSER (button->priv->chooser)); for (l = bookmarks; l; l = l->next) { @@ -2195,7 +2204,7 @@ filter_model_visible_func (GtkTreeModel *model, type = ROW_TYPE_INVALID; data = NULL; - local_only = gtk_file_chooser_get_local_only (GTK_FILE_CHOOSER (priv->dialog)); + local_only = gtk_file_chooser_get_local_only (GTK_FILE_CHOOSER (priv->chooser)); gtk_tree_model_get (model, iter, TYPE_COLUMN, &type, @@ -2616,8 +2625,8 @@ save_inactive_state (GtkFileChooserButton *button) 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)); + priv->current_folder_while_inactive = gtk_file_chooser_get_current_folder_file (GTK_FILE_CHOOSER (priv->chooser)); + priv->selection_while_inactive = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (priv->chooser)); } static void @@ -2626,12 +2635,12 @@ 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); + gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (priv->chooser), 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); + gtk_file_chooser_select_file (GTK_FILE_CHOOSER (priv->chooser), priv->selection_while_inactive, NULL); else - gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (priv->dialog)); + gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (priv->chooser)); } /* Dialog */ @@ -2639,34 +2648,63 @@ static void open_dialog (GtkFileChooserButton *button) { GtkFileChooserButtonPrivate *priv = button->priv; + GtkWidget *toplevel; + + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button)); /* Setup the dialog parent to be chooser button's toplevel, and be modal as needed. */ - if (!gtk_widget_get_visible (priv->dialog)) + if (priv->dialog != NULL) { - GtkWidget *toplevel; - - toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button)); - - if (gtk_widget_is_toplevel (toplevel) && GTK_IS_WINDOW (toplevel)) + if (!gtk_widget_get_visible (priv->dialog)) { - 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)); + if (gtk_widget_is_toplevel (toplevel) && GTK_IS_WINDOW (toplevel)) + { + 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))); - } + gtk_window_set_modal (GTK_WINDOW (priv->dialog), + gtk_window_get_modal (GTK_WINDOW (toplevel))); + } + } + } + else + { + if (!gtk_native_dialog_get_visible (GTK_NATIVE_DIALOG (priv->native))) + { + if (gtk_widget_is_toplevel (toplevel) && GTK_IS_WINDOW (toplevel)) + { + if (GTK_WINDOW (toplevel) != gtk_native_dialog_get_transient_for (GTK_NATIVE_DIALOG (priv->native))) + gtk_native_dialog_set_transient_for (GTK_NATIVE_DIALOG (priv->native), + GTK_WINDOW (toplevel)); + + gtk_native_dialog_set_modal (GTK_NATIVE_DIALOG (priv->native), + gtk_window_get_modal (GTK_WINDOW (toplevel))); + } + } } if (!priv->active) { restore_inactive_state (button); priv->active = TRUE; + + /* Only handle update-preview handler if it is handled on the button */ + if (g_signal_has_handler_pending (button, + g_signal_lookup ("update-preview", GTK_TYPE_FILE_CHOOSER), + 0, TRUE)) + { + g_signal_connect (priv->chooser, "update-preview", + G_CALLBACK (chooser_update_preview_cb), button); + } } gtk_widget_set_sensitive (priv->combo_box, FALSE); - gtk_window_present (GTK_WINDOW (priv->dialog)); + if (priv->dialog) + gtk_window_present (GTK_WINDOW (priv->dialog)); + else + gtk_native_dialog_show (GTK_NATIVE_DIALOG (priv->native)); } /* Combo Box */ @@ -2787,16 +2825,16 @@ button_clicked_cb (GtkButton *real_button, /* Dialog */ static void -dialog_update_preview_cb (GtkFileChooser *dialog, - gpointer user_data) +chooser_update_preview_cb (GtkFileChooser *dialog, + gpointer user_data) { g_signal_emit_by_name (user_data, "update-preview"); } static void -dialog_notify_cb (GObject *dialog, - GParamSpec *pspec, - gpointer user_data) +chooser_notify_cb (GObject *dialog, + GParamSpec *pspec, + gpointer user_data) { gpointer iface; @@ -2826,7 +2864,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 && _gtk_file_has_native_path (G_FILE (data)) && - gtk_file_chooser_get_local_only (GTK_FILE_CHOOSER (priv->dialog))) + gtk_file_chooser_get_local_only (GTK_FILE_CHOOSER (priv->chooser))) { pos--; model_remove_rows (user_data, pos, 2); @@ -2849,11 +2887,9 @@ dialog_delete_event_cb (GtkWidget *dialog, } static void -dialog_response_cb (GtkDialog *dialog, - gint response, - gpointer user_data) +common_response_cb (GtkFileChooserButton *button, + gint response) { - GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data); GtkFileChooserButtonPrivate *priv = button->priv; if (response == GTK_RESPONSE_ACCEPT || @@ -2870,12 +2906,29 @@ dialog_response_cb (GtkDialog *dialog, } if (priv->active) - priv->active = FALSE; + { + priv->active = FALSE; + + g_signal_handlers_disconnect_by_func (priv->chooser, chooser_update_preview_cb, button); + } update_label_and_image (button); update_combo_box (button); gtk_widget_set_sensitive (priv->combo_box, TRUE); +} + + +static void +dialog_response_cb (GtkDialog *dialog, + gint response, + gpointer user_data) +{ + GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data); + GtkFileChooserButtonPrivate *priv = button->priv; + + common_response_cb (button, response); + gtk_widget_hide (priv->dialog); if (response == GTK_RESPONSE_ACCEPT || @@ -2883,6 +2936,22 @@ dialog_response_cb (GtkDialog *dialog, g_signal_emit (button, file_chooser_button_signals[FILE_SET], 0); } +static void +native_response_cb (GtkFileChooserNative *native, + gint response, + gpointer user_data) +{ + GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data); + + common_response_cb (button, response); + + /* dialog already hidden */ + + if (response == GTK_RESPONSE_ACCEPT || + response == GTK_RESPONSE_OK) + g_signal_emit (button, file_chooser_button_signals[FILE_SET], 0); +} + /* ************************************************************************** * * Public API * @@ -2956,7 +3025,10 @@ gtk_file_chooser_button_set_title (GtkFileChooserButton *button, { g_return_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button)); - gtk_window_set_title (GTK_WINDOW (button->priv->dialog), title); + if (button->priv->dialog) + gtk_window_set_title (GTK_WINDOW (button->priv->dialog), title); + else + gtk_native_dialog_set_title (GTK_NATIVE_DIALOG (button->priv->native), title); g_object_notify (G_OBJECT (button), "title"); } @@ -2976,7 +3048,10 @@ gtk_file_chooser_button_get_title (GtkFileChooserButton *button) { g_return_val_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button), NULL); - return gtk_window_get_title (GTK_WINDOW (button->priv->dialog)); + if (button->priv->dialog) + return gtk_window_get_title (GTK_WINDOW (button->priv->dialog)); + else + return gtk_native_dialog_get_title (GTK_NATIVE_DIALOG (button->priv->native)); } /**