[evince/wip/chpe/export-print-portal: 8/8] libview: Add Portal implementation of EvPrintOperationExport




commit 117e36e6ae2bbaa4ea39d521fef99f032fe585a9
Author: Christian Persch <chpe src gnome org>
Date:   Tue Jan 11 18:25:31 2022 +0100

    libview: Add Portal implementation of EvPrintOperationExport
    
    Add EvPrintOperationExportPortal that uses the Print portal.

 libview/ev-print-operation.c | 884 ++++++++++++++++++++++++++++++++++++-------
 meson.build                  |  11 +-
 2 files changed, 765 insertions(+), 130 deletions(-)
---
diff --git a/libview/ev-print-operation.c b/libview/ev-print-operation.c
index 8d8571d69..2a92e7fee 100644
--- a/libview/ev-print-operation.c
+++ b/libview/ev-print-operation.c
@@ -1,6 +1,8 @@
 /* this file is part of evince, a gnome document viewer
  *
- *  Copyright (C) 2008 Carlos Garcia Campos <carlosgc gnome org>
+ * Copyright © 2008 Carlos Garcia Campos <carlosgc gnome org>
+ * Copyright © 2016, Red Hat, Inc.
+ * Copyright © 2018, 2021 Christian Persch
  *
  * Evince is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by
@@ -28,6 +30,10 @@
 #include "ev-jobs.h"
 #include "ev-job-scheduler.h"
 
+#if GTK_CHECK_VERSION (3, 22, 0) && defined (G_OS_UNIX)
+#define PORTAL_ENABLED
+#endif
+
 enum {
        PROP_0,
        PROP_DOCUMENT
@@ -967,8 +973,6 @@ export_print_page (EvPrintOperationExport *export)
                        ev_file_exporter_end (EV_FILE_EXPORTER (op->document));
                        ev_document_doc_mutex_unlock ();
 
-                       close (export->fd);
-                       export->fd = -1;
                        update_progress (export);
                        export_print_done (export);
 
@@ -994,9 +998,6 @@ export_print_page (EvPrintOperationExport *export)
                                        ev_file_exporter_end (EV_FILE_EXPORTER (op->document));
                                        ev_document_doc_mutex_unlock ();
 
-                                       close (export->fd);
-                                       export->fd = -1;
-
                                        update_progress (export);
 
                                        export_print_done (export);
@@ -1128,6 +1129,141 @@ ev_print_operation_export_get_embed_page_setup (EvPrintOperation *op)
        return export->embed_page_setup;
 }
 
+static gboolean
+ev_print_operation_export_mkstemp (EvPrintOperationExport *export,
+                                   EvFileExporterFormat    format)
+{
+        char *filename;
+        GError *err = NULL;
+
+        filename = g_strdup_printf ("evince_print.%s.XXXXXX", format == EV_FILE_FORMAT_PDF ? "pdf" : "ps");
+        export->fd = g_file_open_tmp (filename, &export->temp_file, &err);
+        g_free (filename);
+        if (export->fd == -1) {
+                g_set_error_literal (&export->error,
+                                     GTK_PRINT_ERROR,
+                                     GTK_PRINT_ERROR_GENERAL,
+                                     err->message);
+                g_error_free (err);
+                return FALSE;
+        }
+
+        return TRUE;
+}
+
+static gboolean
+ev_print_operation_export_update_ranges (EvPrintOperationExport *export)
+{
+        GtkPrintPages print_pages;
+
+        export->page_set = gtk_print_settings_get_page_set (export->print_settings);
+        print_pages = gtk_print_settings_get_print_pages (export->print_settings);
+
+        switch (print_pages) {
+        case GTK_PRINT_PAGES_CURRENT: {
+                export->ranges = &export->one_range;
+
+                export->ranges[0].start = export->current_page;
+                export->ranges[0].end = export->current_page;
+                export->n_ranges = 1;
+
+                break;
+        }
+        case GTK_PRINT_PAGES_RANGES: {
+                gint i;
+
+                export->ranges = gtk_print_settings_get_page_ranges (export->print_settings,
+                                                                     &export->n_ranges);
+                for (i = 0; i < export->n_ranges; i++)
+                        if (export->ranges[i].end == -1 || export->ranges[i].end >= export->n_pages)
+                                export->ranges[i].end = export->n_pages - 1;
+
+                break;
+        }
+        default:
+                g_warning ("Unsupported print pages setting\n");
+                /* fallthrough */
+        case GTK_PRINT_PAGES_ALL: {
+                export->ranges = &export->one_range;
+
+                export->ranges[0].start = 0;
+                export->ranges[0].end = export->n_pages - 1;
+                export->n_ranges = 1;
+
+                break;
+        }
+        }
+
+        /* Return %TRUE iff there are any pages in the range(s) */
+        return (export->n_ranges >= 1 && clamp_ranges (export));
+}
+
+static void
+ev_print_operation_export_prepare (EvPrintOperationExport *export,
+                                   EvFileExporterFormat    format)
+{
+        EvPrintOperation *op = EV_PRINT_OPERATION (export);
+        gdouble           scale;
+        gdouble           width;
+        gdouble           height;
+        gint              first_page;
+        gint              last_page;
+
+        ev_print_operation_update_status (op, -1, -1, 0.0);
+
+        width = gtk_page_setup_get_paper_width (export->page_setup, GTK_UNIT_POINTS);
+        height = gtk_page_setup_get_paper_height (export->page_setup, GTK_UNIT_POINTS);
+        scale = gtk_print_settings_get_scale (export->print_settings) * 0.01;
+        if (scale != 1.0) {
+                width *= scale;
+                height *= scale;
+        }
+
+        export->pages_per_sheet = MAX (1, gtk_print_settings_get_number_up (export->print_settings));
+
+        export->copies = gtk_print_settings_get_n_copies (export->print_settings);
+        export->collate = gtk_print_settings_get_collate (export->print_settings);
+        export->reverse = gtk_print_settings_get_reverse (export->print_settings);
+
+        if (export->collate) {
+                export->uncollated_copies = export->copies;
+                export->collated_copies = 1;
+        } else {
+                export->uncollated_copies = 1;
+                export->collated_copies = export->copies;
+        }
+
+        if (export->reverse) {
+                export->range = export->n_ranges - 1;
+                export->inc = -1;
+        } else {
+                export->range = 0;
+                export->inc = 1;
+        }
+        find_range (export);
+
+        export->page = export->start - export->inc;
+        export->collated = export->collated_copies - 1;
+
+        get_first_and_last_page (export, &first_page, &last_page);
+
+        export->fc.format = format;
+        export->fc.filename = export->temp_file;
+        export->fc.first_page = MIN (first_page, last_page);
+        export->fc.last_page = MAX (first_page, last_page);
+        export->fc.paper_width = width;
+        export->fc.paper_height = height;
+        export->fc.duplex = FALSE;
+        export->fc.pages_per_sheet = export->pages_per_sheet;
+
+        if (ev_print_queue_is_empty (op->document))
+                ev_print_operation_export_begin (export);
+
+        ev_print_queue_push (op);
+
+        g_signal_emit (op, signals[BEGIN_PRINT], 0);
+}
+
 static void
 ev_print_operation_export_finalize (GObject *object)
 {
@@ -1232,11 +1368,583 @@ ev_print_operation_export_class_init (EvPrintOperationExportClass *klass)
        g_object_class->finalize = ev_print_operation_export_finalize;
 }
 
+#ifdef PORTAL_ENABLED
+
+/* Export with Portal */
+/* Some code copied from gtk+/gtk/gtkprintoperation-portal.c */
+
+#include "ev-portal.h"
+
+#include <gio/gunixfdlist.h>
+
+#ifdef GDK_WINDOWING_X11
+#include <gdk/gdkx.h>
+#endif
+#ifdef GDK_WINDOWING_WAYLAND
+#include <gdk/gdkwayland.h>
+#endif
+
+#define EV_TYPE_PRINT_OPERATION_EXPORT_PORTAL            (ev_print_operation_export_portal_get_type())
+#define EV_PRINT_OPERATION_EXPORT_PORTAL(object)         (G_TYPE_CHECK_INSTANCE_CAST((object), 
EV_TYPE_PRINT_OPERATION_EXPORT_PORTAL, EvPrintOperationExportPortal))
+#define EV_PRINT_OPERATION_EXPORT_PORTAL_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), 
EV_TYPE_PRINT_OPERATION_EXPORT_PORTAL, EvPrintOperationExportPortalClass))
+#define EV_IS_PRINT_OPERATION_EXPORT_PORTAL(object)      (G_TYPE_CHECK_INSTANCE_TYPE((object), 
EV_TYPE_PRINT_OPERATION_EXPORT_PORTAL))
+#define EV_IS_PRINT_OPERATION_EXPORT_PORTAL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), 
EV_TYPE_PRINT_OPERATION_EXPORT_PORTAL))
+#define EV_PRINT_OPERATION_EXPORT_PORTAL_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), 
EV_TYPE_PRINT_OPERATION_EXPORT_PORTAL, EvPrintOperationExportPortalClass))
+
+typedef struct _EvPrintOperationExportPortal      EvPrintOperationExportPortal;
+typedef struct _EvPrintOperationExportPortalClass EvPrintOperationExportPortalClass;
+
+static GType ev_print_operation_export_portal_get_type (void) G_GNUC_CONST;
+
+struct _EvPrintOperationExportPortal {
+        EvPrintOperationExport parent;
+
+        GDBusProxy *proxy;
+        guint response_signal_id;
+        guint32 token;
+        char *parent_window_handle;
+        char *prepare_print_handle;
+};
+
+struct _EvPrintOperationExportPortalClass {
+        EvPrintOperationExportClass parent_class;
+};
+
+G_DEFINE_TYPE (EvPrintOperationExportPortal, ev_print_operation_export_portal, 
EV_TYPE_PRINT_OPERATION_EXPORT)
+
+/* BEGIN copied from gtkwindow.c */
+/* Thanks, gtk+, for not exporting this essential API! */
+
+typedef void (*EvGtkWindowHandleExported)  (GtkWindow  *window,
+                                            const char *handle,
+                                            gpointer    user_data);
+
+static gboolean ev_gtk_window_export_handle   (GtkWindow                 *window,
+                                               EvGtkWindowHandleExported  callback,
+                                               gpointer                   user_data);
+
+#ifdef GDK_WINDOWING_WAYLAND
+
+typedef void (*EvGdkWaylandWindowExported) (GdkWindow  *window,
+                                            const char *handle,
+                                            gpointer    user_data);
+
+typedef struct {
+        GtkWindow *window;
+        EvGtkWindowHandleExported callback;
+        gpointer user_data;
+} WaylandWindowHandleExportedData;
+
+static void
+wayland_window_handle_exported_cb (GdkWindow  *window,
+                                   const char *wayland_handle_str,
+                                   gpointer    user_data)
+{
+        WaylandWindowHandleExportedData *data = user_data;
+        char *handle_str;
+
+        handle_str = g_strdup_printf ("wayland:%s", wayland_handle_str);
+        data->callback (data->window, handle_str, data->user_data);
+        g_free (handle_str);
+}
+#endif
+
+static gboolean
+ev_gtk_window_export_handle (GtkWindow                 *window,
+                             EvGtkWindowHandleExported  callback,
+                             gpointer                   user_data)
+{
+#ifdef GDK_WINDOWING_X11
+        if (GDK_IS_X11_DISPLAY (gtk_widget_get_display (GTK_WIDGET (window)))) {
+                GdkWindow *gdk_window = gtk_widget_get_window (GTK_WIDGET (window));
+                char *handle_str;
+                guint32 xid = (guint32) gdk_x11_window_get_xid (gdk_window);
+
+                handle_str = g_strdup_printf ("x11:%x", xid);
+                callback (window, handle_str, user_data);
+                g_free (handle_str);
+
+                return TRUE;
+        }
+#endif
+#ifdef GDK_WINDOWING_WAYLAND
+        if (GDK_IS_WAYLAND_DISPLAY (gtk_widget_get_display (GTK_WIDGET (window)))) {
+                GdkWindow *gdk_window = gtk_widget_get_window (GTK_WIDGET (window));
+                WaylandWindowHandleExportedData *data;
+                data = g_new0 (WaylandWindowHandleExportedData, 1);
+                data->window = window;
+                data->callback = callback;
+                data->user_data = user_data;
+
+                if (!gdk_wayland_window_export_handle (gdk_window,
+                                                       wayland_window_handle_exported_cb,
+                                                       data,
+                                                       g_free)) {
+                        g_free (data);
+                        return FALSE;
+                }
+
+                return TRUE;
+        }
+#endif
+
+        g_printerr ("Unsupported windowing system.\n");
+        return FALSE;
+}
+
+#if 0
+static void
+ev_gtk_window_unexport_handle (GtkWindow *window)
+{
+#ifdef GDK_WINDOWING_WAYLAND
+  if (GDK_IS_WAYLAND_DISPLAY (gtk_widget_get_display (GTK_WIDGET (window))))
+    {
+      GdkWindow *gdk_window = gtk_widget_get_window (GTK_WIDGET (window));
+
+      gdk_wayland_window_unexport_handle (gdk_window);
+    }
+#endif
+}
+#endif
+
+/* END copied from gtkwindow.c */
+
+static void
+export_portal_unsubscribe_response (EvPrintOperationExportPortal *export_portal)
+{
+        if (export_portal->response_signal_id == 0)
+                return;
+
+        g_dbus_connection_signal_unsubscribe (g_dbus_proxy_get_connection (G_DBUS_PROXY 
(export_portal->proxy)),
+                                              export_portal->response_signal_id);
+        export_portal->response_signal_id = 0;
+}
+
+static void
+export_portal_request_response_cb (GDBusConnection *connection,
+                                   const char      *sender_name,
+                                   const char      *object_path,
+                                   const char      *interface_name,
+                                   const char      *signal_name,
+                                   GVariant        *parameters,
+                                   gpointer         data)
+{
+        EvPrintOperationExportPortal *export_portal = EV_PRINT_OPERATION_EXPORT_PORTAL (data);
+        EvPrintOperationExport *export = EV_PRINT_OPERATION_EXPORT (data);
+        EvPrintOperation *op = EV_PRINT_OPERATION (data);
+        guint32 response;
+        GVariant *options;
+        GVariant *v;
+        GtkPrintSettings *settings;
+        GtkPageSetup *page_setup;
+        EvFileExporterFormat format;
+
+        export_portal_unsubscribe_response (export_portal);
+
+        g_assert_cmpstr (object_path, ==, export_portal->prepare_print_handle); // FIXMEchpe remove
+
+        if (!g_str_equal (object_path, export_portal->prepare_print_handle))
+                return;
+
+        g_variant_get (parameters, "(u@a{sv})", &response, &options);
+
+        /* Cancelled? */
+        if (response != 0) {
+                g_variant_unref (options);
+
+                g_signal_emit (op, signals[DONE], 0, GTK_PRINT_OPERATION_RESULT_CANCEL);
+                return;
+        }
+
+        /* FIXME: no "preview" for portal? */
+        /* op->print_preview = (response == GTK_RESPONSE_APPLY); */
+        op->print_preview = FALSE;
+
+        v = g_variant_lookup_value (options, "settings", G_VARIANT_TYPE_VARDICT);
+        settings = gtk_print_settings_new_from_gvariant (v);
+        g_variant_unref (v);
+
+        ev_print_operation_set_print_settings (op, 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);
+
+        ev_print_operation_set_default_page_setup (op, page_setup);
+        g_object_unref (page_setup);
+
+        g_variant_lookup (options, "token", "u", &export_portal->token);
+
+        g_variant_unref (options);
+
+        format = get_file_exporter_format (EV_FILE_EXPORTER (op->document),
+                                           export->print_settings);
+
+        if (!ev_print_operation_export_update_ranges (export)) {
+                if (export->error == NULL)
+                        g_set_error_literal (&export->error,
+                                             GTK_PRINT_ERROR,
+                                             GTK_PRINT_ERROR_GENERAL,
+                                             _("Your print range selection does not include any pages"));
+
+                g_signal_emit (op, signals[DONE], 0, GTK_PRINT_OPERATION_RESULT_ERROR);
+                return;
+        }
+
+        if (!ev_print_operation_export_mkstemp (export, format)) {
+                g_signal_emit (op, signals[DONE], 0, GTK_PRINT_OPERATION_RESULT_ERROR);
+                return;
+        }
+
+        ev_print_operation_export_prepare (export, format);
+}
+
+static void
+export_portal_subscribe_response (EvPrintOperationExportPortal *export_portal)
+{
+        export_portal->response_signal_id =
+                g_dbus_connection_signal_subscribe (g_dbus_proxy_get_connection (G_DBUS_PROXY 
(export_portal->proxy)),
+                                                    "org.freedesktop.portal.Desktop",
+                                                    "org.freedesktop.portal.Request",
+                                                    "Response",
+                                                    export_portal->prepare_print_handle,
+                                                    NULL /* cancellable */,
+                                                    G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
+                                                    export_portal_request_response_cb,
+                                                    g_object_ref (export_portal),
+                                                    (GDestroyNotify) g_object_unref);
+}
+
+static void
+export_portal_prepare_print_cb (GObject      *source,
+                                GAsyncResult *result,
+                                gpointer      data)
+{
+        EvPrintOperationExportPortal *export_portal = EV_PRINT_OPERATION_EXPORT_PORTAL (data);
+        EvPrintOperationExport *export = EV_PRINT_OPERATION_EXPORT (data);
+        GError *error = NULL;
+        const char *handle = NULL;
+        GVariant *rv;
+
+        rv = g_dbus_proxy_call_finish (export_portal->proxy, result, &error);
+        if (rv == NULL) {
+                if (export->error == NULL)
+                        g_propagate_error (&export->error, error);
+                else
+                        g_error_free (error);
+
+                g_object_unref (export); /* added in g_dbus_proxy_call */
+                return;
+        }
+
+        g_variant_get (rv, "(&o)", &handle);
+
+        /* This happens on old portal versions only */
+        if (!g_str_equal (export_portal->prepare_print_handle, handle)) {
+                g_free (export_portal->prepare_print_handle);
+                export_portal->prepare_print_handle = g_strdup (handle);
+
+                export_portal_unsubscribe_response (export_portal);
+                export_portal_subscribe_response (export_portal);
+        }
+
+        g_variant_unref (rv);
+        g_object_unref (export); /* added in g_dbus_proxy_call */
+}
+
+static void
+export_portal_create_proxy_cb (GObject *source,
+                               GAsyncResult *result,
+                               EvPrintOperationExportPortal *export_portal)
+{
+        EvPrintOperation *op = EV_PRINT_OPERATION (export_portal);
+        EvPrintOperationExport *export = EV_PRINT_OPERATION_EXPORT (export_portal);
+        GError *err = NULL;
+        GVariantBuilder options_builder;
+        GVariant *options_variant;
+        GVariant *settings_variant;
+        GVariant *setup_variant;
+        char *token;
+        char *sender;
+
+        export_portal->proxy = g_dbus_proxy_new_for_bus_finish (result, &err);
+        if (export_portal->proxy == NULL) {
+                g_printerr("Error creating Print portal proxy: %s\n", err->message);
+                g_propagate_error (&export->error, err);
+
+                g_signal_emit (op, signals[DONE], 0, GTK_PRINT_OPERATION_RESULT_ERROR);
+
+                g_object_unref (export_portal); /* added in create_proxy */
+                return;
+        }
+
+        /* Now we can call PreparePrint */
+        token = g_strdup_printf ("evince%u", (guint)g_random_int_range (0, G_MAXINT));
+
+        sender = g_strdup (g_dbus_connection_get_unique_name (g_dbus_proxy_get_connection 
(export_portal->proxy)) + 1);
+        sender = g_strdelimit (sender, ".", '_');
+
+        export_portal->prepare_print_handle = g_strdup_printf 
("/org/fredesktop/portal/desktop/request/%s/%s", sender, token);
+        g_free (sender);
+
+        export_portal_subscribe_response (export_portal);
+
+#if 0
+        /* FIXMEchpe: There is no way to specify the GtkPrintCapabilities
+         * and the custom options. Needs to be fixed in the Portal!
+         */
+        capabilities = GTK_PRINT_CAPABILITY_PREVIEW |
+                ev_file_exporter_get_capabilities (EV_FILE_EXPORTER (op->document));
+        /* ... */
+        /* install custom options... */
+#endif
+
+        g_variant_builder_init (&options_builder, G_VARIANT_TYPE_VARDICT);
+        g_variant_builder_add (&options_builder, "{sv}", "handle_token", g_variant_new_string (token));
+        g_variant_builder_add (&options_builder, "{sv}", "modal", g_variant_new_boolean (TRUE));
+        g_free (token);
+
+        options_variant = g_variant_builder_end (&options_builder);
+
+        if (export->print_settings) {
+                settings_variant = gtk_print_settings_to_gvariant (export->print_settings);
+        } else {
+                GVariantBuilder builder2;
+                g_variant_builder_init (&builder2, G_VARIANT_TYPE_VARDICT);
+                settings_variant = g_variant_builder_end (&builder2);
+        }
+
+        if (export->page_setup) {
+                setup_variant = gtk_page_setup_to_gvariant (export->page_setup);
+        } else {
+                GtkPageSetup *page_setup = gtk_page_setup_new ();
+                setup_variant = gtk_page_setup_to_gvariant (page_setup);
+                g_object_unref (page_setup);
+        }
+
+        g_dbus_proxy_call (export_portal->proxy,
+                           "PreparePrint",
+                           g_variant_new ("(ss@a{sv}@a{sv}@a{sv})",
+                                          export_portal->parent_window_handle ? 
export_portal->parent_window_handle : "",
+                                          _("Print"), /* title */
+                                          settings_variant,
+                                          setup_variant,
+                                          options_variant),
+                           G_DBUS_CALL_FLAGS_NONE,
+                           -1,
+                           NULL /* cancellable */,
+                           export_portal_prepare_print_cb,
+                           export_portal /* transfer the refcount added in create_proxy */);
+}
+
+static void
+export_portal_create_proxy (EvPrintOperationExportPortal *export_portal)
+{
+        g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION,
+                                  G_DBUS_PROXY_FLAGS_NONE,
+                                  NULL,
+                                  "org.freedesktop.portal.Desktop",
+                                  "/org/freedesktop/portal/desktop",
+                                  "org.freedesktop.portal.Print",
+                                  NULL /* cancellable */,
+                                  (GAsyncReadyCallback) export_portal_create_proxy_cb,
+                                  g_object_ref (export_portal));
+}
+
+static void
+export_portal_window_handle_exported_cb (GtkWindow  *window,
+                                         const char *handle_str,
+                                         gpointer    user_data)
+{
+        EvPrintOperationExportPortal *export_portal = EV_PRINT_OPERATION_EXPORT_PORTAL (user_data);
+
+        export_portal->parent_window_handle = g_strdup (handle_str);
+        export_portal_create_proxy (export_portal);
+        g_object_unref (export_portal);
+}
+
+
+static void
+export_portal_print_finished_cb (GObject                      *source,
+                                 GAsyncResult                 *result,
+                                 EvPrintOperationExportPortal *export_portal)
+{
+        EvPrintOperationExport *export = EV_PRINT_OPERATION_EXPORT (export_portal);
+        EvPrintOperation *op = EV_PRINT_OPERATION (export_portal);
+        GVariant *rv;
+        GError *err = NULL;
+
+        rv = g_dbus_proxy_call_finish (export_portal->proxy, result, &err);
+        if (rv == NULL) {
+                g_set_error_literal (&export->error,
+                                     GTK_PRINT_ERROR,
+                                     GTK_PRINT_ERROR_GENERAL,
+                                     err->message);
+                g_error_free (err);
+
+                g_signal_emit (op, signals[DONE], 0, GTK_PRINT_OPERATION_RESULT_ERROR);
+        } else {
+                g_variant_unref (rv);
+                g_signal_emit (op, signals[DONE], 0, GTK_PRINT_OPERATION_RESULT_APPLY);
+        }
+
+        ev_print_operation_export_clear_temp_file (export);
+
+        ev_print_operation_export_run_next (export);
+
+        g_object_unref (export_portal);
+}
+
+static gboolean
+export_portal_print (EvPrintOperationExportPortal *export_portal,
+                     GtkPrintSettings             *settings,
+                     GError                      **error)
+{
+        EvPrintOperationExport *export = EV_PRINT_OPERATION_EXPORT (export_portal);
+        GUnixFDList *fd_list;
+        int idx;
+        GError *err = NULL;
+        GVariantBuilder builder;
+
+        fd_list = g_unix_fd_list_new ();
+        idx = g_unix_fd_list_append (fd_list, export->fd, &err);
+        if (idx == -1) {
+                g_propagate_error (error, err);
+                return FALSE;
+        }
+
+        g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
+        g_variant_builder_add (&builder, "{sv}",  "token", g_variant_new_uint32 (export_portal->token));
+
+        /* FIXMEchpe: The portal will use the old print settings passed to PreparePrint,
+         * not the cleaned-up ones from @settings !!! To fix this, needs to enhance
+         * the portal.
+         */
+        g_dbus_proxy_call_with_unix_fd_list (export_portal->proxy,
+                                             "Print",
+                                             g_variant_new ("(ssh@a{sv})",
+                                                            export_portal->parent_window_handle,
+                                                            _("Print"), /* title */
+                                                            idx,
+                                                            g_variant_builder_end (&builder)),
+                                             G_DBUS_CALL_FLAGS_NONE,
+                                             -1,
+                                             fd_list,
+                                             NULL /* cancellable */,
+                                             (GAsyncReadyCallback) export_portal_print_finished_cb,
+                                             g_object_ref (export_portal));
+        g_object_unref (fd_list);
+        return TRUE;
+}
+
+static void
+ev_print_operation_export_portal_run (EvPrintOperation *op,
+                                      GtkWindow        *parent)
+{
+        EvPrintOperationExportPortal *export_portal = EV_PRINT_OPERATION_EXPORT_PORTAL (op);
+
+        EV_PRINT_OPERATION_CLASS (ev_print_operation_export_portal_parent_class)->run (op, parent);
+
+        if (parent != NULL &&
+            gtk_widget_is_visible (GTK_WIDGET (parent)) &&
+            ev_gtk_window_export_handle (parent,
+                                         export_portal_window_handle_exported_cb,
+                                         g_object_ref (export_portal)))
+                return;
+
+        export_portal_create_proxy (export_portal);
+}
+
+static void
+ev_print_operation_export_portal_cancel (EvPrintOperation *op)
+{
+        //        EvPrintOperationExport *export = EV_PRINT_OPERATION_EXPORT (op);
+        //        EvPrintOperationExportPortal *export_portal = EV_PRINT_OPERATION_EXPORT_PORTAL (op);
+
+        EV_PRINT_OPERATION_CLASS (ev_print_operation_export_portal_parent_class)->cancel (op);
+}
+
+static gboolean
+ev_print_operation_export_portal_run_previewer (EvPrintOperationExport *export,
+                                                GtkPrintSettings       *settings,
+                                                GError                **error)
+{
+        /* FIXMEchpe: This needs a new PrintPreview portal, that's just like Print
+         * but calls a flatpack'd evince-previewer to do the previewing.
+         */
+
+        g_set_error_literal (error,
+                             GTK_PRINT_ERROR,
+                             GTK_PRINT_ERROR_GENERAL,
+                             "Print preview not possible on portal");
+        return FALSE;
+}
+
+static gboolean
+ev_print_operation_export_portal_send_job (EvPrintOperationExport *export,
+                                           GtkPrintSettings       *settings,
+                                           GError                **error)
+{
+        EvPrintOperationExportPortal *export_portal = EV_PRINT_OPERATION_EXPORT_PORTAL (export);
+        return export_portal_print (export_portal, settings, error);
+}
+
+static void
+ev_print_operation_export_portal_init (EvPrintOperationExportPortal *export)
+{
+}
+
+static void
+ev_print_operation_export_portal_constructed (GObject *object)
+{
+        //        EvPrintOperation *op = EV_PRINT_OPERATION (object);
+        //        EvPrintOperationExport *export = EV_PRINT_OPERATION_EXPORT (object);
+        //        EvPrintOperationExportPortal *export_portal = EV_PRINT_OPERATION_EXPORT_PORTAL (object);
+
+        G_OBJECT_CLASS (ev_print_operation_export_portal_parent_class)->constructed (object);
+}
+
+static void
+ev_print_operation_export_portal_finalize (GObject *object)
+{
+        EvPrintOperationExportPortal *export_portal = EV_PRINT_OPERATION_EXPORT_PORTAL (object);
+
+        export_portal_unsubscribe_response (export_portal);
+
+        g_clear_object (&export_portal->proxy);
+
+        g_free (export_portal->parent_window_handle);
+        g_free (export_portal->prepare_print_handle);
+
+        G_OBJECT_CLASS (ev_print_operation_export_portal_parent_class)->finalize (object);
+}
+
+static void
+ev_print_operation_export_portal_class_init (EvPrintOperationExportPortalClass *klass)
+{
+        GObjectClass          *g_object_class = G_OBJECT_CLASS (klass);
+        EvPrintOperationClass *ev_print_op_class = EV_PRINT_OPERATION_CLASS (klass);
+        EvPrintOperationExportClass *ev_print_op_ex_class = EV_PRINT_OPERATION_EXPORT_CLASS (klass);
+
+        ev_print_op_class->run = ev_print_operation_export_portal_run;
+        ev_print_op_class->cancel = ev_print_operation_export_portal_cancel;
+
+        ev_print_op_ex_class->send_job = ev_print_operation_export_portal_send_job;
+        ev_print_op_ex_class->run_previewer = ev_print_operation_export_portal_run_previewer;
+
+        g_object_class->constructed = ev_print_operation_export_portal_constructed;
+        g_object_class->finalize = ev_print_operation_export_portal_finalize;
+}
+
+#endif /* PORTAL_ENABLED */
+
+/* Export with unix print dialogue */
+
 #if GTKUNIXPRINT_ENABLED
 
 #include <gtk/gtkunixprint.h>
 
-/* Export with unix print dialogue */
 #define EV_TYPE_PRINT_OPERATION_EXPORT_UNIX            (ev_print_operation_export_unix_get_type())
 #define EV_PRINT_OPERATION_EXPORT_UNIX(object)         (G_TYPE_CHECK_INSTANCE_CAST((object), 
EV_TYPE_PRINT_OPERATION_EXPORT_UNIX, EvPrintOperationExportUnix))
 #define EV_PRINT_OPERATION_EXPORT_UNIX_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), 
EV_TYPE_PRINT_OPERATION_EXPORT_UNIX, EvPrintOperationExportUnixClass))
@@ -1285,17 +1993,9 @@ export_unix_print_dialog_response_cb (GtkDialog              *dialog,
 {
        EvPrintOperationExportUnix *export_unix = EV_PRINT_OPERATION_EXPORT_UNIX (export);
        EvPrintOperation     *op = EV_PRINT_OPERATION (export);
-       GtkPrintPages         print_pages;
        GtkPrintSettings     *print_settings;
        GtkPageSetup         *page_setup;
        GtkPrinter           *printer;
-       gdouble               scale;
-       gdouble               width;
-       gdouble               height;
-       gint                  first_page;
-       gint                  last_page;
-       gchar                *filename;
-       GError               *error = NULL;
        EvFileExporterFormat  format;
 
        if (response != GTK_RESPONSE_OK &&
@@ -1318,7 +2018,7 @@ export_unix_print_dialog_response_cb (GtkDialog              *dialog,
        ev_print_operation_export_set_default_page_setup (op, page_setup);
 
        format = get_file_exporter_format (EV_FILE_EXPORTER (op->document),
-                                          print_settings);
+                                           print_settings);
 
        if ((format == EV_FILE_FORMAT_PS && !gtk_printer_accepts_ps (export_unix->printer)) ||
            (format == EV_FILE_FORMAT_PDF && !gtk_printer_accepts_pdf (export_unix->printer))) {
@@ -1333,57 +2033,17 @@ export_unix_print_dialog_response_cb (GtkDialog              *dialog,
                return;
        }
 
-       filename = g_strdup_printf ("evince_print.%s.XXXXXX", format == EV_FILE_FORMAT_PDF ? "pdf" : "ps");
-       export->fd = g_file_open_tmp (filename, &export->temp_file, &error);
-       g_free (filename);
-       if (export->fd <= -1) {
+        if (!ev_print_operation_export_mkstemp (export, format)) {
                gtk_widget_destroy (GTK_WIDGET (dialog));
 
-               g_set_error_literal (&export->error,
-                                    GTK_PRINT_ERROR,
-                                    GTK_PRINT_ERROR_GENERAL,
-                                    error->message);
-               g_error_free (error);
                g_signal_emit (op, signals[DONE], 0, GTK_PRINT_OPERATION_RESULT_ERROR);
-
                return;
        }
 
+        /* FIXMEchpe (why) is this necessary? */
        export->current_page = gtk_print_unix_dialog_get_current_page (GTK_PRINT_UNIX_DIALOG (dialog));
-       export->page_set = gtk_print_settings_get_page_set (print_settings);
-       print_pages = gtk_print_settings_get_print_pages (print_settings);
-
-       switch (print_pages) {
-       case GTK_PRINT_PAGES_CURRENT:
-               export->ranges = &export->one_range;
 
-               export->ranges[0].start = export->current_page;
-               export->ranges[0].end = export->current_page;
-               export->n_ranges = 1;
-
-               break;
-       case GTK_PRINT_PAGES_RANGES: {
-               gint i;
-
-               export->ranges = gtk_print_settings_get_page_ranges (print_settings, &export->n_ranges);
-               for (i = 0; i < export->n_ranges; i++)
-                       if (export->ranges[i].end == -1 || export->ranges[i].end >= export->n_pages)
-                               export->ranges[i].end = export->n_pages - 1;
-       }
-               break;
-       default:
-               g_warning ("Unsupported print pages setting\n");
-       case GTK_PRINT_PAGES_ALL:
-               export->ranges = &export->one_range;
-
-               export->ranges[0].start = 0;
-               export->ranges[0].end = export->n_pages - 1;
-               export->n_ranges = 1;
-
-               break;
-       }
-
-       if (export->n_ranges < 1 || !clamp_ranges (export)) {
+        if (!ev_print_operation_export_update_ranges (export)) {
                GtkWidget *message_dialog;
 
                message_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog),
@@ -1400,61 +2060,11 @@ export_unix_print_dialog_response_cb (GtkDialog              *dialog,
                gtk_widget_show (message_dialog);
 
                return;
-       } else  ev_print_operation_update_status (op, -1, -1, 0.0);
-
-       width = gtk_page_setup_get_paper_width (page_setup, GTK_UNIT_POINTS);
-       height = gtk_page_setup_get_paper_height (page_setup, GTK_UNIT_POINTS);
-       scale = gtk_print_settings_get_scale (print_settings) * 0.01;
-       if (scale != 1.0) {
-               width *= scale;
-               height *= scale;
-       }
-
-       export->pages_per_sheet = MAX (1, gtk_print_settings_get_number_up (print_settings));
-
-       export->copies = gtk_print_settings_get_n_copies (print_settings);
-       export->collate = gtk_print_settings_get_collate (print_settings);
-       export->reverse = gtk_print_settings_get_reverse (print_settings);
-
-       if (export->collate) {
-               export->uncollated_copies = export->copies;
-               export->collated_copies = 1;
-       } else {
-               export->uncollated_copies = 1;
-               export->collated_copies = export->copies;
        }
 
-       if (export->reverse) {
-               export->range = export->n_ranges - 1;
-               export->inc = -1;
-       } else {
-               export->range = 0;
-               export->inc = 1;
-       }
-       find_range (export);
-
-       export->page = export->start - export->inc;
-       export->collated = export->collated_copies - 1;
-
-       get_first_and_last_page (export, &first_page, &last_page);
-
-       export->fc.format = format;
-       export->fc.filename = export->temp_file;
-       export->fc.first_page = MIN (first_page, last_page);
-       export->fc.last_page = MAX (first_page, last_page);
-       export->fc.paper_width = width;
-       export->fc.paper_height = height;
-       export->fc.duplex = FALSE;
-       export->fc.pages_per_sheet = export->pages_per_sheet;
-
-       if (ev_print_queue_is_empty (op->document))
-               ev_print_operation_export_begin (export);
-
-       ev_print_queue_push (op);
-
-       g_signal_emit (op, signals[BEGIN_PRINT], 0);
-
        gtk_widget_destroy (GTK_WIDGET (dialog));
+
+        ev_print_operation_export_prepare (export, format);
 }
 
 static void
@@ -2274,33 +2884,49 @@ ev_print_operation_print_class_init (EvPrintOperationPrintClass *klass)
        g_object_class->finalize = ev_print_operation_print_finalize;
 }
 
+/* Factory functions */
+
+static GType
+ev_print_operation_get_gtype_for_document (EvDocument *document)
+{
+        GType type = G_TYPE_INVALID;
+        const char *env;
+
+        /* Allow to override the selection by an env var */
+        env = g_getenv ("EV_PRINT");
+
+        if (EV_IS_DOCUMENT_PRINT (document) && g_strcmp0 (env, "export") != 0) {
+                type = EV_TYPE_PRINT_OPERATION_PRINT;
+        } else if (EV_IS_FILE_EXPORTER (document)) {
+                if (ev_should_use_portal ()) {
+#ifdef PORTAL_ENABLED
+                        type = EV_TYPE_PRINT_OPERATION_EXPORT_PORTAL;
+#endif
+                } else {
+#if GTKUNIXPRINT_ENABLED
+                        type = EV_TYPE_PRINT_OPERATION_EXPORT_UNIX;
+#endif
+                }
+        }
+
+        return type;
+}
+
 gboolean
 ev_print_operation_exists_for_document (EvDocument *document)
 {
-#if GTKUNIXPRINT_ENABLED
-       return (EV_IS_FILE_EXPORTER(document) || EV_IS_DOCUMENT_PRINT(document));
-#else
-       return EV_IS_DOCUMENT_PRINT(document);
-#endif /* GTKUNIXPRINT_ENABLED */
+        return ev_print_operation_get_gtype_for_document (document) != G_TYPE_INVALID;
 }
 
 /* Factory method */
 EvPrintOperation *
 ev_print_operation_new (EvDocument *document)
 {
-       EvPrintOperation *op = NULL;
+        GType type;
 
-       g_return_val_if_fail (ev_print_operation_exists_for_document (document), NULL);
+        type = ev_print_operation_get_gtype_for_document (document);
+        if (type == G_TYPE_INVALID)
+                return NULL;
 
-       if (EV_IS_DOCUMENT_PRINT (document))
-               op = EV_PRINT_OPERATION (g_object_new (EV_TYPE_PRINT_OPERATION_PRINT,
-                                                      "document", document, NULL));
-       else
-#if GTKUNIXPRINT_ENABLED
-               op = EV_PRINT_OPERATION (g_object_new (EV_TYPE_PRINT_OPERATION_EXPORT_UNIX,
-                                                      "document", document, NULL));
-#else
-               op = NULL;
-#endif
-       return op;
+        return EV_PRINT_OPERATION (g_object_new (type, "document", document, NULL));
 }
diff --git a/meson.build b/meson.build
index 48003b33a..73dfd5381 100644
--- a/meson.build
+++ b/meson.build
@@ -74,6 +74,7 @@ ev_debug = get_option('buildtype').contains('debug')
 cc = meson.get_compiler('c')
 
 config_h = configuration_data()
+config_h.set10('_GNU_SOURCE', true)
 
 # package
 config_h.set_quoted('PACKAGE_ICON_NAME', app_id)
@@ -198,7 +199,6 @@ if ev_platform == 'gnome'
   if enable_dbus
     # Check for dbus service dir
     dbus_service_dir = dependency('dbus-1').get_pkgconfig_variable('session_bus_services_dir', 
define_variable: ['datadir', ev_datadir])
-    gio_unix_dep = dependency('gio-unix-2.0', version: glib_req_version)
   endif
   config_h.set('ENABLE_DBUS', enable_dbus)
 
@@ -211,12 +211,21 @@ if ev_platform == 'gnome'
   gtk_unix_print_dep = dependency('gtk+-unix-print-3.0', version: gtk_req_version, required: 
get_option('gtk_unix_print'))
   enable_gtk_unix_print = gtk_unix_print_dep.found()
   config_h.set10('GTKUNIXPRINT_ENABLED', enable_gtk_unix_print)
+
+  if enable_dbus or enable_gtk_unix_print
+    gio_unix_dep = dependency('gio-unix-2.0', version: glib_req_version)
+  else
+    gio_unix_dep = dependency('', required: false)
+  endif
+
 else
   message('Evince has a soft run-time dependency on hicolor-icon-theme. You are advised to have this theme 
installed when running Evince.')
   enable_nautilus = false
   enable_dbus = false
   enable_keyring = false
   enable_gtk_unix_print = false
+  gio_unix_dep = dependency('', required: false)
+  gtk_unix_print_dep = dependency('', required: false)
 endif
 
 # *** GObject Introspection ***


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]