X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkprintoperation-unix.c;h=16c7eb263abc4fa94cb1632a38b2c1b690cbf13f;hb=bd69ea0a7cc171d4cffa2c6c21a1d8ef2bebfde4;hp=3b6f5561f5361b492c8ccbae642b2db2301c1f52;hpb=c36d8f3cb3781db3cf41b1888c7daf8487324d78;p=~andy%2Fgtk diff --git a/gtk/gtkprintoperation-unix.c b/gtk/gtkprintoperation-unix.c index 3b6f5561f..16c7eb263 100644 --- a/gtk/gtkprintoperation-unix.c +++ b/gtk/gtkprintoperation-unix.c @@ -1,5 +1,6 @@ /* GTK - The GIMP Toolkit - * gtkprintoperation-unix.c: Print Operation Details for Unix and Unix like platforms + * gtkprintoperation-unix.c: Print Operation Details for Unix + * and Unix-like platforms * Copyright (C) 2006, Red Hat, Inc. * * This library is free software; you can redistribute it and/or @@ -25,31 +26,45 @@ #include #include #include +#include +#include +#include +#include #include "gtkprintoperation-private.h" -#include "gtkmarshal.h" #include "gtkmessagedialog.h" +#include +#include +#include "gtkprivate.h" #include "gtkprintunixdialog.h" #include "gtkpagesetupunixdialog.h" #include "gtkprintbackend.h" #include "gtkprinter.h" +#include "gtkprinter-private.h" #include "gtkprintjob.h" -#include "gtkalias.h" +#include "gtklabel.h" #include "gtkintl.h" +#include "gtkalias.h" -typedef struct { - GtkPrintJob *job; /* the job we are sending to the printer */ - gulong job_status_changed_tag; +typedef struct +{ GtkWindow *parent; /* just in case we need to throw error dialogs */ GMainLoop *loop; gboolean data_sent; + + /* Real printing (not preview) */ + GtkPrintJob *job; /* the job we are sending to the printer */ + cairo_surface_t *surface; + gulong job_status_changed_tag; + + } GtkPrintOperationUnix; typedef struct _PrinterFinder PrinterFinder; static void printer_finder_free (PrinterFinder *finder); -static void find_printer (const char *printer, +static void find_printer (const gchar *printer, GFunc func, gpointer data); @@ -58,7 +73,24 @@ unix_start_page (GtkPrintOperation *op, GtkPrintContext *print_context, GtkPageSetup *page_setup) { + GtkPrintOperationUnix *op_unix; + GtkPaperSize *paper_size; + cairo_surface_type_t type; + gdouble w, h; + op_unix = op->priv->platform_data; + + paper_size = gtk_page_setup_get_paper_size (page_setup); + + w = gtk_paper_size_get_width (paper_size, GTK_UNIT_POINTS); + h = gtk_paper_size_get_height (paper_size, GTK_UNIT_POINTS); + + type = cairo_surface_get_type (op_unix->surface); + + if (type == CAIRO_SURFACE_TYPE_PS) + cairo_ps_surface_set_size (op_unix->surface, w, h); + else if (type == CAIRO_SURFACE_TYPE_PDF) + cairo_pdf_surface_set_size (op_unix->surface, w, h); } static void @@ -67,7 +99,7 @@ unix_end_page (GtkPrintOperation *op, { cairo_t *cr; - cr = gtk_print_context_get_cairo (print_context); + cr = gtk_print_context_get_cairo_context (print_context); cairo_show_page (cr); } @@ -84,14 +116,159 @@ op_unix_free (GtkPrintOperationUnix *op_unix) g_free (op_unix); } +static gchar * +shell_command_substitute_file (const gchar *cmd, + const gchar *pdf_filename, + const gchar *settings_filename, + gboolean *pdf_filename_replaced, + gboolean *settings_filename_replaced) +{ + const gchar *inptr, *start; + GString *final; + + g_return_val_if_fail (cmd != NULL, NULL); + g_return_val_if_fail (pdf_filename != NULL, NULL); + g_return_val_if_fail (settings_filename != NULL, NULL); + + final = g_string_new (NULL); + + *pdf_filename_replaced = FALSE; + *settings_filename_replaced = FALSE; + + start = inptr = cmd; + while ((inptr = strchr (inptr, '%')) != NULL) + { + g_string_append_len (final, start, inptr - start); + inptr++; + switch (*inptr) + { + case 'f': + g_string_append (final, pdf_filename); + *pdf_filename_replaced = TRUE; + break; + + case 's': + g_string_append (final, settings_filename); + *settings_filename_replaced = TRUE; + break; + + case '%': + g_string_append_c (final, '%'); + break; + + default: + g_string_append_c (final, '%'); + if (*inptr) + g_string_append_c (final, *inptr); + break; + } + if (*inptr) + inptr++; + start = inptr; + } + g_string_append (final, start); + + return g_string_free (final, FALSE); +} + +void +_gtk_print_operation_platform_backend_launch_preview (GtkPrintOperation *op, + cairo_surface_t *surface, + GtkWindow *parent, + const gchar *filename) +{ + gint argc; + gchar **argv; + gchar *cmd; + gchar *preview_cmd; + GtkSettings *settings; + GtkPrintSettings *print_settings; + gchar *settings_filename = NULL; + gchar *quoted_filename; + gchar *quoted_settings_filename; + gboolean filename_used = FALSE; + gboolean settings_used = FALSE; + GdkScreen *screen; + GError *error = NULL; + gint fd; + gboolean retval; + + cairo_surface_destroy (surface); + + if (parent) + screen = gtk_window_get_screen (parent); + else + screen = gdk_screen_get_default (); + + fd = g_file_open_tmp ("settingsXXXXXX.ini", &settings_filename, &error); + if (fd < 0) + goto out; + + print_settings = gtk_print_operation_get_print_settings (op); + retval = gtk_print_settings_to_file (print_settings, settings_filename, &error); + close (fd); + + if (!retval) + goto out; + + settings = gtk_settings_get_for_screen (screen); + g_object_get (settings, "gtk-print-preview-command", &preview_cmd, NULL); + + quoted_filename = g_shell_quote (filename); + quoted_settings_filename = g_shell_quote (settings_filename); + cmd = shell_command_substitute_file (preview_cmd, quoted_filename, quoted_settings_filename, &filename_used, &settings_used); + g_shell_parse_argv (cmd, &argc, &argv, &error); + + g_free (preview_cmd); + g_free (quoted_filename); + g_free (quoted_settings_filename); + g_free (cmd); + + if (error != NULL) + goto out; + + gdk_spawn_on_screen (screen, NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &error); + + g_strfreev (argv); + + out: + if (error != NULL) + { + GtkWidget *edialog; + edialog = gtk_message_dialog_new (parent, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + _("Error launching preview") /* FIXME better text */); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (edialog), + "%s", error->message); + g_signal_connect (edialog, "response", + G_CALLBACK (gtk_widget_destroy), NULL); + + gtk_window_present (GTK_WINDOW (edialog)); + + g_error_free (error); + + filename_used = FALSE; + settings_used = FALSE; + } + + if (!filename_used) + g_unlink (filename); + + if (!settings_used) + g_unlink (settings_filename); + + g_free (settings_filename); +} + static void unix_finish_send (GtkPrintJob *job, - void *user_data, + gpointer user_data, GError *error) { - GtkPrintOperationUnix *op_unix; - - op_unix = (GtkPrintOperationUnix *) user_data; + GtkPrintOperation *op = (GtkPrintOperation *) user_data; + GtkPrintOperationUnix *op_unix = op->priv->platform_data; if (error != NULL) { @@ -111,27 +288,40 @@ unix_finish_send (GtkPrintJob *job, } op_unix->data_sent = TRUE; + if (op_unix->loop) g_main_loop_quit (op_unix->loop); + + g_object_unref (op); } static void unix_end_run (GtkPrintOperation *op, - gboolean wait) + gboolean wait, + gboolean cancelled) { GtkPrintOperationUnix *op_unix = op->priv->platform_data; + cairo_surface_finish (op_unix->surface); + + if (cancelled) + return; + if (wait) op_unix->loop = g_main_loop_new (NULL, FALSE); /* TODO: Check for error */ - gtk_print_job_send (op_unix->job, - unix_finish_send, - op_unix, NULL, - NULL); + if (op_unix->job != NULL) + { + g_object_ref (op); + gtk_print_job_send (op_unix->job, + unix_finish_send, + op, NULL); + } if (wait) { + g_object_ref (op); if (!op_unix->data_sent) { GDK_THREADS_LEAVE (); @@ -139,6 +329,8 @@ unix_end_run (GtkPrintOperation *op, GDK_THREADS_ENTER (); } g_main_loop_unref (op_unix->loop); + op_unix->loop = NULL; + g_object_unref (op); } } @@ -155,30 +347,54 @@ get_print_dialog (GtkPrintOperation *op, GtkWindow *parent) { GtkPrintOperationPrivate *priv = op->priv; - GtkWidget *pd; - GtkPageSetup *page_setup; + GtkWidget *pd, *label; + const gchar *custom_tab_label; pd = gtk_print_unix_dialog_new (NULL, parent); + gtk_print_unix_dialog_set_manual_capabilities (GTK_PRINT_UNIX_DIALOG (pd), + GTK_PRINT_CAPABILITY_PAGE_SET | + GTK_PRINT_CAPABILITY_COPIES | + GTK_PRINT_CAPABILITY_COLLATE | + GTK_PRINT_CAPABILITY_REVERSE | + GTK_PRINT_CAPABILITY_SCALE | + GTK_PRINT_CAPABILITY_PREVIEW); + if (priv->print_settings) gtk_print_unix_dialog_set_settings (GTK_PRINT_UNIX_DIALOG (pd), priv->print_settings); if (priv->default_page_setup) - page_setup = gtk_page_setup_copy (priv->default_page_setup); - else - page_setup = gtk_page_setup_new (); + gtk_print_unix_dialog_set_page_setup (GTK_PRINT_UNIX_DIALOG (pd), + priv->default_page_setup); + + g_signal_emit_by_name (op, "create-custom-widget", + &priv->custom_widget); - gtk_print_unix_dialog_set_page_setup (GTK_PRINT_UNIX_DIALOG (pd), - page_setup); - g_object_unref (page_setup); + if (priv->custom_widget) + { + custom_tab_label = priv->custom_tab_label; + + if (custom_tab_label == NULL) + { + custom_tab_label = g_get_application_name (); + if (custom_tab_label == NULL) + custom_tab_label = _("Application"); + } + label = gtk_label_new (custom_tab_label); + + gtk_print_unix_dialog_add_custom_tab (GTK_PRINT_UNIX_DIALOG (pd), + priv->custom_widget, label); + } + return pd; } -typedef struct { +typedef struct +{ GtkPrintOperation *op; gboolean do_print; - GError **error; + gboolean do_preview; GtkPrintOperationResult result; GtkPrintOperationPrintFunc print_cb; GDestroyNotify destroy; @@ -197,77 +413,81 @@ print_response_data_free (gpointer data) static void finish_print (PrintResponseData *rdata, - GtkPrinter *printer, - GtkPageSetup *page_setup, - GtkPrintSettings *settings) + GtkPrinter *printer, + GtkPageSetup *page_setup, + GtkPrintSettings *settings) { GtkPrintOperation *op = rdata->op; GtkPrintOperationPrivate *priv = op->priv; - - priv->start_page = unix_start_page; - priv->end_page = unix_end_page; - priv->end_run = unix_end_run; + GtkPrintJob *job; if (rdata->do_print) { - GtkPrintOperationUnix *op_unix; - gtk_print_operation_set_print_settings (op, settings); - - op_unix = g_new0 (GtkPrintOperationUnix, 1); - op_unix->job = gtk_print_job_new (priv->job_name, - printer, - settings, - page_setup); - - rdata->op->priv->surface = gtk_print_job_get_surface (op_unix->job, rdata->error); - if (op->priv->surface == NULL) - { - rdata->do_print = FALSE; - op_unix_free (op_unix); - rdata->result = GTK_PRINT_OPERATION_RESULT_ERROR; - goto out; - } + priv->print_context = _gtk_print_context_new (op); - _gtk_print_operation_set_status (op, gtk_print_job_get_status (op_unix->job), NULL); - op_unix->job_status_changed_tag = - g_signal_connect (op_unix->job, "status_changed", - G_CALLBACK (job_status_changed_cb), op); - - op_unix->parent = rdata->parent; + if ( (page_setup != NULL) && (gtk_print_operation_get_default_page_setup (op) == NULL)) + gtk_print_operation_set_default_page_setup (op, page_setup); - priv->dpi_x = 72; - priv->dpi_y = 72; - - priv->platform_data = op_unix; - priv->free_platform_data = (GDestroyNotify) op_unix_free; - - priv->print_pages = op_unix->job->print_pages; - priv->page_ranges = op_unix->job->page_ranges; - priv->num_page_ranges = op_unix->job->num_page_ranges; - - priv->manual_num_copies = op_unix->job->num_copies; - priv->manual_collation = op_unix->job->collate; - priv->manual_reverse = op_unix->job->reverse; - priv->manual_page_set = op_unix->job->page_set; - priv->manual_scale = op_unix->job->scale; - priv->manual_orientation = op_unix->job->rotate_to_orientation; - } + _gtk_print_context_set_page_setup (priv->print_context, page_setup); - out: + if (!rdata->do_preview) + { + GtkPrintOperationUnix *op_unix; + cairo_t *cr; + + op_unix = g_new0 (GtkPrintOperationUnix, 1); + priv->platform_data = op_unix; + priv->free_platform_data = (GDestroyNotify) op_unix_free; + op_unix->parent = rdata->parent; + + priv->start_page = unix_start_page; + priv->end_page = unix_end_page; + priv->end_run = unix_end_run; + + job = gtk_print_job_new (priv->job_name, printer, settings, page_setup); + op_unix->job = job; + gtk_print_job_set_track_print_status (job, priv->track_print_status); + + op_unix->surface = gtk_print_job_get_surface (job, &priv->error); + if (op_unix->surface == NULL) + { + rdata->result = GTK_PRINT_OPERATION_RESULT_ERROR; + rdata->do_print = FALSE; + goto out; + } + + cr = cairo_create (op_unix->surface); + gtk_print_context_set_cairo_context (priv->print_context, cr, 72, 72); + cairo_destroy (cr); + + _gtk_print_operation_set_status (op, gtk_print_job_get_status (job), NULL); + + op_unix->job_status_changed_tag = + g_signal_connect (job, "status-changed", + G_CALLBACK (job_status_changed_cb), op); + + priv->print_pages = job->print_pages; + priv->page_ranges = job->page_ranges; + priv->num_page_ranges = job->num_page_ranges; + + priv->manual_num_copies = job->num_copies; + priv->manual_collation = job->collate; + priv->manual_reverse = job->reverse; + priv->manual_page_set = job->page_set; + priv->manual_scale = job->scale; + priv->manual_orientation = job->rotate_to_orientation; + } + } + out: if (rdata->print_cb) - { - if (rdata->do_print) - rdata->print_cb (op, FALSE); - else - _gtk_print_operation_set_status (op, GTK_PRINT_STATUS_FINISHED_ABORTED, NULL); - } + rdata->print_cb (op, rdata->parent, rdata->do_print, rdata->result); if (rdata->destroy) rdata->destroy (rdata); } -static void +static void handle_print_response (GtkWidget *dialog, gint response, gpointer data) @@ -280,30 +500,43 @@ handle_print_response (GtkWidget *dialog, if (response == GTK_RESPONSE_OK) { - rdata->result = GTK_PRINT_OPERATION_RESULT_APPLY; - printer = gtk_print_unix_dialog_get_selected_printer (GTK_PRINT_UNIX_DIALOG (pd)); - if (printer == NULL) - goto out; - + + rdata->result = GTK_PRINT_OPERATION_RESULT_APPLY; + rdata->do_preview = FALSE; + if (printer != NULL) + rdata->do_print = TRUE; + } + else if (response == GTK_RESPONSE_APPLY) + { + /* print preview */ + rdata->result = GTK_PRINT_OPERATION_RESULT_APPLY; + rdata->do_preview = TRUE; rdata->do_print = TRUE; + rdata->op->priv->action = GTK_PRINT_OPERATION_ACTION_PREVIEW; + } + + if (rdata->do_print) + { settings = gtk_print_unix_dialog_get_settings (GTK_PRINT_UNIX_DIALOG (pd)); page_setup = gtk_print_unix_dialog_get_page_setup (GTK_PRINT_UNIX_DIALOG (pd)); - } - - out: + + g_signal_emit_by_name (rdata->op, "custom-widget-apply", rdata->op->priv->custom_widget); + } + finish_print (rdata, printer, page_setup, settings); if (settings) g_object_unref (settings); - + gtk_widget_destroy (GTK_WIDGET (pd)); + } static void -found_printer (GtkPrinter *printer, +found_printer (GtkPrinter *printer, PrintResponseData *rdata) { GtkPrintOperation *op = rdata->op; @@ -314,7 +547,8 @@ found_printer (GtkPrinter *printer, if (rdata->loop) g_main_loop_quit (rdata->loop); - if (printer != NULL) { + if (printer != NULL) + { rdata->result = GTK_PRINT_OPERATION_RESULT_APPLY; rdata->do_print = TRUE; @@ -344,24 +578,25 @@ found_printer (GtkPrinter *printer, void _gtk_print_operation_platform_backend_run_dialog_async (GtkPrintOperation *op, + gboolean show_dialog, GtkWindow *parent, GtkPrintOperationPrintFunc print_cb) { GtkWidget *pd; PrintResponseData *rdata; - const char *printer_name; + const gchar *printer_name; rdata = g_new (PrintResponseData, 1); rdata->op = g_object_ref (op); rdata->do_print = FALSE; + rdata->do_preview = FALSE; rdata->result = GTK_PRINT_OPERATION_RESULT_CANCEL; - rdata->error = NULL; rdata->print_cb = print_cb; rdata->parent = parent; rdata->loop = NULL; rdata->destroy = print_response_data_free; - if (op->priv->show_dialog) + if (show_dialog) { pd = get_print_dialog (op, parent); gtk_window_set_modal (GTK_WINDOW (pd), TRUE); @@ -377,34 +612,129 @@ _gtk_print_operation_platform_backend_run_dialog_async (GtkPrintOperation if (op->priv->print_settings) printer_name = gtk_print_settings_get_printer (op->priv->print_settings); - find_printer (printer_name, - (GFunc) found_printer, rdata); + find_printer (printer_name, (GFunc) found_printer, rdata); } } +static cairo_status_t +write_preview (void *closure, + const unsigned char *data, + unsigned int length) +{ + gint fd = GPOINTER_TO_INT (closure); + gssize written; + + while (length > 0) + { + written = write (fd, data, length); + + if (written == -1) + { + if (errno == EAGAIN || errno == EINTR) + continue; + + return CAIRO_STATUS_WRITE_ERROR; + } + + data += written; + length -= written; + } + + return CAIRO_STATUS_SUCCESS; +} + +static void +close_preview (void *data) +{ + gint fd = GPOINTER_TO_INT (data); + + close (fd); +} + +cairo_surface_t * +_gtk_print_operation_platform_backend_create_preview_surface (GtkPrintOperation *op, + GtkPageSetup *page_setup, + gdouble *dpi_x, + gdouble *dpi_y, + gchar **target) +{ + gchar *filename; + gint fd; + GtkPaperSize *paper_size; + gdouble w, h; + cairo_surface_t *surface; + static cairo_user_data_key_t key; + + filename = g_build_filename (g_get_tmp_dir (), "previewXXXXXX.pdf", NULL); + fd = g_mkstemp (filename); + *target = filename; + + paper_size = gtk_page_setup_get_paper_size (page_setup); + w = gtk_paper_size_get_width (paper_size, GTK_UNIT_POINTS); + h = gtk_paper_size_get_height (paper_size, GTK_UNIT_POINTS); + + *dpi_x = *dpi_y = 72; + surface = cairo_pdf_surface_create_for_stream (write_preview, GINT_TO_POINTER(fd), w, h); + + cairo_surface_set_user_data (surface, &key, GINT_TO_POINTER (fd), close_preview); + + return surface; +} + +void +_gtk_print_operation_platform_backend_preview_start_page (GtkPrintOperation *op, + cairo_surface_t *surface, + cairo_t *cr) +{ +} + +void +_gtk_print_operation_platform_backend_preview_end_page (GtkPrintOperation *op, + cairo_surface_t *surface, + cairo_t *cr) +{ + cairo_show_page (cr); +} + +void +_gtk_print_operation_platform_backend_resize_preview_surface (GtkPrintOperation *op, + GtkPageSetup *page_setup, + cairo_surface_t *surface) +{ + GtkPaperSize *paper_size; + gdouble w, h; + + paper_size = gtk_page_setup_get_paper_size (page_setup); + w = gtk_paper_size_get_width (paper_size, GTK_UNIT_POINTS); + h = gtk_paper_size_get_height (paper_size, GTK_UNIT_POINTS); + cairo_pdf_surface_set_size (surface, w, h); +} + + GtkPrintOperationResult _gtk_print_operation_platform_backend_run_dialog (GtkPrintOperation *op, + gboolean show_dialog, GtkWindow *parent, - gboolean *do_print, - GError **error) + gboolean *do_print) { GtkWidget *pd; PrintResponseData rdata; gint response; - const char *printer_name; + const gchar *printer_name; rdata.op = op; rdata.do_print = FALSE; + rdata.do_preview = FALSE; rdata.result = GTK_PRINT_OPERATION_RESULT_CANCEL; - rdata.error = error; rdata.print_cb = NULL; rdata.destroy = NULL; rdata.parent = parent; rdata.loop = NULL; - if (op->priv->show_dialog) + if (show_dialog) { pd = get_print_dialog (op, parent); + response = gtk_dialog_run (GTK_DIALOG (pd)); handle_print_response (pd, response, &rdata); } @@ -423,6 +753,7 @@ _gtk_print_operation_platform_backend_run_dialog (GtkPrintOperation *op, GDK_THREADS_ENTER (); g_main_loop_unref (rdata.loop); + rdata.loop = NULL; } *do_print = rdata.do_print; @@ -431,7 +762,8 @@ _gtk_print_operation_platform_backend_run_dialog (GtkPrintOperation *op, } -typedef struct { +typedef struct +{ GtkPageSetup *page_setup; GtkPageSetupDoneFunc done_cb; gpointer data; @@ -443,7 +775,9 @@ page_setup_data_free (gpointer data) { PageSetupResponseData *rdata = data; - g_object_unref (rdata->page_setup); + if (rdata->page_setup) + g_object_unref (rdata->page_setup); + g_free (rdata); } @@ -571,11 +905,12 @@ gtk_print_run_page_setup_dialog_async (GtkWindow *parent, gtk_window_present (GTK_WINDOW (dialog)); } -struct _PrinterFinder { +struct _PrinterFinder +{ gboolean found_printer; GFunc func; gpointer data; - char *printer_name; + gchar *printer_name; GList *backends; guint timeout_tag; GtkPrinter *printer; @@ -610,8 +945,11 @@ printer_added_cb (GtkPrintBackend *backend, GtkPrinter *printer, PrinterFinder *finder) { - if (gtk_printer_is_virtual (printer) || - finder->found_printer) + if (finder->found_printer) + return; + + /* FIXME this skips "Print to PDF" - is this intentional ? */ + if (gtk_printer_is_virtual (printer)) return; if (finder->printer_name != NULL && @@ -652,7 +990,7 @@ printer_list_done_cb (GtkPrintBackend *backend, } static void -find_printer_init (PrinterFinder *finder, +find_printer_init (PrinterFinder *finder, GtkPrintBackend *backend) { GList *list; @@ -680,12 +1018,10 @@ find_printer_init (PrinterFinder *finder, } else { - g_signal_connect (backend, - "printer-added", + g_signal_connect (backend, "printer-added", (GCallback) printer_added_cb, finder); - g_signal_connect (backend, - "printer-list-done", + g_signal_connect (backend, "printer-list-done", (GCallback) printer_list_done_cb, finder); } @@ -723,9 +1059,9 @@ printer_finder_free (PrinterFinder *finder) } static void -find_printer (const char *printer, - GFunc func, - gpointer data) +find_printer (const gchar *printer, + GFunc func, + gpointer data) { GList *node, *next; PrinterFinder *finder;