Compare commits
20 Commits
wip/matthi
...
portal2
Author | SHA1 | Date | |
---|---|---|---|
|
3a46c75345 | ||
|
e2074dff47 | ||
|
1e77936dc5 | ||
|
ec2a06dcfc | ||
|
d39838e12e | ||
|
e0469b5b47 | ||
|
10cbdc8637 | ||
|
5d9a71a968 | ||
|
17bd545cf9 | ||
|
0d9c2d28de | ||
|
68f8b21ae4 | ||
|
1706a7f21b | ||
|
53a30afc05 | ||
|
d25c0a71e2 | ||
|
1e6a32333d | ||
|
45311e2f06 | ||
|
db8c1a6e11 | ||
|
72fa654fe0 | ||
|
f0e5f3da91 | ||
|
585dfccca3 |
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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__ */
|
||||
|
@@ -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;
|
||||
}
|
||||
|
416
gtk/gtkfilechoosernativeportal.c
Normal file
416
gtk/gtkfilechoosernativeportal.c
Normal 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;
|
||||
}
|
@@ -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__ */
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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__ */
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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__ */
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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__ */
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
|
626
gtk/gtkprintoperation-portal.c
Normal file
626
gtk/gtkprintoperation-portal.c
Normal 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);
|
||||
}
|
40
gtk/gtkprintoperation-portal.h
Normal file
40
gtk/gtkprintoperation-portal.h
Normal 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__ */
|
@@ -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);
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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__ */
|
||||
|
@@ -267,3 +267,27 @@ _gtk_ensure_resources (void)
|
||||
|
||||
g_once (®ister_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';
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
@@ -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__ */
|
||||
|
Reference in New Issue
Block a user