/* gtkprintbackendcloudprint.c: Google Cloud Print implementation of * GtkPrintBackend * Copyright (C) 2014, Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gtkprintbackendcloudprint.h" #include "gtkcloudprintaccount.h" #include "gtkprintercloudprint.h" typedef struct _GtkPrintBackendCloudprintClass GtkPrintBackendCloudprintClass; #define GTK_PRINT_BACKEND_CLOUDPRINT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PRINT_BACKEND_CLOUDPRINT, GtkPrintBackendCloudprintClass)) #define GTK_IS_PRINT_BACKEND_CLOUDPRINT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PRINT_BACKEND_CLOUDPRINT)) #define GTK_PRINT_BACKEND_CLOUDPRINT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_PRINT_BACKEND_CLOUDPRINT, GtkPrintBackendCloudprintClass)) #define _STREAM_MAX_CHUNK_SIZE 8192 #define ONLINE_ACCOUNTS_PATH "/org/gnome/OnlineAccounts" #define OBJECT_MANAGER_IFACE "org.freedesktop.DBus.ObjectManager" struct _GtkPrintBackendCloudprintClass { GtkPrintBackendClass parent_class; }; struct _GtkPrintBackendCloudprint { GtkPrintBackend parent_instance; GCancellable *cancellable; guint accounts_searching; }; struct { char *id; char *path; char *presentation_identity; } typedef TGOAAccount; static GObjectClass *backend_parent_class; static void gtk_print_backend_cloudprint_finalize (GObject *object); static void cloudprint_printer_get_settings_from_options (GtkPrinter *printer, GtkPrinterOptionSet *options, GtkPrintSettings *settings); static GtkPrinterOptionSet *cloudprint_printer_get_options (GtkPrinter *printer, GtkPrintSettings *settings, GtkPageSetup *page_setup, GtkPrintCapabilities capabilities); static void cloudprint_printer_prepare_for_print (GtkPrinter *printer, GtkPrintJob *print_job, GtkPrintSettings *settings, GtkPageSetup *page_setup); static void cloudprint_request_printer_list (GtkPrintBackend *print_backend); static void gtk_print_backend_cloudprint_print_stream (GtkPrintBackend *print_backend, GtkPrintJob *job, GIOChannel *data_io, GtkPrintJobCompleteFunc callback, gpointer user_data, GDestroyNotify dnotify); static cairo_surface_t * cloudprint_printer_create_cairo_surface (GtkPrinter *printer, GtkPrintSettings *settings, double width, double height, GIOChannel *cache_io); static void cloudprint_printer_request_details (GtkPrinter *printer); TGOAAccount * t_goa_account_copy (TGOAAccount *account); void t_goa_account_free (gpointer data); G_DEFINE_DYNAMIC_TYPE (GtkPrintBackendCloudprint, gtk_print_backend_cloudprint, GTK_TYPE_PRINT_BACKEND) void g_io_module_load (GIOModule *module) { g_type_module_use (G_TYPE_MODULE (module)); gtk_print_backend_cloudprint_register_type (G_TYPE_MODULE (module)); gtk_cloudprint_account_register_type (G_TYPE_MODULE (module)); gtk_printer_cloudprint_register_type (G_TYPE_MODULE (module)); g_io_extension_point_implement (GTK_PRINT_BACKEND_EXTENSION_POINT_NAME, GTK_TYPE_PRINT_BACKEND_CLOUDPRINT, "cloudprint", 10); } void g_io_module_unload (GIOModule *module) { } char ** g_io_module_query (void) { char * eps[] = { (char *) GTK_PRINT_BACKEND_EXTENSION_POINT_NAME, NULL }; return g_strdupv (eps); } /** * gtk_print_backend_cloudprint_new: * * Creates a new #GtkPrintBackendCloudprint * object. #GtkPrintBackendCloudprint implements the #GtkPrintBackend * interface using REST API calls to the Google Cloud Print service. * * Returns: the new #GtkPrintBackendCloudprint object **/ GtkPrintBackend * gtk_print_backend_cloudprint_new (void) { return g_object_new (GTK_TYPE_PRINT_BACKEND_CLOUDPRINT, NULL); } static void gtk_print_backend_cloudprint_class_init (GtkPrintBackendCloudprintClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GtkPrintBackendClass *backend_class = GTK_PRINT_BACKEND_CLASS (klass); backend_parent_class = g_type_class_peek_parent (klass); gobject_class->finalize = gtk_print_backend_cloudprint_finalize; backend_class->request_printer_list = cloudprint_request_printer_list; backend_class->print_stream = gtk_print_backend_cloudprint_print_stream; backend_class->printer_create_cairo_surface = cloudprint_printer_create_cairo_surface; backend_class->printer_get_options = cloudprint_printer_get_options; backend_class->printer_get_settings_from_options = cloudprint_printer_get_settings_from_options; backend_class->printer_prepare_for_print = cloudprint_printer_prepare_for_print; backend_class->printer_request_details = cloudprint_printer_request_details; } static void gtk_print_backend_cloudprint_class_finalize (GtkPrintBackendCloudprintClass *class) { } static void gtk_print_backend_cloudprint_init (GtkPrintBackendCloudprint *backend) { backend->cancellable = g_cancellable_new (); GTK_NOTE (PRINTING, g_print ("Cloud Print Backend: +GtkPrintBackendCloudprint(%p)\n", backend)); } static void gtk_print_backend_cloudprint_finalize (GObject *object) { GtkPrintBackendCloudprint *backend; backend = GTK_PRINT_BACKEND_CLOUDPRINT (object); GTK_NOTE (PRINTING, g_print ("Cloud Print Backend: -GtkPrintBackendCloudprint(%p)\n", backend)); g_cancellable_cancel (backend->cancellable); g_clear_object (&(backend->cancellable)); backend_parent_class->finalize (object); } static cairo_status_t _cairo_write (void *closure, const unsigned char *data, unsigned int length) { GIOChannel *io = (GIOChannel *)closure; gsize written; GError *error; error = NULL; while (length > 0) { g_io_channel_write_chars (io, (const char *) data, length, &written, &error); if (error != NULL) { GTK_NOTE (PRINTING, g_print ("Cloud Print Backend: Error writing to temp file, %s\n", error->message)); g_error_free (error); return CAIRO_STATUS_WRITE_ERROR; } data += written; length -= written; } return CAIRO_STATUS_SUCCESS; } static cairo_surface_t * cloudprint_printer_create_cairo_surface (GtkPrinter *printer, GtkPrintSettings *settings, double width, double height, GIOChannel *cache_io) { cairo_surface_t *surface; surface = cairo_pdf_surface_create_for_stream (_cairo_write, cache_io, width, height); cairo_surface_set_fallback_resolution (surface, 2.0 * gtk_print_settings_get_printer_lpi (settings), 2.0 * gtk_print_settings_get_printer_lpi (settings)); return surface; } typedef struct { GtkPrintBackend *backend; GtkPrintJobCompleteFunc callback; GtkPrintJob *job; GIOChannel *target_io; gpointer user_data; GDestroyNotify dnotify; char *path; /* Base64 encoding state */ int b64state; int b64save; } _PrintStreamData; static void cloudprint_submit_cb (GObject *source, GAsyncResult *res, gpointer user_data) { GtkCloudprintAccount *account = GTK_CLOUDPRINT_ACCOUNT (source); _PrintStreamData *ps = (_PrintStreamData *) user_data; JsonObject *result; GError *error = NULL; gboolean success = FALSE; result = gtk_cloudprint_account_submit_finish (account, res, &error); g_object_unref (account); if (result == NULL) { GTK_NOTE (PRINTING, g_print ("Cloud Print Backend: submit REST reply: %s\n", error->message)); goto done; } json_object_unref (result); success = TRUE; done: if (ps->callback != NULL) ps->callback (ps->job, ps->user_data, error); if (ps->dnotify != NULL) ps->dnotify (ps->user_data); gtk_print_job_set_status (ps->job, (success ? GTK_PRINT_STATUS_FINISHED : GTK_PRINT_STATUS_FINISHED_ABORTED)); g_clear_object (&(ps->job)); g_clear_object (&(ps->backend)); g_clear_pointer (&error, g_error_free); g_free (ps->path); g_free (ps); } static void cloudprint_print_cb (GtkPrintBackendCloudprint *print_backend, GError *cb_error, gpointer user_data) { _PrintStreamData *ps = (_PrintStreamData *) user_data; gsize encodedlen; char encoded[4]; /* Up to 4 bytes are needed to finish encoding */ GError *error = NULL; encodedlen = g_base64_encode_close (FALSE, encoded, &ps->b64state, &ps->b64save); if (encodedlen > 0) g_io_channel_write_chars (ps->target_io, encoded, encodedlen, NULL, &error); if (ps->target_io != NULL) g_io_channel_unref (ps->target_io); if (cb_error == NULL) { GMappedFile *map = g_mapped_file_new (ps->path, FALSE, &error); GtkPrinter *printer = gtk_print_job_get_printer (ps->job); GtkCloudprintAccount *account = NULL; if (map == NULL) { GTK_NOTE (PRINTING, g_printerr ("Cloud Print Backend: failed to map file: %s\n", error->message)); g_error_free (error); goto out; } g_object_get (printer, "cloudprint-account", &account, NULL); g_warn_if_fail (account != NULL); GTK_NOTE (PRINTING, g_print ("Cloud Print Backend: submitting job\n")); gtk_cloudprint_account_submit (account, GTK_PRINTER_CLOUDPRINT (printer), map, gtk_print_job_get_title (ps->job), print_backend->cancellable, cloudprint_submit_cb, ps); } out: if (ps->path != NULL) unlink (ps->path); if (cb_error != NULL || error != NULL) { if (ps->callback != NULL) ps->callback (ps->job, ps->user_data, error); if (ps->dnotify != NULL) ps->dnotify (ps->user_data); gtk_print_job_set_status (ps->job, GTK_PRINT_STATUS_FINISHED_ABORTED); g_clear_object (&(ps->job)); g_free (ps->path); g_free (ps); } } static gboolean cloudprint_write (GIOChannel *source, GIOCondition con, gpointer user_data) { char buf[_STREAM_MAX_CHUNK_SIZE]; /* Base64 converts 24 bits into 32 bits, so divide the number of * bytes by 3 (rounding up) and multiply by 4. Also, if the previous * call left a non-zero state we may need an extra 4 bytes. */ char encoded[(_STREAM_MAX_CHUNK_SIZE / 3 + 1) * 4 + 4]; gsize bytes_read; GError *error = NULL; GIOStatus read_status; _PrintStreamData *ps = (_PrintStreamData *) user_data; read_status = g_io_channel_read_chars (source, buf, _STREAM_MAX_CHUNK_SIZE, &bytes_read, &error); if (read_status != G_IO_STATUS_ERROR) { gsize encodedlen = g_base64_encode_step ((guchar *) buf, bytes_read, FALSE, encoded, &ps->b64state, &ps->b64save); g_io_channel_write_chars (ps->target_io, encoded, encodedlen, NULL, &error); } if (error != NULL || read_status == G_IO_STATUS_EOF) { cloudprint_print_cb (GTK_PRINT_BACKEND_CLOUDPRINT (ps->backend), error, user_data); if (error != NULL) { GTK_NOTE (PRINTING, g_print ("Cloud Print Backend: %s\n", error->message)); g_error_free (error); } return FALSE; } GTK_NOTE (PRINTING, g_print ("Cloud Print Backend: Writing %i byte chunk to tempfile\n", (int)bytes_read)); return TRUE; } static void gtk_print_backend_cloudprint_print_stream (GtkPrintBackend *print_backend, GtkPrintJob *job, GIOChannel *data_io, GtkPrintJobCompleteFunc callback, gpointer user_data, GDestroyNotify dnotify) { const char *prefix = "data:application/pdf;base64,"; GError *internal_error = NULL; _PrintStreamData *ps; int tmpfd; ps = g_new0 (_PrintStreamData, 1); ps->callback = callback; ps->user_data = user_data; ps->dnotify = dnotify; ps->job = g_object_ref (job); ps->backend = g_object_ref (print_backend); ps->path = g_strdup_printf ("%s/cloudprintXXXXXX.pdf.b64", g_get_tmp_dir ()); ps->b64state = 0; ps->b64save = 0; internal_error = NULL; if (ps->path == NULL) goto error; tmpfd = g_mkstemp (ps->path); if (tmpfd == -1) { int err = errno; internal_error = g_error_new (gtk_print_error_quark (), GTK_PRINT_ERROR_INTERNAL_ERROR, "Error creating temporary file: %s", g_strerror (err)); goto error; } ps->target_io = g_io_channel_unix_new (tmpfd); if (ps->target_io != NULL) { g_io_channel_set_close_on_unref (ps->target_io, TRUE); g_io_channel_set_encoding (ps->target_io, NULL, &internal_error); } g_io_channel_write_chars (ps->target_io, prefix, strlen (prefix), NULL, &internal_error); error: if (internal_error != NULL) { cloudprint_print_cb (GTK_PRINT_BACKEND_CLOUDPRINT (print_backend), internal_error, ps); g_error_free (internal_error); return; } g_io_add_watch (data_io, G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP, (GIOFunc) cloudprint_write, ps); } TGOAAccount * t_goa_account_copy (TGOAAccount *account) { TGOAAccount *result = NULL; if (account != NULL) { result = g_new0 (TGOAAccount, 1); result->id = g_strdup (account->id); result->path = g_strdup (account->path); result->presentation_identity = g_strdup (account->presentation_identity); } return result; } void t_goa_account_free (gpointer data) { TGOAAccount *account = (TGOAAccount *) data; if (account != NULL) { g_free (account->id); g_free (account->path); g_free (account->presentation_identity); g_free (account); } } static GList * get_accounts (GVariant *output) { GVariant *objects; GList *result = NULL; int i, j, k; g_variant_get (output, "(@a{oa{sa{sv}}})", &objects); if (objects) { for (i = 0; i < g_variant_n_children (objects); i++) { const char *object_name; GVariant *object_variant; g_variant_get_child (objects, i, "{&o@a{sa{sv}}}", &object_name, &object_variant); if (g_str_has_prefix (object_name, "/org/gnome/OnlineAccounts/Accounts/")) { for (j = 0; j < g_variant_n_children (object_variant); j++) { const char *service_name; GVariant *service_variant; g_variant_get_child (object_variant, j, "{&s@a{sv}}", &service_name, &service_variant); if (g_str_has_prefix (service_name, "org.gnome.OnlineAccounts.Account")) { TGOAAccount *account; gboolean printers_disabled = FALSE; char *provider_type = NULL; account = g_new0 (TGOAAccount, 1); account->path = g_strdup (object_name); for (k = 0; k < g_variant_n_children (service_variant); k++) { const char *property_name; GVariant *property_variant; GVariant *value; g_variant_get_child (service_variant, k, "{&s@v}", &property_name, &property_variant); g_variant_get (property_variant, "v", &value); if (g_strcmp0 (property_name, "Id") == 0) account->id = g_variant_dup_string (value, NULL); else if (g_strcmp0 (property_name, "ProviderType") == 0) provider_type = g_variant_dup_string (value, NULL); else if (g_strcmp0 (property_name, "PrintersDisabled") == 0) printers_disabled = g_variant_get_boolean (value); else if (g_strcmp0 (property_name, "PresentationIdentity") == 0) account->presentation_identity = g_variant_dup_string (value, NULL); g_variant_unref (property_variant); g_variant_unref (value); } if (!printers_disabled && g_strcmp0 (provider_type, "google") == 0 && account->presentation_identity != NULL) result = g_list_append (result, account); else t_goa_account_free (account); g_free (provider_type); } g_variant_unref (service_variant); } } g_variant_unref (object_variant); } g_variant_unref (objects); } return result; } static void cloudprint_search_cb (GObject *source, GAsyncResult *res, gpointer user_data) { GtkCloudprintAccount *account = GTK_CLOUDPRINT_ACCOUNT (source); GtkPrintBackendCloudprint *backend = NULL; JsonNode *node; JsonArray *printers; guint i; GError *error = NULL; node = gtk_cloudprint_account_search_finish (account, res, &error); g_object_unref (account); if (node == NULL) { GTK_NOTE (PRINTING, g_print ("Cloud Print Backend: search failed: %s\n", error->message)); if (error->domain != G_IO_ERROR || error->code != G_IO_ERROR_CANCELLED) backend = GTK_PRINT_BACKEND_CLOUDPRINT (user_data); g_error_free (error); goto done; } backend = GTK_PRINT_BACKEND_CLOUDPRINT (user_data); printers = json_node_get_array (node); for (i = 0; i < json_array_get_length (printers); i++) { GtkPrinterCloudprint *printer; JsonObject *json_printer = json_array_get_object_element (printers, i); const char *name = NULL; const char *id = NULL; const char *type = NULL; const char *desc = NULL; const char *status = NULL; gboolean is_virtual; if (json_object_has_member (json_printer, "displayName")) name = json_object_get_string_member (json_printer, "displayName"); if (json_object_has_member (json_printer, "id")) id = json_object_get_string_member (json_printer, "id"); if (name == NULL || id == NULL) { GTK_NOTE (PRINTING, g_print ("Cloud Print Backend: ignoring incomplete " "printer description\n")); continue; } if (json_object_has_member (json_printer, "type")) type = json_object_get_string_member (json_printer, "type"); if (json_object_has_member (json_printer, "description")) desc = json_object_get_string_member (json_printer, "description"); if (json_object_has_member (json_printer, "connectionStatus")) status = json_object_get_string_member (json_printer, "connectionStatus"); is_virtual = (type != NULL && !strcmp (type, "DOCS")); GTK_NOTE (PRINTING, g_print ("Cloud Print Backend: Adding printer %s\n", name)); printer = gtk_printer_cloudprint_new (name, is_virtual, GTK_PRINT_BACKEND (backend), account, id); gtk_printer_set_has_details (GTK_PRINTER (printer), FALSE); gtk_printer_set_icon_name (GTK_PRINTER (printer), "printer"); gtk_printer_set_location (GTK_PRINTER (printer), gtk_cloudprint_account_get_presentation_identity (account)); if (desc != NULL) gtk_printer_set_description (GTK_PRINTER (printer), desc); if (status != NULL) { if (!strcmp (status, "ONLINE")) /* Translators: The printer status is online, i.e. it is * ready to print. */ gtk_printer_set_state_message (GTK_PRINTER (printer), _("Online")); else if (!strcmp (status, "UNKNOWN")) /* Translators: We don't know whether this printer is * available to print to. */ gtk_printer_set_state_message (GTK_PRINTER (printer), _("Unknown")); else if (!strcmp (status, "OFFLINE")) /* Translators: The printer is offline. */ gtk_printer_set_state_message (GTK_PRINTER (printer), _("Offline")); else if (!strcmp (status, "DORMANT")) /* We shouldn't get here because the query omits dormant * printers by default. */ /* Translators: Printer has been offline for a long time. */ gtk_printer_set_state_message (GTK_PRINTER (printer), _("Dormant")); } gtk_printer_set_is_active (GTK_PRINTER (printer), TRUE); gtk_print_backend_add_printer (GTK_PRINT_BACKEND (backend), GTK_PRINTER (printer)); g_signal_emit_by_name (GTK_PRINT_BACKEND (backend), "printer-added", GTK_PRINTER (printer)); g_object_unref (printer); } json_node_free (node); GTK_NOTE (PRINTING, g_print ("Cloud Print Backend: 'search' finished for account %p\n", account)); done: if (backend != NULL && --backend->accounts_searching == 0) { GTK_NOTE (PRINTING, g_print ("Cloud Print Backend: 'search' finished for " "all accounts\n")); gtk_print_backend_set_list_done (GTK_PRINT_BACKEND (backend)); } } static void cloudprint_get_managed_objects_cb (GObject *source, GAsyncResult *res, gpointer user_data) { GtkPrintBackendCloudprint *backend; GVariant *output; GError *error = NULL; output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), res, &error); if (output != NULL) { TGOAAccount *goa_account; GList *accounts = NULL; GList *iter; guint searching; GTK_NOTE (PRINTING, g_print ("Cloud Print Backend: got objects managed by goa\n")); backend = GTK_PRINT_BACKEND_CLOUDPRINT (user_data); accounts = get_accounts (output); g_variant_unref (output); searching = backend->accounts_searching = g_list_length (accounts); for (iter = accounts; iter != NULL; iter = iter->next) { GtkCloudprintAccount *account; goa_account = (TGOAAccount *) iter->data; account = gtk_cloudprint_account_new (goa_account->id, goa_account->path, goa_account->presentation_identity); if (account == NULL) { GTK_NOTE (PRINTING, g_print ("Cloud Print Backend: error constructing " "account object")); backend->accounts_searching--; searching--; continue; } GTK_NOTE (PRINTING, g_print ("Cloud Print Backend: issuing 'search' for %p\n", account)); gtk_cloudprint_account_search (account, G_DBUS_CONNECTION (source), backend->cancellable, cloudprint_search_cb, GTK_PRINT_BACKEND (backend)); } if (searching == 0) gtk_print_backend_set_list_done (GTK_PRINT_BACKEND (backend)); g_list_free_full (accounts, t_goa_account_free); } else { if (error->domain != G_IO_ERROR || error->code != G_IO_ERROR_CANCELLED) { if (error->domain != G_DBUS_ERROR || (error->code != G_DBUS_ERROR_SERVICE_UNKNOWN && error->code != G_DBUS_ERROR_UNKNOWN_METHOD)) { GTK_NOTE (PRINTING, g_print ("Cloud Print Backend: failed to get objects managed by goa: %s\n", error->message)); g_warning ("%s", error->message); } gtk_print_backend_set_list_done (GTK_PRINT_BACKEND (user_data)); } g_error_free (error); } g_object_unref (source); } static void cloudprint_bus_get_cb (GObject *source, GAsyncResult *res, gpointer user_data) { GtkPrintBackendCloudprint *backend; GDBusConnection *connection; GError *error = NULL; connection = g_bus_get_finish (res, &error); if (connection != NULL) { backend = GTK_PRINT_BACKEND_CLOUDPRINT (user_data); GTK_NOTE (PRINTING, g_print ("Cloud Print Backend: got connection to session bus\n")); g_dbus_connection_call (connection, ONLINE_ACCOUNTS_BUS, ONLINE_ACCOUNTS_PATH, OBJECT_MANAGER_IFACE, "GetManagedObjects", NULL, G_VARIANT_TYPE ("(a{oa{sa{sv}}})"), G_DBUS_CALL_FLAGS_NONE, -1, backend->cancellable, cloudprint_get_managed_objects_cb, backend); } else { if (error->domain != G_IO_ERROR || error->code != G_IO_ERROR_CANCELLED) { GTK_NOTE (PRINTING, g_print ("Cloud Print Backend: failed getting session bus: %s\n", error->message)); g_warning ("%s", error->message); gtk_print_backend_set_list_done (GTK_PRINT_BACKEND (user_data)); } g_error_free (error); } } static void cloudprint_request_printer_list (GtkPrintBackend *print_backend) { GtkPrintBackendCloudprint *backend = GTK_PRINT_BACKEND_CLOUDPRINT (print_backend); g_cancellable_reset (backend->cancellable); g_bus_get (G_BUS_TYPE_SESSION, backend->cancellable, cloudprint_bus_get_cb, backend); } static GtkPrinterOptionSet * cloudprint_printer_get_options (GtkPrinter *printer, GtkPrintSettings *settings, GtkPageSetup *page_setup, GtkPrintCapabilities capabilities) { GtkPrinterOptionSet *set; GtkPrinterOption *option; const char *n_up[] = { "1" }; set = gtk_printer_option_set_new (); /* How many document pages to go onto one side of paper. */ option = gtk_printer_option_new ("gtk-n-up", _("Pages per _sheet:"), GTK_PRINTER_OPTION_TYPE_PICKONE); gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (n_up), n_up, n_up /* FIXME i18n (localised digits)! */); gtk_printer_option_set (option, "1"); gtk_printer_option_set_add (set, option); g_object_unref (option); return set; } static void cloudprint_printer_get_settings_from_options (GtkPrinter *printer, GtkPrinterOptionSet *options, GtkPrintSettings *settings) { } static void cloudprint_printer_prepare_for_print (GtkPrinter *printer, GtkPrintJob *print_job, GtkPrintSettings *settings, GtkPageSetup *page_setup) { double scale; gtk_print_job_set_pages (print_job, gtk_print_settings_get_print_pages (settings)); gtk_print_job_set_page_ranges (print_job, NULL, 0); if (gtk_print_job_get_pages (print_job) == GTK_PRINT_PAGES_RANGES) { GtkPageRange *page_ranges; int num_page_ranges; page_ranges = gtk_print_settings_get_page_ranges (settings, &num_page_ranges); gtk_print_job_set_page_ranges (print_job, page_ranges, num_page_ranges); } gtk_print_job_set_collate (print_job, gtk_print_settings_get_collate (settings)); gtk_print_job_set_reverse (print_job, gtk_print_settings_get_reverse (settings)); gtk_print_job_set_num_copies (print_job, gtk_print_settings_get_n_copies (settings)); scale = gtk_print_settings_get_scale (settings); if (scale != 100.0) gtk_print_job_set_scale (print_job, scale/100.0); gtk_print_job_set_page_set (print_job, gtk_print_settings_get_page_set (settings)); gtk_print_job_set_rotate (print_job, TRUE); } static void cloudprint_printer_cb (GObject *source, GAsyncResult *res, gpointer user_data) { GtkCloudprintAccount *account = GTK_CLOUDPRINT_ACCOUNT (source); GtkPrinter *printer = GTK_PRINTER (user_data); JsonObject *result; GError *error = NULL; gboolean success = FALSE; result = gtk_cloudprint_account_printer_finish (account, res, &error); if (result != NULL) { /* Ignore capabilities for now. */ json_object_unref (result); success = TRUE; } else { GTK_NOTE (PRINTING, g_print ("Cloud Print Backend: failure getting details: %s\n", error->message)); if (error->domain == G_IO_ERROR && error->code == G_IO_ERROR_CANCELLED) { g_error_free (error); return; } g_error_free (error); } gtk_printer_set_has_details (printer, success); g_signal_emit_by_name (printer, "details-acquired", success); } static void cloudprint_printer_request_details (GtkPrinter *printer) { GtkPrintBackendCloudprint *backend; GtkCloudprintAccount *account = NULL; char *printerid = NULL; g_object_get (printer, "cloudprint-account", &account, "printer-id", &printerid, NULL); g_warn_if_fail (account != NULL); g_warn_if_fail (printerid != NULL); backend = GTK_PRINT_BACKEND_CLOUDPRINT (gtk_printer_get_backend (printer)); GTK_NOTE (PRINTING, g_print ("Cloud Print Backend: Getting details for printer id %s\n", printerid)); gtk_cloudprint_account_printer (account, printerid, backend->cancellable, cloudprint_printer_cb, printer); g_object_unref (account); g_free (printerid); }