diff --git a/demos/gtk-demo/path_fill.c b/demos/gtk-demo/path_fill.c index 73873fa7c3..ea49bb3e38 100644 --- a/demos/gtk-demo/path_fill.c +++ b/demos/gtk-demo/path_fill.c @@ -2,10 +2,13 @@ * * This demo shows how to use GskPath to draw shapes that are (a bit) * more complex than a rounded rectangle. + * + * It also demonstrates printing to a stream with GtkPrintDialog. */ #include #include +#include #include "paintable.h" @@ -165,6 +168,89 @@ gtk_logo_paintable_new (void) return GDK_PAINTABLE (self); } +static cairo_status_t +write_cairo (void *closure, + const unsigned char *data, + unsigned int length) +{ + GOutputStream *stream = closure; + gsize written; + GError *error = NULL; + + if (!g_output_stream_write_all (stream, data, length, &written, NULL, &error)) + { + g_print ("Error writing pdf stream: %s\n", error->message); + g_error_free (error); + return CAIRO_STATUS_WRITE_ERROR; + } + + return CAIRO_STATUS_SUCCESS; +} + +static void +print_ready (GObject *source, + GAsyncResult *result, + gpointer data) +{ + GtkPrintDialog *dialog = GTK_PRINT_DIALOG (source); + GError *error = NULL; + GOutputStream *stream; + GtkSnapshot *snapshot; + GdkPaintable *paintable; + GskRenderNode *node; + cairo_surface_t *surface; + cairo_t *cr; + + stream = gtk_print_dialog_print_finish (dialog, result, &error); + if (stream == NULL) + { + g_print ("Failed to get output stream: %s\n", error->message); + g_error_free (error); + return; + } + + snapshot = gtk_snapshot_new (); + paintable = gtk_picture_get_paintable (GTK_PICTURE (data)); + gdk_paintable_snapshot (paintable, snapshot, 100, 100); + node = gtk_snapshot_free_to_node (snapshot); + + surface = cairo_pdf_surface_create_for_stream (write_cairo, stream, 100, 100); + cr = cairo_create (surface); + + gsk_render_node_draw (node, cr); + + cairo_destroy (cr); + cairo_surface_destroy (surface); + gsk_render_node_unref (node); + + if (!g_output_stream_close (stream, NULL, &error)) + { + g_print ("Error from close: %s\n", error->message); + g_error_free (error); + } + + g_object_unref (stream); +} + +static void +print (GtkButton *button, + gpointer data) +{ + GtkWidget *picture = data; + GtkPrintDialog *dialog; + + dialog = gtk_print_dialog_new (); + + gtk_print_dialog_print (dialog, + GTK_WINDOW (gtk_widget_get_root (picture)), + NULL, + NULL, + print_ready, + picture); + + g_object_unref (dialog); +} + GtkWidget * do_path_fill (GtkWidget *do_widget) { @@ -172,12 +258,21 @@ do_path_fill (GtkWidget *do_widget) if (!window) { + GtkWidget *header, *button, *label; GtkWidget *picture; GdkPaintable *paintable; window = gtk_window_new (); - gtk_window_set_resizable (GTK_WINDOW (window), TRUE); + gtk_window_set_resizable (GTK_WINDOW (window), FALSE); + gtk_window_set_default_size (GTK_WINDOW (window), 100, 100); gtk_window_set_title (GTK_WINDOW (window), "Fill and Stroke"); + header = gtk_header_bar_new (); + button = gtk_button_new_from_icon_name ("printer-symbolic"); + gtk_header_bar_pack_start (GTK_HEADER_BAR (header), button); + label = gtk_label_new ("Fill and Stroke"); + gtk_widget_add_css_class (label, "title"); + gtk_header_bar_set_title_widget (GTK_HEADER_BAR (header), label); + gtk_window_set_titlebar (GTK_WINDOW (window), header); g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window); paintable = gtk_logo_paintable_new (); @@ -186,6 +281,8 @@ do_path_fill (GtkWidget *do_widget) gtk_picture_set_can_shrink (GTK_PICTURE (picture), FALSE); g_object_unref (paintable); + g_signal_connect (button, "clicked", G_CALLBACK (print), picture); + gtk_window_set_child (GTK_WINDOW (window), picture); } diff --git a/demos/gtk-demo/pickers.c b/demos/gtk-demo/pickers.c index 7f45e98151..5a5b9f6325 100644 --- a/demos/gtk-demo/pickers.c +++ b/demos/gtk-demo/pickers.c @@ -1,8 +1,9 @@ /* Pickers and Launchers - * #Keywords: GtkColorDialog, GtkFontDialog, GtkFileDialog, GtkFileLauncher, GtkUriLauncher + * #Keywords: GtkColorDialog, GtkFontDialog, GtkFileDialog, GtkPrintDialog, GtkFileLauncher, GtkUriLauncher * * The dialogs are mainly intended for use in preference dialogs. - * They allow to select colors, fonts and applications. + * They allow to select colors, fonts and files. There is also a + * print dialog. * * The launchers let you open files or URIs in applications that * can handle them. @@ -168,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 7c131fbf27..8ff6aed80f 100644 --- a/gtk/gtkprintdialog.c +++ b/gtk/gtkprintdialog.c @@ -28,16 +28,188 @@ #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 */ + /** * GtkPrintDialog: * @@ -45,32 +217,23 @@ * 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 */ -/* {{{ GObject implementation */ 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; @@ -80,7 +243,7 @@ struct _GtkPrintDialog enum { PROP_ACCEPT_LABEL = 1, - PROP_DEFAULT_PAGE_SETUP, + PROP_PAGE_SETUP, PROP_MODAL, PROP_PRINT_SETTINGS, PROP_TITLE, @@ -103,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); @@ -129,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: @@ -165,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: @@ -200,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 */ @@ -210,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); @@ -249,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 */ @@ -282,6 +442,25 @@ gtk_print_dialog_new (void) /* }}} */ /* {{{ 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` @@ -295,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]); } @@ -336,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 */ @@ -344,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]); } @@ -405,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]); } /** @@ -448,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 */ @@ -480,43 +653,151 @@ gtk_print_dialog_set_print_settings (GtkPrintDialog *self, g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_PRINT_SETTINGS]); } -/** - * 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; -} - /* }}} */ - /* {{{ Async implementation */ +/* {{{ 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"); @@ -572,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; @@ -580,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, @@ -625,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; @@ -633,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); } @@ -672,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; @@ -686,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, @@ -712,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; @@ -725,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, @@ -753,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 (); @@ -762,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})", @@ -781,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, @@ -791,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); } @@ -806,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; @@ -821,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); } } @@ -868,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", @@ -925,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) @@ -955,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"); @@ -979,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 { @@ -1025,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) @@ -1033,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) @@ -1125,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, @@ -1146,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 @@ -1257,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 @@ -1294,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 @@ -1396,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, diff --git a/gtk/print/gtkprintjob.c b/gtk/print/gtkprintjob.c index 9efc638ef9..b060b78bf7 100644 --- a/gtk/print/gtkprintjob.c +++ b/gtk/print/gtkprintjob.c @@ -699,8 +699,9 @@ gtk_print_job_send (GtkPrintJob *job, g_return_if_fail (job->spool_io != NULL); gtk_print_job_set_status (job, GTK_PRINT_STATUS_SENDING_DATA); - - g_io_channel_seek_position (job->spool_io, 0, G_SEEK_SET, NULL); + + if (g_io_channel_get_flags (job->spool_io) & G_IO_FLAG_IS_SEEKABLE) + g_io_channel_seek_position (job->spool_io, 0, G_SEEK_SET, NULL); gtk_print_backend_print_stream (job->backend, job, job->spool_io,