/*
* GTK - The GIMP Toolkit
* Copyright (C) 2023 Red Hat, Inc.
* All rights reserved.
*
* This Library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library. If not, see .
*/
#include "config.h"
#include "gtkprintdialog.h"
#include "gtkwindowprivate.h"
#include "gtkdialogerror.h"
#include "gtkprivate.h"
#include "deprecated/gtkdialog.h"
#include "print/gtkprinter.h"
#include "print/gtkprinterprivate.h"
#ifdef HAVE_GIO_UNIX
#include
#include
#include
#include
#include
#include "print/gtkprintjob.h"
#include "print/gtkprintunixdialog.h"
#endif
#include
/* {{{ GtkPrintSetup */
/**
* GtkPrintSetup:
*
* A `GtkPrintSetup` is an auxiliary object for printing that allows decoupling
* the setup from the printing.
*
* A print setup is obtained by calling [method@Gtk.PrintDialog.setup],
* and can later be passed to print functions such as [method@Gtk.PrintDialog.print].
*
* Print setups can be reused for multiple print calls.
*
* Applications may wish to store the page_setup and print_settings from the print setup
* and copy them to the PrintDialog if they want to keep using them.
*/
struct _GtkPrintSetup
{
unsigned int ref_count;
GtkPrintSettings *print_settings;
GtkPageSetup *page_setup;
GtkPrinter *printer;
unsigned int token;
};
G_DEFINE_BOXED_TYPE (GtkPrintSetup, gtk_print_setup,
gtk_print_setup_ref,
gtk_print_setup_unref)
static GtkPrintSetup *
gtk_print_setup_new (void)
{
GtkPrintSetup *setup;
setup = g_new0 (GtkPrintSetup, 1);
setup->ref_count = 1;
return setup;
}
/**
* gtk_print_setup_ref:
* @setup: a `GtkPrintSetup`
*
* Increase the reference count of @setup.
*
* Returns: the print setup
*
* Since: 4.14
*/
GtkPrintSetup *
gtk_print_setup_ref (GtkPrintSetup *setup)
{
setup->ref_count++;
return setup;
}
/**
* gtk_print_setup_unref:
* @setup: a `GtkPrintSetup`
*
* Decrease the reference count of @setup.
*
* If the reference count reaches zero,
* the object is freed.
*
* Since: 4.14
*/
void
gtk_print_setup_unref (GtkPrintSetup *setup)
{
setup->ref_count--;
if (setup->ref_count > 0)
return;
g_clear_object (&setup->print_settings);
g_clear_object (&setup->page_setup);
g_clear_object (&setup->printer);
g_free (setup);
}
/**
* gtk_print_setup_get_print_settings:
* @setup: a `GtkPrintSetup`
*
* Returns the print settings of @setup.
*
* They may be different from the `GtkPrintDialog`'s settings
* if the user changed them during the setup process.
*
* Returns: (nullable) (transfer none): the print settings, or `NULL`
*
* Since: 4.14
*/
GtkPrintSettings *
gtk_print_setup_get_print_settings (GtkPrintSetup *setup)
{
return setup->print_settings;
}
static void
gtk_print_setup_set_print_settings (GtkPrintSetup *setup,
GtkPrintSettings *print_settings)
{
g_set_object (&setup->print_settings, print_settings);
}
/**
* gtk_print_setup_get_page_setup:
* @setup: a `GtkPrintSetup`
*
* Returns the page setup of @setup.
*
* It may be different from the `GtkPrintDialog`'s page setup
* if the user changed it during the setup process.
*
* Returns: (nullable) (transfer none): the page setup, or `NULL`
*
* Since: 4.14
*/
GtkPageSetup *
gtk_print_setup_get_page_setup (GtkPrintSetup *setup)
{
return setup->page_setup;
}
static void
gtk_print_setup_set_page_setup (GtkPrintSetup *setup,
GtkPageSetup *page_setup)
{
g_set_object (&setup->page_setup, page_setup);
}
static GtkPrinter *
gtk_print_setup_get_printer (GtkPrintSetup *setup)
{
if (!setup->printer)
{
const char *name = NULL;
if (setup->print_settings)
name = gtk_print_settings_get (setup->print_settings, GTK_PRINT_SETTINGS_PRINTER);
if (name)
setup->printer = gtk_printer_find (name);
}
return setup->printer;
}
static void
gtk_print_setup_set_printer (GtkPrintSetup *setup,
GtkPrinter *printer)
{
g_set_object (&setup->printer, printer);
}
/* }}} */
/* {{{ GObject implementation */
/**
* GtkPrintDialog:
*
* A `GtkPrintDialog` object collects the arguments that
* are needed to present a print dialog to the user, such
* as a title for the dialog and whether it should be modal.
*
* The dialog is shown with the [method@Gtk.PrintDialog.setup] function.
* The actual printing can be done with [method@Gtk.PrintDialog.print] or
* [method@Gtk.PrintDialog.print_file]. These APIs follows the GIO async pattern,
* and the results can be obtained by calling the corresponding finish methods.
*
* Since: 4.14
*/
struct _GtkPrintDialog
{
GObject parent_instance;
GtkPrintSettings *print_settings;
GtkPageSetup *page_setup;
GDBusProxy *portal;
char *accept_label;
char *title;
unsigned int modal : 1;
};
enum
{
PROP_ACCEPT_LABEL = 1,
PROP_PAGE_SETUP,
PROP_MODAL,
PROP_PRINT_SETTINGS,
PROP_TITLE,
NUM_PROPERTIES
};
static GParamSpec *properties[NUM_PROPERTIES];
G_DEFINE_TYPE (GtkPrintDialog, gtk_print_dialog, G_TYPE_OBJECT)
static void
gtk_print_dialog_init (GtkPrintDialog *self)
{
self->modal = TRUE;
}
static void
gtk_print_dialog_finalize (GObject *object)
{
GtkPrintDialog *self = GTK_PRINT_DIALOG (object);
g_clear_object (&self->portal);
g_clear_object (&self->print_settings);
g_clear_object (&self->page_setup);
g_free (self->accept_label);
g_free (self->title);
G_OBJECT_CLASS (gtk_print_dialog_parent_class)->finalize (object);
}
static void
gtk_print_dialog_get_property (GObject *object,
unsigned int property_id,
GValue *value,
GParamSpec *pspec)
{
GtkPrintDialog *self = GTK_PRINT_DIALOG (object);
switch (property_id)
{
case PROP_ACCEPT_LABEL:
g_value_set_string (value, self->accept_label);
break;
case PROP_PAGE_SETUP:
g_value_set_object (value, self->page_setup);
break;
case PROP_MODAL:
g_value_set_boolean (value, self->modal);
break;
case PROP_PRINT_SETTINGS:
g_value_set_object (value, self->print_settings);
break;
case PROP_TITLE:
g_value_set_string (value, self->title);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gtk_print_dialog_set_property (GObject *object,
unsigned int prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkPrintDialog *self = GTK_PRINT_DIALOG (object);
switch (prop_id)
{
case PROP_ACCEPT_LABEL:
gtk_print_dialog_set_accept_label (self, g_value_get_string (value));
break;
case PROP_PAGE_SETUP:
gtk_print_dialog_set_page_setup (self, g_value_get_object (value));
break;
case PROP_MODAL:
gtk_print_dialog_set_modal (self, g_value_get_boolean (value));
break;
case PROP_PRINT_SETTINGS:
gtk_print_dialog_set_print_settings (self, g_value_get_object (value));
break;
case PROP_TITLE:
gtk_print_dialog_set_title (self, g_value_get_string (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_print_dialog_class_init (GtkPrintDialogClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->finalize = gtk_print_dialog_finalize;
object_class->get_property = gtk_print_dialog_get_property;
object_class->set_property = gtk_print_dialog_set_property;
/**
* GtkPrintDialog:accept-label: (attributes org.gtk.Property.get=gtk_print_dialog_get_accept_label org.gtk.Property.set=gtk_print_dialog_set_accept_label)
*
* A label that may be shown on the accept button of a print dialog
* that is presented by [method@Gtk.PrintDialog.setup].
*
* Since: 4.14
*/
properties[PROP_ACCEPT_LABEL] =
g_param_spec_string ("accept-label", NULL, NULL,
NULL,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkPrintDialog:page-setup: (attributes org.gtk.Property.get=gtk_print_dialog_get_page_setup org.gtk.Property.set=gtk_print_dialog_set_page_setup)
*
* The page setup to use.
*
* Since: 4.14
*/
properties[PROP_PAGE_SETUP] =
g_param_spec_object ("page-setup", NULL, NULL,
GTK_TYPE_PAGE_SETUP,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkPrintDialog:modal: (attributes org.gtk.Property.get=gtk_print_dialog_get_modal org.gtk.Property.set=gtk_print_dialog_set_modal)
*
* Whether the print dialog is modal.
*
* Since: 4.14
*/
properties[PROP_MODAL] =
g_param_spec_boolean ("modal", NULL, NULL,
TRUE,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkPrintDialog:print-settings: (attributes org.gtk.Property.get=gtk_print_dialog_get_print_settings org.gtk.Property.set=gtk_print_dialog_set_print_settings)
*
* The print settings to use.
*
* Since: 4.14
*/
properties[PROP_PRINT_SETTINGS] =
g_param_spec_object ("print-settings", NULL, NULL,
GTK_TYPE_PRINT_SETTINGS,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkPrintDialog:title: (attributes org.gtk.Property.get=gtk_print_dialog_get_title org.gtk.Property.set=gtk_print_dialog_set_title)
*
* A title that may be shown on the print dialog that is
* presented by [method@Gtk.PrintDialog.setup].
*
* Since: 4.14
*/
properties[PROP_TITLE] =
g_param_spec_string ("title", NULL, NULL,
NULL,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
}
/* }}} */
/* {{{ API: Constructor */
/**
* gtk_print_dialog_new:
*
* Creates a new `GtkPrintDialog` object.
*
* Returns: the new `GtkPrintDialog`
*
* Since: 4.14
*/
GtkPrintDialog *
gtk_print_dialog_new (void)
{
return g_object_new (GTK_TYPE_PRINT_DIALOG, NULL);
}
/* }}} */
/* {{{ API: Getters and setters */
/**
* gtk_print_dialog_get_title:
* @self: a `GtkPrintDialog`
*
* Returns the title that will be shown on the
* print dialog.
*
* Returns: the title
*
* Since: 4.14
*/
const char *
gtk_print_dialog_get_title (GtkPrintDialog *self)
{
g_return_val_if_fail (GTK_IS_PRINT_DIALOG (self), NULL);
return self->title;
}
/**
* gtk_print_dialog_set_title:
* @self: a `GtkPrintDialog`
* @title: the new title
*
* Sets the title that will be shown on the print dialog.
*
* Since: 4.14
*/
void
gtk_print_dialog_set_title (GtkPrintDialog *self,
const char *title)
{
g_return_if_fail (GTK_IS_PRINT_DIALOG (self));
g_return_if_fail (title != NULL);
if (g_strcmp0 (self->title, title) == 0)
return;
g_free (self->title);
self->title = g_strdup (title);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TITLE]);
}
/**
* gtk_print_dialog_get_accept_label:
* @self: a `GtkPrintDialog`
*
* Returns the label that will be shown on the
* accept button of the print dialog.
*
* Returns: the accept label
*
* Since: 4.14
*/
const char *
gtk_print_dialog_get_accept_label (GtkPrintDialog *self)
{
g_return_val_if_fail (GTK_IS_PRINT_DIALOG (self), NULL);
return self->accept_label;
}
/**
* gtk_print_dialog_set_accept_label:
* @self: a `GtkPrintDialog`
* @accept_label: the new accept label
*
* Sets the label that will be shown on the
* accept button of the print dialog shown for
* [method@Gtk.PrintDialog.setup].
*
* Since: 4.14
*/
void
gtk_print_dialog_set_accept_label (GtkPrintDialog *self,
const char *accept_label)
{
g_return_if_fail (GTK_IS_PRINT_DIALOG (self));
g_return_if_fail (accept_label != NULL);
if (g_strcmp0 (self->accept_label, accept_label) == 0)
return;
g_free (self->accept_label);
self->accept_label = g_strdup (accept_label);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACCEPT_LABEL]);
}
/**
* gtk_print_dialog_get_modal:
* @self: a `GtkPrintDialog`
*
* Returns whether the print dialog blocks
* interaction with the parent window while
* it is presented.
*
* Returns: whether the print dialog is modal
*
* Since: 4.14
*/
gboolean
gtk_print_dialog_get_modal (GtkPrintDialog *self)
{
g_return_val_if_fail (GTK_IS_PRINT_DIALOG (self), TRUE);
return self->modal;
}
/**
* gtk_print_dialog_set_modal:
* @self: a `GtkPrintDialog`
* @modal: the new value
*
* Sets whether the print dialog blocks
* interaction with the parent window while
* it is presented.
*
* Since: 4.14
*/
void
gtk_print_dialog_set_modal (GtkPrintDialog *self,
gboolean modal)
{
g_return_if_fail (GTK_IS_PRINT_DIALOG (self));
if (self->modal == modal)
return;
self->modal = modal;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODAL]);
}
/**
* gtk_print_dialog_get_page_setup:
* @self: a `GtkPrintDialog`
*
* Returns the page setup.
*
* Returns: (transfer none): the page setup
*
* Since: 4.14
*/
GtkPageSetup *
gtk_print_dialog_get_page_setup (GtkPrintDialog *self)
{
g_return_val_if_fail (GTK_IS_PRINT_DIALOG (self), NULL);
return self->page_setup;
}
/**
* gtk_print_dialog_set_page_setup:
* @self: a `GtkPrintDialog`
* @page_setup: the new page setup
*
* Set the page setup for the print dialog.
*
* Since: 4.14
*/
void
gtk_print_dialog_set_page_setup (GtkPrintDialog *self,
GtkPageSetup *page_setup)
{
g_return_if_fail (GTK_IS_PRINT_DIALOG (self));
g_return_if_fail (GTK_IS_PAGE_SETUP (page_setup));
if (g_set_object (&self->page_setup, page_setup))
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_PAGE_SETUP]);
}
/**
* gtk_print_dialog_get_print_settings:
* @self: a `GtkPrintDialog`
*
* Returns the print settings for the print dialog.
*
* Returns: (transfer none): the settings
*
* Since: 4.14
*/
GtkPrintSettings *
gtk_print_dialog_get_print_settings (GtkPrintDialog *self)
{
g_return_val_if_fail (GTK_IS_PRINT_DIALOG (self), NULL);
return self->print_settings;
}
/**
* gtk_print_dialog_set_print_settings:
* @self: a `GtkPrintDialog`
* @print_settings: the new print settings
*
* Sets the print settings for the print dialog.
*
* Since: 4.14
*/
void
gtk_print_dialog_set_print_settings (GtkPrintDialog *self,
GtkPrintSettings *print_settings)
{
g_return_if_fail (GTK_IS_PRINT_DIALOG (self));
g_return_if_fail (GTK_IS_PRINT_SETTINGS (print_settings));
if (g_set_object (&self->print_settings, print_settings))
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_PRINT_SETTINGS]);
}
/* }}} */
/* {{{ Print output stream */
#ifdef HAVE_GIO_UNIX
#define GTK_TYPE_PRINT_OUTPUT_STREAM (gtk_print_output_stream_get_type ())
G_DECLARE_FINAL_TYPE (GtkPrintOutputStream, gtk_print_output_stream, GTK, PRINT_OUTPUT_STREAM, GUnixOutputStream)
struct _GtkPrintOutputStream
{
GUnixOutputStream parent_instance;
gboolean print_done;
GError *print_error;
};
struct _GtkPrintOutputStreamClass
{
GUnixOutputStreamClass parent_class;
};
G_DEFINE_TYPE (GtkPrintOutputStream, gtk_print_output_stream, G_TYPE_UNIX_OUTPUT_STREAM);
static void
gtk_print_output_stream_init (GtkPrintOutputStream *stream)
{
}
static void
gtk_print_output_stream_finalize (GObject *object)
{
GtkPrintOutputStream *stream = GTK_PRINT_OUTPUT_STREAM (object);
g_clear_error (&stream->print_error);
G_OBJECT_CLASS (gtk_print_output_stream_parent_class)->finalize (object);
}
static gboolean
gtk_print_output_stream_close (GOutputStream *ostream,
GCancellable *cancellable,
GError **error)
{
GtkPrintOutputStream *stream = GTK_PRINT_OUTPUT_STREAM (ostream);
G_OUTPUT_STREAM_CLASS (gtk_print_output_stream_parent_class)->close_fn (ostream, cancellable, NULL);
while (!stream->print_done)
g_main_context_iteration (NULL, TRUE);
if (stream->print_error)
{
g_propagate_error (error, stream->print_error);
stream->print_error = NULL;
return FALSE;
}
return TRUE;
}
static void
gtk_print_output_stream_class_init (GtkPrintOutputStreamClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
GOutputStreamClass *stream_class = G_OUTPUT_STREAM_CLASS (class);
object_class->finalize = gtk_print_output_stream_finalize;
stream_class->close_fn = gtk_print_output_stream_close;
}
static GtkPrintOutputStream *
gtk_print_output_stream_new (int fd)
{
return g_object_new (GTK_TYPE_PRINT_OUTPUT_STREAM, "fd", fd, NULL);
}
static void
gtk_print_output_stream_set_print_done (GtkPrintOutputStream *stream,
GError *error)
{
g_assert (!stream->print_done);
stream->print_done = TRUE;
stream->print_error = error;
}
#endif
/* }}} */
/* {{{ Async implementation */
#ifdef HAVE_GIO_UNIX
typedef struct
{
GtkWindow *exported_window;
char *portal_handle;
unsigned int response_signal_id;
unsigned int token;
int fds[2];
gboolean has_returned;
GtkPrintOutputStream *stream;
} PrintTaskData;
static PrintTaskData *
print_task_data_new (void)
{
PrintTaskData *ptd = g_new0 (PrintTaskData, 1);
ptd->fds[0] = ptd->fds[1] = -1;
return ptd;
}
static void
print_task_data_free (gpointer data)
{
PrintTaskData *ptd = data;
g_free (ptd->portal_handle);
g_clear_object (&ptd->exported_window);
if (ptd->fds[0] != -1)
close (ptd->fds[0]);
if (ptd->fds[1] != -1)
close (ptd->fds[1]);
g_free (ptd);
}
/* {{{ Portal helpers */
static void
send_close (GTask *task)
{
GtkPrintDialog *self = GTK_PRINT_DIALOG (g_task_get_source_object (task));
PrintTaskData *ptd = g_task_get_task_data (task);
GDBusConnection *connection = g_dbus_proxy_get_connection (self->portal);
GDBusMessage *message;
GError *error = NULL;
if (!ptd->portal_handle)
return;
message = g_dbus_message_new_method_call (PORTAL_BUS_NAME,
ptd->portal_handle,
PORTAL_REQUEST_INTERFACE,
"Close");
if (!g_dbus_connection_send_message (connection,
message,
G_DBUS_SEND_MESSAGE_FLAGS_NONE,
NULL, &error))
{
g_warning ("unable to send PrintDialog Close message: %s", error->message);
g_error_free (error);
}
g_object_unref (message);
}
static gboolean
ensure_portal_proxy (GtkPrintDialog *self,
GError **error)
{
if (gdk_display_get_debug_flags (NULL) & GDK_DEBUG_NO_PORTALS)
return FALSE;
if (!self->portal)
self->portal = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
G_DBUS_PROXY_FLAGS_NONE,
NULL,
PORTAL_BUS_NAME,
PORTAL_OBJECT_PATH,
PORTAL_PRINT_INTERFACE,
NULL,
error);
return self->portal != NULL;
}
static void cleanup_portal_call_data (GTask *task);
static void
cancelled_cb (GCancellable *cancellable,
GTask *task)
{
send_close (task);
cleanup_portal_call_data (task);
g_task_return_new_error (task,
GTK_DIALOG_ERROR,
GTK_DIALOG_ERROR_CANCELLED,
"Cancelled by application");
g_object_unref (task);
}
static void
cleanup_portal_call_data (GTask *task)
{
GtkPrintDialog *self = GTK_PRINT_DIALOG (g_task_get_source_object (task));
PrintTaskData *ptd = g_task_get_task_data (task);
GDBusConnection *connection = g_dbus_proxy_get_connection (self->portal);
GCancellable *cancellable;
cancellable = g_task_get_cancellable (task);
if (cancellable)
g_signal_handlers_disconnect_by_func (cancellable, cancelled_cb, task);
if (ptd->response_signal_id != 0)
{
g_dbus_connection_signal_unsubscribe (connection, ptd->response_signal_id);
ptd->response_signal_id = 0;
}
g_clear_pointer (&ptd->portal_handle, g_free);
g_clear_object (&ptd->exported_window);
}
/* }}} */
/* {{{ Portal Setup implementation */
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 user_data)
{
GTask *task = user_data;
guint32 response;
GVariant *options = NULL;
cleanup_portal_call_data (task);
g_variant_get (parameters, "(u@a{sv})", &response, &options);
switch (response)
{
case 0:
{
GVariant *v;
GtkPrintSettings *settings;
GtkPageSetup *page_setup;
GtkPrintSetup *setup;
unsigned int token;
setup = gtk_print_setup_new ();
v = g_variant_lookup_value (options, "settings", G_VARIANT_TYPE_VARDICT);
settings = gtk_print_settings_new_from_gvariant (v);
g_variant_unref (v);
gtk_print_setup_set_print_settings (setup, settings);
g_object_unref (settings);
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);
gtk_print_setup_set_page_setup (setup, page_setup);
g_object_unref (page_setup);
g_variant_lookup (options, "token", "u", &token);
setup->token = token;
g_task_return_pointer (task, gtk_print_setup_ref (setup), (GDestroyNotify) gtk_print_setup_unref);
}
break;
case 1:
g_task_return_new_error (task,
GTK_DIALOG_ERROR,
GTK_DIALOG_ERROR_DISMISSED,
"Dismissed by user");
break;
case 2:
default:
g_task_return_new_error (task,
GTK_DIALOG_ERROR,
GTK_DIALOG_ERROR_FAILED,
"Operation failed");
break;
}
g_variant_unref (options);
g_object_unref (task);
}
static void
prepare_print_called (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
GTask *task = user_data;
GtkPrintDialog *self = GTK_PRINT_DIALOG (g_task_get_source_object (task));
GDBusConnection *connection = g_dbus_proxy_get_connection (self->portal);
PrintTaskData *ptd = g_task_get_task_data (task);
GError *error = NULL;
GVariant *ret;
char *path;
ret = g_dbus_proxy_call_finish (self->portal, result, &error);
if (ret == NULL)
{
cleanup_portal_call_data (task);
g_task_return_error (task, error);
g_object_unref (task);
return;
}
g_variant_get (ret, "(o)", &path);
if (strcmp (path, ptd->portal_handle) != 0)
{
g_free (ptd->portal_handle);
ptd->portal_handle = g_steal_pointer (&path);
g_dbus_connection_signal_unsubscribe (connection,
ptd->response_signal_id);
ptd->response_signal_id =
g_dbus_connection_signal_subscribe (connection,
PORTAL_BUS_NAME,
PORTAL_REQUEST_INTERFACE,
"Response",
ptd->portal_handle,
NULL,
G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
prepare_print_response,
self, NULL);
}
g_free (path);
g_variant_unref (ret);
}
static void
setup_window_handle_exported (GtkWindow *window,
const char *window_handle,
gpointer user_data)
{
GTask *task = user_data;
GtkPrintDialog *self = GTK_PRINT_DIALOG (g_task_get_source_object (task));
PrintTaskData *ptd = g_task_get_task_data (task);
GDBusConnection *connection = g_dbus_proxy_get_connection (self->portal);
char *handle_token;
GVariant *settings;
GVariant *setup;
GVariant *options;
GVariantBuilder opt_builder;
g_assert (ptd->portal_handle == NULL);
ptd->portal_handle = gtk_get_portal_request_path (connection, &handle_token);
g_assert (ptd->response_signal_id == 0);
ptd->response_signal_id =
g_dbus_connection_signal_subscribe (connection,
PORTAL_BUS_NAME,
PORTAL_REQUEST_INTERFACE,
"Response",
ptd->portal_handle,
NULL,
G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
prepare_print_response,
task, NULL);
g_variant_builder_init (&opt_builder, G_VARIANT_TYPE_VARDICT);
g_variant_builder_add (&opt_builder, "{sv}", "handle_token", g_variant_new_string (handle_token));
if (self->accept_label)
g_variant_builder_add (&opt_builder, "{sv}", "accept_label", g_variant_new_string (self->accept_label));
options = g_variant_builder_end (&opt_builder);
if (self->print_settings)
settings = gtk_print_settings_to_gvariant (self->print_settings);
else
{
GVariantBuilder builder;
g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
settings = g_variant_builder_end (&builder);
}
if (self->page_setup)
setup = gtk_page_setup_to_gvariant (self->page_setup);
else
{
GtkPageSetup *page_setup = gtk_page_setup_new ();
setup = gtk_page_setup_to_gvariant (page_setup);
g_object_unref (page_setup);
}
g_dbus_proxy_call (self->portal,
"PreparePrint",
g_variant_new ("(ss@a{sv}@a{sv}@a{sv})",
window_handle,
self->title ? self->title : "",
settings,
setup,
options),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
prepare_print_called,
task);
g_free (handle_token);
}
/* }}} */
/* {{{ Portal Print implementation */
static void
print_response (GDBusConnection *connection,
const char *sender_name,
const char *object_path,
const char *interface_name,
const char *signal_name,
GVariant *parameters,
gpointer user_data)
{
GTask *task = user_data;
PrintTaskData *ptd = g_task_get_task_data (task);
guint32 response;
cleanup_portal_call_data (task);
g_variant_get (parameters, "(ua{sv})", &response, NULL);
if (ptd->has_returned)
{
if (ptd->stream)
{
switch (response)
{
case 0:
gtk_print_output_stream_set_print_done (ptd->stream, NULL);
break;
case 1:
gtk_print_output_stream_set_print_done (ptd->stream,
g_error_new_literal (GTK_DIALOG_ERROR,
GTK_DIALOG_ERROR_DISMISSED,
"Dismissed by user"));
break;
case 2:
default:
gtk_print_output_stream_set_print_done (ptd->stream,
g_error_new_literal (GTK_DIALOG_ERROR,
GTK_DIALOG_ERROR_FAILED,
"Operation failed"));
break;
}
}
}
else
{
switch (response)
{
case 0:
g_task_return_boolean (task, TRUE);
break;
case 1:
g_task_return_new_error (task,
GTK_DIALOG_ERROR,
GTK_DIALOG_ERROR_DISMISSED,
"Dismissed by user");
break;
case 2:
default:
g_task_return_new_error (task,
GTK_DIALOG_ERROR,
GTK_DIALOG_ERROR_FAILED,
"Operation failed");
break;
}
}
g_object_unref (task);
}
static void
print_called (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
GTask *task = user_data;
GtkPrintDialog *self = GTK_PRINT_DIALOG (g_task_get_source_object (task));
PrintTaskData *ptd = g_task_get_task_data (task);
GDBusConnection *connection = g_dbus_proxy_get_connection (self->portal);
GError *error = NULL;
GVariant *ret;
char *path;
ret = g_dbus_proxy_call_finish (self->portal, result, &error);
if (ret == NULL)
{
cleanup_portal_call_data (task);
g_task_return_error (task, error);
g_object_unref (task);
return;
}
g_variant_get (ret, "(o)", &path);
if (strcmp (path, ptd->portal_handle) != 0)
{
g_free (ptd->portal_handle);
ptd->portal_handle = g_steal_pointer (&path);
g_dbus_connection_signal_unsubscribe (connection,
ptd->response_signal_id);
ptd->response_signal_id =
g_dbus_connection_signal_subscribe (connection,
PORTAL_BUS_NAME,
PORTAL_REQUEST_INTERFACE,
"Response",
ptd->portal_handle,
NULL,
G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
print_response,
task, NULL);
}
g_free (path);
g_variant_unref (ret);
if (ptd->fds[1] != -1)
{
ptd->stream = gtk_print_output_stream_new (ptd->fds[1]);
ptd->fds[1] = -1;
ptd->has_returned = TRUE;
g_object_add_weak_pointer (G_OBJECT (ptd->stream), (gpointer *)&ptd->stream);
g_task_return_pointer (task, ptd->stream, g_object_unref);
}
}
static void
print_window_handle_exported (GtkWindow *window,
const char *window_handle,
gpointer user_data)
{
GTask *task = user_data;
GtkPrintDialog *self = GTK_PRINT_DIALOG (g_task_get_source_object (task));
PrintTaskData *ptd = g_task_get_task_data (task);
GDBusConnection *connection = g_dbus_proxy_get_connection (self->portal);
char *handle_token;
GVariantBuilder opt_builder;
GUnixFDList *fd_list;
int idx;
if (window)
ptd->exported_window = g_object_ref (window);
g_assert (ptd->fds[0] != -1);
ptd->portal_handle = gtk_get_portal_request_path (connection, &handle_token);
ptd->response_signal_id =
g_dbus_connection_signal_subscribe (connection,
PORTAL_BUS_NAME,
PORTAL_REQUEST_INTERFACE,
"Response",
ptd->portal_handle,
NULL,
G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
print_response,
task, NULL);
fd_list = g_unix_fd_list_new ();
idx = g_unix_fd_list_append (fd_list, ptd->fds[0], NULL);
g_variant_builder_init (&opt_builder, G_VARIANT_TYPE_VARDICT);
g_variant_builder_add (&opt_builder, "{sv}", "handle_token", g_variant_new_string (handle_token));
g_variant_builder_add (&opt_builder, "{sv}", "token", g_variant_new_uint32 (ptd->token));
g_dbus_proxy_call_with_unix_fd_list (self->portal,
"Print",
g_variant_new ("(ssh@a{sv})",
window_handle,
self->title ? self->title : "",
idx,
g_variant_builder_end (&opt_builder)),
G_DBUS_CALL_FLAGS_NONE,
-1,
fd_list,
NULL,
print_called,
task);
g_object_unref (fd_list);
g_free (handle_token);
}
/* }}} */
/* {{{ Local fallback */
static GtkPrintUnixDialog *
create_print_dialog (GtkPrintDialog *self,
GtkPrintSettings *print_settings,
GtkPageSetup *page_setup,
GtkWindow *parent)
{
GtkPrintUnixDialog *dialog;
dialog = GTK_PRINT_UNIX_DIALOG (gtk_print_unix_dialog_new (self->title, parent));
if (print_settings)
gtk_print_unix_dialog_set_settings (dialog, print_settings);
if (page_setup)
gtk_print_unix_dialog_set_page_setup (dialog, page_setup);
gtk_print_unix_dialog_set_embed_page_setup (dialog, TRUE);
return dialog;
}
static void
setup_response_cb (GtkPrintUnixDialog *window,
int response,
GTask *task)
{
GCancellable *cancellable = g_task_get_cancellable (task);
if (cancellable)
g_signal_handlers_disconnect_by_func (cancellable, cancelled_cb, task);
if (response == GTK_RESPONSE_OK)
{
GtkPrintSetup *setup = gtk_print_setup_new ();
gtk_print_setup_set_print_settings (setup, gtk_print_unix_dialog_get_settings (window));
gtk_print_setup_set_page_setup (setup, gtk_print_unix_dialog_get_page_setup (window));
gtk_print_setup_set_printer (setup, gtk_print_unix_dialog_get_selected_printer (window));
g_task_return_pointer (task, setup, (GDestroyNotify) gtk_print_setup_unref);
}
else if (response == GTK_RESPONSE_CLOSE)
g_task_return_new_error (task, GTK_DIALOG_ERROR, GTK_DIALOG_ERROR_CANCELLED, "Cancelled by application");
else if (response == GTK_RESPONSE_CANCEL ||
response == GTK_RESPONSE_DELETE_EVENT)
g_task_return_new_error (task, GTK_DIALOG_ERROR, GTK_DIALOG_ERROR_DISMISSED, "Dismissed by user");
else
g_task_return_new_error (task, GTK_DIALOG_ERROR, GTK_DIALOG_ERROR_FAILED, "Unknown failure (%d)", response);
g_object_unref (task);
gtk_window_destroy (GTK_WINDOW (window));
}
static void
job_complete (GtkPrintJob *job,
gpointer user_data,
const GError *error)
{
GTask *task = user_data;
PrintTaskData *ptd = g_task_get_task_data (task);
if (ptd->has_returned)
{
if (ptd->stream)
gtk_print_output_stream_set_print_done (ptd->stream, error ? g_error_copy (error) : NULL);
}
else if (error)
g_task_return_error (task, g_error_copy (error));
else
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static void
print_content (GtkPrintSetup *setup,
GTask *task)
{
PrintTaskData *ptd = g_task_get_task_data (task);
g_assert (ptd->fds[0] != -1);
if (setup->printer)
{
GtkPrintJob *job;
g_object_ref (task);
job = gtk_print_job_new ("My first printjob",
setup->printer,
setup->print_settings,
setup->page_setup);
gtk_print_job_set_source_fd (job, ptd->fds[0], NULL);
gtk_print_job_send (job, job_complete, g_object_ref (task), g_object_unref);
g_object_unref (job);
if (ptd->fds[1] != -1)
{
ptd->stream = gtk_print_output_stream_new (ptd->fds[1]);
ptd->fds[1] = -1;
ptd->has_returned = TRUE;
g_object_add_weak_pointer (G_OBJECT (ptd->stream), (gpointer *)&ptd->stream);
g_task_return_pointer (task, ptd->stream, g_object_unref);
}
g_object_unref (task);
}
else
{
g_task_return_new_error (task, GTK_DIALOG_ERROR, GTK_DIALOG_ERROR_FAILED, "No printer selected");
g_object_unref (task);
}
}
static void
print_response_cb (GtkPrintUnixDialog *window,
int response,
GTask *task)
{
GCancellable *cancellable = g_task_get_cancellable (task);
if (cancellable)
g_signal_handlers_disconnect_by_func (cancellable, cancelled_cb, task);
if (response == GTK_RESPONSE_OK)
{
GtkPrintSetup *setup = gtk_print_setup_new ();
gtk_print_setup_set_print_settings (setup, gtk_print_unix_dialog_get_settings (window));
gtk_print_setup_set_page_setup (setup, gtk_print_unix_dialog_get_page_setup (window));
gtk_print_setup_set_printer (setup, gtk_print_unix_dialog_get_selected_printer (window));
print_content (setup, task);
gtk_print_setup_unref (setup);
}
else if (response == GTK_RESPONSE_CLOSE)
{
g_task_return_new_error (task, GTK_DIALOG_ERROR, GTK_DIALOG_ERROR_CANCELLED, "Cancelled by application");
g_object_unref (task);
}
else if (response == GTK_RESPONSE_CANCEL ||
response == GTK_RESPONSE_DELETE_EVENT)
{
g_task_return_new_error (task, GTK_DIALOG_ERROR, GTK_DIALOG_ERROR_DISMISSED, "Dismissed by user");
g_object_unref (task);
}
else
{
g_task_return_new_error (task, GTK_DIALOG_ERROR, GTK_DIALOG_ERROR_FAILED, "Unknown failure (%d)", response);
g_object_unref (task);
}
gtk_window_destroy (GTK_WINDOW (window));
}
/* }}} */
#endif /* HAVE_GIO_UNIX */
/* }}} */
/* {{{ Async API */
/**
* gtk_print_dialog_setup:
* @self: a `GtkPrintDialog`
* @parent: (nullable): the parent `GtkWindow`
* @cancellable: (nullable): a `GCancellable` to cancel the operation
* @callback: (scope async): a callback to call when the operation is complete
* @user_data: (closure callback): data to pass to @callback
*
* This function presents a print dialog to let the user select a printer,
* and set up print settings and page setup.
*
* The @callback will be called when the dialog is dismissed.
* It should call [method@Gtk.PrintDialog.setup_finish]
* to obtain the results in the form of a [struct@Gtk.PrintSetup],
* that can then be passed to [method@Gtk.PrintDialog.print]
* or [method@Gtk.PrintDialog.print_file].
*
* One possible use for this method is to have the user select a printer,
* then show a page setup UI in the application (e.g. to arrange images
* on a page), then call [method@Gtk.PrintDialog.print] on @self
* to do the printing without further user interaction.
*
* Since: 4.14
*/
void
gtk_print_dialog_setup (GtkPrintDialog *self,
GtkWindow *parent,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
G_GNUC_UNUSED GError *error = NULL;
g_return_if_fail (GTK_IS_PRINT_DIALOG (self));
g_return_if_fail (parent == NULL || GTK_IS_WINDOW (parent));
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_check_cancellable (task, FALSE);
g_task_set_source_tag (task, gtk_print_dialog_setup);
#ifdef HAVE_GIO_UNIX
if (cancellable)
g_signal_connect (cancellable, "cancelled", G_CALLBACK (cancelled_cb), task);
if (!ensure_portal_proxy (self, &error))
{
GtkPrintUnixDialog *window;
window = create_print_dialog (self, self->print_settings, self->page_setup, parent);
g_signal_connect (window, "response", G_CALLBACK (setup_response_cb), task);
gtk_window_present (GTK_WINDOW (window));
}
else
{
g_task_set_task_data (task, print_task_data_new (), (GDestroyNotify) print_task_data_free);
if (parent &&
gtk_widget_is_visible (GTK_WIDGET (parent)) &&
gtk_window_export_handle (parent, setup_window_handle_exported, task))
return;
setup_window_handle_exported (parent, "", task);
}
#else
g_task_return_new_error (task,
GTK_DIALOG_ERROR, GTK_DIALOG_ERROR_FAILED,
"GtkPrintDialog is not supported on this platform");
g_object_unref (task);
#endif
}
/**
* gtk_print_dialog_setup_finish:
* @self: a `GtkPrintDialog`
* @result: a `GAsyncResult`
* @error: return location for a [enum@Gtk.DialogError] error
*
* Finishes the [method@Gtk.PrintDialog.setup] call.
*
* If the call was successful, it returns a [struct@Gtk.PrintSetup]
* which contains the print settings and page setup information that
* will be used to print.
*
* Returns: (nullable): The `GtkPrintSetup` object that resulted from the call,
* or `NULL` if the call was not successful
*
* Since: 4.14
*/
GtkPrintSetup *
gtk_print_dialog_setup_finish (GtkPrintDialog *self,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (GTK_IS_PRINT_DIALOG (self), FALSE);
g_return_val_if_fail (g_task_is_valid (result, self), FALSE);
g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == gtk_print_dialog_setup, FALSE);
return g_task_propagate_pointer (G_TASK (result), error);
}
/**
* gtk_print_dialog_print:
* @self: a `GtkPrintDialog`
* @parent: (nullable): the parent `GtkWindow`
* @setup: (nullable): the `GtkPrintSetup` to use
* @cancellable: (nullable): a `GCancellable` to cancel the operation
* @callback: (scope async): a callback to call when the operation is complete
* @user_data: (closure callback): data to pass to @callback
*
* This function prints content from a stream.
*
* If you pass `NULL` as @setup, then this method will present a print dialog.
* Otherwise, it will attempt to print directly, without user interaction.
*
* The @callback will be called when the printing is done. It should call
* [method@Gtk.PrintDialog.print_finish] to obtain the results.
*
* Since: 4.14
*/
void
gtk_print_dialog_print (GtkPrintDialog *self,
GtkWindow *parent,
GtkPrintSetup *setup,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
G_GNUC_UNUSED GError *error = NULL;
#ifdef HAVE_GIO_UNIX
PrintTaskData *ptd;
#endif
g_return_if_fail (GTK_IS_PRINT_DIALOG (self));
g_return_if_fail (parent == NULL || GTK_IS_WINDOW (parent));
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_check_cancellable (task, FALSE);
g_task_set_source_tag (task, gtk_print_dialog_print);
#ifdef HAVE_GIO_UNIX
ptd = print_task_data_new ();
ptd->token = setup ? setup->token : 0;
g_task_set_task_data (task, ptd, print_task_data_free);
if (!g_unix_open_pipe (ptd->fds, O_CLOEXEC, &error))
{
g_task_return_error (task, error);
g_object_unref (task);
return;
}
if (cancellable)
g_signal_connect (cancellable, "cancelled", G_CALLBACK (cancelled_cb), task);
if (!ensure_portal_proxy (self, &error))
{
if (setup == NULL || gtk_print_setup_get_printer (setup) == NULL)
{
GtkPrintUnixDialog *window;
window = create_print_dialog (self,
setup ? setup->print_settings : self->print_settings,
setup ? setup->page_setup : self->page_setup,
parent);
g_signal_connect (window, "response", G_CALLBACK (print_response_cb), task);
gtk_window_present (GTK_WINDOW (window));
}
else
{
print_content (setup, task);
}
}
else
{
if (parent &&
gtk_widget_is_visible (GTK_WIDGET (parent)) &&
gtk_window_export_handle (parent, print_window_handle_exported, task))
return;
print_window_handle_exported (parent, "", task);
}
#else
g_task_return_new_error (task,
GTK_DIALOG_ERROR, GTK_DIALOG_ERROR_FAILED,
"GtkPrintDialog is not supported on this platform");
g_object_unref (task);
#endif
}
/**
* gtk_print_dialog_print_finish:
* @self: a `GtkPrintDialog`
* @result: a `GAsyncResult`
* @error: return location for a [enum@Gtk.DialogError] error
*
* Finishes the [method@Gtk.PrintDialog.print] call and
* returns the results.
*
* If the call was successful, the content to be printed should be
* written to the returned output stream. Otherwise, `NULL` is returned.
*
* The overall results of the print operation will be returned in the
* [method@Gio.OutputStream.close] call, so if you are interested in the
* results, you need to explicitly close the output stream (it will be
* closed automatically if you just unref it). Be aware that the close
* call may not be instant as it operation will for the printer to finish
* printing.
*
* Returns: (nullable) (transfer full): a [class@Gio.OutputStream]
*
* Since: 4.14
*/
GOutputStream *
gtk_print_dialog_print_finish (GtkPrintDialog *self,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (GTK_IS_PRINT_DIALOG (self), FALSE);
g_return_val_if_fail (g_task_is_valid (result, self), FALSE);
g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == gtk_print_dialog_print, FALSE);
return g_task_propagate_pointer (G_TASK (result), error);
}
/**
* gtk_print_dialog_print_file:
* @self: a `GtkPrintDialog`
* @parent: (nullable): the parent `GtkWindow`
* @setup: (nullable): the `GtkPrintSetup` to use
* @file: the `GFile` to print
* @cancellable: (nullable): a `GCancellable` to cancel the operation
* @callback: (scope async): a callback to call when the operation is complete
* @user_data: (closure callback): data to pass to @callback
*
* This function prints a file.
*
* If you pass `NULL` as @setup, then this method will present a print dialog.
* Otherwise, it will attempt to print directly, without user interaction.
*
* The @callback will be called when the printing is done. It should call
* [method@Gtk.PrintDialog.print_file_finish] to obtain the results.
*
* Since: 4.14
*/
void
gtk_print_dialog_print_file (GtkPrintDialog *self,
GtkWindow *parent,
GtkPrintSetup *setup,
GFile *file,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
#ifdef HAVE_GIO_UNIX
PrintTaskData *ptd;
GFileInputStream *content;
#endif
GError *error = NULL;
g_return_if_fail (GTK_IS_PRINT_DIALOG (self));
g_return_if_fail (parent == NULL || GTK_IS_WINDOW (parent));
g_return_if_fail (G_IS_FILE (file));
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_check_cancellable (task, FALSE);
g_task_set_source_tag (task, gtk_print_dialog_print_file);
#ifdef HAVE_GIO_UNIX
ptd = print_task_data_new ();
ptd->token = setup ? setup->token : 0;
g_task_set_task_data (task, ptd, print_task_data_free);
content = g_file_read (file, NULL, NULL);
if (G_IS_FILE_DESCRIPTOR_BASED (content))
ptd->fds[0] = dup (g_file_descriptor_based_get_fd (G_FILE_DESCRIPTOR_BASED (content)));
g_clear_object (&content);
if (ptd->fds[0] == -1)
{
g_task_return_new_error (task,
GTK_DIALOG_ERROR, GTK_DIALOG_ERROR_FAILED,
"Failed to create read fd");
g_object_unref (task);
return;
}
if (cancellable)
g_signal_connect (cancellable, "cancelled", G_CALLBACK (cancelled_cb), task);
if (!ensure_portal_proxy (self, &error))
{
if (setup == NULL || gtk_print_setup_get_printer (setup) == NULL)
{
GtkPrintUnixDialog *window;
window = create_print_dialog (self,
setup ? setup->print_settings : self->print_settings,
setup ? setup->page_setup : self->page_setup,
parent);
g_signal_connect (window, "response", G_CALLBACK (print_response_cb), task);
gtk_window_present (GTK_WINDOW (window));
}
else
{
print_content (setup, task);
}
}
else
{
if (parent &&
gtk_widget_is_visible (GTK_WIDGET (parent)) &&
gtk_window_export_handle (parent, print_window_handle_exported, task))
return;
print_window_handle_exported (parent, "", task);
}
#else
g_task_return_new_error (task,
GTK_DIALOG_ERROR, GTK_DIALOG_ERROR_FAILED,
"GtkPrintDialog is not supported on this platform");
g_object_unref (task);
#endif
}
/**
* gtk_print_dialog_print_file_finish:
* @self: a `GtkPrintDialog`
* @result: a `GAsyncResult`
* @error: return location for a [enum@Gtk.DialogError] error
*
* Finishes the [method@Gtk.PrintDialog.print_file] call and
* returns the results.
*
* Returns: Whether the call was successful
*
* Since: 4.14
*/
gboolean
gtk_print_dialog_print_file_finish (GtkPrintDialog *self,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (GTK_IS_PRINT_DIALOG (self), FALSE);
g_return_val_if_fail (g_task_is_valid (result, self), FALSE);
g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == gtk_print_dialog_print_file, FALSE);
return g_task_propagate_boolean (G_TASK (result), error);
}
/* }}} */
/* vim:set foldmethod=marker expandtab: */