Compare commits

...

20 Commits

Author SHA1 Message Date
Matthias Clasen
3a46c75345 print portal: Send the parent window along
This lets gnome-shell attach the dialog to the application window.
2016-07-07 14:03:45 -04:00
Matthias Clasen
e2074dff47 Add portal support to GtkPrintOperation
Make GtkPrintOperation talk to org.freedesktop.portal.Print when
running in a sandbox.
2016-07-07 00:09:42 -04:00
Matthias Clasen
1e77936dc5 print job: Add api to send data from an fd
This will be useful for portalized print support.
2016-07-07 00:09:41 -04:00
Matthias Clasen
ec2a06dcfc page setup: Add api to serialize to a GVariant
This will be useful for portalized print support.
2016-07-07 00:09:41 -04:00
Matthias Clasen
d39838e12e Add api to serialize a GtkPaperSize to a GVariant
This will be useful in portalized printing.
2016-07-07 00:09:41 -04:00
Matthias Clasen
e0469b5b47 print settings: Add api to serialize to a GVariant
This will be useful for portalized print support.
2016-07-07 00:09:41 -04:00
Matthias Clasen
10cbdc8637 Support the Inhibit portal
xdg-desktop-portal now has a portal for inhibiting session status
changes. We don't need to use it if we can talk to the session
manager, but if can't, try org.freedesktop.portal.Inhibit.
2016-07-07 00:07:02 -04:00
Matthias Clasen
5d9a71a968 Implement combobox apis for GtkFileChooserNativePortal 2016-07-07 00:07:02 -04:00
Matthias Clasen
17bd545cf9 Implement combobox apis in GtkFileChooserWidget 2016-07-07 00:07:02 -04:00
Matthias Clasen
0d9c2d28de GtkFileChooser: Add abstract api for comboboxes and checkbuttons
This commit adds API for adding combo boxes and check buttons to
GtkFileChooser, and getting the selected value back in ::response.

In contrast to gtk_file_chooser_set_extra_widget, these APIs are
abstract and suitable for implementation in GtkFileChooserNative.
2016-07-07 00:07:01 -04:00
Matthias Clasen
68f8b21ae4 Adapt to simplified file chooser portal api
OpenFiles is gone, just set the 'multiple' option instead.
2016-07-07 00:07:01 -04:00
Matthias Clasen
1706a7f21b file chooser portal: Port to new Request API
The portal API has changed, we need to adapt.
2016-07-07 00:07:01 -04:00
Matthias Clasen
53a30afc05 Use the portal when sandboxed
Use the sandbox helper api to find out whether to use
the file chooser portal.
2016-07-07 00:07:01 -04:00
Matthias Clasen
d25c0a71e2 portal: Sent more data along
Send the current_name, current_folder or current_file fields to
the portal.
2016-07-07 00:07:01 -04:00
Matthias Clasen
1e6a32333d portal: Send file filters to the portal 2016-07-07 00:07:01 -04:00
Matthias Clasen
45311e2f06 Add api to serialize GtkFileFilter to a variant
This will be used to send filters over D-Bus in future commits.
2016-07-07 00:07:01 -04:00
Alexander Larsson
db8c1a6e11 Initial version of file chooser portal support
Implement GtkFileChooserNative for sandboxed applications
by talking to org.freedesktop.portal.FileChooser. Currently,
this supports OPEN and SAVE mode.
2016-07-07 00:07:01 -04:00
Matthias Clasen
72fa654fe0 about dialog: Use gtk_show_uri_on_window 2016-07-06 23:59:40 -04:00
Matthias Clasen
f0e5f3da91 Add gtk_show_uri_on_window
The gtk_show_uri API doesn't let us specify a parent window. With
portals, there may be an intermediate dialog, for which it is nice
to have parent window information, to place it properly.
2016-07-06 23:59:40 -04:00
Matthias Clasen
585dfccca3 Add a portal helper
Instead of open-coding checks for sandboxing in multiple
places, add a helper function. This function is identical
to glib_should_use_portal() in gio.
2016-07-06 23:59:40 -04:00
30 changed files with 2346 additions and 82 deletions

View File

@@ -1528,6 +1528,11 @@ gtk_file_filter_add_pixbuf_formats
gtk_file_filter_add_custom
gtk_file_filter_get_needed
gtk_file_filter_filter
<SUBSECTION Serialization>
gtk_file_filter_new_from_gvariant
gtk_file_filter_to_gvariant
<SUBSECTION Standard>
GTK_FILE_FILTER
GTK_IS_FILE_FILTER
@@ -7264,10 +7269,12 @@ GTK_PRINT_SETTINGS_WIN32_DRIVER_VERSION
<SUBSECTION Serialization>
gtk_print_settings_new_from_file
gtk_print_settings_new_from_key_file
gtk_print_settings_new_from_gvariant
gtk_print_settings_load_file
gtk_print_settings_load_key_file
gtk_print_settings_to_file
gtk_print_settings_to_key_file
gtk_print_settings_to_gvariant
<SUBSECTION Standard>
GTK_TYPE_PRINT_SETTINGS
@@ -7316,7 +7323,9 @@ gtk_paper_size_get_default
<SUBSECTION Serialization>
gtk_paper_size_new_from_key_file
gtk_paper_size_new_from_gvariant
gtk_paper_size_to_key_file
gtk_paper_size_to_gvariant
<SUBSECTION Standard>
GTK_TYPE_PAPER_SIZE
@@ -7364,10 +7373,13 @@ gtk_page_setup_get_page_height
<SUBSECTION Serialization>
gtk_page_setup_new_from_file
gtk_page_setup_new_from_key_file
gtk_page_setup_new_from_gvariant
gtk_page_setup_load_file
gtk_page_setup_load_key_file
gtk_page_setup_to_file
gtk_page_setup_to_key_file
gtk_page_setup_to_gvariant
<SUBSECTION Standard>
GTK_TYPE_PAGE_SETUP
GTK_PAGE_SETUP
@@ -7514,6 +7526,7 @@ gtk_mount_operation_get_parent
gtk_mount_operation_set_screen
gtk_mount_operation_get_screen
gtk_show_uri
gtk_show_uri_on_parent
<SUBSECTION Standard>
GTK_IS_MOUNT_OPERATION
GTK_IS_MOUNT_OPERATION_CLASS

View File

@@ -753,6 +753,7 @@ gtk_base_c_sources = \
gtkfilechooserembed.c \
gtkfilechooserentry.c \
gtkfilechoosernative.c \
gtkfilechoosernativeportal.c \
gtkfilechooserutils.c \
gtkfilechooserwidget.c \
gtkfilefilter.c \
@@ -986,9 +987,12 @@ gtk_os_unix_c_sources = \
gtkprinteroptionwidget.c \
gtkprintjob.c \
gtkprintoperation-unix.c \
gtkprintoperation-portal.h \
gtkprintoperation-portal.c \
gtkprintunixdialog.c \
gtkprintbackend.c \
gtksearchenginetracker.c
if OS_UNIX
gtk_private_h_sources += \
gtkiconcachevalidator.h \
@@ -998,6 +1002,7 @@ gtk_private_h_sources += \
gtkprinteroptionset.h \
gtkprinteroptionwidget.h \
gtksearchenginetracker.h
gtk_c_sources += $(gtk_os_unix_c_sources)
endif
@@ -1142,6 +1147,7 @@ gtk_extra_sources = \
MAINTAINERCLEANFILES = \
$(gtk_built_sources) \
$(gtk_dbus_built_sources) \
$(print_portal_built_sources) \
$(stamp_files)
DISTCLEANFILES =
@@ -1207,7 +1213,8 @@ CLEANFILES = $(gen_sources)
BUILT_SOURCES = \
$(gtk_built_sources) \
$(gtk_dbus_built_sources)
$(gtk_dbus_built_sources) \
$(print_portal_built_sources)
# all autogenerated files need to be generated in the srcdir,
# so old versions get remade and are not confused with newer

View File

@@ -982,12 +982,9 @@ static gboolean
gtk_about_dialog_activate_link (GtkAboutDialog *about,
const gchar *uri)
{
GdkScreen *screen;
GError *error = NULL;
screen = gtk_widget_get_screen (GTK_WIDGET (about));
if (!gtk_show_uri (screen, uri, gtk_get_current_event_time (), &error))
if (!gtk_show_uri_on_window (GTK_WINDOW (about), uri, gtk_get_current_event_time (), &error))
{
GtkWidget *dialog;

View File

@@ -190,7 +190,6 @@ gtk_application_impl_dbus_startup (GtkApplicationImpl *impl,
g_warning ("Failed to get the GNOME session proxy: %s", error->message);
g_clear_error (&error);
g_clear_object (&dbus->sm_proxy);
goto out;
}
if (!dbus->sm_proxy)
@@ -228,7 +227,7 @@ gtk_application_impl_dbus_startup (GtkApplicationImpl *impl,
if (error)
{
g_warning ("Failed to register client: %s", error->message);
g_error_free (error);
g_clear_error (&error);
g_clear_object (&dbus->sm_proxy);
goto out;
}
@@ -238,7 +237,6 @@ gtk_application_impl_dbus_startup (GtkApplicationImpl *impl,
g_debug ("Registered client at '%s'", dbus->client_path);
/* Try the GNOME client interface */
if (g_str_equal (g_dbus_proxy_get_name (dbus->sm_proxy), GNOME_DBUS_NAME))
{
bus_name = GNOME_DBUS_NAME;
@@ -259,8 +257,10 @@ gtk_application_impl_dbus_startup (GtkApplicationImpl *impl,
&error);
if (error)
{
g_warning ("Failed to connect to the GNOME client proxy: %s", error->message);
g_warning ("Failed to get client proxy: %s", error->message);
g_clear_error (&error);
g_free (dbus->client_path);
dbus->client_path = NULL;
goto out;
}
@@ -313,6 +313,23 @@ gtk_application_impl_dbus_startup (GtkApplicationImpl *impl,
"gtk-shell-shows-app-menu", FALSE,
"gtk-shell-shows-menubar", FALSE,
NULL);
if (dbus->sm_proxy == NULL)
{
dbus->inhibit_proxy = g_dbus_proxy_new_sync (dbus->session,
G_DBUS_PROXY_FLAGS_NONE,
NULL,
"org.freedesktop.portal.Desktop",
"/org/freedesktop/portal/desktop",
"org.freedesktop.portal.Inhibit",
NULL,
&error);
if (error)
{
g_warning ("Failed to get an inhibit portal proxy: %s", error->message);
g_clear_error (&error);
}
}
}
static void
@@ -438,6 +455,22 @@ gtk_application_impl_dbus_get_window_system_id (GtkApplicationImplDBus *dbus,
return GTK_APPLICATION_IMPL_DBUS_GET_CLASS (dbus)->get_window_system_id (dbus, window);
}
static int next_cookie;
typedef struct {
char *handle;
int cookie;
} InhibitHandle;
static void
inhibit_handle_free (gpointer data)
{
InhibitHandle *handle = data;
g_free (handle->handle);
g_free (handle);
}
static guint
gtk_application_impl_dbus_inhibit (GtkApplicationImpl *impl,
GtkWindow *window,
@@ -450,37 +483,84 @@ gtk_application_impl_dbus_inhibit (GtkApplicationImpl *impl,
guint cookie;
static gboolean warned = FALSE;
if (dbus->sm_proxy == NULL)
return 0;
res = g_dbus_proxy_call_sync (dbus->sm_proxy,
"Inhibit",
g_variant_new ("(s@usu)",
dbus->application_id,
window ? gtk_application_impl_dbus_get_window_system_id (dbus, window) : g_variant_new_uint32 (0),
reason,
flags),
G_DBUS_CALL_FLAGS_NONE,
G_MAXINT,
NULL,
&error);
if (error)
if (dbus->sm_proxy)
{
if (!warned)
res = g_dbus_proxy_call_sync (dbus->sm_proxy,
"Inhibit",
g_variant_new ("(s@usu)",
dbus->application_id,
window ? gtk_application_impl_dbus_get_window_system_id (dbus, window) : g_variant_new_uint32 (0),
reason,
flags),
G_DBUS_CALL_FLAGS_NONE,
G_MAXINT,
NULL,
&error);
if (res)
{
g_warning ("Calling Inhibit failed: %s", error->message);
warned = TRUE;
g_variant_get (res, "(u)", &cookie);
g_variant_unref (res);
return cookie;
}
if (error)
{
if (!warned)
{
g_warning ("Calling %s.Inhibit failed: %s",
g_dbus_proxy_get_interface_name (dbus->sm_proxy),
error->message);
warned = TRUE;
}
g_clear_error (&error);
}
}
else if (dbus->inhibit_proxy)
{
GVariantBuilder options;
g_variant_builder_init (&options, G_VARIANT_TYPE_VARDICT);
g_variant_builder_add (&options, "{sv}", "reason", g_variant_new_string (reason));
res = g_dbus_proxy_call_sync (dbus->inhibit_proxy,
"Inhibit",
g_variant_new ("(su@a{sv})",
"", /* window */
flags,
g_variant_builder_end (&options)),
G_DBUS_CALL_FLAGS_NONE,
G_MAXINT,
NULL,
&error);
if (res)
{
InhibitHandle *handle;
handle = g_new (InhibitHandle, 1);
handle->cookie = ++next_cookie;
g_variant_get (res, "(o)", &handle->handle);
g_variant_unref (res);
dbus->inhibit_handles = g_slist_prepend (dbus->inhibit_handles, handle);
return handle->cookie;
}
if (error)
{
if (!warned)
{
g_warning ("Calling %s.Inhibit failed: %s",
g_dbus_proxy_get_interface_name (dbus->inhibit_proxy),
error->message);
warned = TRUE;
}
g_clear_error (&error);
}
g_error_free (error);
return 0;
}
g_variant_get (res, "(u)", &cookie);
g_variant_unref (res);
return cookie;
return 0;
}
static void
@@ -489,16 +569,40 @@ gtk_application_impl_dbus_uninhibit (GtkApplicationImpl *impl,
{
GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) impl;
/* Application could only obtain a cookie through a session
* manager proxy, so it's valid to assert its presence here. */
g_return_if_fail (dbus->sm_proxy != NULL);
if (dbus->sm_proxy)
{
g_dbus_proxy_call (dbus->sm_proxy,
"Uninhibit",
g_variant_new ("(u)", cookie),
G_DBUS_CALL_FLAGS_NONE,
G_MAXINT,
NULL, NULL, NULL);
}
else if (dbus->inhibit_proxy)
{
GSList *l;
g_dbus_proxy_call (dbus->sm_proxy,
"Uninhibit",
g_variant_new ("(u)", cookie),
G_DBUS_CALL_FLAGS_NONE,
G_MAXINT,
NULL, NULL, NULL);
for (l = dbus->inhibit_handles; l; l = l->next)
{
InhibitHandle *handle = l->data;
if (handle->cookie == cookie)
{
g_dbus_connection_call (dbus->session,
"org.freedesktop.portal.Desktop",
handle->handle,
"org.freedesktop.portal.Request",
"Close",
g_variant_new ("()"),
G_VARIANT_TYPE_UNIT,
G_DBUS_CALL_FLAGS_NONE,
G_MAXINT,
NULL, NULL, NULL);
dbus->inhibit_handles = g_slist_remove (dbus->inhibit_handles, handle);
inhibit_handle_free (handle);
break;
}
}
}
}
static gboolean
@@ -525,7 +629,9 @@ gtk_application_impl_dbus_is_inhibited (GtkApplicationImpl *impl,
{
if (!warned)
{
g_warning ("Calling IsInhibited failed: %s", error->message);
g_warning ("Calling %s.IsInhibited failed: %s",
g_dbus_proxy_get_interface_name (dbus->sm_proxy),
error->message);
warned = TRUE;
}
g_error_free (error);
@@ -580,6 +686,8 @@ gtk_application_impl_dbus_finalize (GObject *object)
{
GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) object;
g_clear_object (&dbus->inhibit_proxy);
g_slist_free_full (dbus->inhibit_handles, inhibit_handle_free);
g_free (dbus->app_menu_path);
g_free (dbus->menubar_path);

View File

@@ -129,6 +129,10 @@ typedef struct
GDBusProxy *sm_proxy;
GDBusProxy *client_proxy;
gchar *client_path;
/* Portal support */
GDBusProxy *inhibit_proxy;
GSList *inhibit_handles;
} GtkApplicationImplDBus;
typedef struct

View File

@@ -2263,3 +2263,99 @@ gtk_file_chooser_get_do_overwrite_confirmation (GtkFileChooser *chooser)
return do_overwrite_confirmation;
}
/**
* gtk_file_chooser_add_choice:
* @chooser: a #GtkFileChooser
* @id: id for the added choice
* @label: user-visible label for the added choice
* @options: ids for the options of the choice, or %NULL for a boolean choice
* @option_labels: user-visible labels for the options, must be the same length as @options
*
* Adds a 'choice' to the file chooser. This is typically implemented
* as a combobox or, for boolean choices, as a checkbutton. You can select
* a value using gtk_file_chooser_set_choice() before the dialog is shown,
* and you can obtain the user-selected value in the ::response signal handler
* using gtk_file_chooser_get_choice().
*
* Compare gtk_file_chooser_set_extra_widget().
*
* Since: 3.22
*/
void
gtk_file_chooser_add_choice (GtkFileChooser *chooser,
const char *id,
const char *label,
const char **options,
const char **option_labels)
{
GtkFileChooserIface *iface = GTK_FILE_CHOOSER_GET_IFACE (chooser);
if (iface->add_choice)
iface->add_choice (chooser, id, label, options, option_labels);
}
/**
* gtk_file_chooser_remove_choice:
* @chooser: a #GtkFileChooser
* @id: the ID of the choice to remove
*
* Removes a 'choice' that has been added with gtk_file_chooser_add_choice().
*
* Since: 3.22
*/
void
gtk_file_chooser_remove_choice (GtkFileChooser *chooser,
const char *id)
{
GtkFileChooserIface *iface = GTK_FILE_CHOOSER_GET_IFACE (chooser);
if (iface->remove_choice)
iface->remove_choice (chooser, id);
}
/**
* gtk_file_chooser_set_choice:
* @chooser: a #GtkFileChooser
* @id: the ID of the choice to set
* @selected: the ID of the option to select
*
* Selects an option in a 'choice' that has been added with
* gtk_file_chooser_add_choice(). For a boolean choice, the
* possible options are "true" and "false".
*
* Since: 3.22
*/
void
gtk_file_chooser_set_choice (GtkFileChooser *chooser,
const char *id,
const char *option)
{
GtkFileChooserIface *iface = GTK_FILE_CHOOSER_GET_IFACE (chooser);
if (iface->set_choice)
iface->set_choice (chooser, id, option);
}
/**
* gtk_file_chooser_get_choice:
* @chooser: a #GtkFileChooser
* @id: the ID of the choice to get
*
* Gets the currently selected option in the 'choice' with the given ID.
*
* Returns: the ID of the currenly selected option
* Since: 3.22
*/
const char *
gtk_file_chooser_get_choice (GtkFileChooser *chooser,
const char *id)
{
GtkFileChooserIface *iface = GTK_FILE_CHOOSER_GET_IFACE (chooser);
if (iface->get_choice)
return iface->get_choice (chooser, id);
return NULL;
}

View File

@@ -304,6 +304,23 @@ gboolean gtk_file_chooser_remove_shortcut_folder_uri (GtkFileChooser *chooser,
GDK_AVAILABLE_IN_ALL
GSList *gtk_file_chooser_list_shortcut_folder_uris (GtkFileChooser *chooser);
GDK_AVAILABLE_IN_3_22
void gtk_file_chooser_add_choice (GtkFileChooser *chooser,
const char *id,
const char *label,
const char **options,
const char **option_labels);
GDK_AVAILABLE_IN_3_22
void gtk_file_chooser_remove_choice (GtkFileChooser *chooser,
const char *id);
GDK_AVAILABLE_IN_3_22
void gtk_file_chooser_set_choice (GtkFileChooser *chooser,
const char *id,
const char *option);
GDK_AVAILABLE_IN_3_22
const char *gtk_file_chooser_get_choice (GtkFileChooser *chooser,
const char *id);
G_END_DECLS
#endif /* __GTK_FILE_CHOOSER_H__ */

View File

@@ -51,7 +51,10 @@
* 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.
* file chooser is uses instead. When the application is running in a
* sandboxed environment without direct filesystem access (such as Flatpak),
* #GtkFileChooserNative may call the proper APIs (portals) to let the user
* choose a file and make it available to the application.
*
* 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.
@@ -170,11 +173,26 @@
*
* If any of these features are used the regular #GtkFileChooserDialog
* will be used in place of the native one.
*
* ## Portal details ## {#gtkfilechooserdialognative-portal}
*
* When the org.freedesktop.portal.FileChooser portal is available on the
* session bus, it is used to bring up an out-of-process file chooser. Depending
* on the kind of session the application is running in, this may or may not
* be a GTK+ file chooser. In this situation, the following things are not
* supported and will be silently ignored:
*
* * Extra widgets added with gtk_file_chooser_set_extra_widget().
*
* * Use of custom previews by connecting to #GtkFileChooser::update-preview.
*
* * Any #GtkFileFilter added with a custom filter.
*/
enum {
MODE_FALLBACK,
MODE_WIN32,
MODE_PORTAL,
};
enum {
@@ -285,6 +303,131 @@ gtk_file_chooser_native_set_cancel_label (GtkFileChooserNative *self,
g_object_notify_by_pspec (G_OBJECT (self), native_props[PROP_CANCEL_LABEL]);
}
static GtkFileChooserNativeChoice *
find_choice (GtkFileChooserNative *self,
const char *id)
{
GSList *l;
for (l = self->choices; l; l = l->next)
{
GtkFileChooserNativeChoice *choice = l->data;
if (strcmp (choice->id, id) == 0)
return choice;
}
return NULL;
}
static void
gtk_file_chooser_native_choice_free (GtkFileChooserNativeChoice *choice)
{
g_free (choice->id);
g_free (choice->label);
g_strfreev (choice->options);
g_strfreev (choice->option_labels);
g_free (choice->selected);
g_free (choice);
}
static void
gtk_file_chooser_native_add_choice (GtkFileChooser *chooser,
const char *id,
const char *label,
const char **options,
const char **option_labels)
{
GtkFileChooserNative *self = GTK_FILE_CHOOSER_NATIVE (chooser);
GtkFileChooserNativeChoice *choice = find_choice (self, id);
if (choice != NULL)
{
g_warning ("Choice with id %s already added to %s %p", id, G_OBJECT_TYPE_NAME (self), self);
return;
}
g_assert ((options == NULL && option_labels == NULL) ||
g_strv_length ((char **)options) == g_strv_length ((char **)option_labels));
choice = g_new0 (GtkFileChooserNativeChoice, 1);
choice->id = g_strdup (id);
choice->label = g_strdup (label);
choice->options = g_strdupv ((char **)options);
choice->option_labels = g_strdupv ((char **)option_labels);
self->choices = g_slist_prepend (self->choices, choice);
gtk_file_chooser_add_choice (GTK_FILE_CHOOSER (self->dialog),
id, label, options, option_labels);
}
static void
gtk_file_chooser_native_remove_choice (GtkFileChooser *chooser,
const char *id)
{
GtkFileChooserNative *self = GTK_FILE_CHOOSER_NATIVE (chooser);
GtkFileChooserNativeChoice *choice = find_choice (self, id);
if (choice == NULL)
{
g_warning ("No choice with id %s found in %s %p", id, G_OBJECT_TYPE_NAME (self), self);
return;
}
self->choices = g_slist_remove (self->choices, choice);
gtk_file_chooser_native_choice_free (choice);
gtk_file_chooser_remove_choice (GTK_FILE_CHOOSER (self->dialog), id);
}
static void
gtk_file_chooser_native_set_choice (GtkFileChooser *chooser,
const char *id,
const char *selected)
{
GtkFileChooserNative *self = GTK_FILE_CHOOSER_NATIVE (chooser);
GtkFileChooserNativeChoice *choice = find_choice (self, id);
if (choice == NULL)
{
g_warning ("No choice with id %s found in %s %p", id, G_OBJECT_TYPE_NAME (self), self);
return;
}
if ((choice->options && !g_strv_contains ((const char *const*)choice->options, selected)) ||
(!choice->options && !g_str_equal (selected, "true") && !g_str_equal (selected, "false")))
{
g_warning ("Not a valid option for %s: %s", id, selected);
return;
}
g_free (choice->selected);
choice->selected = g_strdup (selected);
gtk_file_chooser_set_choice (GTK_FILE_CHOOSER (self->dialog), id, selected);
}
static const char *
gtk_file_chooser_native_get_choice (GtkFileChooser *chooser,
const char *id)
{
GtkFileChooserNative *self = GTK_FILE_CHOOSER_NATIVE (chooser);
GtkFileChooserNativeChoice *choice = find_choice (self, id);
if (choice == NULL)
{
g_warning ("No choice with id %s found in %s %p", id, G_OBJECT_TYPE_NAME (self), self);
return NULL;
}
if (self->mode == MODE_FALLBACK)
return gtk_file_chooser_get_choice (GTK_FILE_CHOOSER (self->dialog), id);
return choice->selected;
}
static void
gtk_file_chooser_native_set_property (GObject *object,
guint prop_id,
@@ -348,6 +491,7 @@ gtk_file_chooser_native_finalize (GObject *object)
gtk_widget_destroy (self->dialog);
g_slist_free_full (self->custom_files, g_object_unref);
g_slist_free_full (self->choices, (GDestroyNotify)gtk_file_chooser_native_choice_free);
G_OBJECT_CLASS (gtk_file_chooser_native_parent_class)->finalize (object);
}
@@ -558,6 +702,7 @@ gtk_file_chooser_native_get_files (GtkFileChooser *chooser)
switch (self->mode)
{
case MODE_PORTAL:
case MODE_WIN32:
return g_slist_copy_deep (self->custom_files, (GCopyFunc)g_object_ref, NULL);
@@ -579,6 +724,10 @@ gtk_file_chooser_native_show (GtkNativeDialog *native)
self->mode = MODE_WIN32;
#endif
if (self->mode == MODE_FALLBACK &&
gtk_file_chooser_native_portal_show (self))
self->mode = MODE_PORTAL;
if (self->mode == MODE_FALLBACK)
show_dialog (self);
}
@@ -598,6 +747,9 @@ gtk_file_chooser_native_hide (GtkNativeDialog *native)
gtk_file_chooser_native_win32_hide (self);
#endif
break;
case MODE_PORTAL:
gtk_file_chooser_native_portal_hide (self);
break;
}
}
@@ -653,4 +805,8 @@ _gtk_file_chooser_native_iface_init (GtkFileChooserIface *iface)
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;
iface->add_choice = gtk_file_chooser_native_add_choice;
iface->remove_choice = gtk_file_chooser_native_remove_choice;
iface->set_choice = gtk_file_chooser_native_set_choice;
iface->get_choice = gtk_file_chooser_native_get_choice;
}

View File

@@ -0,0 +1,416 @@
/* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */
/* GTK - The GIMP Toolkit
* gtkfilechoosernativeportal.c: Portal 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 "gtkmain.h"
#include "gtkinvisible.h"
#include "gtkfilechooserentry.h"
#include "gtkfilefilterprivate.h"
#ifdef GDK_WINDOWING_X11
#include "x11/gdkx.h"
#endif
typedef struct {
GtkFileChooserNative *self;
GtkWidget *grab_widget;
GDBusConnection *connection;
char *portal_handle;
guint portal_response_signal_id;
gboolean modal;
gboolean hidden;
} FilechooserPortalData;
static void
filechooser_portal_data_free (FilechooserPortalData *data)
{
if (data->portal_response_signal_id != 0)
g_dbus_connection_signal_unsubscribe (data->connection,
data->portal_response_signal_id);
g_object_unref (data->connection);
if (data->grab_widget)
{
gtk_grab_remove (data->grab_widget);
gtk_widget_destroy (data->grab_widget);
}
g_clear_object (&data->self);
g_free (data->portal_handle);
g_free (data);
}
static void
response_cb (GDBusConnection *connection,
const gchar *sender_name,
const gchar *object_path,
const gchar *interface_name,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
GtkFileChooserNative *self = user_data;
FilechooserPortalData *data = self->mode_data;
guint32 portal_response;
int gtk_response;
const char **uris;
int i;
GVariant *response_data;
g_autoptr (GVariant) choices = NULL;
g_variant_get (parameters, "(u@a{sv})", &portal_response, &response_data);
g_variant_lookup (response_data, "uris", "^a&s", &uris);
choices = g_variant_lookup_value (response_data, "choices", G_VARIANT_TYPE ("a(ss)"));
if (choices)
for (i = 0; i < g_variant_n_children (choices); i++)
{
const char *id;
const char *selected;
g_variant_get_child (choices, i, "(&s&s)", &id, &selected);
gtk_file_chooser_set_choice (GTK_FILE_CHOOSER (self), id, selected);
}
g_slist_free_full (self->custom_files, g_object_unref);
self->custom_files = NULL;
for (i = 0; uris[i]; i++)
self->custom_files = g_slist_prepend (self->custom_files, g_file_new_for_uri (uris[i]));
switch (portal_response)
{
case 0:
gtk_response = GTK_RESPONSE_OK;
break;
case 1:
gtk_response = GTK_RESPONSE_CANCEL;
break;
case 2:
default:
gtk_response = GTK_RESPONSE_DELETE_EVENT;
break;
}
filechooser_portal_data_free (data);
self->mode_data = NULL;
_gtk_native_dialog_emit_response (GTK_NATIVE_DIALOG (self), gtk_response);
}
static void
send_close (FilechooserPortalData *data)
{
GDBusMessage *message;
GError *error = NULL;
message = g_dbus_message_new_method_call ("org.freedesktop.portal.Desktop",
"/org/freedesktop/portal/desktop",
"org.freedesktop.portal.FileChooser",
"Close");
g_dbus_message_set_body (message,
g_variant_new ("(o)", data->portal_handle));
if (!g_dbus_connection_send_message (data->connection,
message,
G_DBUS_SEND_MESSAGE_FLAGS_NONE,
NULL, &error))
{
g_warning ("unable to send FileChooser Close message: %s", error->message);
g_error_free (error);
}
g_object_unref (message);
}
static void
open_file_msg_cb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
FilechooserPortalData *data = user_data;
GtkFileChooserNative *self = data->self;
GDBusMessage *reply;
GError *error = NULL;
reply = g_dbus_connection_send_message_with_reply_finish (data->connection, res, &error);
if (reply && g_dbus_message_to_gerror (reply, &error))
g_clear_object (&reply);
if (reply == NULL)
{
if (!data->hidden)
_gtk_native_dialog_emit_response (GTK_NATIVE_DIALOG (self), GTK_RESPONSE_DELETE_EVENT);
g_warning ("Can't open portal file chooser: %s", error->message);
g_error_free (error);
filechooser_portal_data_free (data);
self->mode_data = NULL;
return;
}
g_variant_get_child (g_dbus_message_get_body (reply), 0, "o",
&data->portal_handle);
if (data->hidden)
{
/* The dialog was hidden before we got the handle, close it now */
send_close (data);
filechooser_portal_data_free (data);
self->mode_data = NULL;
}
else
{
data->portal_response_signal_id =
g_dbus_connection_signal_subscribe (data->connection,
"org.freedesktop.portal.Desktop",
"org.freedesktop.portal.Request",
"Response",
data->portal_handle,
NULL,
G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
response_cb,
self, NULL);
}
g_object_unref (reply);
}
static GVariant *
get_filters (GtkFileChooser *self)
{
GSList *list, *l;
GVariantBuilder builder;
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(sa(us))"));
list = gtk_file_chooser_list_filters (self);
for (l = list; l; l = l->next)
{
GtkFileFilter *filter = l->data;
g_variant_builder_add (&builder, "@(sa(us))", gtk_file_filter_to_gvariant (filter));
}
g_slist_free (list);
return g_variant_builder_end (&builder);
}
static GVariant *
gtk_file_chooser_native_choice_to_variant (GtkFileChooserNativeChoice *choice)
{
GVariantBuilder choices;
int i;
g_variant_builder_init (&choices, G_VARIANT_TYPE ("a(ss)"));
if (choice->options)
{
for (i = 0; choice->options[i]; i++)
g_variant_builder_add (&choices, "(&s&s)", choice->options[i], choice->option_labels[i]);
}
return g_variant_new ("(&s&s@a(ss)&s)",
choice->id,
choice->label,
g_variant_builder_end (&choices),
choice->selected ? choice->selected : "");
}
static GVariant *
serialize_choices (GtkFileChooserNative *self)
{
GVariantBuilder builder;
GSList *l;
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ssa(ss)s)"));
for (l = self->choices; l; l = l->next)
{
GtkFileChooserNativeChoice *choice = l->data;
g_variant_builder_add (&builder, "@(ssa(ss)s)",
gtk_file_chooser_native_choice_to_variant (choice));
}
return g_variant_builder_end (&builder);
}
gboolean
gtk_file_chooser_native_portal_show (GtkFileChooserNative *self)
{
FilechooserPortalData *data;
GtkWindow *transient_for;
GDBusConnection *connection;
char *parent_window_str;
GDBusMessage *message;
GVariantBuilder opt_builder;
GtkFileChooserAction action;
gboolean multiple;
const char *method_name;
if (!gtk_should_use_portal ())
return FALSE;
connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
if (connection == NULL)
return FALSE;
action = gtk_file_chooser_get_action (GTK_FILE_CHOOSER (self));
multiple = gtk_file_chooser_get_select_multiple (GTK_FILE_CHOOSER (self));
if (action == GTK_FILE_CHOOSER_ACTION_OPEN)
method_name = "OpenFile";
else if (action == GTK_FILE_CHOOSER_ACTION_SAVE)
method_name = "SaveFile";
else
{
g_warning ("GTK_FILE_CHOOSER_ACTION_%s is not supported by GtkFileChooserNativePortal", action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ? "SELECT_FOLDER" : "CREATE_FOLDER");
return FALSE;
}
data = g_new0 (FilechooserPortalData, 1);
data->self = g_object_ref (self);
data->connection = connection;
message = g_dbus_message_new_method_call ("org.freedesktop.portal.Desktop",
"/org/freedesktop/portal/desktop",
"org.freedesktop.portal.FileChooser",
method_name);
parent_window_str = NULL;
transient_for = gtk_native_dialog_get_transient_for (GTK_NATIVE_DIALOG (self));
if (transient_for != NULL && gtk_widget_is_visible (GTK_WIDGET (transient_for)))
{
GdkWindow *window = gtk_widget_get_window (GTK_WIDGET (transient_for));
#ifdef GDK_WINDOWING_X11
if (GDK_IS_X11_WINDOW(window))
parent_window_str = g_strdup_printf ("x11:%x", (guint32)gdk_x11_window_get_xid (window));
#endif
}
if (gtk_native_dialog_get_modal (GTK_NATIVE_DIALOG (self)))
data->modal = TRUE;
if (data->modal && transient_for != NULL)
{
data->grab_widget = gtk_invisible_new_for_screen (gtk_widget_get_screen (GTK_WIDGET (transient_for)));
gtk_grab_add (GTK_WIDGET (data->grab_widget));
}
g_variant_builder_init (&opt_builder, G_VARIANT_TYPE_VARDICT);
g_variant_builder_add (&opt_builder, "{sv}", "multiple",
g_variant_new_boolean (multiple));
if (self->accept_label)
g_variant_builder_add (&opt_builder, "{sv}", "accept_label",
g_variant_new_string (self->accept_label));
if (self->cancel_label)
g_variant_builder_add (&opt_builder, "{sv}", "cancel_label",
g_variant_new_string (self->cancel_label));
g_variant_builder_add (&opt_builder, "{sv}", "modal",
g_variant_new_boolean (data->modal));
g_variant_builder_add (&opt_builder, "{sv}", "filters", get_filters (GTK_FILE_CHOOSER (self)));
if (GTK_FILE_CHOOSER_NATIVE (self)->current_name)
g_variant_builder_add (&opt_builder, "{sv}", "current_name",
g_variant_new_string (GTK_FILE_CHOOSER_NATIVE (self)->current_name));
if (GTK_FILE_CHOOSER_NATIVE (self)->current_folder)
{
gchar *path;
path = g_file_get_path (GTK_FILE_CHOOSER_NATIVE (self)->current_folder);
g_variant_builder_add (&opt_builder, "{sv}", "current_folder",
g_variant_new_bytestring (path));
g_free (path);
}
if (GTK_FILE_CHOOSER_NATIVE (self)->current_file)
{
gchar *path;
path = g_file_get_path (GTK_FILE_CHOOSER_NATIVE (self)->current_file);
g_variant_builder_add (&opt_builder, "{sv}", "current_file",
g_variant_new_bytestring (path));
g_free (path);
}
if (GTK_FILE_CHOOSER_NATIVE (self)->choices)
g_variant_builder_add (&opt_builder, "{sv}", "choices",
serialize_choices (GTK_FILE_CHOOSER_NATIVE (self)));
g_dbus_message_set_body (message,
g_variant_new ("(ss@a{sv})",
parent_window_str ? parent_window_str : "",
gtk_native_dialog_get_title (GTK_NATIVE_DIALOG (self)),
g_variant_builder_end (&opt_builder)));
g_free (parent_window_str);
g_dbus_connection_send_message_with_reply (data->connection,
message,
G_DBUS_SEND_MESSAGE_FLAGS_NONE,
G_MAXINT,
NULL,
NULL,
open_file_msg_cb,
data);
g_object_unref (message);
self->mode_data = data;
return TRUE;
}
void
gtk_file_chooser_native_portal_hide (GtkFileChooserNative *self)
{
FilechooserPortalData *data = self->mode_data;
/* This is always set while dialog visible */
g_assert (data != NULL);
data->hidden = TRUE;
if (data->portal_handle)
{
send_close (data);
filechooser_portal_data_free (data);
}
self->mode_data = NULL;
}

View File

@@ -23,6 +23,14 @@
G_BEGIN_DECLS
typedef struct {
char *id;
char *label;
char **options;
char **option_labels;
char *selected;
} GtkFileChooserNativeChoice;
struct _GtkFileChooserNative
{
GtkNativeDialog parent_instance;
@@ -36,6 +44,7 @@ struct _GtkFileChooserNative
GFile *current_folder;
GFile *current_file;
char *current_name;
GSList *choices;
/* Fallback mode */
GtkWidget *dialog;
@@ -48,6 +57,9 @@ struct _GtkFileChooserNative
gboolean gtk_file_chooser_native_win32_show (GtkFileChooserNative *self);
void gtk_file_chooser_native_win32_hide (GtkFileChooserNative *self);
gboolean gtk_file_chooser_native_portal_show (GtkFileChooserNative *self);
void gtk_file_chooser_native_portal_hide (GtkFileChooserNative *self);
G_END_DECLS
#endif /* __GTK_FILE_CHOOSER_NATIVE_PRIVATE_H__ */

View File

@@ -87,7 +87,7 @@ struct _GtkFileChooserIface
GFile *file,
GError **error);
GSList * (*list_shortcut_folders) (GtkFileChooser *chooser);
/* Signals
*/
void (*current_folder_changed) (GtkFileChooser *chooser);
@@ -95,6 +95,20 @@ struct _GtkFileChooserIface
void (*update_preview) (GtkFileChooser *chooser);
void (*file_activated) (GtkFileChooser *chooser);
GtkFileChooserConfirmation (*confirm_overwrite) (GtkFileChooser *chooser);
/* 3.22 additions */
void (*add_choice) (GtkFileChooser *chooser,
const char *id,
const char *label,
const char **options,
const char **option_labels);
void (*remove_choice) (GtkFileChooser *chooser,
const char *id);
void (*set_choice) (GtkFileChooser *chooser,
const char *id,
const char *option);
const char * (*get_choice) (GtkFileChooser *chooser,
const char *id);
};
GtkFileSystem *_gtk_file_chooser_get_file_system (GtkFileChooser *chooser);

View File

@@ -68,6 +68,19 @@ static void delegate_file_activated (GtkFileChooser *choose
static GtkFileChooserConfirmation delegate_confirm_overwrite (GtkFileChooser *chooser,
gpointer data);
static void delegate_add_choice (GtkFileChooser *chooser,
const char *id,
const char *label,
const char **options,
const char **option_labels);
static void delegate_remove_choice (GtkFileChooser *chooser,
const char *id);
static void delegate_set_choice (GtkFileChooser *chooser,
const char *id,
const char *option);
static const char * delegate_get_choice (GtkFileChooser *chooser,
const char *id);
/**
* _gtk_file_chooser_install_properties:
@@ -149,6 +162,10 @@ _gtk_file_chooser_delegate_iface_init (GtkFileChooserIface *iface)
iface->add_shortcut_folder = delegate_add_shortcut_folder;
iface->remove_shortcut_folder = delegate_remove_shortcut_folder;
iface->list_shortcut_folders = delegate_list_shortcut_folders;
iface->add_choice = delegate_add_choice;
iface->remove_choice = delegate_remove_choice;
iface->set_choice = delegate_set_choice;
iface->get_choice = delegate_get_choice;
}
/**
@@ -499,3 +516,35 @@ _gtk_file_chooser_label_for_file (GFile *file)
return label;
}
static void
delegate_add_choice (GtkFileChooser *chooser,
const char *id,
const char *label,
const char **options,
const char **option_labels)
{
gtk_file_chooser_add_choice (get_delegate (chooser),
id, label, options, option_labels);
}
static void
delegate_remove_choice (GtkFileChooser *chooser,
const char *id)
{
gtk_file_chooser_remove_choice (get_delegate (chooser), id);
}
static void
delegate_set_choice (GtkFileChooser *chooser,
const char *id,
const char *option)
{
gtk_file_chooser_set_choice (get_delegate (chooser), id, option);
}
static const char *
delegate_get_choice (GtkFileChooser *chooser,
const char *id)
{
return gtk_file_chooser_get_choice (get_delegate (chooser), id);
}

View File

@@ -66,6 +66,7 @@
#include "gtktreeprivate.h"
#include "gtktreeselection.h"
#include "gtkbox.h"
#include "gtkcheckbutton.h"
#include "gtkwindowgroup.h"
#include "gtkintl.h"
#include "gtkshow.h"
@@ -289,6 +290,9 @@ struct _GtkFileChooserWidgetPrivate {
GtkWidget *external_entry;
GtkWidget *choice_box;
GHashTable *choices;
/* Handles */
GCancellable *file_list_drag_data_received_cancellable;
GCancellable *update_current_folder_cancellable;
@@ -507,6 +511,20 @@ static void gtk_file_chooser_widget_get_default_size (GtkFileCho
static gboolean gtk_file_chooser_widget_should_respond (GtkFileChooserEmbed *chooser_embed);
static void gtk_file_chooser_widget_initial_focus (GtkFileChooserEmbed *chooser_embed);
static void gtk_file_chooser_widget_add_choice (GtkFileChooser *chooser,
const char *id,
const char *label,
const char **options,
const char **option_labels);
static void gtk_file_chooser_widget_remove_choice (GtkFileChooser *chooser,
const char *id);
static void gtk_file_chooser_widget_set_choice (GtkFileChooser *chooser,
const char *id,
const char *option);
static const char *gtk_file_chooser_widget_get_choice (GtkFileChooser *chooser,
const char *id);
static void add_selection_to_recent_list (GtkFileChooserWidget *impl);
static void location_popup_handler (GtkFileChooserWidget *impl,
@@ -625,6 +643,10 @@ gtk_file_chooser_widget_iface_init (GtkFileChooserIface *iface)
iface->add_shortcut_folder = gtk_file_chooser_widget_add_shortcut_folder;
iface->remove_shortcut_folder = gtk_file_chooser_widget_remove_shortcut_folder;
iface->list_shortcut_folders = gtk_file_chooser_widget_list_shortcut_folders;
iface->add_choice = gtk_file_chooser_widget_add_choice;
iface->remove_choice = gtk_file_chooser_widget_remove_choice;
iface->set_choice = gtk_file_chooser_widget_set_choice;
iface->get_choice = gtk_file_chooser_widget_get_choice;
}
static void
@@ -660,6 +682,9 @@ gtk_file_chooser_widget_finalize (GObject *object)
GtkFileChooserWidget *impl = GTK_FILE_CHOOSER_WIDGET (object);
GtkFileChooserWidgetPrivate *priv = impl->priv;
if (priv->choices)
g_hash_table_unref (priv->choices);
if (priv->location_changed_id > 0)
g_source_remove (priv->location_changed_id);
@@ -8676,3 +8701,124 @@ gtk_file_chooser_widget_new (GtkFileChooserAction action)
"action", action,
NULL);
}
static void
gtk_file_chooser_widget_add_choice (GtkFileChooser *chooser,
const char *id,
const char *label,
const char **options,
const char **option_labels)
{
GtkFileChooserWidget *impl = GTK_FILE_CHOOSER_WIDGET (chooser);
GtkFileChooserWidgetPrivate *priv = impl->priv;
GtkWidget *widget;
if (priv->choices == NULL)
{
priv->choices = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
priv->choice_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
set_extra_widget (impl, priv->choice_box);
}
else if (g_hash_table_lookup (priv->choices, id))
{
g_warning ("Duplicate choice %s", id);
return;
}
if (options)
{
GtkWidget *box;
GtkWidget *combo;
int i;
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
gtk_container_add (GTK_CONTAINER (box), gtk_label_new (label));
combo = gtk_combo_box_text_new ();
g_hash_table_insert (priv->choices, g_strdup (id), combo);
gtk_container_add (GTK_CONTAINER (box), combo);
for (i = 0; options[i]; i++)
gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo),
options[i], option_labels[i]);
widget = box;
}
else
{
GtkWidget *check;
check = gtk_check_button_new_with_label (label);
g_hash_table_insert (priv->choices, g_strdup (id), check);
widget = check;
}
gtk_widget_show_all (widget);
gtk_container_add (GTK_CONTAINER (priv->choice_box), widget);
}
static void
gtk_file_chooser_widget_remove_choice (GtkFileChooser *chooser,
const char *id)
{
GtkFileChooserWidget *impl = GTK_FILE_CHOOSER_WIDGET (chooser);
GtkFileChooserWidgetPrivate *priv = impl->priv;
GtkWidget *widget;
if (priv->choices == NULL)
return;
widget = (GtkWidget *)g_hash_table_lookup (priv->choices, id);
g_hash_table_remove (priv->choices, id);
gtk_container_remove (GTK_CONTAINER (priv->choice_box), widget);
if (g_hash_table_size (priv->choices) == 0)
{
set_extra_widget (impl, NULL);
g_hash_table_unref (priv->choices);
priv->choices = NULL;
priv->choice_box = NULL;
}
}
static void
gtk_file_chooser_widget_set_choice (GtkFileChooser *chooser,
const char *id,
const char *option)
{
GtkFileChooserWidget *impl = GTK_FILE_CHOOSER_WIDGET (chooser);
GtkFileChooserWidgetPrivate *priv = impl->priv;
GtkWidget *widget;
if (priv->choices == NULL)
return;
widget = (GtkWidget *)g_hash_table_lookup (priv->choices, id);
if (GTK_IS_COMBO_BOX (widget))
gtk_combo_box_set_active_id (GTK_COMBO_BOX (widget), option);
else if (GTK_IS_TOGGLE_BUTTON (widget))
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), g_str_equal (option, "true"));
}
static const char *
gtk_file_chooser_widget_get_choice (GtkFileChooser *chooser,
const char *id)
{
GtkFileChooserWidget *impl = GTK_FILE_CHOOSER_WIDGET (chooser);
GtkFileChooserWidgetPrivate *priv = impl->priv;
GtkWidget *widget;
if (priv->choices == NULL)
return NULL;
widget = (GtkWidget *)g_hash_table_lookup (priv->choices, id);
if (GTK_IS_COMBO_BOX (widget))
return gtk_combo_box_get_active_id (GTK_COMBO_BOX (widget));
else if (GTK_IS_TOGGLE_BUTTON (widget))
return gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)) ? "true" : "false";
return NULL;
}

View File

@@ -718,3 +718,103 @@ gtk_file_filter_filter (GtkFileFilter *filter,
return FALSE;
}
/**
* gtk_file_filter_to_gvariant:
* @settings: a #GtkFileFilter
*
* Serialize a file filter to an a{sv} variant.
*
* Returns: (transfer none): a new, floating, #GVariant
*
* Since: 3.22
*/
GVariant *
gtk_file_filter_to_gvariant (GtkFileFilter *filter)
{
GVariantBuilder builder;
GSList *l;
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(us)"));
for (l = filter->rules; l; l = l->next)
{
FilterRule *rule = l->data;
switch (rule->type)
{
case FILTER_RULE_PATTERN:
g_variant_builder_add (&builder, "(us)", 0, rule->u.pattern);
break;
case FILTER_RULE_MIME_TYPE:
g_variant_builder_add (&builder, "(us)", 1, rule->u.mime_type);
break;
case FILTER_RULE_PIXBUF_FORMATS:
{
GSList *f;
for (f = rule->u.pixbuf_formats; f; f = f->next)
{
GdkPixbufFormat *fmt = f->data;
gchar **mime_types;
int i;
mime_types = gdk_pixbuf_format_get_mime_types (fmt);
for (i = 0; mime_types[i]; i++)
g_variant_builder_add (&builder, "(us)", 1, mime_types[i]);
g_strfreev (mime_types);
}
}
break;
case FILTER_RULE_CUSTOM:
default:
break;
}
}
return g_variant_new ("(s@a(us))", filter->name, g_variant_builder_end (&builder));
}
/**
* gtk_file_filter_new_from_gvariant:
* @variant: an a{sv} #GVariant
*
* Deserialize a file filter from an a{sv} variant in
* the format produced by gtk_file_filter_to_gvariant().
*
* Returns: (transfer full): a new #GtkFileFilter object
*
* Since: 3.22
*/
GtkFileFilter *
gtk_file_filter_new_from_gvariant (GVariant *variant)
{
GtkFileFilter *filter;
GVariantIter *iter;
const char *name;
int type;
char *tmp;
filter = gtk_file_filter_new ();
g_variant_get (variant, "(&sa(us))", &name, &iter);
gtk_file_filter_set_name (filter, name);
while (g_variant_iter_next (iter, "(u&s)", &type, &tmp))
{
switch (type)
{
case 0:
gtk_file_filter_add_pattern (filter, tmp);
break;
case 1:
gtk_file_filter_add_mime_type (filter, tmp);
break;
default:
break;
}
}
g_variant_iter_free (iter);
return filter;
}

View File

@@ -122,6 +122,11 @@ GDK_AVAILABLE_IN_ALL
gboolean gtk_file_filter_filter (GtkFileFilter *filter,
const GtkFileFilterInfo *filter_info);
GDK_AVAILABLE_IN_3_22
GVariant *gtk_file_filter_to_gvariant (GtkFileFilter *filter);
GDK_AVAILABLE_IN_3_22
GtkFileFilter *gtk_file_filter_new_from_gvariant (GVariant *variant);
G_END_DECLS
#endif /* __GTK_FILE_FILTER_H__ */

View File

@@ -837,3 +837,86 @@ gtk_page_setup_to_key_file (GtkPageSetup *setup,
"Orientation", orientation);
g_free (orientation);
}
/**
* gtk_page_setup_to_gvariant:
* @setup: a #GtkPageSetup
*
* Serialize page setup to an a{sv} variant.
*
* Return: (transfer none): a new, floating, #GVariant
*
* Since: 3.22
*/
GVariant *
gtk_page_setup_to_gvariant (GtkPageSetup *setup)
{
GtkPaperSize *paper_size;
GVariant *variant;
int i;
GVariantBuilder builder;
char *orientation;
g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
paper_size = gtk_page_setup_get_paper_size (setup);
variant = g_variant_ref_sink (gtk_paper_size_to_gvariant (paper_size));
for (i = 0; i < g_variant_n_children (variant); i++)
g_variant_builder_add_value (&builder, g_variant_get_child_value (variant, i));
g_variant_unref (variant);
g_variant_builder_add (&builder, "{sv}", "MarginTop", g_variant_new_double (gtk_page_setup_get_top_margin (setup, GTK_UNIT_MM)));
g_variant_builder_add (&builder, "{sv}", "MarginBottom", g_variant_new_double (gtk_page_setup_get_bottom_margin (setup, GTK_UNIT_MM)));
g_variant_builder_add (&builder, "{sv}", "MarginLeft", g_variant_new_double (gtk_page_setup_get_left_margin (setup, GTK_UNIT_MM)));
g_variant_builder_add (&builder, "{sv}", "MarginRight", g_variant_new_double (gtk_page_setup_get_right_margin (setup, GTK_UNIT_MM)));
orientation = enum_to_string (GTK_TYPE_PAGE_ORIENTATION,
gtk_page_setup_get_orientation (setup));
g_variant_builder_add (&builder, "{sv}", "Orientation", g_variant_new_take_string (orientation));
return g_variant_builder_end (&builder);
}
/**
* gtk_page_setup_new_from_gvariant:
* @variant: an a{sv} #GVariant
*
* Desrialize a page setup from an a{sv} variant in
* the format produced by gtk_page_setup_to_gvariant().
*
* Returns: (transfer full): a new #GtkPageSetup object
*
* Since: 3.22
*/
GtkPageSetup *
gtk_page_setup_new_from_gvariant (GVariant *variant)
{
GtkPageSetup *setup;
const char *orientation;
gdouble margin;
GtkPaperSize *paper_size;
g_return_val_if_fail (g_variant_is_of_type (variant, G_VARIANT_TYPE_VARDICT), NULL);
setup = gtk_page_setup_new ();
paper_size = gtk_paper_size_new_from_gvariant (variant);
gtk_page_setup_set_paper_size (setup, paper_size);
gtk_paper_size_free (paper_size);
if (g_variant_lookup (variant, "MarginTop", "d", &margin))
gtk_page_setup_set_top_margin (setup, margin, GTK_UNIT_MM);
if (g_variant_lookup (variant, "MarginBottom", "d", &margin))
gtk_page_setup_set_bottom_margin (setup, margin, GTK_UNIT_MM);
if (g_variant_lookup (variant, "MarginLeft", "d", &margin))
gtk_page_setup_set_left_margin (setup, margin, GTK_UNIT_MM);
if (g_variant_lookup (variant, "MarginRight", "d", &margin))
gtk_page_setup_set_right_margin (setup, margin, GTK_UNIT_MM);
if (g_variant_lookup (variant, "Orientation", "&s", &orientation))
gtk_page_setup_set_orientation (setup, string_to_enum (GTK_TYPE_PAGE_ORIENTATION,
orientation));
return setup;
}

View File

@@ -127,6 +127,11 @@ void gtk_page_setup_to_key_file (GtkPageSetup *setup,
GKeyFile *key_file,
const gchar *group_name);
GDK_AVAILABLE_IN_3_22
GVariant *gtk_page_setup_to_gvariant (GtkPageSetup *setup);
GDK_AVAILABLE_IN_3_22
GtkPageSetup *gtk_page_setup_new_from_gvariant (GVariant *variant);
G_END_DECLS
#endif /* __GTK_PAGE_SETUP_H__ */

View File

@@ -1078,3 +1078,90 @@ gtk_paper_size_to_key_file (GtkPaperSize *size,
g_key_file_set_double (key_file, group_name,
"Height", gtk_paper_size_get_height (size, GTK_UNIT_MM));
}
/**
* gtk_paper_size_to_gvariant:
* @paper_size: a #GtkPaperSize
*
* Serialize a paper size to an a{sv} variant.
*
* Returns: (transfer none): a new, floating, #GVariant
*
* Since: 3.22
*/
GVariant *
gtk_paper_size_to_gvariant (GtkPaperSize *paper_size)
{
const char *name;
const char *ppd_name;
const char *display_name;
GVariantBuilder builder;
g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
name = gtk_paper_size_get_name (paper_size);
ppd_name = gtk_paper_size_get_ppd_name (paper_size);
display_name = gtk_paper_size_get_display_name (paper_size);
if (ppd_name != NULL)
g_variant_builder_add (&builder, "{sv}", "PPDName", g_variant_new_string (ppd_name));
else
g_variant_builder_add (&builder, "{sv}", "Name", g_variant_new_string (name));
if (display_name != NULL)
g_variant_builder_add (&builder, "{sv}", "DisplayName", g_variant_new_string (display_name));
g_variant_builder_add (&builder, "{sv}", "Width", g_variant_new_double (gtk_paper_size_get_width (paper_size, GTK_UNIT_MM)));
g_variant_builder_add (&builder, "{sv}", "Height", g_variant_new_double (gtk_paper_size_get_height (paper_size, GTK_UNIT_MM)));
return g_variant_builder_end (&builder);
}
/**
* gtk_paper_size_new_from_gvariant:
* @variant: an a{sv} #GVariant
*
* Deserialize a paper size from an a{sv} variant in
* the format produced by gtk_paper_size_to_gvariant().
*
* Returns: (transfer full): a new #GtkPaperSize object
*
* Since: 3.22
*/
GtkPaperSize *
gtk_paper_size_new_from_gvariant (GVariant *variant)
{
GtkPaperSize *paper_size;
const char *name;
const char *ppd_name;
const char *display_name;
gdouble width, height;
g_return_val_if_fail (g_variant_is_of_type (variant, G_VARIANT_TYPE_VARDICT), NULL);
if (!g_variant_lookup (variant, "Width", "d", &width) ||
!g_variant_lookup (variant, "Height", "d", &height))
return NULL;
if (!g_variant_lookup (variant, "Name", "&s", &name))
name = NULL;
if (!g_variant_lookup (variant, "PPDName", "&s", &ppd_name))
ppd_name = NULL;
if (!g_variant_lookup (variant, "DisplayName", "&s", &display_name))
display_name = name;
if (ppd_name != NULL)
paper_size = gtk_paper_size_new_from_ppd (ppd_name,
display_name,
_gtk_print_convert_from_mm (width, GTK_UNIT_POINTS),
_gtk_print_convert_from_mm (height, GTK_UNIT_POINTS));
else if (name != NULL)
paper_size = gtk_paper_size_new_custom (name, display_name,
width, height, GTK_UNIT_MM);
else
paper_size = NULL;
return paper_size;
}

View File

@@ -164,6 +164,11 @@ void gtk_paper_size_to_key_file (GtkPaperSize *size,
GKeyFile *key_file,
const gchar *group_name);
GDK_AVAILABLE_IN_3_22
GtkPaperSize *gtk_paper_size_new_from_gvariant (GVariant *variant);
GDK_AVAILABLE_IN_3_22
GVariant *gtk_paper_size_to_gvariant (GtkPaperSize *size);
G_END_DECLS
#endif /* __GTK_PAPER_SIZE_H__ */

View File

@@ -443,6 +443,41 @@ gtk_print_job_set_source_file (GtkPrintJob *job,
return TRUE;
}
/**
* gtk_print_job_set_source_fd:
* @job: a #GtkPrintJob
* @fd: a file descriptor
* @error: return location for errors
*
* Make the #GtkPrintJob send an existing document to the
* printing system. The file can be in any format understood
* by the platforms printing system (typically PostScript,
* but on many platforms PDF may work too). See
* gtk_printer_accepts_pdf() and gtk_printer_accepts_ps().
*
* This is similar to gtk_print_job_set_source_file(),
* but takes expects an open file descriptor for the file,
* instead of a filename.
*
* Returns: %FALSE if an error occurred
*
* Since: 3.22
*/
gboolean
gtk_print_job_set_source_fd (GtkPrintJob *job,
int fd,
GError **error)
{
g_return_val_if_fail (GTK_IS_PRINT_JOB (job), FALSE);
g_return_val_if_fail (fd >= 0, FALSE);
job->priv->spool_io = g_io_channel_unix_new (fd);
if (g_io_channel_set_encoding (job->priv->spool_io, NULL, error) != G_IO_STATUS_NORMAL)
return FALSE;
return TRUE;
}
/**
* gtk_print_job_get_surface:
* @job: a #GtkPrintJob

View File

@@ -95,6 +95,10 @@ GDK_AVAILABLE_IN_ALL
gboolean gtk_print_job_set_source_file (GtkPrintJob *job,
const gchar *filename,
GError **error);
GDK_AVAILABLE_IN_3_22
gboolean gtk_print_job_set_source_fd (GtkPrintJob *job,
int fd,
GError **error);
GDK_AVAILABLE_IN_ALL
cairo_surface_t *gtk_print_job_get_surface (GtkPrintJob *job,
GError **error);

View File

@@ -0,0 +1,626 @@
/* GTK - The GIMP Toolkit
* gtkprintoperation-portal.c: Print Operation Details for sandboxed apps
* Copyright (C) 2016, 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 <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <cairo-pdf.h>
#include <cairo-ps.h>
#include <gio/gunixfdlist.h>
#include "gtkprintoperation-private.h"
#include "gtkprintoperation-portal.h"
#include "gtkprintsettings.h"
#include "gtkpagesetup.h"
#include "gtkprintbackend.h"
#include "gtkshow.h"
#include "gtkintl.h"
#ifdef GDK_WINDOWING_X11
#include <gdk/gdkx.h>
#endif
typedef struct {
GtkPrintOperation *op;
GDBusProxy *proxy;
guint response_signal_id;
gboolean do_print;
GtkPrintOperationResult result;
GtkPrintOperationPrintFunc print_cb;
GtkWindow *parent;
GMainLoop *loop;
guint32 token;
GDestroyNotify destroy;
} PortalData;
static void
portal_data_free (gpointer data)
{
PortalData *portal = data;
g_object_unref (portal->op);
g_object_unref (portal->proxy);
if (portal->loop)
g_main_loop_unref (portal->loop);
g_free (portal);
}
typedef struct {
GDBusProxy *proxy;
GtkPrintJob *job;
guint32 token;
cairo_surface_t *surface;
GMainLoop *loop;
gboolean file_written;
} GtkPrintOperationPortal;
static void
op_portal_free (GtkPrintOperationPortal *op_portal)
{
g_clear_object (&op_portal->proxy);
g_clear_object (&op_portal->job);
if (op_portal->loop)
g_main_loop_unref (op_portal->loop);
g_free (op_portal);
}
static void
portal_start_page (GtkPrintOperation *op,
GtkPrintContext *print_context,
GtkPageSetup *page_setup)
{
GtkPrintOperationPortal *op_portal = op->priv->platform_data;
GtkPaperSize *paper_size;
cairo_surface_type_t type;
gdouble w, h;
paper_size = gtk_page_setup_get_paper_size (page_setup);
w = gtk_paper_size_get_width (paper_size, GTK_UNIT_POINTS);
h = gtk_paper_size_get_height (paper_size, GTK_UNIT_POINTS);
type = cairo_surface_get_type (op_portal->surface);
if ((op->priv->manual_number_up < 2) ||
(op->priv->page_position % op->priv->manual_number_up == 0))
{
if (type == CAIRO_SURFACE_TYPE_PS)
{
cairo_ps_surface_set_size (op_portal->surface, w, h);
cairo_ps_surface_dsc_begin_page_setup (op_portal->surface);
switch (gtk_page_setup_get_orientation (page_setup))
{
case GTK_PAGE_ORIENTATION_PORTRAIT:
case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT:
cairo_ps_surface_dsc_comment (op_portal->surface, "%%PageOrientation: Portrait");
break;
case GTK_PAGE_ORIENTATION_LANDSCAPE:
case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE:
cairo_ps_surface_dsc_comment (op_portal->surface, "%%PageOrientation: Landscape");
break;
}
}
else if (type == CAIRO_SURFACE_TYPE_PDF)
{
if (!op->priv->manual_orientation)
{
w = gtk_page_setup_get_paper_width (page_setup, GTK_UNIT_POINTS);
h = gtk_page_setup_get_paper_height (page_setup, GTK_UNIT_POINTS);
}
cairo_pdf_surface_set_size (op_portal->surface, w, h);
}
}
}
static void
portal_end_page (GtkPrintOperation *op,
GtkPrintContext *print_context)
{
cairo_t *cr;
cr = gtk_print_context_get_cairo_context (print_context);
if ((op->priv->manual_number_up < 2) ||
((op->priv->page_position + 1) % op->priv->manual_number_up == 0) ||
(op->priv->page_position == op->priv->nr_of_pages_to_print - 1))
cairo_show_page (cr);
}
static void
print_file_done (GObject *source,
GAsyncResult *result,
gpointer data)
{
GtkPrintOperation *op = data;
GtkPrintOperationPortal *op_portal = op->priv->platform_data;
GError *error = NULL;
GVariant *ret;
ret = g_dbus_proxy_call_finish (op_portal->proxy,
result,
&error);
if (ret == NULL)
{
if (op->priv->error == NULL)
op->priv->error = g_error_copy (error);
g_warning ("Print file failed: %s", error->message);
g_error_free (error);
}
else
g_variant_unref (ret);
if (op_portal->loop)
g_main_loop_quit (op_portal->loop);
g_object_unref (op);
}
static void
portal_job_complete (GtkPrintJob *job,
gpointer data,
const GError *error)
{
GtkPrintOperation *op = data;
GtkPrintOperationPortal *op_portal = op->priv->platform_data;
GtkPrintSettings *settings;
const char *uri;
char *filename;
int fd, idx;
GVariantBuilder opt_builder;
GUnixFDList *fd_list;
if (error != NULL && op->priv->error == NULL)
{
g_warning ("Print job failed: %s", error->message);
op->priv->error = g_error_copy (error);
return;
}
op_portal->file_written = TRUE;
settings = gtk_print_job_get_settings (job);
uri = gtk_print_settings_get (settings, GTK_PRINT_SETTINGS_OUTPUT_URI);
filename = g_filename_from_uri (uri, NULL, NULL);
fd = open (filename, O_RDONLY|O_CLOEXEC);
fd_list = g_unix_fd_list_new ();
idx = g_unix_fd_list_append (fd_list, fd, NULL);
close (fd);
g_free (filename);
g_variant_builder_init (&opt_builder, G_VARIANT_TYPE_VARDICT);
g_variant_builder_add (&opt_builder, "{sv}", "token", g_variant_new_uint32 (op_portal->token));
g_dbus_proxy_call_with_unix_fd_list (op_portal->proxy,
"Print",
g_variant_new ("(ssh@a{sv})",
"", /* window */
_("Print"), /* title */
idx,
g_variant_builder_end (&opt_builder)),
G_DBUS_CALL_FLAGS_NONE,
-1,
fd_list,
NULL,
print_file_done,
op);
g_object_unref (fd_list);
}
static void
portal_end_run (GtkPrintOperation *op,
gboolean wait,
gboolean cancelled)
{
GtkPrintOperationPortal *op_portal = op->priv->platform_data;
cairo_surface_finish (op_portal->surface);
if (cancelled)
return;
if (wait)
op_portal->loop = g_main_loop_new (NULL, FALSE);
/* TODO: Check for error */
if (op_portal->job != NULL)
{
g_object_ref (op);
gtk_print_job_send (op_portal->job, portal_job_complete, op, NULL);
}
if (wait)
{
g_object_ref (op);
if (!op_portal->file_written)
{
gdk_threads_leave ();
g_main_loop_run (op_portal->loop);
gdk_threads_enter ();
}
g_object_unref (op);
}
}
static void
finish_print (PortalData *portal,
GtkPrinter *printer,
GtkPageSetup *page_setup,
GtkPrintSettings *settings)
{
GtkPrintOperation *op = portal->op;
GtkPrintOperationPrivate *priv = op->priv;
GtkPrintJob *job;
GtkPrintOperationPortal *op_portal;
cairo_t *cr;
if (portal->do_print)
{
gtk_print_operation_set_print_settings (op, settings);
priv->print_context = _gtk_print_context_new (op);
_gtk_print_context_set_hard_margins (priv->print_context, 0, 0, 0, 0);
gtk_print_operation_set_default_page_setup (op, page_setup);
_gtk_print_context_set_page_setup (priv->print_context, page_setup);
op_portal = g_new0 (GtkPrintOperationPortal, 1);
priv->platform_data = op_portal;
priv->free_platform_data = (GDestroyNotify) op_portal_free;
priv->start_page = portal_start_page;
priv->end_page = portal_end_page;
priv->end_run = portal_end_run;
job = gtk_print_job_new (priv->job_name, printer, settings, page_setup);
op_portal->job = job;
op_portal->proxy = g_object_ref (portal->proxy);
op_portal->token = portal->token;
op_portal->surface = gtk_print_job_get_surface (job, &priv->error);
if (op_portal->surface == NULL)
{
portal->result = GTK_PRINT_OPERATION_RESULT_ERROR;
portal->do_print = FALSE;
goto out;
}
cr = cairo_create (op_portal->surface);
gtk_print_context_set_cairo_context (priv->print_context, cr, 72, 72);
cairo_destroy (cr);
priv->print_pages = gtk_print_job_get_pages (job);
priv->page_ranges = gtk_print_job_get_page_ranges (job, &priv->num_page_ranges);
priv->manual_num_copies = gtk_print_job_get_num_copies (job);
priv->manual_collation = gtk_print_job_get_collate (job);
priv->manual_reverse = gtk_print_job_get_reverse (job);
priv->manual_page_set = gtk_print_job_get_page_set (job);
priv->manual_scale = gtk_print_job_get_scale (job);
priv->manual_orientation = gtk_print_job_get_rotate (job);
priv->manual_number_up = gtk_print_job_get_n_up (job);
priv->manual_number_up_layout = gtk_print_job_get_n_up_layout (job);
}
out:
if (portal->print_cb)
portal->print_cb (op, portal->parent, portal->do_print, portal->result);
if (portal->destroy)
portal->destroy (portal);
}
static GtkPrinter *
find_file_printer (void)
{
GList *backends, *l, *printers;
GtkPrinter *printer;
printer = NULL;
backends = gtk_print_backend_load_modules ();
for (l = backends; l; l = l->next)
{
GtkPrintBackend *backend = l->data;
if (strcmp (G_OBJECT_TYPE_NAME (backend), "GtkPrintBackendFile") == 0)
{
printers = gtk_print_backend_get_printer_list (backend);
printer = printers->data;
g_list_free (printers);
break;
}
}
g_list_free (backends);
return printer;
}
static void
prepare_print_response (GDBusConnection *connection,
const char *sender_name,
const char *object_path,
const char *interface_name,
const char *signal_name,
GVariant *parameters,
gpointer data)
{
PortalData *portal = data;
guint32 response;
GVariant *options;
if (portal->response_signal_id != 0)
{
g_dbus_connection_signal_unsubscribe (connection,
portal->response_signal_id);
portal->response_signal_id = 0;
}
g_variant_get (parameters, "(u@a{sv})", &response, &options);
portal->do_print = (response == 0);
if (portal->do_print)
{
GVariant *v;
GtkPrintSettings *settings;
GtkPageSetup *page_setup;
GtkPrinter *printer;
char *filename;
char *uri;
int fd;
portal->result = GTK_PRINT_OPERATION_RESULT_APPLY;
v = g_variant_lookup_value (options, "settings", G_VARIANT_TYPE_VARDICT);
settings = gtk_print_settings_new_from_gvariant (v);
g_variant_unref (v);
v = g_variant_lookup_value (options, "page-setup", G_VARIANT_TYPE_VARDICT);
page_setup = gtk_page_setup_new_from_gvariant (v);
g_variant_unref (v);
g_variant_lookup (options, "token", "u", &portal->token);
printer = find_file_printer ();
fd = g_file_open_tmp ("gtkprintXXXXXX", &filename, NULL);
uri = g_filename_to_uri (filename, NULL, NULL);
gtk_print_settings_set (settings, GTK_PRINT_SETTINGS_OUTPUT_URI, uri);
g_free (uri);
close (fd);
finish_print (portal, printer, page_setup, settings);
g_free (filename);
}
else
portal->result = GTK_PRINT_OPERATION_RESULT_CANCEL;
if (portal->loop)
g_main_loop_quit (portal->loop);
}
static void
prepare_print_called (GObject *source,
GAsyncResult *result,
gpointer data)
{
PortalData *portal = data;
GError *error = NULL;
const char *handle = NULL;
GVariant *ret;
ret = g_dbus_proxy_call_finish (portal->proxy, result, &error);
if (ret == NULL)
{
if (portal->op->priv->error == NULL)
portal->op->priv->error = g_error_copy (error);
g_error_free (error);
if (portal->loop)
g_main_loop_quit (portal->loop);
return;
}
else
g_variant_get (ret, "(&o)", &handle);
portal->response_signal_id =
g_dbus_connection_signal_subscribe (g_dbus_proxy_get_connection (G_DBUS_PROXY (portal->proxy)),
"org.freedesktop.portal.Desktop",
"org.freedesktop.portal.Request",
"Response",
handle,
NULL,
G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
prepare_print_response,
portal, NULL);
g_variant_unref (ret);
}
PortalData *
create_portal_data (GtkPrintOperation *op,
GtkWindow *parent,
GtkPrintOperationPrintFunc print_cb)
{
GDBusProxy *proxy;
PortalData *portal;
guint signal_id;
GError *error = NULL;
signal_id = g_signal_lookup ("create-custom-widget", GTK_TYPE_PRINT_OPERATION);
if (g_signal_has_handler_pending (op, signal_id, 0, TRUE))
g_warning ("GtkPrintOperation::create-custom-widget not supported with portal");
proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
G_DBUS_PROXY_FLAGS_NONE,
NULL,
"org.freedesktop.portal.Desktop",
"/org/freedesktop/portal/desktop",
"org.freedesktop.portal.Print",
NULL,
&error);
if (proxy == NULL)
{
if (op->priv->error == NULL)
op->priv->error = g_error_copy (error);
g_error_free (error);
return NULL;
}
portal = g_new0 (PortalData, 1);
portal->proxy = proxy;
portal->op = g_object_ref (op);
portal->parent = parent;
portal->result = GTK_PRINT_OPERATION_RESULT_CANCEL;
portal->print_cb = print_cb;
if (print_cb) /* async case */
{
portal->loop = NULL;
portal->destroy = portal_data_free;
}
else
{
portal->loop = g_main_loop_new (NULL, FALSE);
portal->destroy = NULL;
}
return portal;
}
static void
call_prepare_print (GtkPrintOperation *op,
PortalData *portal)
{
GtkPrintOperationPrivate *priv = op->priv;
GVariant *settings;
GVariant *setup;
GVariantBuilder opt_builder;
GVariant *options;
char *parent_window_str = NULL;
g_variant_builder_init (&opt_builder, G_VARIANT_TYPE_VARDICT);
options = g_variant_builder_end (&opt_builder);
if (priv->print_settings)
settings = gtk_print_settings_to_gvariant (priv->print_settings);
else
{
GVariantBuilder builder;
g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
settings = g_variant_builder_end (&builder);
}
if (priv->default_page_setup)
setup = gtk_page_setup_to_gvariant (priv->default_page_setup);
else
{
GtkPageSetup *page_setup = gtk_page_setup_new ();
setup = gtk_page_setup_to_gvariant (page_setup);
g_object_unref (page_setup);
}
if (portal->parent != NULL && gtk_widget_is_visible (GTK_WIDGET (portal->parent)))
{
GdkWindow *window = gtk_widget_get_window (GTK_WIDGET (portal->parent));
#ifdef GDK_WINDOWING_X11
if (GDK_IS_X11_WINDOW (window))
parent_window_str = g_strdup_printf ("x11:%x", (guint32)gdk_x11_window_get_xid (window));
#endif
}
g_dbus_proxy_call (portal->proxy,
"PreparePrint",
g_variant_new ("(ss@a{sv}@a{sv}@a{sv})",
parent_window_str ? parent_window_str : "",
_("Print"), /* title */
settings,
setup,
options),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
prepare_print_called,
portal);
g_free (parent_window_str);
}
GtkPrintOperationResult
gtk_print_operation_portal_run_dialog (GtkPrintOperation *op,
gboolean show_dialog,
GtkWindow *parent,
gboolean *do_print)
{
PortalData *portal;
GtkPrintOperationResult result;
portal = create_portal_data (op, parent, NULL);
if (portal == NULL)
return GTK_PRINT_OPERATION_RESULT_ERROR;
call_prepare_print (op, portal);
gdk_threads_leave ();
g_main_loop_run (portal->loop);
gdk_threads_enter ();
*do_print = portal->do_print;
result = portal->result;
portal_data_free (portal);
return result;
}
void
gtk_print_operation_portal_run_dialog_async (GtkPrintOperation *op,
gboolean show_dialog,
GtkWindow *parent,
GtkPrintOperationPrintFunc print_cb)
{
PortalData *portal;
portal = create_portal_data (op, parent, print_cb);
if (portal == NULL)
return;
call_prepare_print (op, portal);
}
void
gtk_print_operation_portal_launch_preview (GtkPrintOperation *op,
cairo_surface_t *surface,
GtkWindow *parent,
const char *filename)
{
char *uri;
uri = g_filename_to_uri (filename, NULL, NULL);
gtk_show_uri_on_window (parent, uri, GDK_CURRENT_TIME, NULL);
g_free (uri);
}

View File

@@ -0,0 +1,40 @@
/* GTK - The GIMP Toolkit
* Copyright (C) 2016, 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_PRINT_OPERATION_PORTAL_H__
#define __GTK_PRINT_OPERATION_PORTAL_H__
#include "gtkprintoperation.h"
G_BEGIN_DECLS
GtkPrintOperationResult gtk_print_operation_portal_run_dialog (GtkPrintOperation *op,
gboolean show_dialog,
GtkWindow *parent,
gboolean *do_print);
void gtk_print_operation_portal_run_dialog_async (GtkPrintOperation *op,
gboolean show_dialog,
GtkWindow *parent,
GtkPrintOperationPrintFunc print_cb);
void gtk_print_operation_portal_launch_preview (GtkPrintOperation *op,
cairo_surface_t *surface,
GtkWindow *parent,
const char *filename);
G_END_DECLS
#endif /* __GTK_PRINT_OPERATION_PORTAL_H__ */

View File

@@ -30,6 +30,7 @@
#include <glib/gstdio.h>
#include "gtkprintoperation-private.h"
#include "gtkprintoperation-portal.h"
#include "gtkmessagedialog.h"
#include <cairo-pdf.h>
@@ -199,11 +200,11 @@ shell_command_substitute_file (const gchar *cmd,
return g_string_free (final, FALSE);
}
void
_gtk_print_operation_platform_backend_launch_preview (GtkPrintOperation *op,
cairo_surface_t *surface,
GtkWindow *parent,
const gchar *filename)
static void
gtk_print_operation_unix_launch_preview (GtkPrintOperation *op,
cairo_surface_t *surface,
GtkWindow *parent,
const gchar *filename)
{
GAppInfo *appinfo;
GdkAppLaunchContext *context;
@@ -703,11 +704,11 @@ found_printer (GtkPrinter *printer,
g_object_unref (page_setup);
}
void
_gtk_print_operation_platform_backend_run_dialog_async (GtkPrintOperation *op,
gboolean show_dialog,
GtkWindow *parent,
GtkPrintOperationPrintFunc print_cb)
static void
gtk_print_operation_unix_run_dialog_async (GtkPrintOperation *op,
gboolean show_dialog,
GtkWindow *parent,
GtkPrintOperationPrintFunc print_cb)
{
GtkWidget *pd;
PrintResponseData *rdata;
@@ -778,12 +779,12 @@ close_preview (void *data)
close (fd);
}
cairo_surface_t *
_gtk_print_operation_platform_backend_create_preview_surface (GtkPrintOperation *op,
GtkPageSetup *page_setup,
gdouble *dpi_x,
gdouble *dpi_y,
gchar **target)
static cairo_surface_t *
gtk_print_operation_unix_create_preview_surface (GtkPrintOperation *op,
GtkPageSetup *page_setup,
gdouble *dpi_x,
gdouble *dpi_y,
gchar **target)
{
gchar *filename;
gint fd;
@@ -815,25 +816,25 @@ _gtk_print_operation_platform_backend_create_preview_surface (GtkPrintOperation
return surface;
}
void
_gtk_print_operation_platform_backend_preview_start_page (GtkPrintOperation *op,
cairo_surface_t *surface,
cairo_t *cr)
static void
gtk_print_operation_unix_preview_start_page (GtkPrintOperation *op,
cairo_surface_t *surface,
cairo_t *cr)
{
}
void
_gtk_print_operation_platform_backend_preview_end_page (GtkPrintOperation *op,
cairo_surface_t *surface,
cairo_t *cr)
static void
gtk_print_operation_unix_preview_end_page (GtkPrintOperation *op,
cairo_surface_t *surface,
cairo_t *cr)
{
cairo_show_page (cr);
}
void
_gtk_print_operation_platform_backend_resize_preview_surface (GtkPrintOperation *op,
GtkPageSetup *page_setup,
cairo_surface_t *surface)
static void
gtk_print_operation_unix_resize_preview_surface (GtkPrintOperation *op,
GtkPageSetup *page_setup,
cairo_surface_t *surface)
{
gdouble w, h;
@@ -842,12 +843,11 @@ _gtk_print_operation_platform_backend_resize_preview_surface (GtkPrintOperation
cairo_pdf_surface_set_size (surface, w, h);
}
GtkPrintOperationResult
_gtk_print_operation_platform_backend_run_dialog (GtkPrintOperation *op,
gboolean show_dialog,
GtkWindow *parent,
gboolean *do_print)
static GtkPrintOperationResult
gtk_print_operation_unix_run_dialog (GtkPrintOperation *op,
gboolean show_dialog,
GtkWindow *parent,
gboolean *do_print)
{
GtkWidget *pd;
PrintResponseData rdata;
@@ -1218,3 +1218,73 @@ find_printer (const gchar *printer,
if (finder->backends == NULL && !finder->found_printer)
g_idle_add (find_printer_idle, finder);
}
GtkPrintOperationResult
_gtk_print_operation_platform_backend_run_dialog (GtkPrintOperation *op,
gboolean show_dialog,
GtkWindow *parent,
gboolean *do_print)
{
if (gtk_should_use_portal ())
return gtk_print_operation_portal_run_dialog (op, show_dialog, parent, do_print);
else
return gtk_print_operation_unix_run_dialog (op, show_dialog, parent, do_print);
}
void
_gtk_print_operation_platform_backend_run_dialog_async (GtkPrintOperation *op,
gboolean show_dialog,
GtkWindow *parent,
GtkPrintOperationPrintFunc print_cb)
{
if (gtk_should_use_portal ())
gtk_print_operation_portal_run_dialog_async (op, show_dialog, parent, print_cb);
else
gtk_print_operation_unix_run_dialog_async (op, show_dialog, parent, print_cb);
}
void
_gtk_print_operation_platform_backend_launch_preview (GtkPrintOperation *op,
cairo_surface_t *surface,
GtkWindow *parent,
const gchar *filename)
{
if (gtk_should_use_portal ())
gtk_print_operation_portal_launch_preview (op, surface, parent, filename);
else
gtk_print_operation_unix_launch_preview (op, surface, parent, filename);
}
cairo_surface_t *
_gtk_print_operation_platform_backend_create_preview_surface (GtkPrintOperation *op,
GtkPageSetup *page_setup,
gdouble *dpi_x,
gdouble *dpi_y,
gchar **target)
{
return gtk_print_operation_unix_create_preview_surface (op, page_setup, dpi_x, dpi_y, target);
}
void
_gtk_print_operation_platform_backend_resize_preview_surface (GtkPrintOperation *op,
GtkPageSetup *page_setup,
cairo_surface_t *surface)
{
gtk_print_operation_unix_resize_preview_surface (op, page_setup, surface);
}
void
_gtk_print_operation_platform_backend_preview_start_page (GtkPrintOperation *op,
cairo_surface_t *surface,
cairo_t *cr)
{
gtk_print_operation_unix_preview_start_page (op, surface, cr);
}
void
_gtk_print_operation_platform_backend_preview_end_page (GtkPrintOperation *op,
cairo_surface_t *surface,
cairo_t *cr)
{
gtk_print_operation_unix_preview_end_page (op, surface, cr);
}

View File

@@ -1941,3 +1941,68 @@ gtk_print_settings_to_key_file (GtkPrintSettings *settings,
(GtkPrintSettingsFunc) add_value_to_key_file,
&data);
}
static void
add_to_variant (const gchar *key,
const gchar *value,
gpointer data)
{
GVariantBuilder *builder = data;
g_variant_builder_add (builder, "{sv}", key, g_variant_new_string (value));
}
/**
* gtk_print_settings_to_gvariant:
* @settings: a #GtkPrintSettings
*
* Serialize print settings to an a{sv} variant.
*
* Returns: (transfer none): a new, floating, #GVariant
*
* Since: 3.22
*/
GVariant *
gtk_print_settings_to_gvariant (GtkPrintSettings *settings)
{
GVariantBuilder builder;
g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
gtk_print_settings_foreach (settings, add_to_variant, &builder);
return g_variant_builder_end (&builder);
}
/**
* gtk_print_settings_new_from_gvariant:
* @variant: an a{sv} #GVariant
*
* Deserialize print settings from an a{sv} variant in
* the format produced by gtk_print_settings_to_gvariant().
*
* Returns: (transfer full): a new #GtkPrintSettings object
*
* Since: 3.22
*/
GtkPrintSettings *
gtk_print_settings_new_from_gvariant (GVariant *variant)
{
GtkPrintSettings *settings;
int i;
g_return_val_if_fail (g_variant_is_of_type (variant, G_VARIANT_TYPE_VARDICT), NULL);
settings = gtk_print_settings_new ();
for (i = 0; i < g_variant_n_children (variant); i++)
{
const char *key;
GVariant *v;
g_variant_get_child (variant, i, "{&sv}", &key, &v);
if (g_variant_is_of_type (v, G_VARIANT_TYPE_STRING))
gtk_print_settings_set (settings, key, g_variant_get_string (v, NULL));
g_variant_unref (v);
}
return settings;
}

View File

@@ -344,6 +344,12 @@ GDK_AVAILABLE_IN_ALL
void gtk_print_settings_set_output_bin (GtkPrintSettings *settings,
const gchar *output_bin);
GDK_AVAILABLE_IN_3_22
GVariant *gtk_print_settings_to_gvariant (GtkPrintSettings *settings);
GDK_AVAILABLE_IN_3_22
GtkPrintSettings *gtk_print_settings_new_from_gvariant (GVariant *variant);
G_END_DECLS
#endif /* __GTK_PRINT_SETTINGS_H__ */

View File

@@ -267,3 +267,27 @@ _gtk_ensure_resources (void)
g_once (&register_resources_once, register_resources, NULL);
}
gboolean
gtk_should_use_portal (void)
{
static const char *use_portal = NULL;
if (G_UNLIKELY (use_portal == NULL))
{
char *path;
path = g_build_filename (g_get_user_runtime_dir (), "flatpak-info", NULL);
if (g_file_test (path, G_FILE_TEST_EXISTS))
use_portal = "1";
else
{
use_portal = g_getenv ("GTK_USE_PORTAL");
if (!use_portal)
use_portal = "";
}
g_free (path);
}
return use_portal[0] == '1';
}

View File

@@ -95,6 +95,8 @@ gboolean _gtk_propagate_captured_event (GtkWidget *widget,
gdouble _gtk_get_slowdown ();
void _gtk_set_slowdown (gdouble slowdown_factor);
gboolean gtk_should_use_portal (void);
#ifdef G_OS_WIN32
void _gtk_load_dll_with_libgtk3_manifest (const char *dllname);
#endif

View File

@@ -24,6 +24,10 @@
#include "gtkshow.h"
#ifdef GDK_WINDOWING_X11
#include "x11/gdkx.h"
#endif
/**
* gtk_show_uri:
* @screen: (allow-none): screen to show the uri on
@@ -70,7 +74,67 @@ gtk_show_uri (GdkScreen *screen,
gdk_app_launch_context_set_screen (context, screen);
gdk_app_launch_context_set_timestamp (context, timestamp);
ret = g_app_info_launch_default_for_uri (uri, (GAppLaunchContext*)context, error);
ret = g_app_info_launch_default_for_uri (uri, G_APP_LAUNCH_CONTEXT (context), error);
g_object_unref (context);
return ret;
}
/**
* gtk_show_uri_on_window:
* @parent: (allow-none): parent window
* @uri: the uri to show
* @timestamp: a timestamp to prevent focus stealing
* @error: a #GError that is returned in case of errors
*
* A convenience function for launching the default application
* to show the uri. Like gtk_show_uri(), but takes a window
* as transient parent instead of a screen.
*
* Returns: %TRUE on success, %FALSE on error
*
* Since: 3.22
*/
gboolean
gtk_show_uri_on_window (GtkWindow *parent,
const char *uri,
guint32 timestamp,
GError **error)
{
GdkAppLaunchContext *context;
gboolean ret;
GdkDisplay *display;
g_return_val_if_fail (uri != NULL, FALSE);
if (parent)
display = gtk_widget_get_display (GTK_WIDGET (parent));
else
display = gdk_display_get_default ();
context = gdk_display_get_app_launch_context (display);
if (parent)
{
GdkWindow *window = gtk_widget_get_window (GTK_WIDGET (parent));
#ifdef GDK_WINDOWING_X11
if (GDK_IS_X11_WINDOW(window))
{
char *parent_window_str;
parent_window_str = g_strdup_printf ("x11:%x", (guint32)gdk_x11_window_get_xid (window));
g_app_launch_context_setenv (G_APP_LAUNCH_CONTEXT (context),
"PARENT_WINDOW_ID",
parent_window_str);
g_free (parent_window_str);
}
#endif
}
gdk_app_launch_context_set_timestamp (context, timestamp);
ret = g_app_info_launch_default_for_uri (uri, G_APP_LAUNCH_CONTEXT (context), error);
g_object_unref (context);
return ret;

View File

@@ -25,6 +25,8 @@
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gtk/gtkwindow.h>
G_BEGIN_DECLS
GDK_AVAILABLE_IN_ALL
@@ -33,6 +35,12 @@ gboolean gtk_show_uri (GdkScreen *screen,
guint32 timestamp,
GError **error);
GDK_AVAILABLE_IN_3_22
gboolean gtk_show_uri_on_window (GtkWindow *parent,
const char *uri,
guint32 timestamp,
GError **error);
G_END_DECLS
#endif /* __GTK_SHOW_H__ */