forked from AuroraMiddleware/gtk
GtkFileChooserNative: Fallback and win32 implementation
This is a subclass on GtkNativeDialog that uses GtkFileChooserDialog as a fallback, but also has support for the win32 file chooser dialog.
This commit is contained in:
parent
693db082a1
commit
5094900180
@ -204,6 +204,7 @@
|
||||
<xi:include href="xml/gtkfilechooser.xml" />
|
||||
<xi:include href="xml/gtkfilechooserbutton.xml" />
|
||||
<xi:include href="xml/gtknativedialog.xml" />
|
||||
<xi:include href="xml/gtkfilechoosernative.xml" />
|
||||
<xi:include href="xml/gtkfilechooserdialog.xml" />
|
||||
<xi:include href="xml/gtkfilechooserwidget.xml" />
|
||||
<xi:include href="xml/gtkfilefilter.xml" />
|
||||
|
@ -1432,6 +1432,25 @@ gtk_file_chooser_error_quark
|
||||
gtk_file_chooser_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkfilechoosernative</FILE>
|
||||
<TITLE>GtkFileChooserNative</TITLE>
|
||||
gtk_file_chooser_native_new
|
||||
gtk_file_chooser_native_get_accept_label
|
||||
gtk_file_chooser_native_set_accept_label
|
||||
gtk_file_chooser_native_get_cancel_label
|
||||
gtk_file_chooser_native_set_cancel_label
|
||||
<SUBSECTION Standard>
|
||||
GTK_FILE_CHOOSER_NATIVE
|
||||
GTK_IS_FILE_CHOOSER_NATIVE
|
||||
GTK_TYPE_FILE_CHOOSER_NATIVE
|
||||
GTK_FILE_CHOOSER_NATIVE_CLASS
|
||||
GTK_IS_FILE_CHOOSER_NATIVE_CLASS
|
||||
GTK_FILE_CHOOSER_NATIVE_GET_CLASS
|
||||
<SUBSECTION Private>
|
||||
gtk_file_chooser_native_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkfilechooserdialog</FILE>
|
||||
<TITLE>GtkFileChooserDialog</TITLE>
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
656
gtk/gtkfilechoosernative.c
Normal file
656
gtk/gtkfilechoosernative.c
Normal file
@ -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;
|
||||
}
|
56
gtk/gtkfilechoosernative.h
Normal file
56
gtk/gtkfilechoosernative.h
Normal file
@ -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__ */
|
53
gtk/gtkfilechoosernativeprivate.h
Normal file
53
gtk/gtkfilechoosernativeprivate.h
Normal file
@ -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__ */
|
778
gtk/gtkfilechoosernativewin32.c
Normal file
778
gtk/gtkfilechoosernativewin32.c
Normal file
@ -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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user