diff options
author | Alexander Larsson <alexl@redhat.com> | 2006-06-02 15:16:13 +0000 |
---|---|---|
committer | Alexander Larsson <alexl@src.gnome.org> | 2006-06-02 15:16:13 +0000 |
commit | fad69ba06c957dfe192d6a38613fae58cf157be9 (patch) | |
tree | de7aca9f0f8f203d5a7ad2d93d35eba5964e1328 | |
parent | caf6c4196d0843a5112ea5a1e43677b68738a7a5 (diff) | |
download | gtk+-fad69ba06c957dfe192d6a38613fae58cf157be9.tar.gz |
Add gtkprintoperationpreview.[ch] Set default preview command. Hardcoded
2006-06-02 Alexander Larsson <alexl@redhat.com>
* gtk/Makefile.am:
Add gtkprintoperationpreview.[ch]
Set default preview command. Hardcoded for now.
* gtk/gtkmarshalers.list:
Add BOOLEAN:OBJECT,OBJECT,OBJECT
* gtk/gtkprintbackend.c:
Add preview command property.
* gtk/gtkprintcontext.[ch]:
Make less dependent on PrintOperation for output settings
Externally set cairo_t and dpi. Resettable.
Create fontmap without metrics hinting (so that print preview
text layout doesn't depend on zoom level).
* gtk/gtkprintoperation-private.h:
* gtk/gtkprintoperation-unix.c:
* gtk/gtkprintoperation.[ch]:
Initial work on print preview API and default implementation
using an external preview app.
* gtk/gtkprintoperation-win32.c:
Some needed updates. Not done, needs more work.
* gtk/gtkprintoperationpreview.[ch]:
New interface used in print preview api.
* gtk/gtkprintunixdialog.c:
Add print preview dialog.
* tests/print-editor.c:
Test using an custom print preview widget.
-rw-r--r-- | ChangeLog | 36 | ||||
-rw-r--r-- | ChangeLog.pre-2-10 | 36 | ||||
-rw-r--r-- | gtk/Makefile.am | 4 | ||||
-rw-r--r-- | gtk/gtkmarshalers.list | 1 | ||||
-rw-r--r-- | gtk/gtkprintbackend.c | 8 | ||||
-rw-r--r-- | gtk/gtkprintcontext.c | 72 | ||||
-rw-r--r-- | gtk/gtkprintcontext.h | 5 | ||||
-rw-r--r-- | gtk/gtkprintoperation-private.h | 36 | ||||
-rw-r--r-- | gtk/gtkprintoperation-unix.c | 300 | ||||
-rw-r--r-- | gtk/gtkprintoperation-win32.c | 1 | ||||
-rw-r--r-- | gtk/gtkprintoperation.c | 472 | ||||
-rw-r--r-- | gtk/gtkprintoperation.h | 10 | ||||
-rw-r--r-- | gtk/gtkprintoperationpreview.c | 116 | ||||
-rw-r--r-- | gtk/gtkprintoperationpreview.h | 78 | ||||
-rw-r--r-- | gtk/gtkprintunixdialog.c | 3 | ||||
-rw-r--r-- | tests/print-editor.c | 207 |
16 files changed, 1204 insertions, 181 deletions
@@ -1,3 +1,39 @@ +2006-06-02 Alexander Larsson <alexl@redhat.com> + + * gtk/Makefile.am: + Add gtkprintoperationpreview.[ch] + Set default preview command. Hardcoded for now. + + * gtk/gtkmarshalers.list: + Add BOOLEAN:OBJECT,OBJECT,OBJECT + + * gtk/gtkprintbackend.c: + Add preview command property. + + * gtk/gtkprintcontext.[ch]: + Make less dependent on PrintOperation for output settings + Externally set cairo_t and dpi. Resettable. + Create fontmap without metrics hinting (so that print preview + text layout doesn't depend on zoom level). + + * gtk/gtkprintoperation-private.h: + * gtk/gtkprintoperation-unix.c: + * gtk/gtkprintoperation.[ch]: + Initial work on print preview API and default implementation + using an external preview app. + + * gtk/gtkprintoperation-win32.c: + Some needed updates. Not done, needs more work. + + * gtk/gtkprintoperationpreview.[ch]: + New interface used in print preview api. + + * gtk/gtkprintunixdialog.c: + Add print preview dialog. + + * tests/print-editor.c: + Test using an custom print preview widget. + 2006-06-02 Emmanuele Bassi <ebassi@gnome.org> * gtk/gtkrecentmanager.c diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index d0505a9fad..e9e5a46539 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -1,3 +1,39 @@ +2006-06-02 Alexander Larsson <alexl@redhat.com> + + * gtk/Makefile.am: + Add gtkprintoperationpreview.[ch] + Set default preview command. Hardcoded for now. + + * gtk/gtkmarshalers.list: + Add BOOLEAN:OBJECT,OBJECT,OBJECT + + * gtk/gtkprintbackend.c: + Add preview command property. + + * gtk/gtkprintcontext.[ch]: + Make less dependent on PrintOperation for output settings + Externally set cairo_t and dpi. Resettable. + Create fontmap without metrics hinting (so that print preview + text layout doesn't depend on zoom level). + + * gtk/gtkprintoperation-private.h: + * gtk/gtkprintoperation-unix.c: + * gtk/gtkprintoperation.[ch]: + Initial work on print preview API and default implementation + using an external preview app. + + * gtk/gtkprintoperation-win32.c: + Some needed updates. Not done, needs more work. + + * gtk/gtkprintoperationpreview.[ch]: + New interface used in print preview api. + + * gtk/gtkprintunixdialog.c: + Add print preview dialog. + + * tests/print-editor.c: + Test using an custom print preview widget. + 2006-06-02 Emmanuele Bassi <ebassi@gnome.org> * gtk/gtkrecentmanager.c diff --git a/gtk/Makefile.am b/gtk/Makefile.am index d04a1cf666..136527b919 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -4,6 +4,7 @@ SUBDIRS=theme-bits if OS_UNIX SUBDIRS += xdgmime +GTK_PRINT_PREVIEW_COMMAND="evince %f" endif DIST_SUBDIRS=theme-bits xdgmime @@ -25,6 +26,7 @@ INCLUDES = \ -DGTK_HOST=\"$(host)\" \ -DGTK_COMPILATION \ -DGTK_PRINT_BACKENDS=\"$(GTK_PRINT_BACKENDS)\" \ + -DGTK_PRINT_PREVIEW_COMMAND=\"$(GTK_PRINT_PREVIEW_COMMAND)\" \ -I$(top_builddir)/gtk \ -I$(top_srcdir) -I../gdk \ -I$(top_srcdir)/gdk \ @@ -231,6 +233,7 @@ gtk_public_h_sources = \ gtkpreview.h \ gtkprintcontext.h \ gtkprintoperation.h \ + gtkprintoperationpreview.h \ gtkprintsettings.h \ gtkprivate.h \ gtkprogress.h \ @@ -487,6 +490,7 @@ gtk_c_sources = \ gtkpreview.c \ gtkprintcontext.c \ gtkprintoperation.c \ + gtkprintoperationpreview.c \ gtkprintsettings.c \ gtkprintutils.c \ gtkprogress.c \ diff --git a/gtk/gtkmarshalers.list b/gtk/gtkmarshalers.list index 88511709e5..e089d13351 100644 --- a/gtk/gtkmarshalers.list +++ b/gtk/gtkmarshalers.list @@ -32,6 +32,7 @@ BOOLEAN:OBJECT,INT,INT,UINT BOOLEAN:OBJECT,STRING,STRING,BOXED BOOLEAN:OBJECT,BOXED BOOLEAN:OBJECT,BOXED,BOXED +BOOLEAN:OBJECT,OBJECT,OBJECT BOOLEAN:OBJECT,STRING,STRING BOOLEAN:INT BOOLEAN:INT,INT diff --git a/gtk/gtkprintbackend.c b/gtk/gtkprintbackend.c index a2af6c4865..1a139deb52 100644 --- a/gtk/gtkprintbackend.c +++ b/gtk/gtkprintbackend.c @@ -191,7 +191,7 @@ _gtk_print_backend_module_create (GtkPrintBackendModule *pb_module) return NULL; } -GtkPrintBackend * +static GtkPrintBackend * _gtk_print_backend_create (const char *backend_name) { GSList *l; @@ -200,7 +200,6 @@ _gtk_print_backend_create (const char *backend_name) GtkPrintBackendModule *pb_module; GtkPrintBackend *pb; - /* TODO: make module loading code work */ for (l = loaded_backends; l != NULL; l = l->next) { pb_module = l->data; @@ -255,6 +254,11 @@ gtk_print_backend_initialize (void) GTK_PRINT_BACKENDS, GTK_PARAM_READWRITE)); + gtk_settings_install_property (g_param_spec_string ("gtk-print-preview-command", + P_("Default command to run when displaying a print preview"), + P_("Command to run when displaying a print preview"), + GTK_PRINT_PREVIEW_COMMAND, + GTK_PARAM_READWRITE)); initialized = TRUE; } } diff --git a/gtk/gtkprintcontext.c b/gtk/gtkprintcontext.c index 5e68ae2173..219a830ea6 100644 --- a/gtk/gtkprintcontext.c +++ b/gtk/gtkprintcontext.c @@ -40,6 +40,9 @@ struct _GtkPrintContext GtkPageSetup *page_setup; PangoFontMap *fontmap; + gdouble surface_dpi_x; + gdouble surface_dpi_y; + gdouble pixels_per_unit_x; gdouble pixels_per_unit_y; }; @@ -60,8 +63,8 @@ gtk_print_context_finalize (GObject *object) if (context->page_setup) g_object_unref (context->page_setup); - cairo_destroy (context->cr); - + if (context->cr) + cairo_destroy (context->cr); G_OBJECT_CLASS (gtk_print_context_parent_class)->finalize (object); } @@ -83,15 +86,31 @@ gtk_print_context_class_init (GtkPrintContextClass *class) GtkPrintContext * _gtk_print_context_new (GtkPrintOperation *op) { - GtkPrintOperationPrivate *priv = op->priv; GtkPrintContext *context; context = g_object_new (GTK_TYPE_PRINT_CONTEXT, NULL); context->op = op; - context->cr = cairo_create (priv->surface); + context->cr = NULL; + context->fontmap = pango_cairo_font_map_new (); + + return context; +} + +void +gtk_print_context_set_cairo_context (GtkPrintContext *context, + cairo_t *cr, + double dpi_x, + double dpi_y) +{ + if (context->cr) + cairo_destroy (context->cr); + + context->cr = cairo_reference (cr); + context->surface_dpi_x = dpi_x; + context->surface_dpi_y = dpi_y; - switch (priv->unit) + switch (context->op->priv->unit) { default: case GTK_UNIT_PIXEL: @@ -100,34 +119,31 @@ _gtk_print_context_new (GtkPrintOperation *op) context->pixels_per_unit_y = 1.0; break; case GTK_UNIT_POINTS: - context->pixels_per_unit_x = priv->dpi_x / POINTS_PER_INCH; - context->pixels_per_unit_y = priv->dpi_y / POINTS_PER_INCH; + context->pixels_per_unit_x = dpi_x / POINTS_PER_INCH; + context->pixels_per_unit_y = dpi_y / POINTS_PER_INCH; break; case GTK_UNIT_INCH: - context->pixels_per_unit_x = priv->dpi_x; - context->pixels_per_unit_y = priv->dpi_y; + context->pixels_per_unit_x = dpi_x; + context->pixels_per_unit_y = dpi_y; break; case GTK_UNIT_MM: - context->pixels_per_unit_x = priv->dpi_x / MM_PER_INCH; - context->pixels_per_unit_y = priv->dpi_y / MM_PER_INCH; + context->pixels_per_unit_x = dpi_x / MM_PER_INCH; + context->pixels_per_unit_y = dpi_y / MM_PER_INCH; break; } cairo_scale (context->cr, context->pixels_per_unit_x, context->pixels_per_unit_y); - context->fontmap = pango_cairo_font_map_new (); /* We use the unit-scaled resolution, as we still want fonts given in points to work */ pango_cairo_font_map_set_resolution (PANGO_CAIRO_FONT_MAP (context->fontmap), - priv->dpi_y / context->pixels_per_unit_y); - - return context; + dpi_y / context->pixels_per_unit_y); } + void _gtk_print_context_rotate_according_to_orientation (GtkPrintContext *context) { - GtkPrintOperationPrivate *priv = context->op->priv; cairo_t *cr = context->cr; cairo_matrix_t matrix; GtkPaperSize *paper_size; @@ -136,9 +152,9 @@ _gtk_print_context_rotate_according_to_orientation (GtkPrintContext *context) paper_size = gtk_page_setup_get_paper_size (context->page_setup); width = gtk_paper_size_get_width (paper_size, GTK_UNIT_INCH); - width = width * priv->dpi_x / context->pixels_per_unit_x; + width = width * context->surface_dpi_x / context->pixels_per_unit_x; height = gtk_paper_size_get_height (paper_size, GTK_UNIT_INCH); - height = height * priv->dpi_y / context->pixels_per_unit_y; + height = height * context->surface_dpi_y / context->pixels_per_unit_y; switch (gtk_page_setup_get_orientation (context->page_setup)) { @@ -188,8 +204,8 @@ _gtk_print_context_translate_into_margin (GtkPrintContext *context) top = gtk_page_setup_get_top_margin (context->page_setup, GTK_UNIT_INCH); cairo_translate (context->cr, - left * priv->dpi_x / context->pixels_per_unit_x, - top * priv->dpi_y / context->pixels_per_unit_y); + left * context->surface_dpi_x / context->pixels_per_unit_x, + top * context->surface_dpi_y / context->pixels_per_unit_y); } void @@ -272,7 +288,7 @@ gtk_print_context_get_width (GtkPrintContext *context) width = gtk_page_setup_get_page_width (context->page_setup, GTK_UNIT_INCH); /* Really dpi_x? What about landscape? what does dpi_x mean in that case? */ - return width * priv->dpi_x / context->pixels_per_unit_x; + return width * context->surface_dpi_x / context->pixels_per_unit_x; } /** @@ -300,8 +316,8 @@ gtk_print_context_get_height (GtkPrintContext *context) else height = gtk_page_setup_get_page_height (context->page_setup, GTK_UNIT_INCH); - /* Really dpi_x? What about landscape? what does dpi_x mean in that case? */ - return height * priv->dpi_y / context->pixels_per_unit_y; + /* Really dpi_y? What about landscape? what does dpi_y mean in that case? */ + return height * context->surface_dpi_y / context->pixels_per_unit_y; } /** @@ -320,7 +336,7 @@ gtk_print_context_get_dpi_x (GtkPrintContext *context) { g_return_val_if_fail (GTK_IS_PRINT_CONTEXT (context), 0); - return context->op->priv->dpi_x; + return context->surface_dpi_x; } /** @@ -339,7 +355,7 @@ gtk_print_context_get_dpi_y (GtkPrintContext *context) { g_return_val_if_fail (GTK_IS_PRINT_CONTEXT (context), 0); - return context->op->priv->dpi_y; + return context->surface_dpi_y; } /** @@ -376,10 +392,16 @@ PangoContext * gtk_print_context_create_pango_context (GtkPrintContext *context) { PangoContext *pango_context; + cairo_font_options_t *options; g_return_val_if_fail (GTK_IS_PRINT_CONTEXT (context), NULL); pango_context = pango_cairo_font_map_create_context (PANGO_CAIRO_FONT_MAP (context->fontmap)); + + options = cairo_font_options_create (); + cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF); + pango_cairo_context_set_font_options (pango_context, options); + cairo_font_options_destroy (options); return pango_context; } diff --git a/gtk/gtkprintcontext.h b/gtk/gtkprintcontext.h index 0ce480087f..936cae4f40 100644 --- a/gtk/gtkprintcontext.h +++ b/gtk/gtkprintcontext.h @@ -51,6 +51,11 @@ PangoFontMap *gtk_print_context_get_pango_fontmap (GtkPrintContext *context); PangoContext *gtk_print_context_create_pango_context (GtkPrintContext *context); PangoLayout *gtk_print_context_create_pango_layout (GtkPrintContext *context); +/* Needed for preview implementations */ +void gtk_print_context_set_cairo_context (GtkPrintContext *context, + cairo_t *cr, + double dpi_x, + double dpi_y); G_END_DECLS diff --git a/gtk/gtkprintoperation-private.h b/gtk/gtkprintoperation-private.h index c44f1b4923..7edfc0cdce 100644 --- a/gtk/gtkprintoperation-private.h +++ b/gtk/gtkprintoperation-private.h @@ -46,9 +46,11 @@ struct _GtkPrintOperationPrivate guint print_pages_idle_id; guint show_progress_timeout_id; + GtkPrintContext *print_context; + /* Data for the print job: */ - cairo_surface_t *surface; - gdouble dpi_x, dpi_y; + /* cairo_surface_t *surface; */ + /* gdouble dpi_x, dpi_y; */ GtkPrintPages print_pages; GtkPageRange *page_ranges; @@ -78,17 +80,29 @@ struct _GtkPrintOperationPrivate gboolean cancelled); }; -GtkPrintOperationResult _gtk_print_operation_platform_backend_run_dialog (GtkPrintOperation *operation, - GtkWindow *parent, - gboolean *do_print, - GError **error); typedef void (* GtkPrintOperationPrintFunc) (GtkPrintOperation *op, - GtkWindow *parent); - -void _gtk_print_operation_platform_backend_run_dialog_async (GtkPrintOperation *op, - GtkWindow *parent, - GtkPrintOperationPrintFunc print_cb); + GtkWindow *parent, + gboolean is_preview); + +GtkPrintOperationResult _gtk_print_operation_platform_backend_run_dialog (GtkPrintOperation *operation, + GtkWindow *parent, + gboolean *do_print, + GError **error); +void _gtk_print_operation_platform_backend_run_dialog_async (GtkPrintOperation *op, + GtkWindow *parent, + GtkPrintOperationPrintFunc print_cb); +void _gtk_print_operation_platform_backend_launch_preview (GtkPrintOperation *op, + GtkWindow *parent, + const char *filename); +cairo_surface_t * _gtk_print_operation_platform_backend_create_preview_surface (GtkPrintOperation *op, + GtkPageSetup *page_setup, + gdouble *dpi_x, + gdouble *dpi_y, + const gchar *target); +void _gtk_print_operation_platform_backend_resize_preview_surface (GtkPrintOperation *op, + GtkPageSetup *page_setup, + cairo_surface_t *surface); void _gtk_print_operation_set_status (GtkPrintOperation *op, GtkPrintStatus status, diff --git a/gtk/gtkprintoperation-unix.c b/gtk/gtkprintoperation-unix.c index cf57ec6290..d6b486a486 100644 --- a/gtk/gtkprintoperation-unix.c +++ b/gtk/gtkprintoperation-unix.c @@ -25,6 +25,9 @@ #include <sys/types.h> #include <sys/stat.h> #include <string.h> +#include <errno.h> +#include <stdlib.h> +#include <fcntl.h> #include "gtkprintoperation-private.h" #include "gtkmarshal.h" @@ -41,13 +44,17 @@ #include "gtkalias.h" #include "gtkintl.h" - typedef struct { - GtkPrintJob *job; /* the job we are sending to the printer */ - gulong job_status_changed_tag; 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; @@ -62,21 +69,24 @@ unix_start_page (GtkPrintOperation *op, GtkPrintContext *print_context, GtkPageSetup *page_setup) { + GtkPrintOperationUnix *op_unix; GtkPaperSize *paper_size; cairo_surface_type_t type; double 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->priv->surface); + type = cairo_surface_get_type (op_unix->surface); if (type == CAIRO_SURFACE_TYPE_PS) - cairo_ps_surface_set_size (op->priv->surface, w, h); + cairo_ps_surface_set_size (op_unix->surface, w, h); else if (type == CAIRO_SURFACE_TYPE_PDF) - cairo_pdf_surface_set_size (op->priv->surface, w, h); + cairo_pdf_surface_set_size (op_unix->surface, w, h); } static void @@ -102,6 +112,112 @@ op_unix_free (GtkPrintOperationUnix *op_unix) g_free (op_unix); } +static char * +shell_command_substitute_file (const gchar *cmd, + const gchar *filename) +{ + const char *inptr, *start; + char *result; + GString *final; + + g_return_val_if_fail (cmd != NULL, NULL); + g_return_val_if_fail (filename != NULL, NULL); + + result = NULL; + final = g_string_new (NULL); + + 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, filename ? filename : ""); + 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); + + result = final->str; + + g_string_free (final, FALSE); + + return result; +} + +void +_gtk_print_operation_platform_backend_launch_preview (GtkPrintOperation *op, + GtkWindow *parent, + const char *filename) +{ + int argc; + gchar **argv; + gchar *cmd; + gchar *preview_cmd; + GtkSettings *settings; + gchar *quoted_filename; + GdkScreen *screen; + GError *error = NULL; + + settings = gtk_settings_get_default (); + g_object_get (settings, "gtk-print-preview-command", &preview_cmd, NULL); + + quoted_filename = g_shell_quote (filename); + cmd = shell_command_substitute_file (preview_cmd, quoted_filename); + g_shell_parse_argv (cmd, &argc, &argv, &error); + + if (error !=NULL) + goto out; + + if (parent) + screen = gtk_window_get_screen (parent); + else + screen = gdk_screen_get_default (); + + gdk_spawn_on_screen (screen, NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &error); + + 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); + gtk_window_set_modal (GTK_WINDOW (edialog), TRUE); + g_signal_connect (edialog, "response", + G_CALLBACK (gtk_widget_destroy), NULL); + + gtk_window_present (GTK_WINDOW (edialog)); + + g_error_free (error); + } + + g_free (cmd); + g_free (quoted_filename); + g_free (preview_cmd); + g_strfreev (argv); +} + static void unix_finish_send (GtkPrintJob *job, void *user_data, @@ -129,6 +245,7 @@ unix_finish_send (GtkPrintJob *job, } op_unix->data_sent = TRUE; + if (op_unix->loop) g_main_loop_quit (op_unix->loop); } @@ -140,6 +257,8 @@ unix_end_run (GtkPrintOperation *op, { GtkPrintOperationUnix *op_unix = op->priv->platform_data; + cairo_surface_finish (op_unix->surface); + if (cancelled) return; @@ -147,10 +266,11 @@ unix_end_run (GtkPrintOperation *op, 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) + gtk_print_job_send (op_unix->job, + unix_finish_send, + op_unix, NULL, + NULL); if (wait) { @@ -253,64 +373,70 @@ finish_print (PrintResponseData *rdata, { GtkPrintOperation *op = rdata->op; GtkPrintOperationPrivate *priv = op->priv; + gboolean is_preview; - priv->start_page = unix_start_page; - priv->end_page = unix_end_page; - priv->end_run = unix_end_run; + is_preview = rdata->result == GTK_PRINT_OPERATION_RESULT_PREVIEW; 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); + priv->print_context = _gtk_print_context_new (op); + _gtk_print_context_set_page_setup (priv->print_context, page_setup); - gtk_print_job_set_track_print_status (op_unix->job, priv->track_print_status); - - rdata->op->priv->surface = gtk_print_job_get_surface (op_unix->job, rdata->error); - if (op->priv->surface == NULL) + if (!is_preview) { - rdata->do_print = FALSE; - op_unix_free (op_unix); - rdata->result = GTK_PRINT_OPERATION_RESULT_ERROR; - goto out; - } - - _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; - - 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; + 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; + + op_unix->job = gtk_print_job_new (priv->job_name, + printer, + settings, + page_setup); + gtk_print_job_set_track_print_status (op_unix->job, priv->track_print_status); + + op_unix->surface = gtk_print_job_get_surface (op_unix->job, rdata->error); + if (op_unix->surface == NULL) { + rdata->do_print = FALSE; + goto out; + } + + cr = cairo_create (op_unix->surface); + gtk_print_context_set_cairo_context (op->priv->print_context, + cr, 72, 72); + cairo_destroy (cr); + + _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); + + 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; + } } - - out: + out: if (rdata->print_cb) { if (rdata->do_print) - rdata->print_cb (op, rdata->parent); + rdata->print_cb (op, rdata->parent, is_preview); else _gtk_print_operation_set_status (op, GTK_PRINT_STATUS_FINISHED_ABORTED, NULL); } @@ -319,7 +445,7 @@ finish_print (PrintResponseData *rdata, rdata->destroy (rdata); } -static void +static void handle_print_response (GtkWidget *dialog, gint response, gpointer data) @@ -335,24 +461,31 @@ handle_print_response (GtkWidget *dialog, 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; - + if (printer != NULL) + rdata->do_print = TRUE; + } + else if (response == GTK_RESPONSE_APPLY) + { + /* print preview */ + rdata->result = GTK_PRINT_OPERATION_RESULT_PREVIEW; rdata->do_print = TRUE; + } + 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)); - + g_signal_emit_by_name (rdata->op, "custom-widget-apply", rdata->op->priv->custom_widget); - } - - out: + } + finish_print (rdata, printer, page_setup, settings); if (settings) g_object_unref (settings); - + gtk_widget_destroy (GTK_WIDGET (pd)); + } @@ -436,6 +569,39 @@ _gtk_print_operation_platform_backend_run_dialog_async (GtkPrintOperation } } +cairo_surface_t * +_gtk_print_operation_platform_backend_create_preview_surface (GtkPrintOperation *op, + GtkPageSetup *page_setup, + gdouble *dpi_x, + gdouble *dpi_y, + const gchar *target) +{ + GtkPaperSize *paper_size; + double 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); + + *dpi_x = *dpi_y = 72; + return cairo_pdf_surface_create (target, w, h); +} + +void +_gtk_print_operation_platform_backend_resize_preview_surface (GtkPrintOperation *op, + GtkPageSetup *page_setup, + cairo_surface_t *surface) +{ + GtkPaperSize *paper_size; + double 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, GtkWindow *parent, @@ -459,6 +625,7 @@ _gtk_print_operation_platform_backend_run_dialog (GtkPrintOperation *op, if (op->priv->show_dialog) { pd = get_print_dialog (op, parent); + response = gtk_dialog_run (GTK_DIALOG (pd)); handle_print_response (pd, response, &rdata); } @@ -477,6 +644,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; diff --git a/gtk/gtkprintoperation-win32.c b/gtk/gtkprintoperation-win32.c index 97e1aaa605..c546ea4c83 100644 --- a/gtk/gtkprintoperation-win32.c +++ b/gtk/gtkprintoperation-win32.c @@ -492,6 +492,7 @@ win32_end_run (GtkPrintOperation *op, GlobalFree(op_win32->devmode); GlobalFree(op_win32->devnames); + cairo_surface_finish (op->priv->surface); cairo_surface_destroy (op->priv->surface); op->priv->surface = NULL; diff --git a/gtk/gtkprintoperation.c b/gtk/gtkprintoperation.c index efc211fc5c..d4f8fa25a3 100644 --- a/gtk/gtkprintoperation.c +++ b/gtk/gtkprintoperation.c @@ -19,6 +19,10 @@ */ #include "config.h" + +#include <errno.h> +#include <stdlib.h> + #include <string.h> #include "gtkprintoperation-private.h" #include "gtkmarshalers.h" @@ -41,6 +45,7 @@ enum { STATUS_CHANGED, CREATE_CUSTOM_WIDGET, CUSTOM_WIDGET_APPLY, + PREVIEW, LAST_SIGNAL }; @@ -65,7 +70,15 @@ enum { static guint signals[LAST_SIGNAL] = { 0 }; static int job_nr = 0; -G_DEFINE_TYPE (GtkPrintOperation, gtk_print_operation, G_TYPE_OBJECT) +static void preview_iface_init (GtkPrintOperationPreviewIface *iface); +static GtkPageSetup *create_page_setup (GtkPrintOperation *op); +static void common_render_page (GtkPrintOperation *op, + gint page_nr); + + +G_DEFINE_TYPE_WITH_CODE (GtkPrintOperation, gtk_print_operation, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GTK_TYPE_PRINT_OPERATION_PREVIEW, + preview_iface_init)) /** * gtk_print_error_quark: @@ -146,6 +159,94 @@ gtk_print_operation_init (GtkPrintOperation *operation) } static void +preview_iface_render_page (GtkPrintOperationPreview *preview, + gint page_nr) +{ + GtkPrintOperation *op; + + op = GTK_PRINT_OPERATION (preview); + common_render_page (op, page_nr); +} + +static void +preview_iface_end_preview (GtkPrintOperationPreview *preview) +{ + GtkPrintOperation *op; + + op = GTK_PRINT_OPERATION (preview); + + g_signal_emit (op, signals[END_PRINT], 0, op->priv->print_context); + + if (op->priv->rloop) + g_main_loop_quit (op->priv->rloop); + + op->priv->end_run (op, op->priv->is_sync, TRUE); +} + +static gboolean +preview_iface_is_selected (GtkPrintOperationPreview *preview, + gint page_nr) +{ + GtkPrintOperation *op; + GtkPrintOperationPrivate *priv; + int i; + + op = GTK_PRINT_OPERATION (preview); + priv = op->priv; + + switch (priv->print_pages) + { + case GTK_PRINT_PAGES_ALL: + return (page_nr >= 0) && (page_nr < priv->nr_of_pages); + case GTK_PRINT_PAGES_CURRENT: + return page_nr == priv->current_page; + case GTK_PRINT_PAGES_RANGES: + for (i = 0; i < priv->num_page_ranges; i++) + { + if (page_nr >= priv->page_ranges[i].start && + page_nr <= priv->page_ranges[i].end) + return TRUE; + } + return FALSE; + } + return FALSE; +} + +static void +preview_iface_init (GtkPrintOperationPreviewIface *iface) +{ + iface->render_page = preview_iface_render_page; + iface->end_preview = preview_iface_end_preview; + iface->is_selected = preview_iface_is_selected; +} + +static void +preview_start_page (GtkPrintOperation *op, + GtkPrintContext *print_context, + GtkPageSetup *page_setup) +{ + g_signal_emit_by_name (op, "got-page-size",print_context, page_setup); +} + +static void +preview_end_page (GtkPrintOperation *op, + GtkPrintContext *print_context) +{ +} + +static void +preview_end_run (GtkPrintOperation *op, + gboolean wait, + gboolean cancelled) +{ + g_free (op->priv->page_ranges); + op->priv->page_ranges = NULL; + + _gtk_print_operation_set_status (op, GTK_PRINT_STATUS_FINISHED, NULL); +} + + +static void gtk_print_operation_set_property (GObject *object, guint prop_id, const GValue *value, @@ -256,6 +357,151 @@ gtk_print_operation_get_property (GObject *object, } } +typedef struct +{ + GtkPrintOperationPreview *preview; + GtkPrintContext *print_context; + GtkWindow *parent; + cairo_surface_t *surface; + gchar *filename; + guint page_nr; + gboolean wait; +} PreviewOp; + +static void +preview_print_idle_done (gpointer data) +{ + GtkPrintOperation *op; + PreviewOp *pop = (PreviewOp *) data; + + GDK_THREADS_ENTER (); + + op = GTK_PRINT_OPERATION (pop->preview); + + _gtk_print_operation_platform_backend_launch_preview (op, + pop->parent, + pop->filename); + + g_free (pop->filename); + cairo_surface_finish (pop->surface); + cairo_surface_destroy (pop->surface); + + gtk_print_operation_preview_end_preview (pop->preview); + g_free (pop); + + GDK_THREADS_LEAVE (); +} + +static gboolean +preview_print_idle (gpointer data) +{ + PreviewOp *pop; + GtkPrintOperation *op; + gboolean retval = TRUE; + cairo_t *cr; + + GDK_THREADS_ENTER (); + + pop = (PreviewOp *) data; + op = GTK_PRINT_OPERATION (pop->preview); + + gtk_print_operation_preview_render_page (pop->preview, pop->page_nr); + + cr = gtk_print_context_get_cairo_context (pop->print_context); + cairo_show_page (cr); + + /* TODO: print out sheets not pages and follow ranges */ + pop->page_nr++; + if (op->priv->nr_of_pages <= pop->page_nr) + retval = FALSE; + + GDK_THREADS_LEAVE (); + + return retval; +} + +static void +preview_got_page_size (GtkPrintOperationPreview *preview, + GtkPrintContext *context, + GtkPageSetup *page_setup, + PreviewOp *pop) +{ + GtkPrintOperation *op = GTK_PRINT_OPERATION (preview); + + _gtk_print_operation_platform_backend_resize_preview_surface (op, page_setup, pop->surface); +} + +static void +preview_ready (GtkPrintOperationPreview *preview, + GtkPrintContext *context, + PreviewOp *pop) +{ + + pop->page_nr = 0; + pop->print_context = context; + + g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, + preview_print_idle, + pop, + preview_print_idle_done); + +} + +/** + * gtk_print_operation_preview_handler: + * + * Default handler for preview operations + **/ +static gboolean +gtk_print_operation_preview_handler (GtkPrintOperation *op, + GtkPrintOperationPreview *preview, + GtkPrintContext *context, + GtkWindow *parent) +{ + gdouble dpi_x, dpi_y; + gchar *tmp_dir; + gchar *dir_template; + gchar *preview_filename; + PreviewOp *pop; + GtkPageSetup *page_setup; + cairo_t *cr; + + dir_template = g_build_filename (g_get_tmp_dir (), "print-preview-XXXXXX", NULL); + + /* use temp dirs because apps like evince need to have extentions + to determine the mine type */ + tmp_dir = mkdtemp(dir_template); + + preview_filename = g_build_filename (tmp_dir, + "Print Preview.pdf", + NULL); + + g_free (dir_template); + + pop = g_new0 (PreviewOp, 1); + pop->filename = preview_filename; + pop->preview = preview; + pop->parent = parent; + + page_setup = gtk_print_context_get_page_setup (context); + + pop->surface = + _gtk_print_operation_platform_backend_create_preview_surface (op, + page_setup, + &dpi_x, &dpi_y, + pop->filename); + + cr = cairo_create (pop->surface); + gtk_print_context_set_cairo_context (op->priv->print_context, cr, + dpi_x, dpi_y); + cairo_destroy (cr); + + g_signal_connect (preview, "ready", (GCallback) preview_ready, pop); + g_signal_connect (preview, "got-page-size", (GCallback) preview_got_page_size, pop); + + return TRUE; +} + static GtkWidget * gtk_print_operation_create_custom_widget (GtkPrintOperation *operation) { @@ -287,7 +533,8 @@ gtk_print_operation_class_init (GtkPrintOperationClass *class) gobject_class->set_property = gtk_print_operation_set_property; gobject_class->get_property = gtk_print_operation_get_property; gobject_class->finalize = gtk_print_operation_finalize; - + + class->preview = gtk_print_operation_preview_handler; class->create_custom_widget = gtk_print_operation_create_custom_widget; g_type_class_add_private (gobject_class, sizeof (GtkPrintOperationPrivate)); @@ -530,6 +777,36 @@ gtk_print_operation_class_init (GtkPrintOperationClass *class) g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GTK_TYPE_WIDGET); + /** + * GtkPrintOperation::preview: + * @operation: the #GtkPrintOperation on which the signal was emitted + * @preview: the #GtkPrintPreviewOperation for the current operation + * @context: the #GtkPrintContext that will be used + * @parent: the #GtkWindow to use as window parent, or NULL + * + * Gets emitted when a preview is requested from the native dialog. + * If you handle this you must set the cairo context on the printing context. + * + * If you don't override this a default implementation using an external + * viewer will be used. + * + * Returns: #TRUE if the listener wants to take over control of the preview + * + * Since: 2.10 + */ + signals[PREVIEW] = + g_signal_new (I_("preview"), + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkPrintOperationClass, preview), + _gtk_boolean_handled_accumulator, NULL, + _gtk_marshal_BOOLEAN__OBJECT_OBJECT_OBJECT, + G_TYPE_BOOLEAN, 3, + GTK_TYPE_PRINT_OPERATION_PREVIEW, + GTK_TYPE_PRINT_CONTEXT, + GTK_TYPE_WINDOW); + + /** * GtkPrintOperation:default-page-setup: * @@ -1436,6 +1713,7 @@ pdf_start_page (GtkPrintOperation *op, GtkPageSetup *page_setup) { GtkPaperSize *paper_size; + cairo_surface_t *surface = op->priv->platform_data; double w, h; paper_size = gtk_page_setup_get_paper_size (page_setup); @@ -1443,7 +1721,7 @@ pdf_start_page (GtkPrintOperation *op, 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 (op->priv->surface, w, h); + cairo_pdf_surface_set_size (surface, w, h); } static void @@ -1462,9 +1740,10 @@ pdf_end_run (GtkPrintOperation *op, gboolean cancelled) { GtkPrintOperationPrivate *priv = op->priv; + cairo_surface_t *surface = priv->platform_data; - cairo_surface_destroy (priv->surface); - priv->surface = NULL; + cairo_surface_finish (surface); + cairo_surface_destroy (surface); } static GtkPrintOperationResult @@ -1475,6 +1754,8 @@ run_pdf (GtkPrintOperation *op, { GtkPrintOperationPrivate *priv = op->priv; GtkPageSetup *page_setup; + cairo_surface_t *surface; + cairo_t *cr; double width, height; /* This will be overwritten later by the non-default size, but we need to pass some size: */ @@ -1484,13 +1765,19 @@ run_pdf (GtkPrintOperation *op, height = gtk_page_setup_get_paper_height (page_setup, GTK_UNIT_POINTS); g_object_unref (page_setup); - priv->surface = cairo_pdf_surface_create (priv->pdf_target, - width, height); - cairo_pdf_surface_set_dpi (priv->surface, 300, 300); - - priv->dpi_x = 72; - priv->dpi_y = 72; + surface = cairo_pdf_surface_create (priv->pdf_target, + width, height); + cairo_pdf_surface_set_dpi (surface, 300, 300); + + priv->platform_data = surface; + priv->free_platform_data = (GDestroyNotify) cairo_surface_destroy; + cr = cairo_create (surface); + gtk_print_context_set_cairo_context (op->priv->print_context, + cr, 72, 72); + cairo_destroy (cr); + + priv->print_pages = GTK_PRINT_PAGES_ALL; priv->page_ranges = NULL; priv->num_page_ranges = 0; @@ -1524,10 +1811,11 @@ typedef struct gint page, start, end, inc; - GtkPageSetup *initial_page_setup; - GtkPrintContext *print_context; + gboolean initialized; GtkWidget *progress; + + gboolean is_preview; } PrintPagesData; static void @@ -1599,12 +1887,9 @@ print_pages_idle_done (gpointer user_data) if (data->progress) gtk_widget_destroy (data->progress); - g_object_unref (data->print_context); - g_object_unref (data->initial_page_setup); - g_object_unref (data->op); - if (priv->rloop) + if (priv->rloop && !data->is_preview) g_main_loop_quit (priv->rloop); g_free (data); @@ -1640,13 +1925,56 @@ update_progress (PrintPagesData *data) } } +static void +common_render_page (GtkPrintOperation *op, + gint page_nr) +{ + GtkPrintOperationPrivate *priv = op->priv; + GtkPageSetup *page_setup; + GtkPrintContext *print_context; + cairo_t *cr; + + print_context = priv->print_context; + + page_setup = create_page_setup (op); + + g_signal_emit (op, signals[REQUEST_PAGE_SETUP], 0, + print_context, page_nr, page_setup); + + _gtk_print_context_set_page_setup (print_context, page_setup); + + priv->start_page (op, print_context, page_setup); + + cr = gtk_print_context_get_cairo_context (print_context); + + cairo_save (cr); + if (priv->manual_scale != 1.0) + cairo_scale (cr, + priv->manual_scale, + priv->manual_scale); + + if (priv->manual_orientation) + _gtk_print_context_rotate_according_to_orientation (print_context); + + if (!priv->use_full_page) + _gtk_print_context_translate_into_margin (print_context); + + g_signal_emit (op, signals[DRAW_PAGE], 0, + print_context, page_nr); + + priv->end_page (op, print_context); + + cairo_restore (cr); + + g_object_unref (page_setup); +} + static gboolean print_pages_idle (gpointer user_data) { PrintPagesData *data; GtkPrintOperationPrivate *priv; GtkPageSetup *page_setup; - cairo_t *cr; gboolean done = FALSE; GDK_THREADS_ENTER (); @@ -1656,15 +1984,15 @@ print_pages_idle (gpointer user_data) if (priv->status == GTK_PRINT_STATUS_PREPARING) { - if (!data->print_context) + if (!data->initialized) { - data->print_context = _gtk_print_context_new (data->op); - data->initial_page_setup = create_page_setup (data->op); - - _gtk_print_context_set_page_setup (data->print_context, - data->initial_page_setup); + data->initialized = TRUE; + page_setup = create_page_setup (data->op); + _gtk_print_context_set_page_setup (priv->print_context, + page_setup); + g_object_unref (page_setup); - g_signal_emit (data->op, signals[BEGIN_PRINT], 0, data->print_context); + g_signal_emit (data->op, signals[BEGIN_PRINT], 0, priv->print_context); if (priv->manual_collation) { @@ -1684,7 +2012,7 @@ print_pages_idle (gpointer user_data) { gboolean paginated = FALSE; - g_signal_emit (data->op, signals[PAGINATE], 0, data->print_context, &paginated); + g_signal_emit (data->op, signals[PAGINATE], 0, priv->print_context, &paginated); if (!paginated) goto out; } @@ -1751,36 +2079,18 @@ print_pages_idle (gpointer user_data) goto out; } } + + if (data->is_preview) + { + done = TRUE; - page_setup = gtk_page_setup_copy (data->initial_page_setup); - g_signal_emit (data->op, signals[REQUEST_PAGE_SETUP], 0, - data->print_context, data->page, page_setup); - - _gtk_print_context_set_page_setup (data->print_context, page_setup); - priv->start_page (data->op, data->print_context, page_setup); - - cr = gtk_print_context_get_cairo_context (data->print_context); - - cairo_save (cr); - if (priv->manual_scale != 1.0) - cairo_scale (cr, - priv->manual_scale, - priv->manual_scale); - - if (priv->manual_orientation) - _gtk_print_context_rotate_according_to_orientation (data->print_context); - - if (!priv->use_full_page) - _gtk_print_context_translate_into_margin (data->print_context); - - g_signal_emit (data->op, signals[DRAW_PAGE], 0, - data->print_context, data->page); - - priv->end_page (data->op, data->print_context); - - cairo_restore (cr); - - g_object_unref (page_setup); + g_object_ref (data->op); + + g_signal_emit_by_name (data->op, "ready", priv->print_context); + goto out; + } + + common_render_page (data->op, data->page); out: @@ -1791,11 +2101,9 @@ print_pages_idle (gpointer user_data) done = TRUE; } - if (done) + if (done && !data->is_preview) { - g_signal_emit (data->op, signals[END_PRINT], 0, data->print_context); - - cairo_surface_finish (priv->surface); + g_signal_emit (data->op, signals[END_PRINT], 0, priv->print_context); priv->end_run (data->op, priv->is_sync, priv->cancelled); } @@ -1833,15 +2141,17 @@ show_progress_timeout (PrintPagesData *data) static void print_pages (GtkPrintOperation *op, - GtkWindow *parent) + GtkWindow *parent, + gboolean is_preview) { GtkPrintOperationPrivate *priv = op->priv; PrintPagesData *data; - + _gtk_print_operation_set_status (op, GTK_PRINT_STATUS_PREPARING, NULL); data = g_new0 (PrintPagesData, 1); data->op = g_object_ref (op); + data->is_preview = is_preview; if (priv->show_progress) { @@ -1862,6 +2172,37 @@ print_pages (GtkPrintOperation *op, data->progress = progress; } + if (is_preview) + { + gboolean handled; + + g_signal_emit_by_name (op, "preview", + GTK_PRINT_OPERATION_PREVIEW (op), + op->priv->print_context, + parent, + &handled); + + if (!handled || + gtk_print_context_get_cairo_context (priv->print_context) == NULL) { + /* Programmer error */ + g_error ("You must set a cairo context on the print context"); + } + + priv->start_page = preview_start_page; + priv->end_page = preview_end_page; + priv->end_run = preview_end_run; + + priv->print_pages = gtk_print_settings_get_print_pages (priv->print_settings); + priv->page_ranges = gtk_print_settings_get_page_ranges (priv->print_settings, + &priv->num_page_ranges); + priv->manual_num_copies = 1; + priv->manual_collation = FALSE; + priv->manual_reverse = FALSE; + priv->manual_page_set = GTK_PAGE_SET_ALL; + priv->manual_scale = 1.0; + priv->manual_orientation = TRUE; + } + priv->print_pages_idle_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, print_pages_idle, data, @@ -1877,9 +2218,10 @@ print_pages (GtkPrintOperation *op, GDK_THREADS_LEAVE (); g_main_loop_run (priv->rloop); GDK_THREADS_ENTER (); + + g_main_loop_unref (priv->rloop); + priv->rloop = NULL; } - g_main_loop_unref (priv->rloop); - priv->rloop = NULL; } /** @@ -1964,7 +2306,7 @@ gtk_print_operation_run (GtkPrintOperation *op, &do_print, error); if (do_print) - print_pages (op, parent); + print_pages (op, parent, result == GTK_PRINT_OPERATION_RESULT_PREVIEW); else _gtk_print_operation_set_status (op, GTK_PRINT_STATUS_FINISHED_ABORTED, NULL); @@ -2006,7 +2348,7 @@ gtk_print_operation_run_async (GtkPrintOperation *op, { run_pdf (op, parent, &do_print, NULL); if (do_print) - print_pages (op, parent); + print_pages (op, parent, FALSE); else _gtk_print_operation_set_status (op, GTK_PRINT_STATUS_FINISHED_ABORTED, NULL); } diff --git a/gtk/gtkprintoperation.h b/gtk/gtkprintoperation.h index 8f430850fa..4b8443c064 100644 --- a/gtk/gtkprintoperation.h +++ b/gtk/gtkprintoperation.h @@ -29,6 +29,7 @@ #include "gtkpagesetup.h" #include "gtkprintsettings.h" #include "gtkprintcontext.h" +#include "gtkprintoperationpreview.h" G_BEGIN_DECLS @@ -84,7 +85,13 @@ struct _GtkPrintOperationClass GtkWidget *(*create_custom_widget) (GtkPrintOperation *operation); void (*custom_widget_apply) (GtkPrintOperation *operation, GtkWidget *widget); + + gboolean (*preview) (GtkPrintOperation *operation, + GtkPrintOperationPreview *preview, + GtkPrintContext *context, + GtkWindow *parent); + /* Padding for future expansion */ void (*_gtk_reserved1) (void); void (*_gtk_reserved2) (void); @@ -98,7 +105,8 @@ struct _GtkPrintOperationClass typedef enum { GTK_PRINT_OPERATION_RESULT_ERROR, GTK_PRINT_OPERATION_RESULT_APPLY, - GTK_PRINT_OPERATION_RESULT_CANCEL + GTK_PRINT_OPERATION_RESULT_CANCEL, + GTK_PRINT_OPERATION_RESULT_PREVIEW } GtkPrintOperationResult; #define GTK_PRINT_ERROR gtk_print_error_quark () diff --git a/gtk/gtkprintoperationpreview.c b/gtk/gtkprintoperationpreview.c new file mode 100644 index 0000000000..c640bd9e99 --- /dev/null +++ b/gtk/gtkprintoperationpreview.c @@ -0,0 +1,116 @@ +/* GTK - The GIMP Toolkit + * gtkprintoperationpreview.c: Abstract print preview interface + * Copyright (C) 2006, 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, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include "gtkprintoperationpreview.h" +#include "gtkmarshalers.h" +#include "gtkintl.h" + +static void gtk_print_operation_preview_base_init (gpointer g_iface); + +GType +gtk_print_operation_preview_get_type (void) +{ + static GType print_operation_preview_type = 0; + + if (!print_operation_preview_type) + { + static const GTypeInfo print_operation_preview_info = + { + sizeof (GtkPrintOperationPreviewIface), /* class_size */ + gtk_print_operation_preview_base_init, /* base_init */ + NULL, /* base_finalize */ + NULL, + NULL, /* class_finalize */ + NULL, /* class_data */ + 0, + 0, /* n_preallocs */ + NULL + }; + + print_operation_preview_type = + g_type_register_static (G_TYPE_INTERFACE, I_("GtkPrintOperationPreview"), + &print_operation_preview_info, 0); + + g_type_interface_add_prerequisite (print_operation_preview_type, G_TYPE_OBJECT); + } + + return print_operation_preview_type; +} + +static void +gtk_print_operation_preview_base_init (gpointer g_iface) +{ + static gboolean initialized = FALSE; + + if (!initialized) + { + g_signal_new (I_("ready"), + GTK_TYPE_PRINT_OPERATION_PREVIEW, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkPrintOperationPreviewIface, ready), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + G_TYPE_OBJECT); + + g_signal_new (I_("got-page-size"), + GTK_TYPE_PRINT_OPERATION_PREVIEW, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkPrintOperationPreviewIface, ready), + NULL, NULL, + _gtk_marshal_VOID__OBJECT_OBJECT, + G_TYPE_NONE, 2, + GTK_TYPE_PRINT_CONTEXT, + GTK_TYPE_PAGE_SETUP); + + initialized = TRUE; + } +} + + +void +gtk_print_operation_preview_render_page (GtkPrintOperationPreview *preview, + gint page_nr) +{ + g_return_if_fail (GTK_IS_PRINT_OPERATION_PREVIEW (preview)); + + GTK_PRINT_OPERATION_PREVIEW_GET_IFACE (preview)->render_page (preview, + page_nr); +} + +void +gtk_print_operation_preview_end_preview (GtkPrintOperationPreview *preview) +{ + g_return_if_fail (GTK_IS_PRINT_OPERATION_PREVIEW (preview)); + + GTK_PRINT_OPERATION_PREVIEW_GET_IFACE (preview)->end_preview (preview); +} + +gboolean +gtk_print_operation_preview_is_selected (GtkPrintOperationPreview *preview, + gint page_nr) +{ + g_return_if_fail (GTK_IS_PRINT_OPERATION_PREVIEW (preview)); + + return GTK_PRINT_OPERATION_PREVIEW_GET_IFACE (preview)->is_selected (preview, page_nr); +} + diff --git a/gtk/gtkprintoperationpreview.h b/gtk/gtkprintoperationpreview.h new file mode 100644 index 0000000000..b4a776d08d --- /dev/null +++ b/gtk/gtkprintoperationpreview.h @@ -0,0 +1,78 @@ +/* GTK - The GIMP Toolkit + * gtkprintoperationpreview.h: Abstract print preview interface + * Copyright (C) 2006, 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, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GTK_PRINT_OPERATION_PREVIEW_H__ +#define __GTK_PRINT_OPERATION_PREVIEW_H__ + +#include <glib-object.h> +#include <cairo.h> + +#include "gtkprintcontext.h" + +G_BEGIN_DECLS + +#define GTK_TYPE_PRINT_OPERATION_PREVIEW (gtk_print_operation_preview_get_type ()) +#define GTK_PRINT_OPERATION_PREVIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_PRINT_OPERATION_PREVIEW, GtkPrintOperationPreview)) +#define GTK_IS_PRINT_OPERATION_PREVIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_PRINT_OPERATION_PREVIEW)) +#define GTK_PRINT_OPERATION_PREVIEW_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GTK_TYPE_PRINT_OPERATION_PREVIEW, GtkPrintOperationPreviewIface)) + +typedef struct _GtkPrintOperationPreview GtkPrintOperationPreview; /*dummy typedef */ +typedef struct _GtkPrintOperationPreviewIface GtkPrintOperationPreviewIface; + + +struct _GtkPrintOperationPreviewIface +{ + GTypeInterface g_iface; + + /* signals */ + void (*ready) (GtkPrintOperationPreview *preview, + GtkPrintContext *context); + void (*got_page_size) (GtkPrintOperationPreview *preview, + GtkPrintContext *context, + GtkPageSetup *page_setup); + + + /* methods */ + void (*render_page) (GtkPrintOperationPreview *preview, + gint page_nr); + gboolean (*is_selected) (GtkPrintOperationPreview *preview, + gint page_nr); + void (*end_preview) (GtkPrintOperationPreview *preview); + + + /* Padding for future expansion */ + void (*_gtk_reserved1) (void); + void (*_gtk_reserved2) (void); + void (*_gtk_reserved3) (void); + void (*_gtk_reserved4) (void); + void (*_gtk_reserved5) (void); + void (*_gtk_reserved6) (void); + void (*_gtk_reserved7) (void); +}; + +GType gtk_print_operation_preview_get_type (void) G_GNUC_CONST; + +void gtk_print_operation_preview_render_page (GtkPrintOperationPreview *preview, + gint page_nr); +void gtk_print_operation_preview_end_preview (GtkPrintOperationPreview *preview); +gboolean gtk_print_operation_preview_is_selected (GtkPrintOperationPreview *preview, + gint page_nr); + +#endif /* __GTK_PRINT_OPERATION_PREVIEW_H__ */ diff --git a/gtk/gtkprintunixdialog.c b/gtk/gtkprintunixdialog.c index e92822d7a0..c6ebc21134 100644 --- a/gtk/gtkprintunixdialog.c +++ b/gtk/gtkprintunixdialog.c @@ -275,7 +275,8 @@ gtk_print_unix_dialog_init (GtkPrintUnixDialog *dialog) (GCallback) gtk_print_unix_dialog_destroy, NULL); - gtk_dialog_add_buttons (GTK_DIALOG (dialog), + gtk_dialog_add_buttons (GTK_DIALOG (dialog), + GTK_STOCK_PRINT_PREVIEW, GTK_RESPONSE_APPLY, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_PRINT, GTK_RESPONSE_OK, NULL); diff --git a/tests/print-editor.c b/tests/print-editor.c index c49ede459b..b18cbe8316 100644 --- a/tests/print-editor.c +++ b/tests/print-editor.c @@ -1,3 +1,4 @@ +#include <math.h> #include <pango/pangocairo.h> #include <gtk/gtk.h> #include <gtk/gtkprintoperation.h> @@ -303,7 +304,6 @@ begin_print (GtkPrintOperation *operation, gtk_print_operation_set_n_pages (operation, g_list_length (page_breaks) + 1); print_data->page_breaks = page_breaks; - } static void @@ -317,6 +317,7 @@ draw_page (GtkPrintOperation *operation, int start, end, i; PangoLayoutIter *iter; double start_pos; + if (page_nr == 0) start = 0; else @@ -430,17 +431,195 @@ custom_widget_apply (GtkPrintOperation *operation, data->font = g_strdup (selected_font); } +typedef struct +{ + GtkPrintOperation *op; + GtkPrintOperationPreview *preview; + GtkWidget *spin; + GtkWidget *area; + gint page; + PrintData *data; + gdouble dpi_x, dpi_y; +} PreviewOp; + +static gboolean +preview_expose (GtkWidget *widget, + GdkEventExpose *event, + gpointer data) +{ + PreviewOp *pop = data; + + gdk_window_clear (pop->area->window); + gtk_print_operation_preview_render_page (pop->preview, + pop->page - 1); + + return TRUE; +} + +static void +preview_ready (GtkPrintOperationPreview *preview, + GtkPrintContext *context, + gpointer data) +{ + PreviewOp *pop = data; + gint n_pages; + + g_object_get (pop->op, "n-pages", &n_pages, NULL); + + gtk_spin_button_set_range (GTK_SPIN_BUTTON (pop->spin), + 1.0, n_pages); + + g_signal_connect (pop->area, "expose_event", + G_CALLBACK (preview_expose), + pop); + + gtk_widget_queue_draw (pop->area); +} + +static void +preview_got_page_size (GtkPrintOperationPreview *preview, + GtkPrintContext *context, + GtkPageSetup *page_setup, + gpointer data) +{ + PreviewOp *pop = data; + GtkPaperSize *paper_size; + double w, h; + cairo_t *cr; + gdouble dpi_x, dpi_y; + + paper_size = gtk_page_setup_get_paper_size (page_setup); + + w = gtk_paper_size_get_width (paper_size, GTK_UNIT_INCH); + h = gtk_paper_size_get_height (paper_size, GTK_UNIT_INCH); + + cr = gdk_cairo_create (pop->area->window); + + dpi_x = pop->area->allocation.width/w; + dpi_y = pop->area->allocation.height/h; + + if (fabs (dpi_x - pop->dpi_x) > 0.001 || + fabs (dpi_y - pop->dpi_y) > 0.001) + { + gtk_print_context_set_cairo_context (context, cr, dpi_x, dpi_y); + pop->dpi_x = dpi_x; + pop->dpi_y = dpi_y; + } + + pango_cairo_update_layout (cr, pop->data->layout); + cairo_destroy (cr); +} + +static void +update_page (GtkSpinButton *widget, + gpointer data) +{ + PreviewOp *pop = data; + + pop->page = gtk_spin_button_get_value_as_int (widget); + gtk_widget_queue_draw (pop->area); +} + +static void +preview_destroy (GtkWindow *window, + PreviewOp *pop) +{ + gtk_print_operation_preview_end_preview (pop->preview); + g_object_unref (pop->op); + + g_free (pop); +} + +static gboolean +do_preview (GtkPrintOperation *op, + GtkPrintOperationPreview *preview, + GtkPrintContext *context, + GtkWindow *parent, + gpointer data) +{ + GtkPrintSettings *settings; + GtkWidget *window, *close, *page, *hbox, *vbox, *da; + gdouble width, height; + cairo_t *cr; + PreviewOp *pop; + PrintData *print_data = data; + + pop = g_new0 (PreviewOp, 1); + + pop->data = print_data; + settings = gtk_print_operation_get_print_settings (op); + + width = 200; + height = 300; + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_transient_for (GTK_WINDOW (window), + GTK_WINDOW (main_window)); + vbox = gtk_vbox_new (FALSE, 0); + gtk_container_add (GTK_CONTAINER (window), vbox); + hbox = gtk_hbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (vbox), hbox, + FALSE, FALSE, 0); + page = gtk_spin_button_new_with_range (1, 100, 1); + gtk_box_pack_start (GTK_BOX (hbox), page, FALSE, FALSE, 0); + + close = gtk_button_new_from_stock (GTK_STOCK_CLOSE); + gtk_box_pack_start (GTK_BOX (hbox), close, FALSE, FALSE, 0); + + da = gtk_drawing_area_new (); + gtk_widget_set_size_request (GTK_WIDGET (da), width, height); + gtk_box_pack_start (GTK_BOX (vbox), da, TRUE, TRUE, 0); + + gtk_widget_set_double_buffered (da, FALSE); + + gtk_widget_realize (da); + + cr = gdk_cairo_create (da->window); + + /* TODO: What dpi to use here? This will be used for pagination.. */ + gtk_print_context_set_cairo_context (context, cr, 72, 72); + cairo_destroy (cr); + + pop->op = op; + pop->preview = preview; + pop->spin = page; + pop->area = da; + pop->page = 1; + + g_signal_connect (page, "value-changed", + G_CALLBACK (update_page), pop); + g_signal_connect_swapped (close, "clicked", + G_CALLBACK (gtk_widget_destroy), window); + + g_signal_connect (preview, "ready", + G_CALLBACK (preview_ready), pop); + g_signal_connect (preview, "got-page-size", + G_CALLBACK (preview_got_page_size), pop); + + g_signal_connect (window, "destroy", + G_CALLBACK (preview_destroy), pop); + + gtk_widget_show_all (window); + + return TRUE; +} + +/* FIXME had to move this to the heap, since previewing + * returns too early from the sync api + */ +PrintData *print_data; + static void do_print (GtkAction *action) { GtkWidget *error_dialog; GtkPrintOperation *print; - PrintData print_data; GtkPrintOperationResult res; GError *error; - print_data.text = get_text (); - print_data.font = g_strdup ("Sans 12"); + print_data = g_new0 (PrintData, 1); + + print_data->text = get_text (); + print_data->font = g_strdup ("Sans 12"); print = gtk_print_operation_new (); @@ -452,12 +631,15 @@ do_print (GtkAction *action) if (page_setup != NULL) gtk_print_operation_set_default_page_setup (print, page_setup); - g_signal_connect (print, "begin_print", G_CALLBACK (begin_print), &print_data); - g_signal_connect (print, "draw_page", G_CALLBACK (draw_page), &print_data); - g_signal_connect (print, "create_custom_widget", G_CALLBACK (create_custom_widget), &print_data); - g_signal_connect (print, "custom_widget_apply", G_CALLBACK (custom_widget_apply), &print_data); - + g_signal_connect (print, "begin_print", G_CALLBACK (begin_print), print_data); + g_signal_connect (print, "draw_page", G_CALLBACK (draw_page), print_data); + g_signal_connect (print, "create_custom_widget", G_CALLBACK (create_custom_widget), print_data); + g_signal_connect (print, "custom_widget_apply", G_CALLBACK (custom_widget_apply), print_data); + g_signal_connect (print, "preview", G_CALLBACK (do_preview), print_data); + error = NULL; + +#if 1 res = gtk_print_operation_run (print, GTK_WINDOW (main_window), &error); if (res == GTK_PRINT_OPERATION_RESULT_ERROR) @@ -467,7 +649,7 @@ do_print (GtkAction *action) GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "Error printing file:\n%s", - error->message); + error ? error->message : "no details"); g_signal_connect (error_dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL); gtk_widget_show (error_dialog); g_error_free (error); @@ -489,10 +671,15 @@ do_print (GtkAction *action) g_signal_connect (print, "status_changed", G_CALLBACK (status_changed_cb), NULL); } +#else + gtk_print_operation_run_async (print, GTK_WINDOW (main_window)); +#endif g_object_unref (print); +#if 0 g_free (print_data.text); g_free (print_data.font); +#endif } static void |