diff options
Diffstat (limited to 'gtk')
-rw-r--r-- | gtk/Makefile.am | 4 | ||||
-rw-r--r-- | gtk/gtk.h | 1 | ||||
-rw-r--r-- | gtk/gtkfilechooserdialog.c | 7 | ||||
-rw-r--r-- | gtk/gtkfilechoosernative.c | 656 | ||||
-rw-r--r-- | gtk/gtkfilechoosernative.h | 56 | ||||
-rw-r--r-- | gtk/gtkfilechoosernativeprivate.h | 53 | ||||
-rw-r--r-- | gtk/gtkfilechoosernativewin32.c | 778 |
7 files changed, 1554 insertions, 1 deletions
diff --git a/gtk/Makefile.am b/gtk/Makefile.am index 84f3ffe032..db6f55df5e 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -180,6 +180,7 @@ gtk_public_h_sources = \ gtkfilechooser.h \ gtkfilechooserbutton.h \ gtkfilechooserdialog.h \ + gtkfilechoosernative.h \ gtkfilechooserwidget.h \ gtkfilefilter.h \ gtkfixed.h \ @@ -440,6 +441,7 @@ gtk_private_h_sources = \ gtkfilechooserembed.h \ gtkfilechooserentry.h \ gtkfilechooserprivate.h \ + gtkfilechoosernativeprivate.h \ gtkfilechooserwidgetprivate.h \ gtkfilechooserutils.h \ gtkfilefilterprivate.h \ @@ -696,6 +698,7 @@ gtk_base_c_sources = \ gtkfilechooserdialog.c \ gtkfilechooserembed.c \ gtkfilechooserentry.c \ + gtkfilechoosernative.c \ gtkfilechooserutils.c \ gtkfilechooserwidget.c \ gtkfilefilter.c \ @@ -940,6 +943,7 @@ endif gtk_os_win32_c_sources = \ gtkprint-win32.c \ gtkprintoperation-win32.c \ + gtkfilechoosernativewin32.c \ gtkwin32.c if OS_WIN32 gtk_private_h_sources += gtkprint-win32.h @@ -97,6 +97,7 @@ #include <gtk/gtkfilechooser.h> #include <gtk/gtkfilechooserbutton.h> #include <gtk/gtkfilechooserdialog.h> +#include <gtk/gtkfilechoosernative.h> #include <gtk/gtkfilechooserwidget.h> #include <gtk/gtkfilefilter.h> #include <gtk/gtkflowbox.h> diff --git a/gtk/gtkfilechooserdialog.c b/gtk/gtkfilechooserdialog.c index d96939450a..e82bb0d2ed 100644 --- a/gtk/gtkfilechooserdialog.c +++ b/gtk/gtkfilechooserdialog.c @@ -45,7 +45,7 @@ * SECTION:gtkfilechooserdialog * @Short_description: A file chooser dialog, suitable for “File/Open” or “File/Save” commands * @Title: GtkFileChooserDialog - * @See_also: #GtkFileChooser, #GtkDialog + * @See_also: #GtkFileChooser, #GtkDialog, GtkFileChooserNative * * #GtkFileChooserDialog is a dialog box suitable for use with * “File/Open” or “File/Save as” commands. This widget works by @@ -58,6 +58,11 @@ * own. Instead, you should use the functions that work on a * #GtkFileChooser. * + * If you want to integrate well with the platform you should use the + * #GtkFileChooserNative API, which will use a platform-specific + * dialog if available and fall back to GtkFileChooserDialog + * otherwise. + * * ## Typical usage ## {#gtkfilechooser-typical-usage} * * In the simplest of cases, you can the following code to use diff --git a/gtk/gtkfilechoosernative.c b/gtk/gtkfilechoosernative.c new file mode 100644 index 0000000000..c43e86f730 --- /dev/null +++ b/gtk/gtkfilechoosernative.c @@ -0,0 +1,656 @@ +/* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */ +/* GTK - The GIMP Toolkit + * gtkfilechoosernative.c: Native File selector dialog + * Copyright (C) 2015, Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include "gtkfilechoosernativeprivate.h" +#include "gtknativedialogprivate.h" + +#include "gtkprivate.h" +#include "gtkfilechooserdialog.h" +#include "gtkfilechooserprivate.h" +#include "gtkfilechooserwidget.h" +#include "gtkfilechooserwidgetprivate.h" +#include "gtkfilechooserutils.h" +#include "gtkfilechooserembed.h" +#include "gtkfilesystem.h" +#include "gtksizerequest.h" +#include "gtktypebuiltins.h" +#include "gtkintl.h" +#include "gtksettings.h" +#include "gtktogglebutton.h" +#include "gtkstylecontext.h" +#include "gtkheaderbar.h" +#include "gtklabel.h" +#include "gtkfilechooserentry.h" +#include "gtkfilefilterprivate.h" + +/** + * SECTION:gtkfilechoosernative + * @Short_description: A native file chooser dialog, suitable for “File/Open” or “File/Save” commands + * @Title: GtkFileChooserNative + * @See_also: #GtkFileChooser, #GtkNativeDialog, #GtkFileChooserDialog + * + * #GtkFileChooserNative is an abstraction of a dialog box suitable + * for use with “File/Open” or “File/Save as” commands. By default, this + * just uses a #GtkFileChooserDialog to implement the actual dialog. + * However, on certain platforms, such as Windows, the native platform + * file chooser is uses instead. + * + * While the API of #GtkFileChooserNative closely mirrors #GtkFileChooserDialog, the main + * difference is that there is no access to any #GtkWindow or #GtkWidget for the dialog. + * This is required, as there may not be one in the case of a platform native dialog. + * Showing, hiding and running the dialog is handled by the #GtkNativeDialog functions. + * + * ## Typical usage ## {#gtkfilechoosernative-typical-usage} + * + * In the simplest of cases, you can the following code to use + * #GtkFileChooserDialog to select a file for opening: + * + * |[ + * GtkFileChooserNative *native; + * GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN; + * gint res; + * + * native = gtk_file_chooser_native_new ("Open File", + * parent_window, + * action, + * "_Open", + * "_Cancel"); + * + * res = gtk_native_dialog_run (GTK_NATIVE_DIALOG (native)); + * if (res == GTK_RESPONSE_ACCEPT) + * { + * char *filename; + * GtkFileChooser *chooser = GTK_FILE_CHOOSER (native); + * filename = gtk_file_chooser_get_filename (chooser); + * open_file (filename); + * g_free (filename); + * } + * + * g_object_unref (native); + * ]| + * + * To use a dialog for saving, you can use this: + * + * |[ + * GtkFileChooserNative *native; + * GtkFileChooser *chooser; + * GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_SAVE; + * gint res; + * + * native = gtk_file_chooser_native_new ("Save File", + * parent_window, + * action, + * "_Save", + * "_Cancel"); + * chooser = GTK_FILE_CHOOSER (native); + * + * gtk_file_chooser_set_do_overwrite_confirmation (chooser, TRUE); + * + * if (user_edited_a_new_document) + * gtk_file_chooser_set_current_name (chooser, + * _("Untitled document")); + * else + * gtk_file_chooser_set_filename (chooser, + * existing_filename); + * + * res = gtk_native_dialog_run (GTK_NATIVE_DIALOG (native)); + * if (res == GTK_RESPONSE_ACCEPT) + * { + * char *filename; + * + * filename = gtk_file_chooser_get_filename (chooser); + * save_to_file (filename); + * g_free (filename); + * } + * + * g_object_unref (native); + * ]| + * + * For more information on how to best set up a file dialog, see #GtkFileChooserDialog. + * + * ## Response Codes ## {#gtkfilechooserdialognative-responses} + * + * #GtkFileChooserNative inherits from #GtkNativeDialog, which means it + * will return #GTK_RESPONSE_ACCEPT if the user accepted, and + * #GTK_RESPONSE_CANCEL if he pressed cancel. It can also return + * #GTK_RESPONSE_DELETE_EVENT if the window was unexpectedly closed. + * + * ## Differences from #GtkFileChooserDialog ## {#gtkfilechooserdialognative-differences} + * + * There are a few things in the GtkFileChooser API that are not + * possible to use with #GtkFileChooserNative, as such use would + * prohibit the use of a native dialog. + * + * There is no support for the signals that are emitted when the user + * navigates in the dialog, including: + * * #GtkFileChooser::current-folder-changed + * * #GtkFileChooser::selection-changed + * * #GtkFileChooser::file-activated + * * #GtkFileChooser::confirm-overwrite + * + * You can also not use the methods that directly control user navigation: + * * gtk_file_chooser_unselect_filename() + * * gtk_file_chooser_select_all() + * * gtk_file_chooser_unselect_all() + * + * If you need any of the above you will have to use #GtkFileChooserDialog directly. + * + * No operations that change the the dialog work while the dialog is + * visible. Set all the properties that are required before showing the dialog. + * + * ## Win32 details ## {#gtkfilechooserdialognative-win32} + * + * On windows the IFileDialog implementation (added in Windows Vista) is + * used. It supports many of the features that #GtkFileChooserDialog + * does, but there are some things it does not handle: + * + * * Extra widgets added with gtk_file_chooser_set_extra_widget(). + * + * * Use of custom previews by connecting to #GtkFileChooser::update-preview. + * + * * Any #GtkFileFilter added using a mimetype or custom filter. + * + * If any of these features are used the regular #GtkFileChooserDialog + * will be used in place of the native one. + */ + +enum { + MODE_FALLBACK, + MODE_WIN32, +}; + +enum { + PROP_0, + PROP_ACCEPT_LABEL, + PROP_CANCEL_LABEL, + LAST_ARG, +}; + +static GParamSpec *native_props[LAST_ARG] = { NULL, }; + +static void _gtk_file_chooser_native_iface_init (GtkFileChooserIface *iface); + +G_DEFINE_TYPE_WITH_CODE (GtkFileChooserNative, gtk_file_chooser_native, GTK_TYPE_NATIVE_DIALOG, + G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_CHOOSER, + _gtk_file_chooser_native_iface_init)) + + +/** + * gtk_file_chooser_native_get_accept_label: + * @self: a #GtFileChooserNative + * + * Retrieves the custom label text for the accept button. + * + * Returns: The custom label, or %NULL for the default. This string is + * owned by GTK+ and should not be modified or freed + * + * Since: 3.20 + **/ +const char * +gtk_file_chooser_native_get_accept_label (GtkFileChooserNative *self) +{ + g_return_val_if_fail (GTK_IS_FILE_CHOOSER_NATIVE (self), NULL); + + return self->accept_label; +} + +/** + * gtk_file_chooser_native_set_accept_label: + * @self: a #GtFileChooserNative + * @accept_label: (allow-none): custom label or %NULL for the default + * + * Sets the custom label text for the accept button. + * + * If characters in @label are preceded by an underscore, they are underlined. + * If you need a literal underscore character in a label, use “__” (two + * underscores). The first underlined character represents a keyboard + * accelerator called a mnemonic. + * Pressing Alt and that key activates the button. + * + * Since: 3.20 + **/ +void +gtk_file_chooser_native_set_accept_label (GtkFileChooserNative *self, + const char *accept_label) +{ + g_return_if_fail (GTK_IS_FILE_CHOOSER_NATIVE (self)); + + g_free (self->accept_label); + self->accept_label = g_strdup (accept_label); + + g_object_notify_by_pspec (G_OBJECT (self), native_props[PROP_ACCEPT_LABEL]); +} + +/** + * gtk_file_chooser_native_get_cancel_label: + * @self: a #GtFileChooserNative + * + * Retrieves the custom label text for the cancel button. + * + * Returns: The custom label, or %NULL for the default. This string is + * owned by GTK+ and should not be modified or freed + * + * Since: 3.20 + **/ +const char * +gtk_file_chooser_native_get_cancel_label (GtkFileChooserNative *self) +{ + g_return_val_if_fail (GTK_IS_FILE_CHOOSER_NATIVE (self), NULL); + + return self->cancel_label; +} + +/** + * gtk_file_chooser_native_set_cancel_label: + * @self: a #GtFileChooserNative + * @cancel_label: (allow-none): custom label or %NULL for the default + * + * Sets the custom label text for the cancel button. + * + * If characters in @label are preceded by an underscore, they are underlined. + * If you need a literal underscore character in a label, use “__” (two + * underscores). The first underlined character represents a keyboard + * accelerator called a mnemonic. + * Pressing Alt and that key activates the button. + * + * Since: 3.20 + **/ +void +gtk_file_chooser_native_set_cancel_label (GtkFileChooserNative *self, + const char *cancel_label) +{ + g_return_if_fail (GTK_IS_FILE_CHOOSER_NATIVE (self)); + + g_free (self->cancel_label); + self->cancel_label = g_strdup (cancel_label); + + g_object_notify_by_pspec (G_OBJECT (self), native_props[PROP_CANCEL_LABEL]); +} + +static void +gtk_file_chooser_native_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) + +{ + GtkFileChooserNative *self = GTK_FILE_CHOOSER_NATIVE (object); + + switch (prop_id) + { + case PROP_ACCEPT_LABEL: + gtk_file_chooser_native_set_accept_label (self, g_value_get_string (value)); + break; + + case PROP_CANCEL_LABEL: + gtk_file_chooser_native_set_cancel_label (self, g_value_get_string (value)); + break; + + default: + g_object_set_property (G_OBJECT (self->dialog), pspec->name, value); + break; + } +} + +static void +gtk_file_chooser_native_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkFileChooserNative *self = GTK_FILE_CHOOSER_NATIVE (object); + + switch (prop_id) + { + case PROP_ACCEPT_LABEL: + g_value_set_string (value, self->accept_label); + break; + + case PROP_CANCEL_LABEL: + g_value_set_string (value, self->cancel_label); + break; + + default: + g_object_get_property (G_OBJECT (self->dialog), pspec->name, value); + break; + } +} + +static void +gtk_file_chooser_native_finalize (GObject *object) +{ + GtkFileChooserNative *self = GTK_FILE_CHOOSER_NATIVE (object); + + g_clear_pointer (&self->current_name, g_free); + g_clear_object (&self->current_file); + g_clear_object (&self->current_folder); + + g_clear_pointer (&self->accept_label, g_free); + g_clear_pointer (&self->cancel_label, g_free); + gtk_widget_destroy (self->dialog); + + g_slist_free_full (self->custom_files, g_object_unref); + + G_OBJECT_CLASS (gtk_file_chooser_native_parent_class)->finalize (object); +} + +static gint +override_delete_handler (GtkDialog *dialog, + GdkEventAny *event, + gpointer data) +{ + return TRUE; /* Do not destroy */ +} + +static void +gtk_file_chooser_native_init (GtkFileChooserNative *self) +{ + /* We always create a File chooser dialog and delegate all properties to it. + * This way we can reuse that store, plus we always have a dialog we can use + * in case something makes the native one not work (like the custom widgets) */ + self->dialog = g_object_new (GTK_TYPE_FILE_CHOOSER_DIALOG, NULL); + self->cancel_button = gtk_dialog_add_button (GTK_DIALOG (self->dialog), _("_Cancel"), GTK_RESPONSE_CANCEL); + self->accept_button = gtk_dialog_add_button (GTK_DIALOG (self->dialog), _("_Open"), GTK_RESPONSE_ACCEPT); + + gtk_dialog_set_default_response (GTK_DIALOG (self->dialog), + GTK_RESPONSE_ACCEPT); +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + gtk_dialog_set_alternative_button_order (GTK_DIALOG (self->dialog), + GTK_RESPONSE_ACCEPT, + GTK_RESPONSE_CANCEL, + -1); +G_GNUC_END_IGNORE_DEPRECATIONS + + /* We don't want to destroy on delete event, instead we hide in the response cb */ + g_signal_connect (self->dialog, + "delete-event", + G_CALLBACK (override_delete_handler), + NULL); + + /* This is used, instead of the standard delegate, to ensure that signals are not delegated. */ + g_object_set_qdata (G_OBJECT (self), GTK_FILE_CHOOSER_DELEGATE_QUARK, self->dialog); +} + +/** + * gtk_file_chooser_native_new: + * @title: (allow-none): Title of the native, or %NULL + * @parent: (allow-none): Transient parent of the native, or %NULL + * @action: Open or save mode for the dialog + * @accept_label: (allow-none): text to go in the accept button, or %NULL for the default + * @cancel_label: (allow-none): text to go in the cancel button, or %NULL for the default + * + * Creates a new #GtkFileChooserNative. + * + * Returns: a new #GtkFileChooserNative + * + * Since: 3.20 + **/ +GtkFileChooserNative * +gtk_file_chooser_native_new (const gchar *title, + GtkWindow *parent, + GtkFileChooserAction action, + const gchar *accept_label, + const gchar *cancel_label) +{ + GtkFileChooserNative *result; + + result = g_object_new (GTK_TYPE_FILE_CHOOSER_NATIVE, + "title", title, + "action", action, + "transient-for", parent, + "accept-label", accept_label, + "cancel-label", cancel_label, + NULL); + + return result; +} + +static void +dialog_response_cb (GtkDialog *dialog, + gint response_id, + gpointer data) +{ + GtkFileChooserNative *self = data; + + g_signal_handlers_disconnect_by_func (self->dialog, dialog_response_cb, self); + gtk_widget_hide (self->dialog); + + _gtk_native_dialog_emit_response (GTK_NATIVE_DIALOG (self), response_id); +} + +static void +dialog_update_preview_cb (GtkFileChooser *file_chooser, + gpointer data) +{ + g_signal_emit_by_name (data, "update-preview"); +} + +static void +show_dialog (GtkFileChooserNative *self) +{ + GtkFileChooserAction action; + const char *accept_label, *cancel_label; + + action = gtk_file_chooser_get_action (GTK_FILE_CHOOSER (self->dialog)); + + accept_label = self->accept_label; + if (accept_label == NULL) + accept_label = (action == GTK_FILE_CHOOSER_ACTION_SAVE) ? _("_Save") : _("_Open"); + + gtk_button_set_label (GTK_BUTTON (self->accept_button), accept_label); + + cancel_label = self->cancel_label; + if (cancel_label == NULL) + cancel_label = _("_Cancel"); + + gtk_button_set_label (GTK_BUTTON (self->cancel_button), cancel_label); + + gtk_window_set_title (GTK_WINDOW (self->dialog), + gtk_native_dialog_get_title (GTK_NATIVE_DIALOG (self))); + + gtk_window_set_transient_for (GTK_WINDOW (self->dialog), + gtk_native_dialog_get_transient_for (GTK_NATIVE_DIALOG (self))); + + gtk_window_set_modal (GTK_WINDOW (self->dialog), + gtk_native_dialog_get_modal (GTK_NATIVE_DIALOG (self))); + + g_signal_connect (self->dialog, + "response", + G_CALLBACK (dialog_response_cb), + self); + + g_signal_connect (self->dialog, + "update-preview", + G_CALLBACK (dialog_update_preview_cb), + self); + + gtk_window_present (GTK_WINDOW (self->dialog)); +} + +static void +hide_dialog (GtkFileChooserNative *self) +{ + g_signal_handlers_disconnect_by_func (self->dialog, dialog_response_cb, self); + g_signal_handlers_disconnect_by_func (self->dialog, dialog_update_preview_cb, self); + gtk_widget_hide (self->dialog); +} + +static gboolean +gtk_file_chooser_native_set_current_folder (GtkFileChooser *chooser, + GFile *file, + GError **error) +{ + GtkFileChooserNative *self = GTK_FILE_CHOOSER_NATIVE (chooser); + gboolean res; + + res = gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (self->dialog), + file, error); + + + if (res) + { + g_set_object (&self->current_folder, file); + + g_clear_object (&self->current_file); + } + + return res; +} + +static gboolean +gtk_file_chooser_native_select_file (GtkFileChooser *chooser, + GFile *file, + GError **error) +{ + GtkFileChooserNative *self = GTK_FILE_CHOOSER_NATIVE (chooser); + gboolean res; + + res = gtk_file_chooser_select_file (GTK_FILE_CHOOSER (self->dialog), + file, error); + + if (res) + { + g_set_object (&self->current_file, file); + + g_clear_object (&self->current_folder); + g_clear_pointer (&self->current_name, g_free); + } + + return res; +} + +static void +gtk_file_chooser_native_set_current_name (GtkFileChooser *chooser, + const gchar *name) +{ + GtkFileChooserNative *self = GTK_FILE_CHOOSER_NATIVE (chooser); + + gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (self->dialog), name); + + g_clear_pointer (&self->current_name, g_free); + self->current_name = g_strdup (name); + + g_clear_object (&self->current_file); +} + +static GSList * +gtk_file_chooser_native_get_files (GtkFileChooser *chooser) +{ + GtkFileChooserNative *self = GTK_FILE_CHOOSER_NATIVE (chooser); + + switch (self->mode) + { + case MODE_WIN32: + return g_slist_copy_deep (self->custom_files, (GCopyFunc)g_object_ref, NULL); + + case MODE_FALLBACK: + default: + return gtk_file_chooser_get_files (GTK_FILE_CHOOSER (self->dialog)); + } +} + +static void +gtk_file_chooser_native_show (GtkNativeDialog *native) +{ + GtkFileChooserNative *self = GTK_FILE_CHOOSER_NATIVE (native); + + self->mode = MODE_FALLBACK; + +#ifdef GDK_WINDOWING_WIN32 + if (gtk_file_chooser_native_win32_show (self)) + self->mode = MODE_WIN32; +#endif + + if (self->mode == MODE_FALLBACK) + show_dialog (self); +} + +static void +gtk_file_chooser_native_hide (GtkNativeDialog *native) +{ + GtkFileChooserNative *self = GTK_FILE_CHOOSER_NATIVE (native); + + switch (self->mode) + { + case MODE_FALLBACK: + hide_dialog (self); + break; + case MODE_WIN32: +#ifdef GDK_WINDOWING_WIN32 + gtk_file_chooser_native_win32_hide (self); +#endif + break; + } +} + +static void +gtk_file_chooser_native_class_init (GtkFileChooserNativeClass *class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (class); + GtkNativeDialogClass *native_dialog_class = GTK_NATIVE_DIALOG_CLASS (class); + + gobject_class->finalize = gtk_file_chooser_native_finalize; + gobject_class->set_property = gtk_file_chooser_native_set_property; + gobject_class->get_property = gtk_file_chooser_native_get_property; + + native_dialog_class->show = gtk_file_chooser_native_show; + native_dialog_class->hide = gtk_file_chooser_native_hide; + + _gtk_file_chooser_install_properties (gobject_class); + + /** + * GtkFileChooserNative:accept-label: + * + * The text used for the label on the accept button in the dialog, or + * %NULL to use the default text. + */ + native_props[PROP_ACCEPT_LABEL] = + g_param_spec_string ("accept-label", + P_("Accept label"), + P_("The label on the accept button"), + NULL, + GTK_PARAM_READWRITE); + + /** + * GtkFileChooserNative:cancel-label: + * + * The text used for the label on the cancel button in the dialog, or + * %NULL to use the default text. + */ + native_props[PROP_CANCEL_LABEL] = + g_param_spec_string ("cancel-label", + P_("Cancel label"), + P_("The label on the cancel button"), + NULL, + GTK_PARAM_READWRITE); + + g_object_class_install_properties (gobject_class, LAST_ARG, native_props); +} + +static void +_gtk_file_chooser_native_iface_init (GtkFileChooserIface *iface) +{ + _gtk_file_chooser_delegate_iface_init (iface); + iface->select_file = gtk_file_chooser_native_select_file; + iface->set_current_name = gtk_file_chooser_native_set_current_name; + iface->set_current_folder = gtk_file_chooser_native_set_current_folder; + iface->get_files = gtk_file_chooser_native_get_files; +} diff --git a/gtk/gtkfilechoosernative.h b/gtk/gtkfilechoosernative.h new file mode 100644 index 0000000000..c2c21d765b --- /dev/null +++ b/gtk/gtkfilechoosernative.h @@ -0,0 +1,56 @@ +/* GTK - The GIMP Toolkit + * gtkfilechoosernative.h: Native File selector dialog + * Copyright (C) 2015, Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __GTK_FILE_CHOOSER_NATIVE_H__ +#define __GTK_FILE_CHOOSER_NATIVE_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only <gtk/gtk.h> can be included directly." +#endif + +#include <gtk/gtkfilechooser.h> +#include <gtk/gtknativedialog.h> + +G_BEGIN_DECLS + +#define GTK_TYPE_FILE_CHOOSER_NATIVE (gtk_file_chooser_native_get_type ()) + +GDK_AVAILABLE_IN_3_20 +G_DECLARE_FINAL_TYPE (GtkFileChooserNative, gtk_file_chooser_native, GTK, FILE_CHOOSER_NATIVE, GtkNativeDialog) + +GDK_AVAILABLE_IN_3_20 +GtkFileChooserNative *gtk_file_chooser_native_new (const gchar *title, + GtkWindow *parent, + GtkFileChooserAction action, + const gchar *accept_label, + const gchar *cancel_label); + +GDK_AVAILABLE_IN_3_20 +const char *gtk_file_chooser_native_get_accept_label (GtkFileChooserNative *self); +GDK_AVAILABLE_IN_3_20 +void gtk_file_chooser_native_set_accept_label (GtkFileChooserNative *self, + const char *accept_label); +GDK_AVAILABLE_IN_3_20 +const char *gtk_file_chooser_native_get_cancel_label (GtkFileChooserNative *self); +GDK_AVAILABLE_IN_3_20 +void gtk_file_chooser_native_set_cancel_label (GtkFileChooserNative *self, + const char *cancel_label); + +G_END_DECLS + +#endif /* __GTK_FILE_CHOOSER_NATIVE_H__ */ diff --git a/gtk/gtkfilechoosernativeprivate.h b/gtk/gtkfilechoosernativeprivate.h new file mode 100644 index 0000000000..6e271ffc0e --- /dev/null +++ b/gtk/gtkfilechoosernativeprivate.h @@ -0,0 +1,53 @@ +/* GTK - The GIMP Toolkit + * gtkfilechoosernativeprivate.h: Native File selector dialog + * Copyright (C) 2015, Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __GTK_FILE_CHOOSER_NATIVE_PRIVATE_H__ +#define __GTK_FILE_CHOOSER_NATIVE_PRIVATE_H__ + +#include <gtk/gtkfilechoosernative.h> + +G_BEGIN_DECLS + +struct _GtkFileChooserNative +{ + GtkNativeDialog parent_instance; + + char *accept_label; + char *cancel_label; + + int mode; + GSList *custom_files; + + GFile *current_folder; + GFile *current_file; + char *current_name; + + /* Fallback mode */ + GtkWidget *dialog; + GtkWidget *accept_button; + GtkWidget *cancel_button; + + gpointer mode_data; +}; + +gboolean gtk_file_chooser_native_win32_show (GtkFileChooserNative *self); +void gtk_file_chooser_native_win32_hide (GtkFileChooserNative *self); + +G_END_DECLS + +#endif /* __GTK_FILE_CHOOSER_NATIVE_PRIVATE_H__ */ diff --git a/gtk/gtkfilechoosernativewin32.c b/gtk/gtkfilechoosernativewin32.c new file mode 100644 index 0000000000..9e60e276d4 --- /dev/null +++ b/gtk/gtkfilechoosernativewin32.c @@ -0,0 +1,778 @@ +/* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */ +/* GTK - The GIMP Toolkit + * gtkfilechoosernativewin32.c: Win32 Native File selector dialog + * Copyright (C) 2015, Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include "gtkfilechoosernativeprivate.h" +#include "gtknativedialogprivate.h" + +#include "gtkprivate.h" +#include "gtkfilechooserdialog.h" +#include "gtkfilechooserprivate.h" +#include "gtkfilechooserwidget.h" +#include "gtkfilechooserwidgetprivate.h" +#include "gtkfilechooserutils.h" +#include "gtkfilechooserembed.h" +#include "gtkfilesystem.h" +#include "gtksizerequest.h" +#include "gtktypebuiltins.h" +#include "gtkintl.h" +#include "gtksettings.h" +#include "gtktogglebutton.h" +#include "gtkstylecontext.h" +#include "gtkheaderbar.h" +#include "gtklabel.h" +#include "gtkfilechooserentry.h" +#include "gtkfilefilterprivate.h" + +/* Vista or newer */ +#define _WIN32_WINNT 0x0600 +#define WINVER _WIN32_WINNT +#define NTDDI_VERSION NTDDI_VISTA +#define COBJMACROS + +#include "win32/gdkwin32.h" +#include <shlobj.h> +#include <windows.h> + +typedef struct { + GtkFileChooserNative *self; + IFileDialogEvents *events; + + HWND parent; + gboolean skip_response; + gboolean save; + gboolean folder; + gboolean modal; + gboolean overwrite_confirmation; + gboolean select_multiple; + gboolean show_hidden; + + char *accept_label; + char *cancel_label; + char *title; + + GSList *shortcut_uris; + + GFile *current_folder; + GFile *current_file; + char *current_name; + + COMDLG_FILTERSPEC *filters; + + GSList *files; + int response; +} FilechooserWin32ThreadData; + +static void +g_warning_hr (const char *msg, HRESULT hr) +{ + char *errmsg; + errmsg = g_win32_error_message (hr); + g_warning ("%s: %s\n", msg, errmsg); + g_free (errmsg); +} + +/* {3CAFD12E-82AE-4184-8309-848C0104B4DC} */ +static const GUID myIID_IFileDialogEvents = +{ 0x3cafd12e, 0x82ae, 0x4184, { 0x83, 0x9, 0x84, 0x8c, 0x1, 0x4, 0xb4, 0xdc } }; + +/* Protects access to dialog_hwnd, do_close and ref_count */ +G_LOCK_DEFINE_STATIC(FileDialogEvents); + +typedef struct { + IFileDialogEvents iFileDialogEvents; + int ref_count; + gboolean enable_owner; + gboolean got_hwnd; + HWND dialog_hwnd; + gboolean do_close; /* Set if hide was called before dialog_hwnd was set */ +} FileDialogEvents; + + +static ULONG STDMETHODCALLTYPE +ifiledialogevents_AddRef (IFileDialogEvents *self) +{ + FileDialogEvents *events = (FileDialogEvents *)self; + ULONG ref_count; + + G_LOCK (FileDialogEvents); + ref_count = ++events->ref_count; + G_UNLOCK (FileDialogEvents); + + return ref_count; +} + +static ULONG STDMETHODCALLTYPE +ifiledialogevents_Release (IFileDialogEvents *self) +{ + FileDialogEvents *events = (FileDialogEvents *)self; + int ref_count; + + G_LOCK (FileDialogEvents); + ref_count = --events->ref_count; + G_UNLOCK (FileDialogEvents); + + if (ref_count == 0) + g_free (self); + + return ref_count; +} + +static HRESULT STDMETHODCALLTYPE +ifiledialogevents_QueryInterface (IFileDialogEvents *self, + REFIID riid, + LPVOID *ppvObject) +{ + if (IsEqualIID (riid, &IID_IUnknown) || + IsEqualIID (riid, &myIID_IFileDialogEvents)) + { + *ppvObject = self; + IUnknown_AddRef ((IUnknown *)self); + return NOERROR; + } + else + { + *ppvObject = NULL; + return E_NOINTERFACE; + } +} + +static HRESULT STDMETHODCALLTYPE +ifiledialogevents_OnFileOk (IFileDialogEvents *self, + IFileDialog *pfd) +{ + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE +ifiledialogevents_OnFolderChanging (IFileDialogEvents *self, + IFileDialog *pfd, + IShellItem *psiFolder) +{ + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE +ifiledialogevents_OnFolderChange (IFileDialogEvents *self, + IFileDialog *pfd) +{ + FileDialogEvents *events = (FileDialogEvents *)self; + IOleWindow *olew = NULL; + HWND dialog_hwnd; + HRESULT hr; + + if (!events->got_hwnd) + { + events->got_hwnd = TRUE; + + hr = IFileDialog_QueryInterface (pfd, &IID_IOleWindow, &olew); + if (SUCCEEDED (hr)) + { + hr = IOleWindow_GetWindow (olew, &dialog_hwnd); + if (SUCCEEDED (hr)) + { + G_LOCK (FileDialogEvents); + events->dialog_hwnd = dialog_hwnd; + if (events->do_close) + SendMessage (events->dialog_hwnd, WM_CLOSE, 0, 0); + G_UNLOCK (FileDialogEvents); + } + else + g_warning_hr ("Can't get HWND", hr); + + hr = IOleWindow_Release (olew); + if (FAILED (hr)) + g_warning_hr ("Can't unref IOleWindow", hr); + } + else + g_warning_hr ("Can't get IOleWindow", hr); + + if (events->enable_owner && events->dialog_hwnd) + { + HWND owner = GetWindow (events->dialog_hwnd, GW_OWNER); + if (owner) + EnableWindow (owner, TRUE); + } + } + + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE +ifiledialogevents_OnSelectionChange (IFileDialogEvents * self, + IFileDialog *pfd) +{ + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE +ifiledialogevents_OnShareViolation (IFileDialogEvents * self, + IFileDialog *pfd, + IShellItem *psi, + FDE_SHAREVIOLATION_RESPONSE *pResponse) +{ + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE +ifiledialogevents_OnTypeChange (IFileDialogEvents * self, + IFileDialog *pfd) +{ + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE +ifiledialogevents_OnOverwrite (IFileDialogEvents * self, + IFileDialog *pfd, + IShellItem *psi, + FDE_OVERWRITE_RESPONSE *pResponse) +{ + return E_NOTIMPL; +} + +static IFileDialogEventsVtbl ifde_vtbl = { + ifiledialogevents_QueryInterface, + ifiledialogevents_AddRef, + ifiledialogevents_Release, + ifiledialogevents_OnFileOk, + ifiledialogevents_OnFolderChanging, + ifiledialogevents_OnFolderChange, + ifiledialogevents_OnSelectionChange, + ifiledialogevents_OnShareViolation, + ifiledialogevents_OnTypeChange, + ifiledialogevents_OnOverwrite +}; + +file_dialog_events_send_close (IFileDialogEvents *self) +{ + FileDialogEvents *events = (FileDialogEvents *)self; + + G_LOCK (FileDialogEvents); + + if (events->dialog_hwnd) + SendMessage (events->dialog_hwnd, WM_CLOSE, 0, 0); + else + events->do_close = TRUE; + + G_UNLOCK (FileDialogEvents); +} + +static IFileDialogEvents * +file_dialog_events_new (gboolean enable_owner) +{ + FileDialogEvents *events; + + events = g_new0 (FileDialogEvents, 1); + events->iFileDialogEvents.lpVtbl = &ifde_vtbl; + events->ref_count = 1; + events->enable_owner = enable_owner; + + return &events->iFileDialogEvents; +} + +static void +filechooser_win32_thread_data_free (FilechooserWin32ThreadData *data) +{ + int i; + if (data->filters) + { + for (i = 0; data->filters[i].pszName != NULL; i++) + { + g_free ((char *)data->filters[i].pszName); + g_free ((char *)data->filters[i].pszSpec); + } + g_free (data->filters); + } + + if (data->events) + IFileDialogEvents_Release (data->events); + + g_clear_object (&data->current_folder); + g_clear_object (&data->current_file); + g_free (data->current_name); + + g_slist_free_full (data->shortcut_uris, g_free); + g_slist_free_full (data->files, g_object_unref); + if (data->self) + g_object_unref (data->self); + g_free (data->accept_label); + g_free (data->cancel_label); + g_free (data->title); + g_free (data); +} + +static gboolean +filechooser_win32_thread_done (gpointer _data) +{ + FilechooserWin32ThreadData *data = _data; + GtkFileChooserNative *self = data->self; + + self->mode_data = NULL; + + if (!data->skip_response) + { + g_slist_free_full (self->custom_files, g_object_unref); + self->custom_files = data->files; + data->files = NULL; + + _gtk_native_dialog_emit_response (GTK_NATIVE_DIALOG (data->self), + data->response); + } + + filechooser_win32_thread_data_free (data); + + return FALSE; +} + +static void +data_add_shell_item (FilechooserWin32ThreadData *data, + IShellItem *item) +{ + HRESULT hr; + PWSTR urlw = NULL; + char *url; + + hr = IShellItem_GetDisplayName (item, SIGDN_URL, &urlw); + if (SUCCEEDED (hr)) + { + url = g_utf16_to_utf8 (urlw, -1, NULL, NULL, NULL); + CoTaskMemFree (urlw); + data->files = g_slist_prepend (data->files, g_file_new_for_uri (url)); + data->response = GTK_RESPONSE_ACCEPT; + g_free (url); + } +} + +static IShellItem * +get_shell_item_for_uri (const char *uri) +{ + IShellItem *item; + HRESULT hr; + gunichar2 *uri_w = g_utf8_to_utf16 (uri, -1, NULL, NULL, NULL); + + hr = SHCreateItemFromParsingName(uri_w, 0, &IID_IShellItem, &item); + if (SUCCEEDED (hr)) + return item; + else + g_warning_hr ("Can't create shell item from shortcut", hr); + + return NULL; +} + +static IShellItem * +get_shell_item_for_file (GFile *file) +{ + char *uri; + IShellItem *item; + + uri = g_file_get_uri (file); + item = get_shell_item_for_uri (uri); + g_free (uri); + + return item; +} + +static gpointer +filechooser_win32_thread (gpointer _data) +{ + FilechooserWin32ThreadData *data = _data; + HRESULT hr; + IFileDialog *pfd = NULL; + IFileDialog2 *pfd2 = NULL; + gboolean res = FALSE; + DWORD flags; + HWND parent = NULL; + HWND dialog_hwnd; + GtkWindow *transient_for; + DWORD cookie; + GSList *l; + + CoInitializeEx (NULL, COINIT_APARTMENTTHREADED); + + if (data->save && !data->folder) + hr = CoCreateInstance (&CLSID_FileSaveDialog, + NULL, CLSCTX_INPROC_SERVER, + &IID_IFileSaveDialog, &pfd); + else + hr = CoCreateInstance (&CLSID_FileOpenDialog, + NULL, CLSCTX_INPROC_SERVER, + &IID_IFileOpenDialog, &pfd); + + if (FAILED (hr)) + g_error ("Can't create FileOpenDialog: %s\n", g_win32_error_message (hr)); + + hr = IFileDialog_GetOptions (pfd, &flags); + if (FAILED (hr)) + g_error ("Can't get FileDialog options: %s\n", g_win32_error_message (hr)); + + flags |= FOS_FORCEFILESYSTEM; + + if (data->folder) + flags |= FOS_PICKFOLDERS; + + if (data->folder && data->save) + flags &= ~(FOS_FILEMUSTEXIST); + + if (data->select_multiple) + flags |= FOS_ALLOWMULTISELECT; + + if (data->show_hidden) + flags |= FOS_FORCESHOWHIDDEN; + + if (data->overwrite_confirmation) + flags |= FOS_OVERWRITEPROMPT; + else + flags &= ~(FOS_OVERWRITEPROMPT); + + hr = IFileDialog_SetOptions (pfd, flags); + if (FAILED (hr)) + g_error ("Can't set FileDialog options: %s\n", g_win32_error_message (hr)); + + if (data->title) + { + gunichar2 *label = g_utf8_to_utf16 (data->title, -1, + NULL, NULL, NULL); + IFileDialog_SetTitle (pfd, label); + g_free (label); + } + + if (data->accept_label) + { + gunichar2 *label = g_utf8_to_utf16 (data->accept_label, -1, + NULL, NULL, NULL); + IFileDialog_SetOkButtonLabel (pfd, label); + g_free (label); + } + + if (data->cancel_label) + { + gunichar2 *label = g_utf8_to_utf16 (data->cancel_label, -1, + NULL, NULL, NULL); + hr = IFileDialog_QueryInterface (pfd, &IID_IFileDialog2, &pfd2); + if (SUCCEEDED (hr)) + { + IFileDialog2_SetCancelButtonLabel (pfd2, label); + IFileDialog2_Release (pfd2); + } + g_free (label); + } + + for (l = data->shortcut_uris; l != NULL; l = l->next) + { + IShellItem *item = get_shell_item_for_uri (l->data); + if (item) + { + hr = IFileDialog_AddPlace (pfd, item, FDAP_BOTTOM); + if (FAILED (hr)) + g_warning_hr ("Can't add dialog shortcut", hr); + IShellItem_Release (item); + } + } + + if (data->current_file) + { + IFileSaveDialog *pfsd; + hr = IFileDialog_QueryInterface (pfd, &IID_IFileSaveDialog, &pfsd); + if (SUCCEEDED (hr)) + { + IShellItem *item = get_shell_item_for_file (data->current_file); + if (item) + { + hr = IFileSaveDialog_SetSaveAsItem (pfsd, item); + if (FAILED (hr)) + g_warning_hr ("Can't set save as item", hr); + IShellItem_Release (item); + } + IFileSaveDialog_Release (pfsd); + } + } + + if (data->current_folder) + { + IShellItem *item = get_shell_item_for_file (data->current_folder); + if (item) + { + hr = IFileDialog_SetFolder (pfd, item); + if (FAILED (hr)) + g_warning_hr ("Can't set folder", hr); + IShellItem_Release (item); + } + } + + if (data->current_name) + { + gunichar2 *name = g_utf8_to_utf16 (data->current_name, -1, NULL, NULL, NULL); + hr = IFileDialog_SetFileName (pfd, name); + if (FAILED (hr)) + g_warning_hr ("Can't set file name", hr); + g_free (name); + } + + if (data->filters) + { + int n; + for (n = 0; data->filters[n].pszName != NULL; n++) + {} + hr = IFileDialog_SetFileTypes (pfd, n, data->filters); + if (FAILED (hr)) + g_warning_hr ("Can't set file types", hr); + } + + data->response = GTK_RESPONSE_CANCEL; + + hr = IFileDialog_Advise (pfd, data->events, &cookie); + if (FAILED (hr)) + g_error ("Can't Advise FileDialog: %s\n", g_win32_error_message (hr)); + + hr = IFileDialog_Show (pfd, data->parent); + if (SUCCEEDED (hr)) + { + IFileOpenDialog *pfod = NULL; + hr = IFileDialog_QueryInterface (pfd,&IID_IFileOpenDialog, &pfod); + + if (SUCCEEDED (hr)) + { + IShellItemArray *res; + DWORD i, count; + + hr = IFileOpenDialog_GetResults (pfod, &res); + if (FAILED (hr)) + g_error ("Can't get FileOpenDialog results: %s\n", g_win32_error_message (hr)); + + hr = IShellItemArray_GetCount (res, &count); + if (FAILED (hr)) + g_error ("Can't get FileOpenDialog count: %s\n", g_win32_error_message (hr)); + + for (i = 0; i < count; i++) + { + IShellItem *item; + hr = IShellItemArray_GetItemAt (res, i, &item); + if (FAILED (hr)) + g_error ("Can't get item at %d: %s\n", i, g_win32_error_message (hr)); + data_add_shell_item (data, item); + IShellItem_Release (item); + } + IShellItemArray_Release (res); + + IFileOpenDialog_Release (pfod); + } + else + { + IShellItem *item; + hr = IFileDialog_GetResult (pfd, &item); + if (FAILED (hr)) + g_error ("Can't get FileDialog result: %s\n", g_win32_error_message (hr)); + + data_add_shell_item (data, item); + IShellItem_Release (item); + } + } + + hr = IFileDialog_Unadvise (pfd, cookie); + if (FAILED (hr)) + g_error ("Can't Unadvise FileDialog: %s\n", g_win32_error_message (hr)); + + IFileDialog_Release ((IUnknown *)pfd); + + g_main_context_invoke (NULL, + filechooser_win32_thread_done, + data); + + return NULL; +} + +static gboolean +file_filter_to_win32 (GtkFileFilter *filter, + COMDLG_FILTERSPEC *spec) +{ + const char *name; + char **patterns; + char *pattern_list; + + patterns = _gtk_file_filter_get_as_patterns (filter); + if (patterns == NULL) + return FALSE; + + pattern_list = g_strjoinv (";", patterns); + g_strfreev (patterns); + + name = gtk_file_filter_get_name (filter); + if (name == NULL) + name = pattern_list; + spec->pszName = g_utf8_to_utf16 (name, -1, NULL, NULL, NULL); + spec->pszSpec = g_utf8_to_utf16 (pattern_list, -1, NULL, NULL, NULL); + + g_free (pattern_list); + + return TRUE; +} + +static char * +translate_mnemonics (const char *src) +{ + GString *s; + const char *p; + char c; + + if (src == NULL) + return NULL; + + s = g_string_sized_new (strlen (src)); + + for (p = src; *p; p++) + { + c = *p; + switch (c) + { + case '_': + /* __ is _ escaped */ + if (*(p+1) == '_') + { + g_string_append_c (s, '_'); + p++; + } + else + g_string_append_c (s, '&'); + break; + case '&': + /* Win32 needs ampersands escaped */ + g_string_append (s, "&&"); + default: + g_string_append_c (s, c); + } + } + + return g_string_free (s, FALSE); +} + +gboolean +gtk_file_chooser_native_win32_show (GtkFileChooserNative *self) +{ + GThread *thread; + FilechooserWin32ThreadData *data; + GtkWindow *transient_for; + GtkFileChooserAction action; + guint update_preview_signal; + GSList *filters, *l; + int n_filters, i; + COMDLG_FILTERSPEC *win32_filters; + + if (gtk_file_chooser_get_extra_widget (GTK_FILE_CHOOSER (self)) != NULL) + return FALSE; + + update_preview_signal = g_signal_lookup ("update-preview", GTK_TYPE_FILE_CHOOSER); + if (g_signal_has_handler_pending (self, update_preview_signal, 0, TRUE)) + return FALSE; + + data = g_new0 (FilechooserWin32ThreadData, 1); + + filters = gtk_file_chooser_list_filters (GTK_FILE_CHOOSER (self)); + n_filters = g_slist_length (filters); + if (n_filters > 0) + { + data->filters = g_new0 (COMDLG_FILTERSPEC, n_filters + 1); + + for (l = filters, i = 0; l != NULL; l = l->next, i++) + { + if (!file_filter_to_win32 (l->data, &data->filters[i])) + { + filechooser_win32_thread_data_free (data); + return FALSE; + } + } + } + + self->mode_data = data; + data->self = g_object_ref (self); + + data->shortcut_uris = + gtk_file_chooser_list_shortcut_folder_uris (GTK_FILE_CHOOSER (self->dialog)); + + data->accept_label = translate_mnemonics (self->accept_label); + data->cancel_label = translate_mnemonics (self->cancel_label); + + action = gtk_file_chooser_get_action (GTK_FILE_CHOOSER (self->dialog)); + if (action == GTK_FILE_CHOOSER_ACTION_SAVE || + action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) + data->save = TRUE; + + if (action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER || + action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) + data->folder = TRUE; + + if ((action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER || + action == GTK_FILE_CHOOSER_ACTION_OPEN) && + gtk_file_chooser_get_select_multiple (GTK_FILE_CHOOSER (self->dialog))) + data->select_multiple = TRUE; + + if (gtk_file_chooser_get_do_overwrite_confirmation (GTK_FILE_CHOOSER (self->dialog))) + data->overwrite_confirmation = TRUE; + + if (gtk_file_chooser_get_show_hidden (GTK_FILE_CHOOSER (self->dialog))) + data->show_hidden = TRUE; + + transient_for = gtk_native_dialog_get_transient_for (GTK_NATIVE_DIALOG (self)); + if (transient_for) + { + gtk_widget_realize (GTK_WIDGET (transient_for)); + data->parent = gdk_win32_window_get_handle (gtk_widget_get_window (GTK_WIDGET (transient_for))); + + if (gtk_native_dialog_get_modal (GTK_NATIVE_DIALOG (self))) + data->modal = TRUE; + } + + data->title = + g_strdup (gtk_native_dialog_get_title (GTK_NATIVE_DIALOG (self))); + + if (self->current_file) + data->current_file = g_object_ref (self->current_file); + else + { + if (self->current_folder) + data->current_folder = g_object_ref (self->current_folder); + + if (action == GTK_FILE_CHOOSER_ACTION_SAVE || + action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) + data->current_name = g_strdup (self->current_name); + } + + data->events = file_dialog_events_new (!data->modal); + + thread = g_thread_new ("win32 filechooser", filechooser_win32_thread, data); + if (thread == NULL) + { + filechooser_win32_thread_data_free (data); + return FALSE; + } + + return TRUE; +} + +void +gtk_file_chooser_native_win32_hide (GtkFileChooserNative *self) +{ + FilechooserWin32ThreadData *data = self->mode_data; + + /* This is always set while dialog visible */ + g_assert (data != NULL); + + data->skip_response = TRUE; + file_dialog_events_send_close (data->events); +} |