From fda9fd5ced24917d49f271b2376392d2633cc968 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Fri, 29 Sep 2023 22:01:32 -0400 Subject: [PATCH] print dialog: Some API revisions Make gtk_print_dialog_setup_finish return a GtkPrintSetup object, which encapsulates all the data that needs to be transferred between the setup and print calls, and make the print_file and print methods take an extra GtkPrintSetup argument. Change the print call to return an output stream, rather than take an input stream. The results are now returned when the output stream is closed. With some further cleanup, this makes the GtkPrintDialog object a proper builder object - you can create multiple print dialogs from the same GtkPrintDialog object, in parallel, and they won't interfere with each other. --- demos/gtk-demo/pickers.c | 2 +- gtk/gtkprintdialog.c | 973 +++++++++++++++++++++++++++------------ gtk/gtkprintdialog.h | 38 +- 3 files changed, 699 insertions(+), 314 deletions(-) diff --git a/demos/gtk-demo/pickers.c b/demos/gtk-demo/pickers.c index 74efb8d843..5a5b9f6325 100644 --- a/demos/gtk-demo/pickers.c +++ b/demos/gtk-demo/pickers.c @@ -169,7 +169,7 @@ print_file (GtkButton *picker) abort_mission, g_object_ref (cancellable), g_object_unref); g_object_set_data (G_OBJECT (cancellable), "timeout", GUINT_TO_POINTER (id)); - gtk_print_dialog_print_file (dialog, parent, file, cancellable, print_file_done, NULL); + gtk_print_dialog_print_file (dialog, parent, NULL, file, cancellable, print_file_done, NULL); g_object_unref (cancellable); g_object_unref (dialog); diff --git a/gtk/gtkprintdialog.c b/gtk/gtkprintdialog.c index 4c16919dd8..8ff6aed80f 100644 --- a/gtk/gtkprintdialog.c +++ b/gtk/gtkprintdialog.c @@ -28,16 +28,186 @@ #include "print/gtkprinter.h" +#include "print/gtkprinterprivate.h" + #ifdef HAVE_GIO_UNIX + +#include + +#include #include +#include #include #include "print/gtkprintjob.h" -#include "print/gtkprinterprivate.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 */ /** @@ -47,9 +217,9 @@ * 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.prepare_print] - * function. The actual printing can be done with [method@Gtk.PrintDialog.print_stream] - * or [method@Gtk.PrintDialog.print_file]. These APIs follows the GIO async pattern, + * 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 @@ -60,18 +230,10 @@ struct _GtkPrintDialog GObject parent_instance; GtkPrintSettings *print_settings; - GtkPageSetup *default_page_setup; - - GtkPrinter *printer; + GtkPageSetup *page_setup; GDBusProxy *portal; - GtkWindow *exported_window; - - char *portal_handle; - unsigned int token; - unsigned int response_signal_id; - char *accept_label; char *title; @@ -81,7 +243,7 @@ struct _GtkPrintDialog enum { PROP_ACCEPT_LABEL = 1, - PROP_DEFAULT_PAGE_SETUP, + PROP_PAGE_SETUP, PROP_MODAL, PROP_PRINT_SETTINGS, PROP_TITLE, @@ -104,12 +266,9 @@ gtk_print_dialog_finalize (GObject *object) { GtkPrintDialog *self = GTK_PRINT_DIALOG (object); - g_clear_object (&self->printer); g_clear_object (&self->portal); - g_free (self->portal_handle); - g_clear_object (&self->exported_window); g_clear_object (&self->print_settings); - g_clear_object (&self->default_page_setup); + g_clear_object (&self->page_setup); g_free (self->accept_label); g_free (self->title); @@ -130,8 +289,8 @@ gtk_print_dialog_get_property (GObject *object, g_value_set_string (value, self->accept_label); break; - case PROP_DEFAULT_PAGE_SETUP: - g_value_set_object (value, self->default_page_setup); + case PROP_PAGE_SETUP: + g_value_set_object (value, self->page_setup); break; case PROP_MODAL: @@ -166,8 +325,8 @@ gtk_print_dialog_set_property (GObject *object, gtk_print_dialog_set_accept_label (self, g_value_get_string (value)); break; - case PROP_DEFAULT_PAGE_SETUP: - gtk_print_dialog_set_default_page_setup (self, g_value_get_object (value)); + case PROP_PAGE_SETUP: + gtk_print_dialog_set_page_setup (self, g_value_get_object (value)); break; case PROP_MODAL: @@ -201,7 +360,7 @@ gtk_print_dialog_class_init (GtkPrintDialogClass *class) * 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.prepare_print]. + * that is presented by [method@Gtk.PrintDialog.setup]. * * Since: 4.14 */ @@ -211,14 +370,14 @@ gtk_print_dialog_class_init (GtkPrintDialogClass *class) G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY); /** - * GtkPrintDialog:default-page-setup: (attributes org.gtk.Property.get=gtk_print_dialog_get_default_page_setup org.gtk.Property.set=gtk_print_dialog_set_default_page_setup) + * 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 default page setup to use. + * The page setup to use. * * Since: 4.14 */ - properties[PROP_DEFAULT_PAGE_SETUP] = - g_param_spec_object ("default-page-setup", NULL, NULL, + 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); @@ -250,7 +409,7 @@ gtk_print_dialog_class_init (GtkPrintDialogClass *class) * 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.prepare_print]. + * presented by [method@Gtk.PrintDialog.setup]. * * Since: 4.14 */ @@ -315,17 +474,14 @@ void gtk_print_dialog_set_title (GtkPrintDialog *self, const char *title) { - char *new_title; - g_return_if_fail (GTK_IS_PRINT_DIALOG (self)); g_return_if_fail (title != NULL); if (g_strcmp0 (self->title, title) == 0) return; - new_title = g_strdup (title); g_free (self->title); - self->title = new_title; + self->title = g_strdup (title); g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TITLE]); } @@ -356,7 +512,7 @@ gtk_print_dialog_get_accept_label (GtkPrintDialog *self) * * Sets the label that will be shown on the * accept button of the print dialog shown for - * [method@Gtk.PrintDialog.prepare_print]. + * [method@Gtk.PrintDialog.setup]. * * Since: 4.14 */ @@ -364,17 +520,14 @@ void gtk_print_dialog_set_accept_label (GtkPrintDialog *self, const char *accept_label) { - char *new_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; - new_label = g_strdup (accept_label); g_free (self->accept_label); - self->accept_label = new_label; + self->accept_label = g_strdup (accept_label); g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACCEPT_LABEL]); } @@ -425,41 +578,41 @@ gtk_print_dialog_set_modal (GtkPrintDialog *self, } /** - * gtk_print_dialog_get_default_page_setup: + * gtk_print_dialog_get_page_setup: * @self: a `GtkPrintDialog` * - * Returns the default page setup. + * Returns the page setup. * - * Returns: (transfer none): the default page setup + * Returns: (transfer none): the page setup * * Since: 4.14 */ GtkPageSetup * -gtk_print_dialog_get_default_page_setup (GtkPrintDialog *self) +gtk_print_dialog_get_page_setup (GtkPrintDialog *self) { g_return_val_if_fail (GTK_IS_PRINT_DIALOG (self), NULL); - return self->default_page_setup; + return self->page_setup; } /** - * gtk_print_dialog_set_default_page_setup: + * gtk_print_dialog_set_page_setup: * @self: a `GtkPrintDialog` - * @default_page_setup: the new default page setup + * @page_setup: the new page setup * - * Set the default page setup for the print dialog. + * Set the page setup for the print dialog. * * Since: 4.14 */ void -gtk_print_dialog_set_default_page_setup (GtkPrintDialog *self, - GtkPageSetup *default_page_setup) +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 (default_page_setup)); + g_return_if_fail (GTK_IS_PAGE_SETUP (page_setup)); - if (g_set_object (&self->default_page_setup, default_page_setup)) - g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DEFAULT_PAGE_SETUP]); + if (g_set_object (&self->page_setup, page_setup)) + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_PAGE_SETUP]); } /** @@ -468,7 +621,7 @@ gtk_print_dialog_set_default_page_setup (GtkPrintDialog *self, * * Returns the print settings for the print dialog. * - * Returns: (transfer none): the print settings + * Returns: (transfer none): the settings * * Since: 4.14 */ @@ -500,24 +653,151 @@ gtk_print_dialog_set_print_settings (GtkPrintDialog *self, 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 (!self->portal_handle) + if (!ptd->portal_handle) return; message = g_dbus_message_new_method_call (PORTAL_BUS_NAME, - self->portal_handle, + ptd->portal_handle, PORTAL_REQUEST_INTERFACE, "Close"); @@ -573,6 +853,7 @@ 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; @@ -581,40 +862,18 @@ cleanup_portal_call_data (GTask *task) if (cancellable) g_signal_handlers_disconnect_by_func (cancellable, cancelled_cb, task); - if (self->response_signal_id != 0) + if (ptd->response_signal_id != 0) { - g_dbus_connection_signal_unsubscribe (connection, self->response_signal_id); - self->response_signal_id = 0; + g_dbus_connection_signal_unsubscribe (connection, ptd->response_signal_id); + ptd->response_signal_id = 0; } - g_clear_pointer (&self->portal_handle, g_free); - g_clear_object (&self->exported_window); + g_clear_pointer (&ptd->portal_handle, g_free); + g_clear_object (&ptd->exported_window); } -static void -response_to_task (unsigned int response, - GTask *task) -{ - 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; - } -} +/* }}} */ +/* {{{ Portal Setup implementation */ static void prepare_print_response (GDBusConnection *connection, @@ -626,7 +885,6 @@ prepare_print_response (GDBusConnection *connection, gpointer user_data) { GTask *task = user_data; - GtkPrintDialog *self = GTK_PRINT_DIALOG (g_task_get_source_object (task)); guint32 response; GVariant *options = NULL; @@ -634,34 +892,57 @@ prepare_print_response (GDBusConnection *connection, g_variant_get (parameters, "(u@a{sv})", &response, &options); - if (response == 0) + switch (response) { - GVariant *v; - GtkPrintSettings *settings; - GtkPageSetup *page_setup; - unsigned int token; + case 0: + { + GVariant *v; + GtkPrintSettings *settings; + GtkPageSetup *page_setup; + GtkPrintSetup *setup; + unsigned int token; - v = g_variant_lookup_value (options, "settings", G_VARIANT_TYPE_VARDICT); - settings = gtk_print_settings_new_from_gvariant (v); - g_variant_unref (v); + setup = gtk_print_setup_new (); - gtk_print_dialog_set_print_settings (self, settings); - g_object_unref (settings); + 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); + gtk_print_setup_set_print_settings (setup, settings); + g_object_unref (settings); - gtk_print_dialog_set_default_page_setup (self, page_setup); - g_object_unref (page_setup); + 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", &token); - self->token = token; + 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); - response_to_task (response, task); g_object_unref (task); } @@ -673,6 +954,7 @@ prepare_print_called (GObject *source, 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; @@ -687,20 +969,20 @@ prepare_print_called (GObject *source, } g_variant_get (ret, "(o)", &path); - if (strcmp (path, self->portal_handle) != 0) + if (strcmp (path, ptd->portal_handle) != 0) { - g_free (self->portal_handle); - self->portal_handle = g_steal_pointer (&path); + g_free (ptd->portal_handle); + ptd->portal_handle = g_steal_pointer (&path); g_dbus_connection_signal_unsubscribe (connection, - self->response_signal_id); + ptd->response_signal_id); - self->response_signal_id = + ptd->response_signal_id = g_dbus_connection_signal_subscribe (connection, PORTAL_BUS_NAME, PORTAL_REQUEST_INTERFACE, "Response", - self->portal_handle, + ptd->portal_handle, NULL, G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE, prepare_print_response, @@ -713,12 +995,13 @@ prepare_print_called (GObject *source, } static void -prepare_print_window_handle_exported (GtkWindow *window, - const char *window_handle, - gpointer user_data) +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; @@ -726,14 +1009,16 @@ prepare_print_window_handle_exported (GtkWindow *window, GVariant *options; GVariantBuilder opt_builder; - self->portal_handle = gtk_get_portal_request_path (connection, &handle_token); + g_assert (ptd->portal_handle == NULL); + ptd->portal_handle = gtk_get_portal_request_path (connection, &handle_token); - self->response_signal_id = + g_assert (ptd->response_signal_id == 0); + ptd->response_signal_id = g_dbus_connection_signal_subscribe (connection, PORTAL_BUS_NAME, PORTAL_REQUEST_INTERFACE, "Response", - self->portal_handle, + ptd->portal_handle, NULL, G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE, prepare_print_response, @@ -754,8 +1039,8 @@ prepare_print_window_handle_exported (GtkWindow *window, settings = g_variant_builder_end (&builder); } - if (self->default_page_setup) - setup = gtk_page_setup_to_gvariant (self->default_page_setup); + if (self->page_setup) + setup = gtk_page_setup_to_gvariant (self->page_setup); else { GtkPageSetup *page_setup = gtk_page_setup_new (); @@ -763,8 +1048,6 @@ prepare_print_window_handle_exported (GtkWindow *window, g_object_unref (page_setup); } - self->token = 0; - g_dbus_proxy_call (self->portal, "PreparePrint", g_variant_new ("(ss@a{sv}@a{sv}@a{sv})", @@ -782,6 +1065,9 @@ prepare_print_window_handle_exported (GtkWindow *window, g_free (handle_token); } +/* }}} */ +/* {{{ Portal Print implementation */ + static void print_response (GDBusConnection *connection, const char *sender_name, @@ -792,11 +1078,62 @@ print_response (GDBusConnection *connection, 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); - response_to_task (response, task); + + 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); } @@ -807,6 +1144,7 @@ print_called (GObject *source, { 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; @@ -822,43 +1160,37 @@ print_called (GObject *source, } g_variant_get (ret, "(o)", &path); - if (strcmp (path, self->portal_handle) != 0) + if (strcmp (path, ptd->portal_handle) != 0) { - g_free (self->portal_handle); - self->portal_handle = g_steal_pointer (&path); + g_free (ptd->portal_handle); + ptd->portal_handle = g_steal_pointer (&path); g_dbus_connection_signal_unsubscribe (connection, - self->response_signal_id); + ptd->response_signal_id); - self->response_signal_id = + ptd->response_signal_id = g_dbus_connection_signal_subscribe (connection, PORTAL_BUS_NAME, PORTAL_REQUEST_INTERFACE, "Response", - self->portal_handle, + ptd->portal_handle, NULL, G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE, print_response, - self, NULL); + task, NULL); } g_free (path); g_variant_unref (ret); -} -static int -get_content_fd (GObject *content, - GError **error) -{ - if (G_IS_FILE_DESCRIPTOR_BASED (content)) - return g_file_descriptor_based_get_fd (G_FILE_DESCRIPTOR_BASED (content)); - else + if (ptd->fds[1] != -1) { - g_set_error (error, - GTK_DIALOG_ERROR, GTK_DIALOG_ERROR_FAILED, - "Not implemented"); - return -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); } } @@ -869,45 +1201,37 @@ print_window_handle_exported (GtkWindow *window, { 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); - int fd; char *handle_token; GVariantBuilder opt_builder; GUnixFDList *fd_list; int idx; - GError *error = NULL; if (window) - self->exported_window = g_object_ref (window); + ptd->exported_window = g_object_ref (window); - fd = get_content_fd (g_task_get_task_data (task), &error); - if (fd == -1) - { - cleanup_portal_call_data (task); - g_task_return_error (task, error); - g_object_unref (task); - return; - } + g_assert (ptd->fds[0] != -1); - self->portal_handle = gtk_get_portal_request_path (connection, &handle_token); + ptd->portal_handle = gtk_get_portal_request_path (connection, &handle_token); - self->response_signal_id = + ptd->response_signal_id = g_dbus_connection_signal_subscribe (connection, PORTAL_BUS_NAME, PORTAL_REQUEST_INTERFACE, "Response", - self->portal_handle, + 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, fd, NULL); + 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 (self->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", @@ -926,29 +1250,33 @@ print_window_handle_exported (GtkWindow *window, g_free (handle_token); } +/* }}} */ +/* {{{ Local fallback */ + static GtkPrintUnixDialog * -create_print_dialog (GtkPrintDialog *self, - GtkWindow *parent) +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 (self->print_settings) - gtk_print_unix_dialog_set_settings (dialog, self->print_settings); + if (print_settings) + gtk_print_unix_dialog_set_settings (dialog, print_settings); - if (self->default_page_setup) - gtk_print_unix_dialog_set_page_setup (dialog, self->default_page_setup); + if (page_setup) + gtk_print_unix_dialog_set_page_setup (dialog, page_setup); return dialog; } static void -response_cb (GtkPrintUnixDialog *window, - int response, - GTask *task) +setup_response_cb (GtkPrintUnixDialog *window, + int response, + GTask *task) { - GtkPrintDialog *self = GTK_PRINT_DIALOG (g_task_get_source_object (task)); GCancellable *cancellable = g_task_get_cancellable (task); if (cancellable) @@ -956,11 +1284,13 @@ response_cb (GtkPrintUnixDialog *window, if (response == GTK_RESPONSE_OK) { - gtk_print_dialog_set_print_settings (self, gtk_print_unix_dialog_get_settings (window)); - gtk_print_dialog_set_default_page_setup (self, gtk_print_unix_dialog_get_page_setup (window)); - g_set_object (&self->printer, gtk_print_unix_dialog_get_selected_printer (window)); + GtkPrintSetup *setup = gtk_print_setup_new (); - g_task_return_boolean (task, TRUE); + 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"); @@ -980,39 +1310,53 @@ job_complete (GtkPrintJob *job, const GError *error) { GTask *task = user_data; - if (error) + 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_file (GtkPrintDialog *self, - GTask *task) +print_content (GtkPrintSetup *setup, + GTask *task) { - int fd; - GError *error = NULL; + PrintTaskData *ptd = g_task_get_task_data (task); - fd = get_content_fd (g_task_get_task_data (task), &error); - if (fd == -1) - { - cleanup_portal_call_data (task); - g_task_return_error (task, error); - g_object_unref (task); - return; - } + g_assert (ptd->fds[0] != -1); - if (self->printer) + if (setup->printer) { GtkPrintJob *job; + g_object_ref (task); + job = gtk_print_job_new ("My first printjob", - self->printer, - self->print_settings, - self->default_page_setup); - gtk_print_job_set_source_fd (job, fd, NULL); - gtk_print_job_send (job, job_complete, task, g_object_unref); + 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 { @@ -1026,7 +1370,6 @@ print_response_cb (GtkPrintUnixDialog *window, int response, GTask *task) { - GtkPrintDialog *self = GTK_PRINT_DIALOG (g_task_get_source_object (task)); GCancellable *cancellable = g_task_get_cancellable (task); if (cancellable) @@ -1034,89 +1377,83 @@ print_response_cb (GtkPrintUnixDialog *window, if (response == GTK_RESPONSE_OK) { - gtk_print_dialog_set_print_settings (self, gtk_print_unix_dialog_get_settings (window)); - gtk_print_dialog_set_default_page_setup (self, gtk_print_unix_dialog_get_page_setup (window)); - g_set_object (&self->printer, gtk_print_unix_dialog_get_selected_printer (window)); + GtkPrintSetup *setup = gtk_print_setup_new (); - print_file (self, g_object_ref (task)); + 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_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_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_task_return_new_error (task, GTK_DIALOG_ERROR, GTK_DIALOG_ERROR_FAILED, "Unknown failure (%d)", response); + g_object_unref (task); + } - g_object_unref (task); gtk_window_destroy (GTK_WINDOW (window)); } -static void -try_to_find_printer (GtkPrintDialog *self) -{ - const char *name; - - if (self->printer) - return; - - if (!self->print_settings) - return; - - name = gtk_print_settings_get (self->print_settings, GTK_PRINT_SETTINGS_PRINTER); - - if (!name) - return; - - self->printer = gtk_printer_find (name); -} +/* }}} */ #endif /* HAVE_GIO_UNIX */ /* }}} */ -/* {{{ Async API */ + /* {{{ Async API */ /** - * gtk_print_dialog_prepare_print: + * 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. + * 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.prepare_print_finish] - * to obtain the results. + * 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_stream] on @self + * 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_prepare_print (GtkPrintDialog *self, - GtkWindow *parent, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) +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)); - g_return_if_fail (self->response_signal_id == 0); - g_return_if_fail (self->exported_window == NULL); 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_prepare_print); + g_task_set_source_tag (task, gtk_print_dialog_setup); #ifdef HAVE_GIO_UNIX if (cancellable) @@ -1126,17 +1463,20 @@ gtk_print_dialog_prepare_print (GtkPrintDialog *self, { GtkPrintUnixDialog *window; - window = create_print_dialog (self, parent); - g_signal_connect (window, "response", G_CALLBACK (response_cb), task); + 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, prepare_print_window_handle_exported, task)) + gtk_window_export_handle (parent, setup_window_handle_exported, task)) return; - prepare_print_window_handle_exported (parent, "", task); + + setup_window_handle_exported (parent, "", task); } #else g_task_return_new_error (task, @@ -1147,97 +1487,105 @@ gtk_print_dialog_prepare_print (GtkPrintDialog *self, } /** - * gtk_print_dialog_prepare_print_finish: + * 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.prepare_print] call. + * Finishes the [method@Gtk.PrintDialog.setup] call. * - * If the call was successful, the print settings and the - * default page setup will be updated with the users changes. + * 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: Whether the call was successful + * Returns: (nullable): The `GtkPrintSetup` object that resulted from the call, + * or `NULL` if the call was not successful * * Since: 4.14 */ -gboolean -gtk_print_dialog_prepare_print_finish (GtkPrintDialog *self, - GAsyncResult *result, - GError **error) +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_prepare_print, FALSE); - g_return_val_if_fail (self->response_signal_id == 0, FALSE); - g_return_val_if_fail (self->exported_window == NULL, FALSE); + g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == gtk_print_dialog_setup, FALSE); - return g_task_propagate_boolean (G_TASK (result), error); + return g_task_propagate_pointer (G_TASK (result), error); } /** - * gtk_print_dialog_print_stream: + * gtk_print_dialog_print: * @self: a `GtkPrintDialog` * @parent: (nullable): the parent `GtkWindow` - * @content: the `GInputStream` to print + * @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 an input stream. + * This function prints content from a stream. * - * If [method@Gtk.PrintDialog.prepare_print] has not been called - * on @self before, then this method might present a print dialog. - * Otherwise, it will attempt to print directly, without user - * interaction. + * 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_stream_finish] - * to obtain the results. + * 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_stream (GtkPrintDialog *self, - GtkWindow *parent, - GInputStream *content, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) +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)); - g_return_if_fail (G_IS_INPUT_STREAM (content)); - g_return_if_fail (self->response_signal_id == 0); - g_return_if_fail (self->exported_window == NULL); 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_stream); - g_task_set_task_data (task, g_object_ref (content), g_object_unref); + 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, FD_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)) { - try_to_find_printer (self); - - if (!self->printer) + if (setup == NULL || gtk_print_setup_get_printer (setup) == NULL) { GtkPrintUnixDialog *window; - window = create_print_dialog (self, parent); + 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_file (self, task); + print_content (setup, task); } } else @@ -1258,36 +1606,45 @@ gtk_print_dialog_print_stream (GtkPrintDialog *self, } /** - * gtk_print_dialog_print_stream_finish: + * 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_stream] call and + * Finishes the [method@Gtk.PrintDialog.print] call and * returns the results. * - * Returns: Whether the call was successful + * 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 */ -gboolean -gtk_print_dialog_print_stream_finish (GtkPrintDialog *self, - GAsyncResult *result, - GError **error) +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_stream, FALSE); - g_return_val_if_fail (self->response_signal_id == 0, FALSE); - g_return_val_if_fail (self->exported_window == NULL, FALSE); + g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == gtk_print_dialog_print, FALSE); - return g_task_propagate_boolean (G_TASK (result), error); + 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 @@ -1295,68 +1652,76 @@ gtk_print_dialog_print_stream_finish (GtkPrintDialog *self, * * This function prints a file. * - * If [method@Gtk.PrintDialog.prepare_print] has not been called - * on @self before, then this method might present a print dialog. - * Otherwise, it will attempt to print directly, without user - * interaction. + * 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. + * 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)); - g_return_if_fail (self->response_signal_id == 0); - g_return_if_fail (self->exported_window == NULL); 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); - content = g_file_read (file, NULL, &error); - if (!content) +#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_error (task, error); + g_task_return_new_error (task, + GTK_DIALOG_ERROR, GTK_DIALOG_ERROR_FAILED, + "Failed to create read fd"); g_object_unref (task); return; } -#ifdef HAVE_GIO_UNIX - g_task_set_task_data (task, content, g_object_unref); - if (cancellable) g_signal_connect (cancellable, "cancelled", G_CALLBACK (cancelled_cb), task); if (!ensure_portal_proxy (self, &error)) { - try_to_find_printer (self); - - if (!self->printer) + if (setup == NULL || gtk_print_setup_get_printer (setup) == NULL) { GtkPrintUnixDialog *window; - window = create_print_dialog (self, parent); + 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_file (self, task); + print_content (setup, task); } } else @@ -1397,8 +1762,6 @@ gtk_print_dialog_print_file_finish (GtkPrintDialog *self, 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); - g_return_val_if_fail (self->response_signal_id == 0, FALSE); - g_return_val_if_fail (self->exported_window == NULL, FALSE); return g_task_propagate_boolean (G_TASK (result), error); } diff --git a/gtk/gtkprintdialog.h b/gtk/gtkprintdialog.h index 7851783895..1617e5b147 100644 --- a/gtk/gtkprintdialog.h +++ b/gtk/gtkprintdialog.h @@ -29,6 +29,27 @@ G_BEGIN_DECLS +typedef struct _GtkPrintSetup GtkPrintSetup; + +#define GTK_TYPE_PRINT_SETUP (gtk_print_setup_get_type ()) + +GDK_AVAILABLE_IN_4_14 +GType gtk_print_setup_get_type (void) G_GNUC_CONST; + +GDK_AVAILABLE_IN_4_14 +GtkPrintSetup *gtk_print_setup_ref (GtkPrintSetup *setup); + +GDK_AVAILABLE_IN_4_14 +void gtk_print_setup_unref (GtkPrintSetup *setup); + +GDK_AVAILABLE_IN_4_14 +GtkPrintSettings * + gtk_print_setup_get_print_settings (GtkPrintSetup *setup); + +GDK_AVAILABLE_IN_4_14 +GtkPageSetup * gtk_print_setup_get_page_setup (GtkPrintSetup *setup); + + #define GTK_TYPE_PRINT_DIALOG (gtk_print_dialog_get_type ()) GDK_AVAILABLE_IN_4_14 @@ -59,11 +80,11 @@ void gtk_print_dialog_set_modal (GtkPrintDialog *s gboolean modal); GDK_AVAILABLE_IN_4_14 -GtkPageSetup * gtk_print_dialog_get_default_page_setup (GtkPrintDialog *self); +GtkPageSetup * gtk_print_dialog_get_page_setup (GtkPrintDialog *self); GDK_AVAILABLE_IN_4_14 -void gtk_print_dialog_set_default_page_setup (GtkPrintDialog *self, - GtkPageSetup *default_page_setup); +void gtk_print_dialog_set_page_setup (GtkPrintDialog *self, + GtkPageSetup *page_setup); GDK_AVAILABLE_IN_4_14 GtkPrintSettings * gtk_print_dialog_get_print_settings (GtkPrintDialog *self); @@ -73,33 +94,34 @@ void gtk_print_dialog_set_print_settings (GtkPrintDialog *s GtkPrintSettings *print_settings); GDK_AVAILABLE_IN_4_14 -void gtk_print_dialog_prepare_print (GtkPrintDialog *self, +void gtk_print_dialog_setup (GtkPrintDialog *self, GtkWindow *parent, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); GDK_AVAILABLE_IN_4_14 -gboolean gtk_print_dialog_prepare_print_finish (GtkPrintDialog *self, +GtkPrintSetup *gtk_print_dialog_setup_finish (GtkPrintDialog *self, GAsyncResult *result, GError **error); GDK_AVAILABLE_IN_4_14 -void gtk_print_dialog_print_stream (GtkPrintDialog *self, +void gtk_print_dialog_print (GtkPrintDialog *self, GtkWindow *parent, - GInputStream *content, + GtkPrintSetup *setup, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); GDK_AVAILABLE_IN_4_14 -gboolean gtk_print_dialog_print_stream_finish (GtkPrintDialog *self, +GOutputStream * gtk_print_dialog_print_finish (GtkPrintDialog *self, GAsyncResult *result, GError **error); GDK_AVAILABLE_IN_4_14 void gtk_print_dialog_print_file (GtkPrintDialog *self, GtkWindow *parent, + GtkPrintSetup *setup, GFile *file, GCancellable *cancellable, GAsyncReadyCallback callback,