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