X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkprintoperation.c;h=3fd4f93dc7c0b746515f244dc74e39f08d057e9f;hb=HEAD;hp=424054601ffc8b4443f6a13aec398f4ba2d97cfc;hpb=26c10075f95268ed2d8dbfacadeb1cdc0f559da6;p=~andy%2Fgtk diff --git a/gtk/gtkprintoperation.c b/gtk/gtkprintoperation.c index 424054601..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, @@ -68,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, @@ -147,24 +224,31 @@ 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 (); if (appname == NULL) @@ -191,6 +275,7 @@ static void preview_iface_end_preview (GtkPrintOperationPreview *preview) { GtkPrintOperation *op; + GtkPrintOperationResult result; op = GTK_PRINT_OPERATION (preview); @@ -204,7 +289,14 @@ preview_iface_end_preview (GtkPrintOperationPreview *preview) _gtk_print_operation_set_status (op, GTK_PRINT_STATUS_FINISHED, NULL); - g_signal_emit (op, signals[DONE], 0, GTK_PRINT_OPERATION_RESULT_APPLY); + 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 @@ -220,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: @@ -249,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 @@ -314,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; @@ -373,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; @@ -386,8 +535,8 @@ typedef struct GtkWindow *parent; cairo_surface_t *surface; gchar *filename; - guint page_nr; gboolean wait; + PrintPagesData *pages_data; } PreviewOp; static void @@ -399,16 +548,28 @@ preview_print_idle_done (gpointer data) 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); } @@ -418,35 +579,37 @@ preview_print_idle (gpointer data) { PreviewOp *pop; GtkPrintOperation *op; - gboolean retval = TRUE; - cairo_t *cr; GtkPrintOperationPrivate *priv; + gboolean done = FALSE; pop = (PreviewOp *) data; op = GTK_PRINT_OPERATION (pop->preview); priv = op->priv; - if (priv->page_drawing_state == GTK_PAGE_DRAWING_STATE_READY) { - /* TODO: print out sheets not pages and follow ranges */ - if (pop->page_nr >= op->priv->nr_of_pages) - retval = FALSE; - - if (pop->page_nr > 0) + if (priv->cancelled) + { + done = TRUE; + _gtk_print_operation_set_status (op, GTK_PRINT_STATUS_FINISHED_ABORTED, NULL); + } + else if (!pop->pages_data->initialized) { - cr = gtk_print_context_get_cairo_context (pop->print_context); - _gtk_print_operation_platform_backend_preview_end_page (op, pop->surface, cr); + pop->pages_data->initialized = TRUE; + prepare_data (pop->pages_data); } - - if (retval) + else { - gtk_print_operation_preview_render_page (pop->preview, pop->page_nr); - pop->page_nr++; + 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 @@ -470,7 +633,6 @@ preview_ready (GtkPrintOperationPreview *preview, GtkPrintContext *context, PreviewOp *pop) { - pop->page_nr = 0; pop->print_context = context; g_object_ref (preview); @@ -497,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); @@ -806,8 +971,8 @@ gtk_print_operation_class_init (GtkPrintOperationClass *class) * 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 */ @@ -827,9 +992,9 @@ gtk_print_operation_class_init (GtkPrintOperationClass *class) * @setup: actual page setup * @settings: actual print settings * - * Emmited after change of selected printer. The actual page setup and + * 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. + * itself according to this change. * * Since: 2.18 */ @@ -867,9 +1032,9 @@ 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. * @@ -1068,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)); @@ -1190,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)); } /** @@ -1214,8 +1451,8 @@ 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(), @@ -1254,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 */ @@ -1273,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(). @@ -1310,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 * @@ -1605,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), ""); @@ -1707,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. * @@ -1733,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 @@ -1813,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); } @@ -1832,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 @@ -1905,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; @@ -1916,43 +2156,6 @@ run_pdf (GtkPrintOperation *op, return GTK_PRINT_OPERATION_RESULT_APPLY; } -typedef struct -{ - GtkPrintOperation *op; - gint uncollated_copies; - gint collated_copies; - gint uncollated, collated, total; - - gint range, num_ranges; - GtkPageRange *ranges; - GtkPageRange one_range; - - gint page, start, end, inc; - - GtkWidget *progress; - - gboolean initialized; - gboolean is_preview; -} PrintPagesData; - -static void -find_range (PrintPagesData *data) -{ - GtkPageRange *range; - - range = &data->ranges[data->range]; - - if (data->inc < 0) - { - data->start = range->end; - data->end = range->start - 1; - } - else - { - data->start = range->start; - data->end = range->end + 1; - } -} static void clamp_page_ranges (PrintPagesData *data) @@ -1994,32 +2197,98 @@ clamp_page_ranges (PrintPagesData *data) data->num_ranges = num_of_correct_ranges; } -static gboolean +static void increment_page_sequence (PrintPagesData *data) { GtkPrintOperationPrivate *priv = data->op->priv; + gint inc; - 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)); + if (data->total == -1) + { + data->total = 0; + return; + } - return TRUE; + /* check whether we reached last position */ + if (priv->page_position == data->last_position && + !(data->collated_copies > 1 && data->collated < (data->collated_copies - 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 + { + 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; + } + + /* 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]; + + data->total++; } static void @@ -2046,12 +2315,21 @@ 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); } @@ -2067,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")); } @@ -2105,6 +2383,52 @@ gtk_print_operation_set_defer_drawing (GtkPrintOperation *op) 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 @@ -2164,16 +2488,199 @@ 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); - - if (!priv->use_full_page) - _gtk_print_context_translate_into_margin (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); priv->page_drawing_state = GTK_PAGE_DRAWING_STATE_DRAWING; @@ -2189,31 +2696,31 @@ prepare_data (PrintPagesData *data) { GtkPrintOperationPrivate *priv; GtkPageSetup *page_setup; - gint i; + gint i, j, counter; priv = data->op->priv; + 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; + } + if (!data->initialized) { data->initialized = TRUE; page_setup = create_page_setup (data->op); - _gtk_print_context_set_page_setup (priv->print_context, - page_setup); + _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; - } - return; } @@ -2267,21 +2774,90 @@ prepare_data (PrintPagesData *data) return; } + 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; + + 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->manual_number_up > 1) + { + 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 (priv->manual_reverse) + { + /* 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; + } + + 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; + } + + data->page = data->pages[priv->page_position]; + data->first_position = priv->page_position; + data->first_sheet = data->sheet; + if (priv->manual_reverse) { - data->range = data->num_ranges - 1; - data->inc = -1; + 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 { - data->range = 0; - data->inc = 1; + 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; } - find_range (data); - /* go back one page, since we preincrement below */ - data->page = data->start - data->inc; - data->collated = data->collated_copies - 1; _gtk_print_operation_set_status (data->op, GTK_PRINT_STATUS_GENERATING_DATA, @@ -2292,7 +2868,7 @@ static gboolean print_pages_idle (gpointer user_data) { PrintPagesData *data; - GtkPrintOperationPrivate *priv; + GtkPrintOperationPrivate *priv; gboolean done = FALSE; data = (PrintPagesData*)user_data; @@ -2306,19 +2882,6 @@ print_pages_idle (gpointer user_data) goto out; } - data->total++; - data->collated++; - if (data->collated == data->collated_copies) - { - data->collated = 0; - if (!increment_page_sequence (data)) - { - done = TRUE; - - goto out; - } - } - if (data->is_preview && !priv->cancelled) { done = TRUE; @@ -2327,7 +2890,12 @@ print_pages_idle (gpointer user_data) goto out; } - common_render_page (data->op, data->page); + 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: @@ -2383,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; } @@ -2436,8 +3015,9 @@ print_pages (GtkPrintOperation *op, 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 && parent->group) - gtk_window_group_add_window (parent->group, GTK_WINDOW (error_dialog)); + 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); @@ -2464,10 +3044,12 @@ 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 = gdk_threads_add_idle_full (G_PRIORITY_DEFAULT_IDLE + 10, @@ -2481,9 +3063,9 @@ print_pages (GtkPrintOperation *op, priv->rloop = g_main_loop_new (NULL, FALSE); g_object_ref (op); - GDK_THREADS_LEAVE (); + gdk_threads_leave (); g_main_loop_run (priv->rloop); - GDK_THREADS_ENTER (); + gdk_threads_enter (); g_main_loop_unref (priv->rloop); priv->rloop = NULL; @@ -2519,9 +3101,9 @@ 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. * @@ -2659,8 +3241,13 @@ gtk_print_operation_run (GtkPrintOperation *op, 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; } @@ -2684,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"); + } +} -#define __GTK_PRINT_OPERATION_C__ -#include "gtkaliasdef.c" +/** + * 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"); + } +} + +/** + * 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; +}