X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkprintoperation.c;h=3fd4f93dc7c0b746515f244dc74e39f08d057e9f;hb=HEAD;hp=1b403d0d92bf0a51976ef4773f31a72148247272;hpb=f8794ccccadc91bd37a4fe19577fc789cf0d0107;p=~andy%2Fgtk diff --git a/gtk/gtkprintoperation.c b/gtk/gtkprintoperation.c index 1b403d0d9..3fd4f93dc 100644 --- a/gtk/gtkprintoperation.c +++ b/gtk/gtkprintoperation.c @@ -13,30 +13,99 @@ * 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. + * License along with this library. If not, see . */ #include "config.h" #include -#include - +#include +#include #include + +#include + #include "gtkprintoperation-private.h" #include "gtkmarshalers.h" -#include #include "gtkintl.h" #include "gtkprivate.h" #include "gtkmessagedialog.h" -#include "gtkalias.h" +#include "gtktypebuiltins.h" + +/** + * SECTION:gtkprintoperation + * @Title: GtkPrintOperation + * @Short_description: High-level Printing API + * @See_also: #GtkPrintContext, #GtkPrintUnixDialog + * + * GtkPrintOperation is the high-level, portable printing API. + * It looks a bit different than other GTK+ dialogs such as the + * #GtkFileChooser, since some platforms don't expose enough + * infrastructure to implement a good print dialog. On such + * platforms, GtkPrintOperation uses the native print dialog. + * On platforms which do not provide a native print dialog, GTK+ + * uses its own, see #GtkPrintUnixDialog. + * + * The typical way to use the high-level printing API is to create + * a GtkPrintOperation object with gtk_print_operation_new() when + * the user selects to print. Then you set some properties on it, + * e.g. the page size, any #GtkPrintSettings from previous print + * operations, the number of pages, the current page, etc. + * + * Then you start the print operation by calling gtk_print_operation_run(). + * It will then show a dialog, let the user select a printer and + * options. When the user finished the dialog various signals will + * be emitted on the #GtkPrintOperation, the main one being + * #GtkPrintOperation::draw-page, which you are supposed to catch + * and render the page on the provided #GtkPrintContext using Cairo. + * + * + * The high-level printing API + * + * static GtkPrintSettings *settings = NULL; + * + * static void + * do_print (void) + * { + * GtkPrintOperation *print; + * GtkPrintOperationResult res; + * + * print = gtk_print_operation_new (); + * + * if (settings != NULL) + * gtk_print_operation_set_print_settings (print, settings); + * + * g_signal_connect (print, "begin_print", G_CALLBACK (begin_print), NULL); + * g_signal_connect (print, "draw_page", G_CALLBACK (draw_page), NULL); + * + * res = gtk_print_operation_run (print, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, + * GTK_WINDOW (main_window), NULL); + * + * if (res == GTK_PRINT_OPERATION_RESULT_APPLY) + * { + * if (settings != NULL) + * g_object_unref (settings); + * settings = g_object_ref (gtk_print_operation_get_print_settings (print)); + * } + * + * g_object_unref (print); + * } + * + * + * + * By default GtkPrintOperation uses an external application to do + * print preview. To implement a custom print preview, an application + * must connect to the preview signal. The functions + * gtk_print_operation_preview_render_page(), + * gtk_print_operation_preview_end_preview() and + * gtk_print_operation_preview_is_selected() + * are useful when implementing a print preview. + */ #define SHOW_PROGRESS_TIME 1200 -#define GTK_PRINT_OPERATION_GET_PRIVATE(obj)(G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_PRINT_OPERATION, GtkPrintOperationPrivate)) -enum +enum { DONE, BEGIN_PRINT, @@ -48,6 +117,7 @@ enum CREATE_CUSTOM_WIDGET, CUSTOM_WIDGET_APPLY, PREVIEW, + UPDATE_CUSTOM_WIDGET, LAST_SIGNAL }; @@ -67,16 +137,24 @@ enum PROP_EXPORT_FILENAME, PROP_STATUS, PROP_STATUS_STRING, - PROP_CUSTOM_TAB_LABEL + PROP_CUSTOM_TAB_LABEL, + PROP_EMBED_PAGE_SETUP, + PROP_HAS_SELECTION, + PROP_SUPPORT_SELECTION, + PROP_N_PAGES_TO_PRINT }; static guint signals[LAST_SIGNAL] = { 0 }; static int job_nr = 0; +typedef struct _PrintPagesData PrintPagesData; -static void preview_iface_init (GtkPrintOperationPreviewIface *iface); -static GtkPageSetup *create_page_setup (GtkPrintOperation *op); -static void common_render_page (GtkPrintOperation *op, - gint page_nr); +static void preview_iface_init (GtkPrintOperationPreviewIface *iface); +static GtkPageSetup *create_page_setup (GtkPrintOperation *op); +static void common_render_page (GtkPrintOperation *op, + gint page_nr); +static void increment_page_sequence (PrintPagesData *data); +static void prepare_data (PrintPagesData *data); +static void clamp_page_ranges (PrintPagesData *data); G_DEFINE_TYPE_WITH_CODE (GtkPrintOperation, gtk_print_operation, G_TYPE_OBJECT, @@ -120,9 +198,13 @@ gtk_print_operation_finalize (GObject *object) if (priv->print_settings) g_object_unref (priv->print_settings); + if (priv->print_context) + g_object_unref (priv->print_context); + g_free (priv->export_filename); g_free (priv->job_name); g_free (priv->custom_tab_label); + g_free (priv->status_string); if (priv->print_pages_idle_id > 0) g_source_remove (priv->print_pages_idle_id); @@ -142,25 +224,40 @@ gtk_print_operation_init (GtkPrintOperation *operation) GtkPrintOperationPrivate *priv; const char *appname; - priv = operation->priv = GTK_PRINT_OPERATION_GET_PRIVATE (operation); + priv = operation->priv = G_TYPE_INSTANCE_GET_PRIVATE (operation, + GTK_TYPE_PRINT_OPERATION, + GtkPrintOperationPrivate); priv->status = GTK_PRINT_STATUS_INITIAL; priv->status_string = g_strdup (""); priv->default_page_setup = NULL; priv->print_settings = NULL; priv->nr_of_pages = -1; + priv->nr_of_pages_to_print = -1; + priv->page_position = -1; priv->current_page = -1; priv->use_full_page = FALSE; priv->show_progress = FALSE; priv->export_filename = NULL; priv->track_print_status = FALSE; priv->is_sync = FALSE; + priv->support_selection = FALSE; + priv->has_selection = FALSE; + priv->embed_page_setup = FALSE; + + priv->page_drawing_state = GTK_PAGE_DRAWING_STATE_READY; priv->rloop = NULL; - priv->unit = GTK_UNIT_PIXEL; + priv->unit = GTK_UNIT_NONE; appname = g_get_application_name (); - priv->job_name = g_strdup_printf ("%s job #%d", appname, ++job_nr); + if (appname == NULL) + appname = ""; + /* translators: this string is the default job title for print + * jobs. %s gets replaced by the application name, %d gets replaced + * by the job number. + */ + priv->job_name = g_strdup_printf (_("%s job #%d"), appname, ++job_nr); } static void @@ -178,6 +275,7 @@ static void preview_iface_end_preview (GtkPrintOperationPreview *preview) { GtkPrintOperation *op; + GtkPrintOperationResult result; op = GTK_PRINT_OPERATION (preview); @@ -185,11 +283,20 @@ preview_iface_end_preview (GtkPrintOperationPreview *preview) if (op->priv->rloop) g_main_loop_quit (op->priv->rloop); - - op->priv->end_run (op, op->priv->is_sync, TRUE); - g_signal_emit (op, signals[DONE], 0, - GTK_PRINT_OPERATION_RESULT_APPLY); + if (op->priv->end_run) + op->priv->end_run (op, op->priv->is_sync, TRUE); + + _gtk_print_operation_set_status (op, GTK_PRINT_STATUS_FINISHED, NULL); + + if (op->priv->error) + result = GTK_PRINT_OPERATION_RESULT_ERROR; + else if (op->priv->cancelled) + result = GTK_PRINT_OPERATION_RESULT_CANCEL; + else + result = GTK_PRINT_OPERATION_RESULT_APPLY; + + g_signal_emit (op, signals[DONE], 0, result); } static gboolean @@ -205,6 +312,7 @@ preview_iface_is_selected (GtkPrintOperationPreview *preview, switch (priv->print_pages) { + case GTK_PRINT_PAGES_SELECTION: case GTK_PRINT_PAGES_ALL: return (page_nr >= 0) && (page_nr < priv->nr_of_pages); case GTK_PRINT_PAGES_CURRENT: @@ -213,7 +321,7 @@ preview_iface_is_selected (GtkPrintOperationPreview *preview, for (i = 0; i < priv->num_page_ranges; i++) { if (page_nr >= priv->page_ranges[i].start && - page_nr <= priv->page_ranges[i].end) + (page_nr <= priv->page_ranges[i].end || priv->page_ranges[i].end == -1)) return TRUE; } return FALSE; @@ -234,13 +342,23 @@ preview_start_page (GtkPrintOperation *op, GtkPrintContext *print_context, GtkPageSetup *page_setup) { - g_signal_emit_by_name (op, "got-page-size", print_context, page_setup); + if ((op->priv->manual_number_up < 2) || + (op->priv->page_position % op->priv->manual_number_up == 0)) + g_signal_emit_by_name (op, "got-page-size", print_context, page_setup); } static void preview_end_page (GtkPrintOperation *op, GtkPrintContext *print_context) { + cairo_t *cr; + + cr = gtk_print_context_get_cairo_context (print_context); + + if ((op->priv->manual_number_up < 2) || + ((op->priv->page_position + 1) % op->priv->manual_number_up == 0) || + (op->priv->page_position == op->priv->nr_of_pages_to_print - 1)) + cairo_show_page (cr); } static void @@ -250,8 +368,6 @@ preview_end_run (GtkPrintOperation *op, { g_free (op->priv->page_ranges); op->priv->page_ranges = NULL; - - _gtk_print_operation_set_status (op, GTK_PRINT_STATUS_FINISHED, NULL); } @@ -301,6 +417,15 @@ gtk_print_operation_set_property (GObject *object, case PROP_CUSTOM_TAB_LABEL: gtk_print_operation_set_custom_tab_label (op, g_value_get_string (value)); break; + case PROP_EMBED_PAGE_SETUP: + gtk_print_operation_set_embed_page_setup (op, g_value_get_boolean (value)); + break; + case PROP_HAS_SELECTION: + gtk_print_operation_set_has_selection (op, g_value_get_boolean (value)); + break; + case PROP_SUPPORT_SELECTION: + gtk_print_operation_set_support_selection (op, g_value_get_boolean (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -360,12 +485,49 @@ gtk_print_operation_get_property (GObject *object, case PROP_CUSTOM_TAB_LABEL: g_value_set_string (value, priv->custom_tab_label); break; + case PROP_EMBED_PAGE_SETUP: + g_value_set_boolean (value, priv->embed_page_setup); + break; + case PROP_HAS_SELECTION: + g_value_set_boolean (value, priv->has_selection); + break; + case PROP_SUPPORT_SELECTION: + g_value_set_boolean (value, priv->support_selection); + break; + case PROP_N_PAGES_TO_PRINT: + g_value_set_int (value, priv->nr_of_pages_to_print); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } +struct _PrintPagesData +{ + GtkPrintOperation *op; + gint uncollated_copies; + gint collated_copies; + gint uncollated, collated, total; + + gint range, num_ranges; + GtkPageRange *ranges; + GtkPageRange one_range; + + gint page; + gint sheet; + gint first_position, last_position; + gint first_sheet; + gint num_of_sheets; + gint *pages; + + GtkWidget *progress; + + gboolean initialized; + gboolean is_preview; + gboolean done; +}; + typedef struct { GtkPrintOperationPreview *preview; @@ -373,8 +535,8 @@ typedef struct GtkWindow *parent; cairo_surface_t *surface; gchar *filename; - guint page_nr; gboolean wait; + PrintPagesData *pages_data; } PreviewOp; static void @@ -383,23 +545,33 @@ preview_print_idle_done (gpointer data) GtkPrintOperation *op; PreviewOp *pop = (PreviewOp *) data; - GDK_THREADS_ENTER (); - op = GTK_PRINT_OPERATION (pop->preview); cairo_surface_finish (pop->surface); - /* Surface is destroyed in launch_preview */ - _gtk_print_operation_platform_backend_launch_preview (op, - pop->surface, - pop->parent, - pop->filename); + + if (op->priv->status == GTK_PRINT_STATUS_FINISHED_ABORTED) + { + cairo_surface_destroy (pop->surface); + } + else + { + /* Surface is destroyed in launch_preview */ + _gtk_print_operation_platform_backend_launch_preview (op, + pop->surface, + pop->parent, + pop->filename); + } g_free (pop->filename); gtk_print_operation_preview_end_preview (pop->preview); + + g_object_unref (pop->pages_data->op); + g_free (pop->pages_data->pages); + g_free (pop->pages_data); + + g_object_unref (op); g_free (pop); - - GDK_THREADS_LEAVE (); } static gboolean @@ -407,27 +579,37 @@ preview_print_idle (gpointer data) { PreviewOp *pop; GtkPrintOperation *op; - gboolean retval = TRUE; - cairo_t *cr; - - GDK_THREADS_ENTER (); + GtkPrintOperationPrivate *priv; + gboolean done = FALSE; pop = (PreviewOp *) data; op = GTK_PRINT_OPERATION (pop->preview); + priv = op->priv; - gtk_print_operation_preview_render_page (pop->preview, pop->page_nr); - - cr = gtk_print_context_get_cairo_context (pop->print_context); - _gtk_print_operation_platform_backend_preview_end_page (op, pop->surface, 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 (); + if (priv->page_drawing_state == GTK_PAGE_DRAWING_STATE_READY) + { + if (priv->cancelled) + { + done = TRUE; + _gtk_print_operation_set_status (op, GTK_PRINT_STATUS_FINISHED_ABORTED, NULL); + } + else if (!pop->pages_data->initialized) + { + pop->pages_data->initialized = TRUE; + prepare_data (pop->pages_data); + } + else + { + increment_page_sequence (pop->pages_data); + + if (!pop->pages_data->done) + gtk_print_operation_preview_render_page (pop->preview, pop->pages_data->page); + else + done = priv->page_drawing_state == GTK_PAGE_DRAWING_STATE_READY; + } + } - return retval; + return !done; } static void @@ -451,13 +633,14 @@ 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); + g_object_ref (preview); + + gdk_threads_add_idle_full (G_PRIORITY_DEFAULT_IDLE + 10, + preview_print_idle, + pop, + preview_print_idle_done); } @@ -476,6 +659,9 @@ gtk_print_operation_preview_handler (GtkPrintOperation *op, pop->filename = NULL; pop->preview = preview; pop->parent = parent; + pop->pages_data = g_new0 (PrintPagesData, 1); + pop->pages_data->op = g_object_ref (GTK_PRINT_OPERATION (preview)); + pop->pages_data->is_preview = TRUE; page_setup = gtk_print_context_get_page_setup (context); @@ -485,6 +671,12 @@ gtk_print_operation_preview_handler (GtkPrintOperation *op, &dpi_x, &dpi_y, &pop->filename); + if (pop->surface == NULL) + { + g_free (pop); + return FALSE; + } + cr = cairo_create (pop->surface); gtk_print_context_set_cairo_context (op->priv->print_context, cr, dpi_x, dpi_y); @@ -502,6 +694,19 @@ gtk_print_operation_create_custom_widget (GtkPrintOperation *operation) return NULL; } +static void +gtk_print_operation_done (GtkPrintOperation *operation, + GtkPrintOperationResult result) +{ + GtkPrintOperationPrivate *priv = operation->priv; + + if (priv->print_context) + { + g_object_unref (priv->print_context); + priv->print_context = NULL; + } +} + static gboolean custom_widget_accumulator (GSignalInvocationHint *ihint, GValue *return_accu, @@ -530,6 +735,7 @@ gtk_print_operation_class_init (GtkPrintOperationClass *class) class->preview = gtk_print_operation_preview_handler; class->create_custom_widget = gtk_print_operation_create_custom_widget; + class->done = gtk_print_operation_done; g_type_class_add_private (gobject_class, sizeof (GtkPrintOperationPrivate)); @@ -539,14 +745,15 @@ gtk_print_operation_class_init (GtkPrintOperationClass *class) * @result: the result of the print operation * * Emitted when the print operation run has finished doing - * everything required for printing. @result gives you information - * about what happened during the run. If @result is - * %GTK_PRINT_OPERATION_RESULT_ERROR then you can call + * everything required for printing. + * + * @result gives you information about what happened during the run. + * If @result is %GTK_PRINT_OPERATION_RESULT_ERROR then you can call * gtk_print_operation_get_error() for more information. * * If you enabled print status tracking then * gtk_print_operation_is_finished() may still return %FALSE - * after this was emitted. + * after #GtkPrintOperation::done was emitted. * * Since: 2.10 */ @@ -567,7 +774,7 @@ gtk_print_operation_class_init (GtkPrintOperationClass *class) * Emitted after the user has finished changing print settings * in the dialog, before the actual rendering starts. * - * A typical use for this signal is to use the parameters from the + * A typical use for ::begin-print is to use the parameters from the * #GtkPrintContext and paginate the document accordingly, and then * set the number of pages with gtk_print_operation_set_n_pages(). * @@ -587,17 +794,18 @@ gtk_print_operation_class_init (GtkPrintOperationClass *class) * @operation: the #GtkPrintOperation on which the signal was emitted * @context: the #GtkPrintContext for the current operation * - * Emitted after the begin-print signal, but before the actual - * rendering starts. It keeps getting emitted until it returns %FALSE. + * Emitted after the #GtkPrintOperation::begin-print signal, but before + * the actual rendering starts. It keeps getting emitted until a connected + * signal handler returns %TRUE. * - * This signal is intended to be used for paginating the document + * The ::paginate signal is intended to be used for paginating a document * in small chunks, to avoid blocking the user interface for a long * time. The signal handler should update the number of pages using * gtk_print_operation_set_n_pages(), and return %TRUE if the document * has been completely paginated. * * If you don't need to do pagination in chunks, you can simply do - * it all in the begin-print handler, and set the number of pages + * it all in the ::begin-print handler, and set the number of pages * from there. * * Return value: %TRUE if pagination is complete @@ -618,7 +826,7 @@ gtk_print_operation_class_init (GtkPrintOperationClass *class) * GtkPrintOperation::request-page-setup: * @operation: the #GtkPrintOperation on which the signal was emitted * @context: the #GtkPrintContext for the current operation - * @page_nr: the number of the currently printed page + * @page_nr: the number of the currently printed page (0-based) * @setup: the #GtkPageSetup * * Emitted once for every page that is printed, to give @@ -643,13 +851,12 @@ gtk_print_operation_class_init (GtkPrintOperationClass *class) * GtkPrintOperation::draw-page: * @operation: the #GtkPrintOperation on which the signal was emitted * @context: the #GtkPrintContext for the current operation - * @page_nr: the number of the currently printed page + * @page_nr: the number of the currently printed page (0-based) * * Emitted for every page that is printed. The signal handler * must render the @page_nr's page onto the cairo context obtained * from @context using gtk_print_context_get_cairo_context(). - * - * + * |[ * static void * draw_page (GtkPrintOperation *operation, * GtkPrintContext *context, @@ -677,10 +884,10 @@ gtk_print_operation_class_init (GtkPrintOperationClass *class) * pango_font_description_free (desc); * * pango_layout_set_text (layout, "some text", -1); - * pango_layout_set_width (layout, width); + * pango_layout_set_width (layout, width * PANGO_SCALE); * pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER); * - * pango_layout_get_size (layout, NULL, &layout_height); + * pango_layout_get_size (layout, NULL, &layout_height); * text_height = (gdouble)layout_height / PANGO_SCALE; * * cairo_move_to (cr, width / 2, (HEADER_HEIGHT - text_height) / 2); @@ -688,7 +895,7 @@ gtk_print_operation_class_init (GtkPrintOperationClass *class) * * g_object_unref (layout); * } - * + * ]| * * Use gtk_print_operation_set_use_full_page() and * gtk_print_operation_set_unit() before starting the print operation @@ -715,7 +922,7 @@ gtk_print_operation_class_init (GtkPrintOperationClass *class) * * Emitted after all pages have been rendered. * A handler for this signal can clean up any resources that have - * been allocated in the ::begin-print handler. + * been allocated in the #GtkPrintOperation::begin-print handler. * * Since: 2.10 */ @@ -758,14 +965,14 @@ gtk_print_operation_class_init (GtkPrintOperationClass *class) * tab in the print dialog. You typically return a container widget * with multiple widgets in it. * - * The print dialog owns the returned widget, and its lifetime - * isn't controlled by the app. However, the widget is guaranteed - * to stay around until the custom-widget-apply signal is emitted - * on the operation. Then you can read out any information you need - * from the widgets. + * The print dialog owns the returned widget, and its lifetime is not + * controlled by the application. However, the widget is guaranteed + * to stay around until the #GtkPrintOperation::custom-widget-apply + * signal is emitted on the operation. Then you can read out any + * information you need from the widgets. * - * Returns: A custom widget that gets embedded in the print dialog, - * or %NULL + * Returns: (transfer none): A custom widget that gets embedded in + * the print dialog, or %NULL * * Since: 2.10 */ @@ -778,15 +985,38 @@ gtk_print_operation_class_init (GtkPrintOperationClass *class) _gtk_marshal_OBJECT__VOID, G_TYPE_OBJECT, 0); + /** + * GtkPrintOperation::update-custom-widget: + * @operation: the #GtkPrintOperation on which the signal was emitted + * @widget: the custom widget added in create-custom-widget + * @setup: actual page setup + * @settings: actual print settings + * + * Emitted after change of selected printer. The actual page setup and + * print settings are passed to the custom widget, which can actualize + * itself according to this change. + * + * Since: 2.18 + */ + signals[UPDATE_CUSTOM_WIDGET] = + g_signal_new (I_("update-custom-widget"), + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkPrintOperationClass, update_custom_widget), + NULL, NULL, + _gtk_marshal_VOID__OBJECT_OBJECT_OBJECT, + G_TYPE_NONE, 3, GTK_TYPE_WIDGET, GTK_TYPE_PAGE_SETUP, GTK_TYPE_PRINT_SETTINGS); + /** * GtkPrintOperation::custom-widget-apply: * @operation: the #GtkPrintOperation on which the signal was emitted * @widget: the custom widget added in create-custom-widget * - * Emitted right before begin-print if you added - * a custom widget in the create-custom-widget handler. When you get - * this signal you should read the information from the custom widgets, - * as the widgets are not guaraneed to be around at a later time. + * Emitted right before #GtkPrintOperation::begin-print if you added + * a custom widget in the #GtkPrintOperation::create-custom-widget handler. + * When you get this signal you should read the information from the + * custom widgets, as the widgets are not guaraneed to be around at a + * later time. * * Since: 2.10 */ @@ -802,15 +1032,26 @@ gtk_print_operation_class_init (GtkPrintOperationClass *class) /** * GtkPrintOperation::preview: * @operation: the #GtkPrintOperation on which the signal was emitted - * @preview: the #GtkPrintPreviewOperation for the current operation + * @preview: the #GtkPrintOperationPreview for the current operation * @context: the #GtkPrintContext that will be used - * @parent: the #GtkWindow to use as window parent, or %NULL + * @parent: (allow-none): 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. + * The default handler for this signal uses an external viewer + * application to preview. + * + * To implement a custom print preview, an application must return + * %TRUE from its handler for this signal. In order to use the + * provided @context for the preview implementation, it must be + * given a suitable cairo context with gtk_print_context_set_cairo_context(). + * + * The custom preview implementation can use + * gtk_print_operation_preview_is_selected() and + * gtk_print_operation_preview_render_page() to find pages which + * are selected for print and render them. The preview must be + * finished by calling gtk_print_operation_preview_end_preview() + * (typically in response to the user clicking a close button). * * Returns: %TRUE if the listener wants to take over control of the preview * @@ -836,7 +1077,7 @@ gtk_print_operation_class_init (GtkPrintOperationClass *class) * * This page setup will be used by gtk_print_operation_run(), * but it can be overridden on a per-page basis by connecting - * to the ::request-page-setup signal. + * to the #GtkPrintOperation::request-page-setup signal. * * Since: 2.10 */ @@ -892,13 +1133,14 @@ gtk_print_operation_class_init (GtkPrintOperationClass *class) * The number of pages in the document. * * This must be set to a positive number - * before the rendering starts. It may be set in a ::begin-print - * signal hander. + * before the rendering starts. It may be set in a + * #GtkPrintOperation::begin-print signal hander. * - * Note that the page numbers passed to the ::request-page-setup - * and ::draw-page signals are 0-based, i.e. if the user chooses - * to print all pages, the last ::draw-page signal will be for - * page @n_pages - 1. + * Note that the page numbers passed to the + * #GtkPrintOperation::request-page-setup and + * #GtkPrintOperation::draw-page signals are 0-based, i.e. if + * the user chooses to print all pages, the last ::draw-page signal + * will be for page @n_pages - 1. * * Since: 2.10 */ @@ -950,7 +1192,7 @@ gtk_print_operation_class_init (GtkPrintOperationClass *class) PROP_USE_FULL_PAGE, g_param_spec_boolean ("use-full-page", P_("Use full page"), - P_("TRUE if the the origin of the context should be at the corner of the page and not the corner of the imageable area"), + P_("TRUE if the origin of the context should be at the corner of the page and not the corner of the imageable area"), FALSE, GTK_PARAM_READWRITE)); @@ -991,7 +1233,7 @@ gtk_print_operation_class_init (GtkPrintOperationClass *class) P_("Unit"), P_("The unit in which distances can be measured in the context"), GTK_TYPE_UNIT, - GTK_UNIT_PIXEL, + GTK_UNIT_NONE, GTK_PARAM_READWRITE)); @@ -1018,12 +1260,12 @@ gtk_print_operation_class_init (GtkPrintOperationClass *class) * * Some systems don't support asynchronous printing, but those that do * will return %GTK_PRINT_OPERATION_RESULT_IN_PROGRESS as the status, and - * emit the done signal when the operation is actually done. + * emit the #GtkPrintOperation::done signal when the operation is actually + * done. * - * The Windows port does not support asynchronous operation - * at all (this is unlikely to change). On other platforms, all actions - * except for %GTK_PRINT_OPERATION_ACTION_EXPORT support asynchronous - * operation. + * The Windows port does not support asynchronous operation at all (this + * is unlikely to change). On other platforms, all actions except for + * %GTK_PRINT_OPERATION_ACTION_EXPORT support asynchronous operation. * * Since: 2.10 */ @@ -1038,9 +1280,8 @@ gtk_print_operation_class_init (GtkPrintOperationClass *class) /** * GtkPrintOperation:export-filename: * - * The name of a file file to generate instead of showing - * the print dialog. Currently, PDF is the only supported - * format. + * The name of a file to generate instead of showing the print dialog. + * Currently, PDF is the only supported format. * * The intended use of this property is for implementing * "Export to PDF" actions. @@ -1082,8 +1323,8 @@ gtk_print_operation_class_init (GtkPrintOperationClass *class) * The string is translated and suitable for displaying the print * status e.g. in a #GtkStatusbar. * - * See the ::status property for a status value that is suitable - * for programmatic use. + * See the #GtkPrintOperation:status property for a status value that + * is suitable for programmatic use. * * Since: 2.10 */ @@ -1114,6 +1355,78 @@ gtk_print_operation_class_init (GtkPrintOperationClass *class) NULL, GTK_PARAM_READWRITE)); + /** + * GtkPrintOperation:support-selection: + * + * If %TRUE, the print operation will support print of selection. + * This allows the print dialog to show a "Selection" button. + * + * Since: 2.18 + */ + g_object_class_install_property (gobject_class, + PROP_SUPPORT_SELECTION, + g_param_spec_boolean ("support-selection", + P_("Support Selection"), + P_("TRUE if the print operation will support print of selection."), + FALSE, + GTK_PARAM_READWRITE)); + + /** + * GtkPrintOperation:has-selection: + * + * Determines whether there is a selection in your application. + * This can allow your application to print the selection. + * This is typically used to make a "Selection" button sensitive. + * + * Since: 2.18 + */ + g_object_class_install_property (gobject_class, + PROP_HAS_SELECTION, + g_param_spec_boolean ("has-selection", + P_("Has Selection"), + P_("TRUE if a selection exists."), + FALSE, + GTK_PARAM_READWRITE)); + + + /** + * GtkPrintOperation:embed-page-setup: + * + * If %TRUE, page size combo box and orientation combo box are embedded into page setup page. + * + * Since: 2.18 + */ + g_object_class_install_property (gobject_class, + PROP_EMBED_PAGE_SETUP, + g_param_spec_boolean ("embed-page-setup", + P_("Embed Page Setup"), + P_("TRUE if page setup combos are embedded in GtkPrintUnixDialog"), + FALSE, + GTK_PARAM_READWRITE)); + /** + * GtkPrintOperation:n-pages-to-print: + * + * The number of pages that will be printed. + * + * Note that this value is set during print preparation phase + * (%GTK_PRINT_STATUS_PREPARING), so this value should never be + * get before the data generation phase (%GTK_PRINT_STATUS_GENERATING_DATA). + * You can connect to the #GtkPrintOperation::status-changed signal + * and call gtk_print_operation_get_n_pages_to_print() when + * print status is %GTK_PRINT_STATUS_GENERATING_DATA. + * This is typically used to track the progress of print operation. + * + * Since: 2.18 + */ + g_object_class_install_property (gobject_class, + PROP_N_PAGES_TO_PRINT, + g_param_spec_int ("n-pages-to-print", + P_("Number of Pages To Print"), + P_("The number of pages that will be printed."), + -1, + G_MAXINT, + -1, + GTK_PARAM_READABLE)); } /** @@ -1138,13 +1451,13 @@ gtk_print_operation_new (void) /** * gtk_print_operation_set_default_page_setup: * @op: a #GtkPrintOperation - * @default_page_setup: a #GtkPageSetup, or %NULL - * + * @default_page_setup: (allow-none): a #GtkPageSetup, or %NULL + * * Makes @default_page_setup the default page setup for @op. * * This page setup will be used by gtk_print_operation_run(), * but it can be overridden on a per-page basis by connecting - * to the ::request-page-setup signal. + * to the #GtkPrintOperation::request-page-setup signal. * * Since: 2.10 **/ @@ -1178,10 +1491,10 @@ gtk_print_operation_set_default_page_setup (GtkPrintOperation *op, * gtk_print_operation_get_default_page_setup: * @op: a #GtkPrintOperation * - * Returns the default page setup, see + * Returns the default page setup, see * gtk_print_operation_set_default_page_setup(). * - * Returns: the default page setup + * Returns: (transfer none): the default page setup * * Since: 2.10 */ @@ -1197,8 +1510,8 @@ gtk_print_operation_get_default_page_setup (GtkPrintOperation *op) /** * gtk_print_operation_set_print_settings: * @op: a #GtkPrintOperation - * @print_settings: #GtkPrintSettings, or %NULL - * + * @print_settings: (allow-none): #GtkPrintSettings + * * Sets the print settings for @op. This is typically used to * re-establish print settings from a previous print operation, * see gtk_print_operation_run(). @@ -1234,15 +1547,15 @@ gtk_print_operation_set_print_settings (GtkPrintOperation *op, /** * gtk_print_operation_get_print_settings: * @op: a #GtkPrintOperation - * - * Returns the current print settings. * - * Note that the return value is %NULL until either - * gtk_print_operation_set_print_settings() or + * Returns the current print settings. + * + * Note that the return value is %NULL until either + * gtk_print_operation_set_print_settings() or * gtk_print_operation_run() have been called. - * - * Return value: the current print settings of @op. - * + * + * Return value: (transfer none): the current print settings of @op. + * * Since: 2.10 **/ GtkPrintSettings * @@ -1273,7 +1586,7 @@ gtk_print_operation_set_job_name (GtkPrintOperation *op, GtkPrintOperationPrivate *priv; g_return_if_fail (GTK_IS_PRINT_OPERATION (op)); - g_return_if_fail (g_utf8_validate (job_name, -1, NULL)); + g_return_if_fail (job_name != NULL); priv = op->priv; @@ -1291,13 +1604,14 @@ gtk_print_operation_set_job_name (GtkPrintOperation *op, * Sets the number of pages in the document. * * This must be set to a positive number - * before the rendering starts. It may be set in a ::begin-print - * signal hander. + * before the rendering starts. It may be set in a + * #GtkPrintOperation::begin-print signal hander. * - * Note that the page numbers passed to the ::request-page-setup - * and ::draw-page signals are 0-based, i.e. if the user chooses - * to print all pages, the last ::draw-page signal will be - * for page @n_pages - 1. + * Note that the page numbers passed to the + * #GtkPrintOperation::request-page-setup + * and #GtkPrintOperation::draw-page signals are 0-based, i.e. if + * the user chooses to print all pages, the last ::draw-page signal + * will be for page @n_pages - 1. * * Since: 2.10 **/ @@ -1460,32 +1774,22 @@ _gtk_print_operation_set_status (GtkPrintOperation *op, { GtkPrintOperationPrivate *priv = op->priv; static const gchar *status_strs[] = { - /* translators, strip the prefix up to and including the first | */ - N_("print operation status|Initial state"), - /* translators, strip the prefix up to and including the first | */ - N_("print operation status|Preparing to print"), - /* translators, strip the prefix up to and including the first | */ - N_("print operation status|Generating data"), - /* translators, strip the prefix up to and including the first | */ - N_("print operation status|Sending data"), - /* translators, strip the prefix up to and including the first | */ - N_("print operation status|Waiting"), - /* translators, strip the prefix up to and including the first | */ - N_("print operation status|Blocking on issue"), - /* translators, strip the prefix up to and including the first | */ - N_("print operation status|Printing"), - /* translators, strip the prefix up to and including the first | */ - N_("print operation status|Finished"), - /* translators, strip the prefix up to and including the first | */ - N_("print operation status|Finished with error") + NC_("print operation status", "Initial state"), + NC_("print operation status", "Preparing to print"), + NC_("print operation status", "Generating data"), + NC_("print operation status", "Sending data"), + NC_("print operation status", "Waiting"), + NC_("print operation status", "Blocking on issue"), + NC_("print operation status", "Printing"), + NC_("print operation status", "Finished"), + NC_("print operation status", "Finished with error") }; if (status < 0 || status > GTK_PRINT_STATUS_FINISHED_ABORTED) status = GTK_PRINT_STATUS_FINISHED_ABORTED; if (string == NULL) - string = g_strip_context (status_strs[status], - gettext (status_strs[status])); + string = g_dpgettext2 (GETTEXT_PACKAGE, "print operation status", status_strs[status]); if (priv->status == status && strcmp (string, priv->status_string) == 0) @@ -1538,7 +1842,7 @@ gtk_print_operation_get_status (GtkPrintOperation *op) * * Since: 2.10 **/ -G_CONST_RETURN gchar * +const gchar * gtk_print_operation_get_status_string (GtkPrintOperation *op) { g_return_val_if_fail (GTK_IS_PRINT_OPERATION (op), ""); @@ -1640,7 +1944,7 @@ gtk_print_operation_set_allow_async (GtkPrintOperation *op, /** * gtk_print_operation_set_custom_tab_label: * @op: a #GtkPrintOperation - * @label: the label to use, or %NULL to use the default label + * @label: (allow-none): the label to use, or %NULL to use the default label * * Sets the label for the tab holding custom widgets. * @@ -1666,7 +1970,7 @@ gtk_print_operation_set_custom_tab_label (GtkPrintOperation *op, /** * gtk_print_operation_set_export_filename: * @op: a #GtkPrintOperation - * @filename: the filename for the exported file + * @filename: (type filename): the filename for the exported file * * Sets up the #GtkPrintOperation to generate a file instead * of showing the print dialog. The indended use of this function @@ -1746,14 +2050,11 @@ pdf_start_page (GtkPrintOperation *op, GtkPrintContext *print_context, GtkPageSetup *page_setup) { - GtkPaperSize *paper_size; cairo_surface_t *surface = op->priv->platform_data; 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); + w = gtk_page_setup_get_paper_width (page_setup, GTK_UNIT_POINTS); + h = gtk_page_setup_get_paper_height (page_setup, GTK_UNIT_POINTS); cairo_pdf_surface_set_size (surface, w, h); } @@ -1765,7 +2066,11 @@ pdf_end_page (GtkPrintOperation *op, cairo_t *cr; cr = gtk_print_context_get_cairo_context (print_context); - cairo_show_page (cr); + + if ((op->priv->manual_number_up < 2) || + ((op->priv->page_position + 1) % op->priv->manual_number_up == 0) || + (op->priv->page_position == op->priv->nr_of_pages_to_print - 1)) + cairo_show_page (cr); } static void @@ -1778,6 +2083,9 @@ pdf_end_run (GtkPrintOperation *op, cairo_surface_finish (surface); cairo_surface_destroy (surface); + + priv->platform_data = NULL; + priv->free_platform_data = NULL; } static GtkPrintOperationResult @@ -1804,6 +2112,17 @@ run_pdf (GtkPrintOperation *op, surface = cairo_pdf_surface_create (priv->export_filename, width, height); + if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS) + { + g_set_error_literal (&priv->error, + GTK_PRINT_ERROR, + GTK_PRINT_ERROR_GENERAL, + cairo_status_to_string (cairo_surface_status (surface))); + *do_print = FALSE; + return GTK_PRINT_OPERATION_RESULT_ERROR; + } + + /* this would crash on a nil surface */ cairo_surface_set_fallback_resolution (surface, 300, 300); priv->platform_data = surface; @@ -1824,7 +2143,9 @@ run_pdf (GtkPrintOperation *op, priv->manual_reverse = FALSE; priv->manual_page_set = GTK_PAGE_SET_ALL; priv->manual_scale = 1.0; - priv->manual_orientation = TRUE; + priv->manual_orientation = FALSE; + priv->manual_number_up = 1; + priv->manual_number_up_layout = GTK_NUMBER_UP_LAYOUT_LEFT_TO_RIGHT_TOP_TO_BOTTOM; *do_print = TRUE; @@ -1835,70 +2156,139 @@ run_pdf (GtkPrintOperation *op, return GTK_PRINT_OPERATION_RESULT_APPLY; } -typedef struct + +static void +clamp_page_ranges (PrintPagesData *data) { - GtkPrintOperation *op; - gint uncollated_copies; - gint collated_copies; - gint uncollated, collated, total; + GtkPrintOperationPrivate *priv; + gint num_of_correct_ranges; + gint i; - gint range, num_ranges; - GtkPageRange *ranges; - GtkPageRange one_range; + priv = data->op->priv; - gint page, start, end, inc; + num_of_correct_ranges = 0; - GtkWidget *progress; - - gboolean initialized; - gboolean is_preview; -} PrintPagesData; + for (i = 0; i < data->num_ranges; i++) + if ((data->ranges[i].start >= 0) && + (data->ranges[i].start < priv->nr_of_pages) && + (data->ranges[i].end >= 0) && + (data->ranges[i].end < priv->nr_of_pages)) + { + data->ranges[num_of_correct_ranges] = data->ranges[i]; + num_of_correct_ranges++; + } + else if ((data->ranges[i].start >= 0) && + (data->ranges[i].start < priv->nr_of_pages) && + (data->ranges[i].end >= priv->nr_of_pages)) + { + data->ranges[i].end = priv->nr_of_pages - 1; + data->ranges[num_of_correct_ranges] = data->ranges[i]; + num_of_correct_ranges++; + } + else if ((data->ranges[i].end >= 0) && + (data->ranges[i].end < priv->nr_of_pages) && + (data->ranges[i].start < 0)) + { + data->ranges[i].start = 0; + data->ranges[num_of_correct_ranges] = data->ranges[i]; + num_of_correct_ranges++; + } + + data->num_ranges = num_of_correct_ranges; +} static void -find_range (PrintPagesData *data) +increment_page_sequence (PrintPagesData *data) { - GtkPageRange *range; + GtkPrintOperationPrivate *priv = data->op->priv; + gint inc; - range = &data->ranges[data->range]; + if (data->total == -1) + { + data->total = 0; + return; + } - if (data->inc < 0) + /* check whether we reached last position */ + if (priv->page_position == data->last_position && + !(data->collated_copies > 1 && data->collated < (data->collated_copies - 1))) { - data->start = range->end; - data->end = range->start - 1; + if (data->uncollated_copies > 1 && data->uncollated < (data->uncollated_copies - 1)) + { + priv->page_position = data->first_position; + data->sheet = data->first_sheet; + data->uncollated++; + } + else + { + data->done = TRUE; + return; + } } else { - data->start = range->start; - data->end = range->end + 1; + if (priv->manual_reverse) + inc = -1; + else + inc = 1; + + /* changing sheet */ + if (priv->manual_number_up < 2 || + (priv->page_position + 1) % priv->manual_number_up == 0 || + priv->page_position == data->last_position || + priv->page_position == priv->nr_of_pages_to_print - 1) + { + /* check whether to print the same sheet again */ + if (data->collated_copies > 1) + { + if (data->collated < (data->collated_copies - 1)) + { + data->collated++; + data->total++; + priv->page_position = data->sheet * priv->manual_number_up; + + if (priv->page_position < 0 || + priv->page_position >= priv->nr_of_pages_to_print || + data->sheet < 0 || + data->sheet >= data->num_of_sheets) + { + data->done = TRUE; + return; + } + else + data->page = data->pages[priv->page_position]; + + return; + } + else + data->collated = 0; + } + + if (priv->manual_page_set == GTK_PAGE_SET_ODD || + priv->manual_page_set == GTK_PAGE_SET_EVEN) + data->sheet += 2 * inc; + else + data->sheet += inc; + + priv->page_position = data->sheet * priv->manual_number_up; + } + else + priv->page_position += 1; } -} - -static gboolean -increment_page_sequence (PrintPagesData *data) -{ - GtkPrintOperationPrivate *priv = data->op->priv; - do { - data->page += data->inc; - if (data->page == data->end) - { - data->range += data->inc; - if (data->range == -1 || data->range == data->num_ranges) - { - data->uncollated++; - if (data->uncollated == data->uncollated_copies) - return FALSE; - - data->range = data->inc < 0 ? data->num_ranges - 1 : 0; - } - find_range (data); - data->page = data->start; - } - } - while ((priv->manual_page_set == GTK_PAGE_SET_EVEN && data->page % 2 == 0) || - (priv->manual_page_set == GTK_PAGE_SET_ODD && data->page % 2 == 1)); + /* general check */ + if (priv->page_position < 0 || + priv->page_position >= priv->nr_of_pages_to_print || + data->sheet < 0 || + data->sheet >= data->num_of_sheets) + { + data->done = TRUE; + return; + } + else + data->page = data->pages[priv->page_position]; - return TRUE; + data->total++; } static void @@ -1907,8 +2297,6 @@ print_pages_idle_done (gpointer user_data) PrintPagesData *data; GtkPrintOperationPrivate *priv; - GDK_THREADS_ENTER (); - data = (PrintPagesData*)user_data; priv = data->op->priv; @@ -1927,16 +2315,22 @@ print_pages_idle_done (gpointer user_data) g_main_loop_quit (priv->rloop); if (!data->is_preview) - g_signal_emit (data->op, signals[DONE], 0, - priv->cancelled ? - GTK_PRINT_OPERATION_RESULT_CANCEL : - GTK_PRINT_OPERATION_RESULT_APPLY); + { + GtkPrintOperationResult result; + + if (priv->error) + result = GTK_PRINT_OPERATION_RESULT_ERROR; + else if (priv->cancelled) + result = GTK_PRINT_OPERATION_RESULT_CANCEL; + else + result = GTK_PRINT_OPERATION_RESULT_APPLY; + + g_signal_emit (data->op, signals[DONE], 0, result); + } g_object_unref (data->op); - + g_free (data->pages); g_free (data); - - GDK_THREADS_LEAVE (); } static void @@ -1951,8 +2345,8 @@ update_progress (PrintPagesData *data) { if (priv->status == GTK_PRINT_STATUS_PREPARING) { - if (priv->nr_of_pages > 0) - text = g_strdup_printf (_("Preparing %d"), priv->nr_of_pages); + if (priv->nr_of_pages_to_print > 0) + text = g_strdup_printf (_("Preparing %d"), priv->nr_of_pages_to_print); else text = g_strdup (_("Preparing")); } @@ -1967,6 +2361,110 @@ update_progress (PrintPagesData *data) } } +/** + * gtk_print_operation_set_defer_drawing: + * @op: a #GtkPrintOperation + * + * Sets up the #GtkPrintOperation to wait for calling of + * gtk_print_operation_draw_page_finish() from application. It can + * be used for drawing page in another thread. + * + * This function must be called in the callback of "draw-page" signal. + * + * Since: 2.16 + **/ +void +gtk_print_operation_set_defer_drawing (GtkPrintOperation *op) +{ + GtkPrintOperationPrivate *priv = op->priv; + + g_return_if_fail (priv->page_drawing_state == GTK_PAGE_DRAWING_STATE_DRAWING); + + priv->page_drawing_state = GTK_PAGE_DRAWING_STATE_DEFERRED_DRAWING; +} + +/** + * gtk_print_operation_set_embed_page_setup: + * @op: a #GtkPrintOperation + * @embed: %TRUE to embed page setup selection in the #GtkPrintUnixDialog + * + * Embed page size combo box and orientation combo box into page setup page. + * Selected page setup is stored as default page setup in #GtkPrintOperation. + * + * Since: 2.18 + **/ +void +gtk_print_operation_set_embed_page_setup (GtkPrintOperation *op, + gboolean embed) +{ + GtkPrintOperationPrivate *priv; + + g_return_if_fail (GTK_IS_PRINT_OPERATION (op)); + + priv = op->priv; + + embed = embed != FALSE; + if (priv->embed_page_setup != embed) + { + priv->embed_page_setup = embed; + g_object_notify (G_OBJECT (op), "embed-page-setup"); + } +} + +/** + * gtk_print_operation_get_embed_page_setup: + * @op: a #GtkPrintOperation + * + * Gets the value of #GtkPrintOperation:embed-page-setup property. + * + * Returns: whether page setup selection combos are embedded + * + * Since: 2.18 + */ +gboolean +gtk_print_operation_get_embed_page_setup (GtkPrintOperation *op) +{ + g_return_val_if_fail (GTK_IS_PRINT_OPERATION (op), FALSE); + + return op->priv->embed_page_setup; +} + +/** + * gtk_print_operation_draw_page_finish: + * @op: a #GtkPrintOperation + * + * Signalize that drawing of particular page is complete. + * + * It is called after completion of page drawing (e.g. drawing in another + * thread). + * If gtk_print_operation_set_defer_drawing() was called before, then this function + * has to be called by application. In another case it is called by the library + * itself. + * + * Since: 2.16 + **/ +void +gtk_print_operation_draw_page_finish (GtkPrintOperation *op) +{ + GtkPrintOperationPrivate *priv = op->priv; + GtkPageSetup *page_setup; + GtkPrintContext *print_context; + cairo_t *cr; + + print_context = priv->print_context; + page_setup = gtk_print_context_get_page_setup (print_context); + + cr = gtk_print_context_get_cairo_context (print_context); + + priv->end_page (op, print_context); + + cairo_restore (cr); + + g_object_unref (page_setup); + + priv->page_drawing_state = GTK_PAGE_DRAWING_STATE_READY; +} + static void common_render_page (GtkPrintOperation *op, gint page_nr) @@ -1990,168 +2488,433 @@ common_render_page (GtkPrintOperation *op, cr = gtk_print_context_get_cairo_context (print_context); cairo_save (cr); - if (priv->manual_scale != 1.0) + if (priv->manual_scale != 1.0 && priv->manual_number_up <= 1) cairo_scale (cr, priv->manual_scale, priv->manual_scale); if (priv->manual_orientation) _gtk_print_context_rotate_according_to_orientation (print_context); + else + _gtk_print_context_reverse_according_to_orientation (print_context); + + if (priv->manual_number_up > 1) + { + GtkPageOrientation orientation; + GtkPageSetup *page_setup; + gdouble paper_width, paper_height; + gdouble page_width, page_height; + gdouble context_width, context_height; + gdouble bottom_margin, top_margin, left_margin, right_margin; + gdouble x_step, y_step; + gdouble x_scale, y_scale, scale; + gdouble horizontal_offset = 0.0, vertical_offset = 0.0; + gint columns, rows, x, y, tmp_length; + + page_setup = gtk_print_context_get_page_setup (print_context); + orientation = gtk_page_setup_get_orientation (page_setup); + + top_margin = gtk_page_setup_get_top_margin (page_setup, GTK_UNIT_POINTS); + bottom_margin = gtk_page_setup_get_bottom_margin (page_setup, GTK_UNIT_POINTS); + left_margin = gtk_page_setup_get_left_margin (page_setup, GTK_UNIT_POINTS); + right_margin = gtk_page_setup_get_right_margin (page_setup, GTK_UNIT_POINTS); + + paper_width = gtk_page_setup_get_paper_width (page_setup, GTK_UNIT_POINTS); + paper_height = gtk_page_setup_get_paper_height (page_setup, GTK_UNIT_POINTS); + + context_width = gtk_print_context_get_width (print_context); + context_height = gtk_print_context_get_height (print_context); + + if (orientation == GTK_PAGE_ORIENTATION_PORTRAIT || + orientation == GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT) + { + page_width = paper_width - (left_margin + right_margin); + page_height = paper_height - (top_margin + bottom_margin); + } + else + { + page_width = paper_width - (top_margin + bottom_margin); + page_height = paper_height - (left_margin + right_margin); + } + + if (orientation == GTK_PAGE_ORIENTATION_PORTRAIT || + orientation == GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT) + cairo_translate (cr, left_margin, top_margin); + else + cairo_translate (cr, top_margin, left_margin); + + switch (priv->manual_number_up) + { + default: + columns = 1; + rows = 1; + break; + case 2: + columns = 2; + rows = 1; + break; + case 4: + columns = 2; + rows = 2; + break; + case 6: + columns = 3; + rows = 2; + break; + case 9: + columns = 3; + rows = 3; + break; + case 16: + columns = 4; + rows = 4; + break; + } + + if (orientation == GTK_PAGE_ORIENTATION_LANDSCAPE || + orientation == GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE) + { + tmp_length = columns; + columns = rows; + rows = tmp_length; + } + + switch (priv->manual_number_up_layout) + { + case GTK_NUMBER_UP_LAYOUT_LEFT_TO_RIGHT_TOP_TO_BOTTOM: + x = priv->page_position % columns; + y = (priv->page_position / columns) % rows; + break; + case GTK_NUMBER_UP_LAYOUT_LEFT_TO_RIGHT_BOTTOM_TO_TOP: + x = priv->page_position % columns; + y = rows - 1 - (priv->page_position / columns) % rows; + break; + case GTK_NUMBER_UP_LAYOUT_RIGHT_TO_LEFT_TOP_TO_BOTTOM: + x = columns - 1 - priv->page_position % columns; + y = (priv->page_position / columns) % rows; + break; + case GTK_NUMBER_UP_LAYOUT_RIGHT_TO_LEFT_BOTTOM_TO_TOP: + x = columns - 1 - priv->page_position % columns; + y = rows - 1 - (priv->page_position / columns) % rows; + break; + case GTK_NUMBER_UP_LAYOUT_TOP_TO_BOTTOM_LEFT_TO_RIGHT: + x = (priv->page_position / rows) % columns; + y = priv->page_position % rows; + break; + case GTK_NUMBER_UP_LAYOUT_TOP_TO_BOTTOM_RIGHT_TO_LEFT: + x = columns - 1 - (priv->page_position / rows) % columns; + y = priv->page_position % rows; + break; + case GTK_NUMBER_UP_LAYOUT_BOTTOM_TO_TOP_LEFT_TO_RIGHT: + x = (priv->page_position / rows) % columns; + y = rows - 1 - priv->page_position % rows; + break; + case GTK_NUMBER_UP_LAYOUT_BOTTOM_TO_TOP_RIGHT_TO_LEFT: + x = columns - 1 - (priv->page_position / rows) % columns; + y = rows - 1 - priv->page_position % rows; + break; + default: + g_assert_not_reached(); + x = 0; + y = 0; + } + + if (priv->manual_number_up == 4 || priv->manual_number_up == 9 || priv->manual_number_up == 16) + { + x_scale = page_width / (columns * paper_width); + y_scale = page_height / (rows * paper_height); + + scale = x_scale < y_scale ? x_scale : y_scale; + + x_step = paper_width * (x_scale / scale); + y_step = paper_height * (y_scale / scale); + + if ((left_margin + right_margin) > 0) + { + horizontal_offset = left_margin * (x_step - context_width) / (left_margin + right_margin); + vertical_offset = top_margin * (y_step - context_height) / (top_margin + bottom_margin); + } + else + { + horizontal_offset = (x_step - context_width) / 2.0; + vertical_offset = (y_step - context_height) / 2.0; + } + + cairo_scale (cr, scale, scale); + + cairo_translate (cr, + x * x_step + horizontal_offset, + y * y_step + vertical_offset); + + if (priv->manual_scale != 1.0) + cairo_scale (cr, priv->manual_scale, priv->manual_scale); + } + + if (priv->manual_number_up == 2 || priv->manual_number_up == 6) + { + x_scale = page_height / (columns * paper_width); + y_scale = page_width / (rows * paper_height); + + scale = x_scale < y_scale ? x_scale : y_scale; + + horizontal_offset = (paper_width * (x_scale / scale) - paper_width) / 2.0 * columns; + vertical_offset = (paper_height * (y_scale / scale) - paper_height) / 2.0 * rows; + + if (!priv->use_full_page) + { + horizontal_offset -= right_margin; + vertical_offset += top_margin; + } + + cairo_scale (cr, scale, scale); + + cairo_translate (cr, + y * paper_height + vertical_offset, + (columns - x) * paper_width + horizontal_offset); + + if (priv->manual_scale != 1.0) + cairo_scale (cr, priv->manual_scale, priv->manual_scale); + + cairo_rotate (cr, - G_PI / 2); + } + } + else + if (!priv->use_full_page) + _gtk_print_context_translate_into_margin (print_context); - if (!priv->use_full_page) - _gtk_print_context_translate_into_margin (print_context); - + priv->page_drawing_state = GTK_PAGE_DRAWING_STATE_DRAWING; + 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); + if (priv->page_drawing_state == GTK_PAGE_DRAWING_STATE_DRAWING) + gtk_print_operation_draw_page_finish (op); } -static gboolean -print_pages_idle (gpointer user_data) +static void +prepare_data (PrintPagesData *data) { - PrintPagesData *data; - GtkPrintOperationPrivate *priv; - GtkPageSetup *page_setup; - gboolean done = FALSE; - - GDK_THREADS_ENTER (); + GtkPrintOperationPrivate *priv; + GtkPageSetup *page_setup; + gint i, j, counter; - data = (PrintPagesData*)user_data; priv = data->op->priv; - if (priv->status == GTK_PRINT_STATUS_PREPARING) + if (priv->manual_collation) { - if (!data->initialized) - { - 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, priv->print_context); - - if (priv->manual_collation) - { - data->uncollated_copies = priv->manual_num_copies; - data->collated_copies = 1; - } - else - { - data->uncollated_copies = 1; - data->collated_copies = priv->manual_num_copies; - } + data->uncollated_copies = priv->manual_num_copies; + data->collated_copies = 1; + } + else + { + data->uncollated_copies = 1; + data->collated_copies = priv->manual_num_copies; + } - goto out; - } - - if (g_signal_has_handler_pending (data->op, signals[PAGINATE], 0, FALSE)) - { - gboolean paginated = FALSE; + if (!data->initialized) + { + 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[PAGINATE], 0, priv->print_context, &paginated); - if (!paginated) - goto out; - } + g_signal_emit (data->op, signals[BEGIN_PRINT], 0, priv->print_context); - /* FIXME handle this better */ - if (priv->nr_of_pages == 0) - g_warning ("no pages to print"); - - /* Initialize parts of PrintPagesData that depend on nr_of_pages - */ - if (priv->print_pages == GTK_PRINT_PAGES_RANGES) - { - data->ranges = priv->page_ranges; - data->num_ranges = priv->num_page_ranges; - } - else if (priv->print_pages == GTK_PRINT_PAGES_CURRENT && - priv->current_page != -1) - { - data->ranges = &data->one_range; - data->num_ranges = 1; - data->ranges[0].start = priv->current_page; - data->ranges[0].end = priv->current_page; - } - else - { - data->ranges = &data->one_range; - data->num_ranges = 1; - data->ranges[0].start = 0; - data->ranges[0].end = priv->nr_of_pages - 1; - } - - if (priv->manual_reverse) - { - data->range = data->num_ranges - 1; - data->inc = -1; - } - else - { - data->range = 0; - data->inc = 1; - } - find_range (data); - - /* go back one page, since we preincrement below */ - data->page = data->start - data->inc; - data->collated = data->collated_copies - 1; + return; + } - _gtk_print_operation_set_status (data->op, - GTK_PRINT_STATUS_GENERATING_DATA, - NULL); + if (g_signal_has_handler_pending (data->op, signals[PAGINATE], 0, FALSE)) + { + gboolean paginated = FALSE; - goto out; + g_signal_emit (data->op, signals[PAGINATE], 0, priv->print_context, &paginated); + if (!paginated) + return; } - data->total++; - data->collated++; - if (data->collated == data->collated_copies) + /* Initialize parts of PrintPagesData that depend on nr_of_pages + */ + if (priv->print_pages == GTK_PRINT_PAGES_RANGES) { - data->collated = 0; - if (!increment_page_sequence (data)) - { - done = TRUE; - - goto out; - } + if (priv->page_ranges == NULL) + { + g_warning ("no pages to print"); + priv->cancelled = TRUE; + return; + } + data->ranges = priv->page_ranges; + data->num_ranges = priv->num_page_ranges; + for (i = 0; i < data->num_ranges; i++) + if (data->ranges[i].end == -1 || + data->ranges[i].end >= priv->nr_of_pages) + data->ranges[i].end = priv->nr_of_pages - 1; } - - if (data->is_preview) + else if (priv->print_pages == GTK_PRINT_PAGES_CURRENT && + priv->current_page != -1) { - done = TRUE; + data->ranges = &data->one_range; + data->num_ranges = 1; + data->ranges[0].start = priv->current_page; + data->ranges[0].end = priv->current_page; + } + else + { + data->ranges = &data->one_range; + data->num_ranges = 1; + data->ranges[0].start = 0; + data->ranges[0].end = priv->nr_of_pages - 1; + } - g_object_ref (data->op); - - g_signal_emit_by_name (data->op, "ready", priv->print_context); - goto out; + clamp_page_ranges (data); + + if (data->num_ranges < 1) + { + priv->cancelled = TRUE; + return; } - common_render_page (data->op, data->page); + priv->nr_of_pages_to_print = 0; + for (i = 0; i < data->num_ranges; i++) + priv->nr_of_pages_to_print += data->ranges[i].end - data->ranges[i].start + 1; - out: + data->pages = g_new (gint, priv->nr_of_pages_to_print); + counter = 0; + for (i = 0; i < data->num_ranges; i++) + for (j = data->ranges[i].start; j <= data->ranges[i].end; j++) + { + data->pages[counter] = j; + counter++; + } + + data->total = -1; + data->collated = 0; + data->uncollated = 0; - if (priv->cancelled) + if (priv->manual_number_up > 1) { - _gtk_print_operation_set_status (data->op, GTK_PRINT_STATUS_FINISHED_ABORTED, NULL); - - done = TRUE; + if (priv->nr_of_pages_to_print % priv->manual_number_up == 0) + data->num_of_sheets = priv->nr_of_pages_to_print / priv->manual_number_up; + else + data->num_of_sheets = priv->nr_of_pages_to_print / priv->manual_number_up + 1; } + else + data->num_of_sheets = priv->nr_of_pages_to_print; - if (done && !data->is_preview) + if (priv->manual_reverse) { - g_signal_emit (data->op, signals[END_PRINT], 0, priv->print_context); - priv->end_run (data->op, priv->is_sync, priv->cancelled); + /* data->sheet is 0-based */ + if (priv->manual_page_set == GTK_PAGE_SET_ODD) + data->sheet = (data->num_of_sheets - 1) - (data->num_of_sheets - 1) % 2; + else if (priv->manual_page_set == GTK_PAGE_SET_EVEN) + data->sheet = (data->num_of_sheets - 1) - (1 - (data->num_of_sheets - 1) % 2); + else + data->sheet = data->num_of_sheets - 1; + } + else + { + /* data->sheet is 0-based */ + if (priv->manual_page_set == GTK_PAGE_SET_ODD) + data->sheet = 0; + else if (priv->manual_page_set == GTK_PAGE_SET_EVEN) + { + if (data->num_of_sheets > 1) + data->sheet = 1; + else + data->sheet = -1; + } + else + data->sheet = 0; } - update_progress (data); + priv->page_position = data->sheet * priv->manual_number_up; + + if (priv->page_position < 0 || priv->page_position >= priv->nr_of_pages_to_print) + { + priv->cancelled = TRUE; + return; + } - GDK_THREADS_LEAVE (); + data->page = data->pages[priv->page_position]; + data->first_position = priv->page_position; + data->first_sheet = data->sheet; + + if (priv->manual_reverse) + { + if (priv->manual_page_set == GTK_PAGE_SET_ODD) + data->last_position = MIN (priv->manual_number_up - 1, priv->nr_of_pages_to_print - 1); + else if (priv->manual_page_set == GTK_PAGE_SET_EVEN) + data->last_position = MIN (2 * priv->manual_number_up - 1, priv->nr_of_pages_to_print - 1); + else + data->last_position = MIN (priv->manual_number_up - 1, priv->nr_of_pages_to_print - 1); + } + else + { + if (priv->manual_page_set == GTK_PAGE_SET_ODD) + data->last_position = MIN (((data->num_of_sheets - 1) - ((data->num_of_sheets - 1) % 2)) * priv->manual_number_up - 1, priv->nr_of_pages_to_print - 1); + else if (priv->manual_page_set == GTK_PAGE_SET_EVEN) + data->last_position = MIN (((data->num_of_sheets - 1) - (1 - (data->num_of_sheets - 1) % 2)) * priv->manual_number_up - 1, priv->nr_of_pages_to_print - 1); + else + data->last_position = priv->nr_of_pages_to_print - 1; + } + + + _gtk_print_operation_set_status (data->op, + GTK_PRINT_STATUS_GENERATING_DATA, + NULL); +} + +static gboolean +print_pages_idle (gpointer user_data) +{ + PrintPagesData *data; + GtkPrintOperationPrivate *priv; + gboolean done = FALSE; + + data = (PrintPagesData*)user_data; + priv = data->op->priv; + + if (priv->page_drawing_state == GTK_PAGE_DRAWING_STATE_READY) + { + if (priv->status == GTK_PRINT_STATUS_PREPARING) + { + prepare_data (data); + goto out; + } + + if (data->is_preview && !priv->cancelled) + { + done = TRUE; + + g_signal_emit_by_name (data->op, "ready", priv->print_context); + goto out; + } + + increment_page_sequence (data); + + if (!data->done) + common_render_page (data->op, data->page); + else + done = priv->page_drawing_state == GTK_PAGE_DRAWING_STATE_READY; + + out: + + if (priv->cancelled) + { + _gtk_print_operation_set_status (data->op, GTK_PRINT_STATUS_FINISHED_ABORTED, NULL); + + data->is_preview = FALSE; + done = TRUE; + } + + if (done && !data->is_preview) + { + g_signal_emit (data->op, signals[END_PRINT], 0, priv->print_context); + priv->end_run (data->op, priv->is_sync, priv->cancelled); + } + + update_progress (data); + } return !done; } @@ -2170,14 +2933,10 @@ handle_progress_response (GtkWidget *dialog, static gboolean show_progress_timeout (PrintPagesData *data) { - GDK_THREADS_ENTER (); - gtk_window_present (GTK_WINDOW (data->progress)); data->op->priv->show_progress_timeout_id = 0; - GDK_THREADS_LEAVE (); - return FALSE; } @@ -2192,8 +2951,19 @@ print_pages (GtkPrintOperation *op, if (!do_print) { + GtkPrintOperationResult tmp_result; + _gtk_print_operation_set_status (op, GTK_PRINT_STATUS_FINISHED_ABORTED, NULL); - g_signal_emit (op, signals[DONE], 0, result); + + if (priv->error) + tmp_result = GTK_PRINT_OPERATION_RESULT_ERROR; + else if (priv->cancelled) + tmp_result = GTK_PRINT_OPERATION_RESULT_CANCEL; + else + tmp_result = result; + + g_signal_emit (op, signals[DONE], 0, tmp_result); + return; } @@ -2215,7 +2985,7 @@ print_pages (GtkPrintOperation *op, G_CALLBACK (handle_progress_response), op); priv->show_progress_timeout_id = - g_timeout_add (SHOW_PROGRESS_TIME, + gdk_threads_add_timeout (SHOW_PROGRESS_TIME, (GSourceFunc)show_progress_timeout, data); @@ -2231,13 +3001,39 @@ print_pages (GtkPrintOperation *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"); - } + + if (!handled) + { + GtkWidget *error_dialog; + + error_dialog = gtk_message_dialog_new (parent, + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + _("Error creating print preview")); + + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (error_dialog), + _("The most probable reason is that a temporary file could not be created.")); + + if (parent && gtk_window_has_group (parent)) + gtk_window_group_add_window (gtk_window_get_group (parent), + GTK_WINDOW (error_dialog)); + + g_signal_connect (error_dialog, "response", + G_CALLBACK (gtk_widget_destroy), NULL); + + gtk_widget_show (error_dialog); + + print_pages_idle_done (data); + + return; + } + + if (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; @@ -2248,28 +3044,32 @@ print_pages (GtkPrintOperation *op, &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->manual_reverse = gtk_print_settings_get_reverse (priv->print_settings); + priv->manual_page_set = gtk_print_settings_get_page_set (priv->print_settings); + priv->manual_scale = gtk_print_settings_get_scale (priv->print_settings) / 100.0; + priv->manual_orientation = FALSE; + priv->manual_number_up = gtk_print_settings_get_number_up (priv->print_settings); + priv->manual_number_up_layout = gtk_print_settings_get_number_up_layout (priv->print_settings); } - priv->print_pages_idle_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, - print_pages_idle, - data, - print_pages_idle_done); + priv->print_pages_idle_id = gdk_threads_add_idle_full (G_PRIORITY_DEFAULT_IDLE + 10, + print_pages_idle, + data, + print_pages_idle_done); /* Recursive main loop to make sure we don't exit on sync operations */ if (priv->is_sync) { priv->rloop = g_main_loop_new (NULL, FALSE); - GDK_THREADS_LEAVE (); + g_object_ref (op); + gdk_threads_leave (); g_main_loop_run (priv->rloop); - GDK_THREADS_ENTER (); + gdk_threads_enter (); g_main_loop_unref (priv->rloop); priv->rloop = NULL; + g_object_unref (op); } } @@ -2280,8 +3080,8 @@ print_pages (GtkPrintOperation *op, * * Call this when the result of a print operation is * %GTK_PRINT_OPERATION_RESULT_ERROR, either as returned by - * gtk_print_operation_run(), or in the ::done signal handler. - * The returned #GError will contain more details on what went wrong. + * gtk_print_operation_run(), or in the #GtkPrintOperation::done signal + * handler. The returned #GError will contain more details on what went wrong. * * Since: 2.10 **/ @@ -2301,25 +3101,25 @@ gtk_print_operation_get_error (GtkPrintOperation *op, * gtk_print_operation_run: * @op: a #GtkPrintOperation * @action: the action to start - * @parent: Transient parent of the dialog, or %NULL - * @error: Return location for errors, or %NULL - * + * @parent: (allow-none): Transient parent of the dialog + * @error: (allow-none): Return location for errors, or %NULL + * * Runs the print operation, by first letting the user modify - * print settings in the print dialog, and then print the - * document. + * print settings in the print dialog, and then print the document. * * Normally that this function does not return until the rendering of all - * pages is complete. You can connect to the ::status-changed signal on - * @op to obtain some information about the progress of the print operation. + * pages is complete. You can connect to the + * #GtkPrintOperation::status-changed signal on @op to obtain some + * information about the progress of the print operation. * Furthermore, it may use a recursive mainloop to show the print dialog. * - * If you call gtk_print_operation_set_allow_async() or set the allow-async - * property the operation will run asyncronously if this is supported on the - * platform. The ::done signal will be emitted with the operation results when - * the operation is done (i.e. when the dialog is canceled, or when the print - * succeeds or fails). - * - * + * If you call gtk_print_operation_set_allow_async() or set the + * #GtkPrintOperation:allow-async property the operation will run + * asynchronously if this is supported on the platform. The + * #GtkPrintOperation::done signal will be emitted with the result of the + * operation when the it is done (i.e. when the dialog is canceled, or when + * the print succeeds or fails). + * |[ * if (settings != NULL) * gtk_print_operation_set_print_settings (print, settings); * @@ -2327,11 +3127,14 @@ gtk_print_operation_get_error (GtkPrintOperation *op, * gtk_print_operation_set_default_page_setup (print, page_setup); * * g_signal_connect (print, "begin-print", - * G_CALLBACK (begin_print), &data); + * G_CALLBACK (begin_print), &data); * g_signal_connect (print, "draw-page", - * G_CALLBACK (draw_page), &data); + * G_CALLBACK (draw_page), &data); * - * res = gtk_print_operation_run (print, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, parent, &error); + * res = gtk_print_operation_run (print, + * GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, + * parent, + * &error); * * if (res == GTK_PRINT_OPERATION_RESULT_ERROR) * { @@ -2352,7 +3155,10 @@ gtk_print_operation_get_error (GtkPrintOperation *op, * g_object_unref (settings); * settings = g_object_ref (gtk_print_operation_get_print_settings (print)); * } - * + * ]| + * + * Note that gtk_print_operation_run() can only be called once on a + * given #GtkPrintOperation. * * Return value: the result of the print operation. A return value of * %GTK_PRINT_OPERATION_RESULT_APPLY indicates that the printing was @@ -2360,7 +3166,8 @@ gtk_print_operation_get_error (GtkPrintOperation *op, * the used print settings with gtk_print_operation_get_print_settings() * and store them for reuse with the next print operation. A value of * %GTK_PRINT_OPERATION_RESULT_IN_PROGRESS means the operation is running - * asynchronously, and will emit the ::done signal when done. + * asynchronously, and will emit the #GtkPrintOperation::done signal when + * done. * * Since: 2.10 **/ @@ -2374,12 +3181,15 @@ gtk_print_operation_run (GtkPrintOperation *op, GtkPrintOperationResult result; GtkPageSetup *page_setup; gboolean do_print; + gboolean run_print_pages; g_return_val_if_fail (GTK_IS_PRINT_OPERATION (op), GTK_PRINT_OPERATION_RESULT_ERROR); - + g_return_val_if_fail (op->priv->status == GTK_PRINT_STATUS_INITIAL, + GTK_PRINT_OPERATION_RESULT_ERROR); priv = op->priv; - + + run_print_pages = TRUE; do_print = FALSE; priv->error = NULL; priv->action = action; @@ -2415,6 +3225,7 @@ gtk_print_operation_run (GtkPrintOperation *op, parent, print_pages); result = GTK_PRINT_OPERATION_RESULT_IN_PROGRESS; + run_print_pages = FALSE; /* print_pages is called asynchronously from dialog */ } #endif else @@ -2426,12 +3237,17 @@ gtk_print_operation_run (GtkPrintOperation *op, &do_print); } - if (result != GTK_PRINT_OPERATION_RESULT_IN_PROGRESS) + if (run_print_pages) print_pages (op, parent, do_print, result); if (priv->error && error) - *error = g_error_copy (priv->error); - + { + *error = g_error_copy (priv->error); + result = GTK_PRINT_OPERATION_RESULT_ERROR; + } + else if (priv->cancelled) + result = GTK_PRINT_OPERATION_RESULT_CANCEL; + return result; } @@ -2440,7 +3256,8 @@ gtk_print_operation_run (GtkPrintOperation *op, * @op: a #GtkPrintOperation * * Cancels a running print operation. This function may - * be called from a begin-print, paginate or draw-page + * be called from a #GtkPrintOperation::begin-print, + * #GtkPrintOperation::paginate or #GtkPrintOperation::draw-page * signal handler to stop the currently running print * operation. * @@ -2454,7 +3271,122 @@ gtk_print_operation_cancel (GtkPrintOperation *op) op->priv->cancelled = TRUE; } +/** + * gtk_print_operation_set_support_selection: + * @op: a #GtkPrintOperation + * @support_selection: %TRUE to support selection + * + * Sets whether selection is supported by #GtkPrintOperation. + * + * Since: 2.18 + */ +void +gtk_print_operation_set_support_selection (GtkPrintOperation *op, + gboolean support_selection) +{ + GtkPrintOperationPrivate *priv; + + g_return_if_fail (GTK_IS_PRINT_OPERATION (op)); + + priv = op->priv; + + support_selection = support_selection != FALSE; + if (priv->support_selection != support_selection) + { + priv->support_selection = support_selection; + g_object_notify (G_OBJECT (op), "support-selection"); + } +} + +/** + * gtk_print_operation_get_support_selection: + * @op: a #GtkPrintOperation + * + * Gets the value of #GtkPrintOperation:support-selection property. + * + * Returns: whether the application supports print of selection + * + * Since: 2.18 + */ +gboolean +gtk_print_operation_get_support_selection (GtkPrintOperation *op) +{ + g_return_val_if_fail (GTK_IS_PRINT_OPERATION (op), FALSE); + + return op->priv->support_selection; +} + +/** + * gtk_print_operation_set_has_selection: + * @op: a #GtkPrintOperation + * @has_selection: %TRUE indicates that a selection exists + * + * Sets whether there is a selection to print. + * + * Application has to set number of pages to which the selection + * will draw by gtk_print_operation_set_n_pages() in a callback of + * #GtkPrintOperation::begin-print. + * + * Since: 2.18 + */ +void +gtk_print_operation_set_has_selection (GtkPrintOperation *op, + gboolean has_selection) +{ + GtkPrintOperationPrivate *priv; + + g_return_if_fail (GTK_IS_PRINT_OPERATION (op)); + + priv = op->priv; + has_selection = has_selection != FALSE; + if (priv->has_selection != has_selection) + { + priv->has_selection = has_selection; + g_object_notify (G_OBJECT (op), "has-selection"); + } +} -#define __GTK_PRINT_OPERATION_C__ -#include "gtkaliasdef.c" +/** + * gtk_print_operation_get_has_selection: + * @op: a #GtkPrintOperation + * + * Gets the value of #GtkPrintOperation:has-selection property. + * + * Returns: whether there is a selection + * + * Since: 2.18 + */ +gboolean +gtk_print_operation_get_has_selection (GtkPrintOperation *op) +{ + g_return_val_if_fail (GTK_IS_PRINT_OPERATION (op), FALSE); + + return op->priv->has_selection; +} + +/** + * gtk_print_operation_get_n_pages_to_print: + * @op: a #GtkPrintOperation + * + * Returns the number of pages that will be printed. + * + * Note that this value is set during print preparation phase + * (%GTK_PRINT_STATUS_PREPARING), so this function should never be + * called before the data generation phase (%GTK_PRINT_STATUS_GENERATING_DATA). + * You can connect to the #GtkPrintOperation::status-changed signal + * and call gtk_print_operation_get_n_pages_to_print() when + * print status is %GTK_PRINT_STATUS_GENERATING_DATA. + * This is typically used to track the progress of print operation. + * + * Returns: the number of pages that will be printed + * + * Since: 2.18 + **/ +gint +gtk_print_operation_get_n_pages_to_print (GtkPrintOperation *op) +{ + g_return_val_if_fail (GTK_IS_PRINT_OPERATION (op), -1); + + return op->priv->nr_of_pages_to_print; +}