GtkFileChooserButton: Use native dialogs

Unless you explicitly set the dialog constructor property we use a
native dialog (GtkFileChooserNative).
This commit is contained in:
Alexander Larsson 2015-10-29 15:13:14 +01:00
parent 5094900180
commit b3d02671ca

View File

@ -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));
}
/**