/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * Pix * * Copyright (C) 2009 The Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include "gth-image-info.h" #include "gth-image-print-job.h" #include "gth-load-image-info-task.h" #include "preferences.h" #define GET_WIDGET(name) _gtk_builder_get_widget (self->priv->builder, (name)) static GthTemplateCode Text_Special_Codes[] = { { GTH_TEMPLATE_CODE_TYPE_SIMPLE, N_("Current page number"), 'p', 0 }, { GTH_TEMPLATE_CODE_TYPE_SIMPLE, N_("Total number of pages"), 'P', 0 }, { GTH_TEMPLATE_CODE_TYPE_DATE, N_("Current date"), 'D', 1 }, { GTH_TEMPLATE_CODE_TYPE_SIMPLE, N_("Total number of files"), 'F', 0 }, { GTH_TEMPLATE_CODE_TYPE_SIMPLE, N_("Event description"), 'E', 0 }, }; struct _GthImagePrintJobPrivate { GSettings *settings; GtkPrintOperationAction action; GthBrowser *browser; GtkPrintOperation *print_operation; GtkBuilder *builder; GtkWidget *caption_chooser; GthImageInfo *selected; char *event_name; gulong rotation_combobox_changed_event; gulong scale_adjustment_value_changed_event; gulong left_adjustment_value_changed_event; gulong top_adjustment_value_changed_event; gulong width_adjustment_value_changed_event; gulong height_adjustment_value_changed_event; gulong position_combobox_changed_event; GthMetric unit; gboolean centered; /* settings */ GthImageInfo **images; int n_images; int n_rows; int n_columns; int image_width; int image_height; GtkPageSetup *page_setup; char *caption_attributes; char *caption_font_name; char *header_font_name; char *footer_font_name; double scale_factor; int dpi; char *header_template; char *footer_template; char *header; char *footer; /* layout info */ GthTask *task; double max_image_width; double max_image_height; double x_padding; double y_padding; GthRectangle header_rectangle; GthRectangle footer_rectangle; int n_pages; int current_page; gboolean printing; }; G_DEFINE_TYPE_WITH_CODE (GthImagePrintJob, gth_image_print_job, G_TYPE_OBJECT, G_ADD_PRIVATE (GthImagePrintJob)) static void gth_image_print_job_finalize (GObject *base) { GthImagePrintJob *self; int i; self = GTH_IMAGE_PRINT_JOB (base); _g_object_unref (self->priv->task); g_free (self->priv->footer); g_free (self->priv->header); g_free (self->priv->footer_template); g_free (self->priv->header_template); g_free (self->priv->footer_font_name); g_free (self->priv->header_font_name); g_free (self->priv->caption_font_name); g_free (self->priv->caption_attributes); _g_object_unref (self->priv->page_setup); for (i = 0; i < self->priv->n_images; i++) gth_image_info_unref (self->priv->images[i]); g_free (self->priv->images); _g_object_unref (self->priv->print_operation); _g_object_unref (self->priv->builder); g_free (self->priv->event_name); _g_object_unref (self->priv->settings); G_OBJECT_CLASS (gth_image_print_job_parent_class)->finalize (base); } static void gth_image_print_job_class_init (GthImagePrintJobClass *klass) { GObjectClass *object_class; object_class = (GObjectClass*) klass; object_class->finalize = gth_image_print_job_finalize; } static void gth_image_print_job_init (GthImagePrintJob *self) { self->priv = gth_image_print_job_get_instance_private (self); self->priv->settings = g_settings_new (PIX_IMAGE_PRINT_SCHEMA); self->priv->event_name = NULL; self->priv->builder = NULL; self->priv->task = NULL; self->priv->page_setup = NULL; self->priv->current_page = 0; self->priv->caption_attributes = g_settings_get_string (self->priv->settings, PREF_IMAGE_PRINT_CAPTION); self->priv->caption_font_name = g_settings_get_string (self->priv->settings, PREF_IMAGE_PRINT_FONT_NAME); self->priv->header_font_name = g_settings_get_string (self->priv->settings, PREF_IMAGE_PRINT_HEADER_FONT_NAME); self->priv->footer_font_name = g_settings_get_string (self->priv->settings, PREF_IMAGE_PRINT_FOOTER_FONT_NAME); self->priv->selected = NULL; self->priv->n_rows = g_settings_get_int (self->priv->settings, PREF_IMAGE_PRINT_N_ROWS); self->priv->n_columns = g_settings_get_int (self->priv->settings, PREF_IMAGE_PRINT_N_COLUMNS); self->priv->unit = g_settings_get_enum (self->priv->settings, PREF_IMAGE_PRINT_UNIT); self->priv->header_rectangle.height = 0; self->priv->footer_rectangle.height = 0; self->priv->header_template = g_settings_get_string (self->priv->settings, PREF_IMAGE_PRINT_HEADER); self->priv->footer_template = g_settings_get_string (self->priv->settings, PREF_IMAGE_PRINT_FOOTER); self->priv->header = NULL; self->priv->footer = NULL; self->priv->printing = FALSE; self->priv->centered = TRUE; } static double get_text_height (GthImagePrintJob *self, PangoLayout *pango_layout, const char *text, int width) { PangoRectangle logical_rect; if (text == NULL) return 0.0; pango_layout_set_text (pango_layout, text, -1); pango_layout_set_width (pango_layout, width * self->priv->scale_factor * PANGO_SCALE); pango_layout_get_pixel_extents (pango_layout, NULL, &logical_rect); return logical_rect.height / self->priv->scale_factor; } static void gth_image_print_job_set_font_options (GthImagePrintJob *self, PangoLayout *pango_layout, const char *font_name, gboolean preview) { PangoFontDescription *font_desc; double size_in_points; cairo_font_options_t *options; PangoContext *pango_context; pango_layout_set_wrap (pango_layout, PANGO_WRAP_WORD_CHAR); pango_layout_set_justify (pango_layout, FALSE); pango_layout_set_alignment (pango_layout, PANGO_ALIGN_CENTER); font_desc = pango_font_description_from_string (font_name); if (preview) self->priv->scale_factor = 2.83; else self->priv->scale_factor = 1.0; size_in_points = (double) pango_font_description_get_size (font_desc) / PANGO_SCALE; pango_font_description_set_absolute_size (font_desc, size_in_points * PANGO_SCALE); pango_layout_set_font_description (pango_layout, font_desc); options = cairo_font_options_create (); cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF); pango_context = pango_layout_get_context (pango_layout); pango_cairo_context_set_font_options (pango_context, options); cairo_font_options_destroy (options); pango_font_description_free (font_desc); } static gboolean template_eval_cb (TemplateFlags flags, gunichar parent_code, gunichar code, char **args, GString *result, gpointer user_data) { GthImagePrintJob *self = user_data; gboolean preview; char *text = NULL; GDateTime *timestamp; preview = flags & TEMPLATE_FLAGS_PREVIEW; if (parent_code == 'D') { /* strftime code, return the code itself. */ _g_string_append_template_code (result, code, args); return FALSE; } if (preview && (code != 0)) g_string_append (result, ""); switch (code) { case 'p': text = g_strdup_printf ("%d", self->priv->current_page + 1); break; case 'P': text = g_strdup_printf ("%d", self->priv->n_pages); break; case 'F': text = g_strdup_printf ("%d", self->priv->n_images); break; case 'D': timestamp = g_date_time_new_now_local (); text = g_date_time_format (timestamp, (args[0] != NULL) ? args[0] : DEFAULT_STRFTIME_FORMAT); g_date_time_unref (timestamp); break; case 'E': if (self->priv->event_name != NULL) g_string_append (result, self->priv->event_name); break; default: break; } if (text != NULL) { g_string_append (result, text); g_free (text); } if (preview && (code != 0)) g_string_append (result, ""); return FALSE; } static char * get_text_from_template (GthImagePrintJob *self, const char *text) { return _g_template_eval (text, 0, template_eval_cb, self); } static void update_header_and_footer_texts (GthImagePrintJob *self) { g_free (self->priv->header); self->priv->header = NULL; if (! _g_str_empty (self->priv->header_template)) self->priv->header = get_text_from_template (self, self->priv->header_template); g_free (self->priv->footer); self->priv->footer = NULL; if (! _g_str_empty (self->priv->footer_template)) self->priv->footer = get_text_from_template (self, self->priv->footer_template); } static void gth_image_print_job_update_layout_info (GthImagePrintJob *self, gdouble page_width, gdouble page_height, GtkPageOrientation orientation, PangoLayout *pango_layout, gboolean preview) { gboolean height_changed = FALSE; int height; int rows; int columns; int current_page; int current_row; int current_column; int i; self->priv->x_padding = page_width / 40.0; self->priv->y_padding = page_height / 40.0; /* header */ gth_image_print_job_set_font_options (self, pango_layout, self->priv->header_font_name, preview); height = get_text_height (self, pango_layout, self->priv->header, page_width); if (height != self->priv->header_rectangle.height) height_changed = TRUE; self->priv->header_rectangle.height = height; self->priv->header_rectangle.y = 0.0; self->priv->header_rectangle.x = 0.0; self->priv->header_rectangle.width = page_width; /* footer */ gth_image_print_job_set_font_options (self, pango_layout, self->priv->footer_font_name, preview); height = get_text_height (self, pango_layout, self->priv->footer, page_width); if (height != self->priv->footer_rectangle.height) height_changed = TRUE; self->priv->footer_rectangle.height = height; self->priv->footer_rectangle.y = page_height - self->priv->footer_rectangle.height; self->priv->footer_rectangle.x = 0.0; self->priv->footer_rectangle.width = page_width; /* images */ if (! self->priv->printing && height_changed) { for (i = 0; i < self->priv->n_images; i++) gth_image_info_reset (self->priv->images[i]); } rows = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (GET_WIDGET ("rows_spinbutton"))); columns = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (GET_WIDGET ("columns_spinbutton"))); if ((orientation == GTK_PAGE_ORIENTATION_LANDSCAPE) || (orientation == GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE)) { int tmp = rows; rows = columns; columns = tmp; } if (self->priv->header_rectangle.height > 0) page_height -= self->priv->header_rectangle.height + self->priv->y_padding; if (self->priv->footer_rectangle.height > 0) page_height -= self->priv->footer_rectangle.height + self->priv->y_padding; self->priv->n_rows = rows; self->priv->n_columns = columns; self->priv->max_image_width = (page_width - ((columns - 1) * self->priv->x_padding)) / columns; self->priv->max_image_height = (page_height - ((rows - 1) * self->priv->y_padding)) / rows; self->priv->n_pages = MAX ((int) ceil ((double) self->priv->n_images / (self->priv->n_rows * self->priv->n_columns)), 1); if (self->priv->current_page >= self->priv->n_pages) self->priv->current_page = self->priv->n_pages - 1; current_page = 0; current_row = 1; current_column = 1; for (i = 0; i < self->priv->n_images; i++) { GthImageInfo *image_info = self->priv->images[i]; image_info->page = current_page; image_info->col = current_column; image_info->row = current_row; current_column++; if (current_column > columns) { current_row++; current_column = 1; } if (current_row > rows) { current_page++; current_column = 1; current_row = 1; } } } static void gth_image_print_job_update_image_layout (GthImagePrintJob *self, GthImageInfo *image_info, PangoLayout *pango_layout, char **attributes_v, gdouble page_width, gdouble page_height, GtkPageOrientation orientation, gboolean preview) { double max_image_width; double max_image_height; double factor; if (self->priv->selected == NULL) self->priv->selected = image_info; image_info->boundary_box.x = (image_info->col - 1) * (self->priv->max_image_width + self->priv->x_padding); image_info->boundary_box.y = (image_info->row - 1) * (self->priv->max_image_height + self->priv->y_padding); if (self->priv->header_rectangle.height > 0) image_info->boundary_box.y += self->priv->header_rectangle.height + self->priv->y_padding; image_info->boundary_box.width = self->priv->max_image_width; image_info->boundary_box.height = self->priv->max_image_height; max_image_width = image_info->boundary_box.width; max_image_height = image_info->boundary_box.height; image_info->print_comment = FALSE; g_free (image_info->comment_text); image_info->comment_text = NULL; image_info->comment_box.x = 0.0; image_info->comment_box.y = 0.0; image_info->comment_box.width = 0.0; image_info->comment_box.height = 0.0; if (strcmp (self->priv->caption_attributes, "") != 0) { gboolean comment_present = FALSE; GString *text; int j; text = g_string_new (""); for (j = 0; attributes_v[j] != NULL; j++) { char *value; value = gth_file_data_get_attribute_as_string (image_info->file_data, attributes_v[j]); if ((value != NULL) && (strcmp (value, "") != 0)) { if (comment_present) g_string_append (text, "\n"); g_string_append (text, value); comment_present = TRUE; } g_free (value); } image_info->comment_text = g_string_free (text, FALSE); if (comment_present) { PangoRectangle logical_rect; image_info->print_comment = TRUE; pango_layout_set_text (pango_layout, image_info->comment_text, -1); pango_layout_set_width (pango_layout, max_image_width * self->priv->scale_factor * PANGO_SCALE); pango_layout_get_pixel_extents (pango_layout, NULL, &logical_rect); image_info->comment_box.x = 0; image_info->comment_box.y = 0; image_info->comment_box.width = image_info->boundary_box.width; image_info->comment_box.height = logical_rect.height / self->priv->scale_factor; max_image_height -= image_info->comment_box.height; if (max_image_height < 0) { image_info->print_comment = FALSE; max_image_height = image_info->boundary_box.height; } } } factor = MIN (max_image_width / image_info->image_width, max_image_height / image_info->image_height); image_info->maximized_box.width = (double) image_info->image_width * factor; image_info->maximized_box.height = (double) image_info->image_height * factor; image_info->maximized_box.x = image_info->boundary_box.x + ((max_image_width - image_info->maximized_box.width) / 2); image_info->maximized_box.y = image_info->boundary_box.y + ((max_image_height - image_info->maximized_box.height) / 2); if (image_info->reset) { /* calculate the transformation to center the image_box */ image_info->transformation.x = (image_info->maximized_box.x - image_info->boundary_box.x) / self->priv->max_image_width; image_info->transformation.y = (image_info->maximized_box.y - image_info->boundary_box.y) / self->priv->max_image_height; image_info->zoom = 1.0; image_info->reset = FALSE; } image_info->image_box.x = image_info->boundary_box.x + (self->priv->max_image_width * image_info->transformation.x); image_info->image_box.y = image_info->boundary_box.y + (self->priv->max_image_height * image_info->transformation.y); image_info->image_box.width = image_info->maximized_box.width * image_info->zoom; image_info->image_box.height = image_info->maximized_box.height * image_info->zoom; /* check the limits */ if (image_info->image_box.x - image_info->boundary_box.x + image_info->image_box.width > image_info->boundary_box.width) { image_info->image_box.x = image_info->boundary_box.x + image_info->boundary_box.width - image_info->image_box.width; image_info->transformation.x = (image_info->image_box.x - image_info->boundary_box.x) / self->priv->max_image_width; } if (image_info->image_box.y - image_info->boundary_box.y + image_info->image_box.height > image_info->boundary_box.height) { image_info->image_box.y = image_info->boundary_box.y + image_info->boundary_box.height - image_info->image_box.height; image_info->transformation.y = (image_info->image_box.y - image_info->boundary_box.y) / self->priv->max_image_height; } /* the comment_box position */ if (image_info->print_comment) { image_info->comment_box.x += image_info->boundary_box.x; image_info->comment_box.y += image_info->image_box.y + image_info->image_box.height; } } static void gth_image_print_job_update_page_layout (GthImagePrintJob *self, int page, gdouble page_width, gdouble page_height, GtkPageOrientation orientation, PangoLayout *pango_layout, gboolean preview) { char **attributes_v; int i; gth_image_print_job_set_font_options (self, pango_layout, self->priv->caption_font_name, preview); attributes_v = g_strsplit (self->priv->caption_attributes, ",", -1); for (i = 0; i < self->priv->n_images; i++) { GthImageInfo *image_info = self->priv->images[i]; if (image_info->page != page) continue; gth_image_print_job_update_image_layout (self, image_info, pango_layout, attributes_v, page_width, page_height, orientation, preview); } g_strfreev (attributes_v); } static void gth_image_print_job_update_layout (GthImagePrintJob *self, gdouble page_width, gdouble page_height, GtkPageOrientation orientation) { PangoLayout *pango_layout; update_header_and_footer_texts (self); pango_layout = gtk_widget_create_pango_layout (GTK_WIDGET (self->priv->browser), NULL); gth_image_print_job_update_layout_info (self, page_width, page_height, orientation, pango_layout, TRUE); gth_image_print_job_update_page_layout (self, self->priv->current_page, page_width, page_height, orientation, pango_layout, TRUE); g_object_unref (pango_layout); } static void _cairo_paint_image (cairo_t *cr, double x, double y, double width, double height, cairo_surface_t *original_image, int dpi) { double scale_factor; cairo_surface_t *scaled; cairo_pattern_t *pattern; cairo_matrix_t matrix; /* For higher-resolution images, cairo will render the bitmaps at a miserable 72 dpi unless we apply a scaling factor. This scaling boosts the output to 300 dpi (if required). */ scale_factor = MIN ((double) cairo_image_surface_get_width (original_image) / width, (double) dpi / 72.0); scaled = _cairo_image_surface_scale (original_image, width * scale_factor, height * scale_factor, SCALE_FILTER_BEST, NULL); cairo_save (cr); pattern = cairo_pattern_create_for_surface (scaled); cairo_matrix_init_translate (&matrix, -x * scale_factor, -y * scale_factor); cairo_matrix_scale (&matrix, scale_factor, scale_factor); cairo_pattern_set_matrix (pattern, &matrix); cairo_pattern_set_extend (pattern, CAIRO_EXTEND_NONE); cairo_pattern_set_filter (pattern, CAIRO_FILTER_BEST); cairo_set_source (cr, pattern); cairo_paint (cr); cairo_restore (cr); cairo_pattern_destroy (pattern); cairo_surface_destroy (scaled); } static void gth_image_print_job_paint (GthImagePrintJob *self, cairo_t *cr, PangoLayout *pango_layout, double x_offset, double y_offset, int page, gboolean preview) { int i; if (self->priv->header != NULL) { gth_image_print_job_set_font_options (self, pango_layout, self->priv->header_font_name, preview); cairo_save (cr); pango_layout_set_width (pango_layout, self->priv->header_rectangle.width * self->priv->scale_factor * PANGO_SCALE); pango_layout_set_text (pango_layout, self->priv->header, -1); cairo_move_to (cr, x_offset + self->priv->header_rectangle.x, y_offset + self->priv->header_rectangle.y); if (preview) cairo_scale (cr, 1.0 / self->priv->scale_factor, 1.0 / self->priv->scale_factor); pango_cairo_layout_path (cr, pango_layout); cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); cairo_fill (cr); cairo_restore (cr); } if (self->priv->footer != NULL) { gth_image_print_job_set_font_options (self, pango_layout, self->priv->footer_font_name, preview); cairo_save (cr); pango_layout_set_width (pango_layout, self->priv->footer_rectangle.width * self->priv->scale_factor * PANGO_SCALE); pango_layout_set_text (pango_layout, self->priv->footer, -1); cairo_move_to (cr, x_offset + self->priv->footer_rectangle.x, y_offset + self->priv->footer_rectangle.y); if (preview) cairo_scale (cr, 1.0 / self->priv->scale_factor, 1.0 / self->priv->scale_factor); pango_cairo_layout_path (cr, pango_layout); cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); cairo_fill (cr); cairo_restore (cr); } gth_image_print_job_set_font_options (self, pango_layout, self->priv->caption_font_name, preview); for (i = 0; i < self->priv->n_images; i++) { GthImageInfo *image_info = self->priv->images[i]; cairo_surface_t *fullsize_image; if (image_info->page != page) continue; if (preview) { #if 0 cairo_save (cr); cairo_set_line_width (cr, 0.5); cairo_set_source_rgb (cr, 1.0, 0.0, 0.0); cairo_rectangle (cr, x_offset + image_info->comment_box.x, y_offset + image_info->comment_box.y, image_info->comment_box.width, image_info->comment_box.height); cairo_stroke (cr); cairo_restore (cr); #endif cairo_save (cr); cairo_set_line_width (cr, 0.5); if (image_info->active) cairo_set_source_rgb (cr, 1.0, 0.0, 0.0); else if (image_info == self->priv->selected) cairo_set_source_rgb (cr, 0.5, 0.5, 0.5); else cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); cairo_rectangle (cr, x_offset + image_info->boundary_box.x, y_offset + image_info->boundary_box.y, image_info->boundary_box.width, image_info->boundary_box.height); cairo_stroke (cr); cairo_restore (cr); } if (! preview) { if (image_info->rotation != GTH_TRANSFORM_NONE) fullsize_image = _cairo_image_surface_transform (image_info->image, image_info->rotation); else fullsize_image = cairo_surface_reference (image_info->image); } else if (image_info->active) fullsize_image = cairo_surface_reference (image_info->thumbnail_active); else fullsize_image = cairo_surface_reference (image_info->thumbnail); if ((image_info->image_box.width >= 1.0) && (image_info->image_box.height >= 1.0)) { if (preview) { cairo_surface_t *scaled; scaled = _cairo_image_surface_scale (fullsize_image, image_info->image_box.width, image_info->image_box.height, (preview ? SCALE_FILTER_FAST : SCALE_FILTER_BEST), NULL); cairo_save (cr); cairo_set_source_surface (cr, scaled, x_offset + image_info->image_box.x, y_offset + image_info->image_box.y); cairo_rectangle (cr, x_offset + image_info->image_box.x, y_offset + image_info->image_box.y, cairo_image_surface_get_width (scaled), cairo_image_surface_get_height (scaled)); cairo_clip (cr); cairo_paint (cr); cairo_restore (cr); cairo_surface_destroy (scaled); } else _cairo_paint_image (cr, x_offset + image_info->image_box.x, y_offset + image_info->image_box.y, image_info->image_box.width, image_info->image_box.height, fullsize_image, self->priv->dpi); } if (image_info->print_comment) { cairo_save (cr); pango_layout_set_width (pango_layout, image_info->comment_box.width * self->priv->scale_factor * PANGO_SCALE); pango_layout_set_text (pango_layout, image_info->comment_text, -1); cairo_move_to (cr, x_offset + image_info->comment_box.x, y_offset + image_info->comment_box.y); if (preview) cairo_scale (cr, 1.0 / self->priv->scale_factor, 1.0 / self->priv->scale_factor); pango_cairo_layout_path (cr, pango_layout); cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); cairo_fill (cr); cairo_restore (cr); } cairo_surface_destroy (fullsize_image); } } static int get_combo_box_index_from_rotation (GthTransform rotation) { int idx = 0; switch (rotation) { case GTH_TRANSFORM_NONE: idx = 0; break; case GTH_TRANSFORM_ROTATE_90: idx = 1; break; case GTH_TRANSFORM_ROTATE_180: idx = 2; break; case GTH_TRANSFORM_ROTATE_270: idx = 3; break; default: break; } return idx; } static double from_unit_to_pixels (GthMetric unit, double value) { switch (unit) { case GTH_METRIC_INCHES: value = value * 25.4; break; case GTH_METRIC_MILLIMETERS: break; } return value; } static double from_pixels_to_unit (GthMetric unit, double value) { switch (unit) { case GTH_METRIC_INCHES: value = value / 25.4; break; case GTH_METRIC_MILLIMETERS: break; } return value; } #define TO_UNIT(x) (from_pixels_to_unit (self->priv->unit, (x))) #define TO_PIXELS(x) (from_unit_to_pixels (self->priv->unit, (x))) static void gth_image_print_job_update_image_controls (GthImagePrintJob *self) { gboolean centered; if (self->priv->selected == NULL) return; g_signal_handler_block (GET_WIDGET ("rotation_combobox"), self->priv->rotation_combobox_changed_event); gtk_combo_box_set_active (GTK_COMBO_BOX (GET_WIDGET ("rotation_combobox")), get_combo_box_index_from_rotation (self->priv->selected->rotation)); g_signal_handler_unblock (GET_WIDGET ("rotation_combobox"), self->priv->rotation_combobox_changed_event); g_signal_handler_block (GET_WIDGET ("scale_adjustment"), self->priv->scale_adjustment_value_changed_event); gtk_adjustment_set_value (GTK_ADJUSTMENT (GET_WIDGET ("scale_adjustment")), self->priv->selected->zoom); g_signal_handler_unblock (GET_WIDGET ("scale_adjustment"), self->priv->scale_adjustment_value_changed_event); g_signal_handler_block (GET_WIDGET ("left_adjustment"), self->priv->left_adjustment_value_changed_event); gtk_adjustment_set_lower (GTK_ADJUSTMENT (GET_WIDGET ("left_adjustment")), 0.0); gtk_adjustment_set_upper (GTK_ADJUSTMENT (GET_WIDGET ("left_adjustment")), TO_UNIT (self->priv->selected->boundary_box.width - self->priv->selected->image_box.width)); gtk_adjustment_set_value (GTK_ADJUSTMENT (GET_WIDGET ("left_adjustment")), TO_UNIT (self->priv->selected->image_box.x - self->priv->selected->boundary_box.x)); g_signal_handler_unblock (GET_WIDGET ("left_adjustment"), self->priv->left_adjustment_value_changed_event); g_signal_handler_block (GET_WIDGET ("top_adjustment"), self->priv->top_adjustment_value_changed_event); gtk_adjustment_set_lower (GTK_ADJUSTMENT (GET_WIDGET ("top_adjustment")), 0.0); gtk_adjustment_set_upper (GTK_ADJUSTMENT (GET_WIDGET ("top_adjustment")), TO_UNIT (self->priv->selected->boundary_box.height - self->priv->selected->comment_box.height - self->priv->selected->image_box.height)); gtk_adjustment_set_value (GTK_ADJUSTMENT (GET_WIDGET ("top_adjustment")), TO_UNIT (self->priv->selected->image_box.y - self->priv->selected->boundary_box.y)); g_signal_handler_unblock (GET_WIDGET ("top_adjustment"), self->priv->top_adjustment_value_changed_event); g_signal_handler_block (GET_WIDGET ("width_adjustment"), self->priv->width_adjustment_value_changed_event); gtk_adjustment_set_lower (GTK_ADJUSTMENT (GET_WIDGET ("width_adjustment")), 0.0); gtk_adjustment_set_upper (GTK_ADJUSTMENT (GET_WIDGET ("width_adjustment")), TO_UNIT (self->priv->selected->maximized_box.width)); gtk_adjustment_set_value (GTK_ADJUSTMENT (GET_WIDGET ("width_adjustment")), TO_UNIT (self->priv->selected->image_box.width)); g_signal_handler_unblock (GET_WIDGET ("width_adjustment"), self->priv->width_adjustment_value_changed_event); g_signal_handler_block (GET_WIDGET ("height_adjustment"), self->priv->height_adjustment_value_changed_event); gtk_adjustment_set_lower (GTK_ADJUSTMENT (GET_WIDGET ("height_adjustment")), 0.0); gtk_adjustment_set_upper (GTK_ADJUSTMENT (GET_WIDGET ("height_adjustment")), TO_UNIT (self->priv->selected->maximized_box.height)); gtk_adjustment_set_value (GTK_ADJUSTMENT (GET_WIDGET ("height_adjustment")), TO_UNIT (self->priv->selected->image_box.height)); g_signal_handler_unblock (GET_WIDGET ("height_adjustment"), self->priv->height_adjustment_value_changed_event); g_signal_handler_block (GET_WIDGET ("position_combobox"), self->priv->position_combobox_changed_event); gtk_combo_box_set_active (GTK_COMBO_BOX (GET_WIDGET ("position_combobox")), self->priv->centered ? 0 : 1); g_signal_handler_unblock (GET_WIDGET ("position_combobox"), self->priv->position_combobox_changed_event); } static void gth_image_print_job_update_preview (GthImagePrintJob *self) { char *text; g_return_if_fail (GTK_IS_PAGE_SETUP (self->priv->page_setup)); gth_image_print_job_update_layout (self, gtk_page_setup_get_page_width (self->priv->page_setup, GTK_UNIT_MM), gtk_page_setup_get_page_height (self->priv->page_setup, GTK_UNIT_MM), gtk_page_setup_get_orientation (self->priv->page_setup)); gtk_widget_queue_draw (GET_WIDGET ("preview_drawingarea")); gth_image_print_job_update_image_controls (self); text = g_strdup_printf (_("Page %d of %d"), self->priv->current_page + 1, self->priv->n_pages); gtk_label_set_text (GTK_LABEL (GET_WIDGET ("page_label")), text); gtk_widget_set_sensitive (GET_WIDGET ("next_page_button"), self->priv->current_page < self->priv->n_pages - 1); gtk_widget_set_sensitive (GET_WIDGET ("prev_page_button"), self->priv->current_page > 0); g_free (text); } static void gth_image_print_job_update_image_preview (GthImagePrintJob *self, GthImageInfo *image_info) { PangoLayout *pango_layout; char **attributes_v; pango_layout = gtk_widget_create_pango_layout (GTK_WIDGET (self->priv->browser), NULL); attributes_v = g_strsplit (self->priv->caption_attributes, ",", -1); gth_image_print_job_update_image_layout (self, image_info, pango_layout, attributes_v, gtk_page_setup_get_page_width (self->priv->page_setup, GTK_UNIT_MM), gtk_page_setup_get_page_height (self->priv->page_setup, GTK_UNIT_MM), gtk_page_setup_get_orientation (self->priv->page_setup), TRUE); gtk_widget_queue_draw (GET_WIDGET ("preview_drawingarea")); gth_image_print_job_update_image_controls (self); g_strfreev (attributes_v); g_object_unref (pango_layout); } static gboolean preview_draw_cb (GtkWidget *widget, cairo_t *cr, gpointer user_data) { GthImagePrintJob *self = user_data; GtkAllocation allocation; PangoLayout *pango_layout; g_return_val_if_fail (GTH_IS_IMAGE_PRINT_JOB (self), FALSE); g_return_val_if_fail ((self->priv->page_setup != NULL) && GTK_IS_PAGE_SETUP (self->priv->page_setup), FALSE); /* paint the paper */ cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); gtk_widget_get_allocation (widget, &allocation); cairo_rectangle (cr, 0, 0, allocation.width - 1, allocation.height - 1); cairo_fill_preserve (cr); cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); cairo_stroke (cr); /* paint the current page */ pango_layout = gtk_widget_create_pango_layout (GTK_WIDGET (self->priv->browser), NULL); gth_image_print_job_paint (self, cr, pango_layout, gtk_page_setup_get_left_margin (self->priv->page_setup, GTK_UNIT_MM), gtk_page_setup_get_top_margin (self->priv->page_setup, GTK_UNIT_MM), self->priv->current_page, TRUE); g_object_unref (pango_layout); return TRUE; } static gboolean preview_motion_notify_event_cb (GtkWidget *widget, GdkEventMotion *event, gpointer user_data) { GthImagePrintJob *self = user_data; double x, y; int i; gboolean changed = FALSE; x = event->x - gtk_page_setup_get_left_margin (self->priv->page_setup, GTK_UNIT_MM); y = event->y - gtk_page_setup_get_top_margin (self->priv->page_setup, GTK_UNIT_MM); for (i = 0; i < self->priv->n_images; i++) { GthImageInfo *image_info = self->priv->images[i]; if (image_info->page != self->priv->current_page) continue; if ((x >= image_info->boundary_box.x) && (x <= image_info->boundary_box.x + image_info->boundary_box.width) && (y >= image_info->boundary_box.y ) && (y <= image_info->boundary_box.y + image_info->boundary_box.height)) { if (! image_info->active) { image_info->active = TRUE; changed = TRUE; } } else if (image_info->active) { image_info->active = FALSE; changed = TRUE; } } if (changed) gtk_widget_queue_draw (GET_WIDGET ("preview_drawingarea")); return FALSE; } static gboolean preview_leave_notify_event_cb (GtkWidget *widget, GdkEventCrossing *event, gpointer user_data) { GthImagePrintJob *self = user_data; int i; gboolean changed = FALSE; for (i = 0; i < self->priv->n_images; i++) { GthImageInfo *image_info = self->priv->images[i]; if (image_info->page != self->priv->current_page) continue; if (image_info->active) { image_info->active = FALSE; changed = TRUE; } } if (changed) gtk_widget_queue_draw (GET_WIDGET ("preview_drawingarea")); return FALSE; } static gboolean preview_button_press_event_cb (GtkWidget *widget, GdkEventButton *event, gpointer user_data) { GthImagePrintJob *self = user_data; double x, y; int i; x = event->x - gtk_page_setup_get_left_margin (self->priv->page_setup, GTK_UNIT_MM); y = event->y - gtk_page_setup_get_top_margin (self->priv->page_setup, GTK_UNIT_MM); for (i = 0; i < self->priv->n_images; i++) { GthImageInfo *image_info = self->priv->images[i]; if (image_info->page != self->priv->current_page) continue; if ((x >= image_info->boundary_box.x) && (x <= image_info->boundary_box.x + image_info->boundary_box.width) && (y >= image_info->boundary_box.y ) && (y <= image_info->boundary_box.y + image_info->boundary_box.height)) { self->priv->selected = image_info; gtk_widget_queue_draw (GET_WIDGET ("preview_drawingarea")); gth_image_print_job_update_image_controls (self); break; } } return FALSE; } static void rows_spinbutton_changed_cb (GtkSpinButton *widget, gpointer user_data) { GthImagePrintJob *self = user_data; int i; self->priv->n_rows = gtk_spin_button_get_value_as_int (widget); for (i = 0; i < self->priv->n_images; i++) gth_image_info_reset (self->priv->images[i]); gth_image_print_job_update_preview (self); } static void columns_spinbutton_changed_cb (GtkSpinButton *widget, gpointer user_data) { GthImagePrintJob *self = user_data; int i; self->priv->n_columns = gtk_spin_button_get_value_as_int (widget); for (i = 0; i < self->priv->n_images; i++) gth_image_info_reset (self->priv->images[i]); gth_image_print_job_update_preview (self); } static void next_page_button_clicked_cb (GtkWidget *widget, gpointer user_data) { GthImagePrintJob *self = user_data; self->priv->current_page = MIN (self->priv->current_page + 1, self->priv->n_pages - 1); self->priv->selected = NULL; gth_image_print_job_update_preview (self); } static void prev_page_button_clicked_cb (GtkWidget *widget, gpointer user_data) { GthImagePrintJob *self = user_data; self->priv->current_page = MAX (0, self->priv->current_page - 1); self->priv->selected = NULL; gth_image_print_job_update_preview (self); } static void metadata_ready_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { GthImagePrintJob *self = user_data; GError *error; _g_query_metadata_finish (result, &error); gth_image_print_job_update_preview (self); } static void gth_image_print_job_load_metadata (GthImagePrintJob *self) { GList *files; int i; files = NULL; for (i = 0; i < self->priv->n_images; i++) files = g_list_prepend (files, self->priv->images[i]->file_data); files = g_list_reverse (files); _g_query_metadata_async (files, self->priv->caption_attributes, NULL, metadata_ready_cb, self); g_list_free (files); } static void caption_chooser_changed_cb (GthMetadataChooser *chooser, gpointer user_data) { GthImagePrintJob *self = user_data; char *new_caption_attributes; gboolean reload_required; new_caption_attributes = gth_metadata_chooser_get_selection (chooser); reload_required = attribute_list_reload_required (self->priv->caption_attributes, new_caption_attributes); g_free (self->priv->caption_attributes); self->priv->caption_attributes = new_caption_attributes; g_settings_set_string (self->priv->settings, PREF_IMAGE_PRINT_CAPTION, self->priv->caption_attributes); if (reload_required) gth_image_print_job_load_metadata (self); else gth_image_print_job_update_preview (self); } static void update_spinbutton_props (GtkWidget *spin_button, gint digits, gdouble step, gdouble climb_rate) { GtkAdjustment *adjust = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (spin_button)); gtk_spin_button_configure (GTK_SPIN_BUTTON (spin_button), adjust, climb_rate, digits); gtk_adjustment_set_step_increment (adjust, step); } static void unit_combobox_changed_cb (GtkComboBox *combo_box, gpointer user_data) { GthImagePrintJob *self = user_data; int digits; gdouble step, climb_rate; self->priv->unit = gtk_combo_box_get_active (combo_box); switch (self->priv->unit) { case GTH_METRIC_INCHES: digits = 2; step = 0.01; climb_rate = .1; break; case GTH_METRIC_MILLIMETERS: default: digits = 0; step = 1.0; climb_rate = 1.0; break; } update_spinbutton_props (GET_WIDGET ("img_left_spinbutton"), digits, step, climb_rate); update_spinbutton_props (GET_WIDGET ("img_top_spinbutton"), digits, step, climb_rate); update_spinbutton_props (GET_WIDGET ("img_width_spinbutton"), digits, step, climb_rate); update_spinbutton_props (GET_WIDGET ("img_height_spinbutton"), digits, step, climb_rate); gth_image_print_job_update_image_controls (self); } static void header_entry_changed_cb (GtkEditable *editable, gpointer user_data) { GthImagePrintJob *self = user_data; _g_str_set (&self->priv->header_template, gtk_entry_get_text (GTK_ENTRY (editable))); if (g_strcmp0 (self->priv->header_template, "") == 0) { g_free (self->priv->header_template); self->priv->header_template = NULL; } gth_image_print_job_update_preview (self); } static void footer_entry_changed_cb (GtkEditable *editable, gpointer user_data) { GthImagePrintJob *self = user_data; _g_str_set (&self->priv->footer_template, gtk_entry_get_text (GTK_ENTRY (editable))); if (g_strcmp0 (self->priv->footer_template, "") == 0) { g_free (self->priv->footer_template); self->priv->footer_template = NULL; } gth_image_print_job_update_preview (self); } static void edit_header_button_clicked_cb (GtkButton *button, gpointer user_data) { GthImagePrintJob *self = user_data; GtkWidget *dialog; dialog = gth_template_editor_dialog_new (Text_Special_Codes, G_N_ELEMENTS (Text_Special_Codes), 0, _("Edit Template"), NULL); gth_template_editor_dialog_set_preview_cb (GTH_TEMPLATE_EDITOR_DIALOG (dialog), template_eval_cb, self); gth_template_editor_dialog_set_template (GTH_TEMPLATE_EDITOR_DIALOG (dialog), gtk_entry_get_text (GTK_ENTRY (GET_WIDGET ("header_entry")))); g_signal_connect (dialog, "response", G_CALLBACK (gth_template_editor_dialog_default_response), GET_WIDGET ("header_entry")); gtk_widget_show (dialog); } static void edit_footer_button_clicked_cb (GtkButton *button, gpointer user_data) { GthImagePrintJob *self = user_data; GtkWidget *dialog; dialog = gth_template_editor_dialog_new (Text_Special_Codes, G_N_ELEMENTS (Text_Special_Codes), 0, _("Edit Template"), NULL); gth_template_editor_dialog_set_preview_cb (GTH_TEMPLATE_EDITOR_DIALOG (dialog), template_eval_cb, self); gth_template_editor_dialog_set_template (GTH_TEMPLATE_EDITOR_DIALOG (dialog), gtk_entry_get_text (GTK_ENTRY (GET_WIDGET ("footer_entry")))); g_signal_connect (dialog, "response", G_CALLBACK (gth_template_editor_dialog_default_response), GET_WIDGET ("footer_entry")); gtk_widget_show (dialog); } static void center_image (GthImagePrintJob *self) { self->priv->selected->image_box.x = (self->priv->selected->boundary_box.width - self->priv->selected->image_box.width) / 2.0; self->priv->selected->image_box.y = (self->priv->selected->boundary_box.height - self->priv->selected->comment_box.height - self->priv->selected->image_box.height) / 2.0; self->priv->selected->transformation.x = self->priv->selected->image_box.x / self->priv->max_image_width; self->priv->selected->transformation.y = self->priv->selected->image_box.y / self->priv->max_image_height; } static void rotation_combobox_changed_cb (GtkComboBox *combo_box, gpointer user_data) { GthImagePrintJob *self = user_data; if (self->priv->selected == NULL) return; gth_image_info_rotate (self->priv->selected, gtk_combo_box_get_active (combo_box) * 90); gth_image_info_reset (self->priv->selected); gth_image_print_job_update_preview (self); } static void gth_image_print_job_set_selected_zoom (GthImagePrintJob *self, double zoom) { double x, y; self->priv->selected->zoom = CLAMP (zoom, 0.0, 1.0); self->priv->selected->image_box.width = self->priv->selected->maximized_box.width * self->priv->selected->zoom; self->priv->selected->image_box.height = self->priv->selected->maximized_box.height * self->priv->selected->zoom; x = self->priv->selected->image_box.x - self->priv->selected->boundary_box.x; y = self->priv->selected->image_box.y - self->priv->selected->boundary_box.y; if (x + self->priv->selected->image_box.width > self->priv->selected->boundary_box.width) x = self->priv->selected->boundary_box.width - self->priv->selected->image_box.width; if (x + self->priv->selected->image_box.width > self->priv->selected->boundary_box.width) self->priv->selected->image_box.width = self->priv->selected->boundary_box.width - x; if (y + self->priv->selected->image_box.height > self->priv->selected->boundary_box.height - self->priv->selected->comment_box.height) y = self->priv->selected->boundary_box.height - self->priv->selected->comment_box.height - self->priv->selected->image_box.height; if (y + self->priv->selected->image_box.height > self->priv->selected->boundary_box.height - self->priv->selected->comment_box.height) self->priv->selected->image_box.height = self->priv->selected->boundary_box.height - self->priv->selected->comment_box.height - y; self->priv->selected->zoom = MIN (self->priv->selected->image_box.width / self->priv->selected->maximized_box.width, self->priv->selected->image_box.height / self->priv->selected->maximized_box.height); self->priv->selected->transformation.x = x / self->priv->max_image_width; self->priv->selected->transformation.y = y / self->priv->max_image_height; if (self->priv->centered) { center_image (self); } gth_image_print_job_update_image_preview (self, self->priv->selected); } static void scale_adjustment_value_changed_cb (GtkAdjustment *adjustment, gpointer user_data) { GthImagePrintJob *self = user_data; if (self->priv->selected == NULL) return; gth_image_print_job_set_selected_zoom (self, gtk_adjustment_get_value (adjustment)); } static void left_adjustment_value_changed_cb (GtkAdjustment *adjustment, gpointer user_data) { GthImagePrintJob *self = user_data; if (self->priv->selected == NULL) return; self->priv->centered = FALSE; self->priv->selected->transformation.x = TO_PIXELS (gtk_adjustment_get_value (adjustment)) / self->priv->max_image_width; gth_image_print_job_update_preview (self); } static void top_adjustment_value_changed_cb (GtkAdjustment *adjustment, gpointer user_data) { GthImagePrintJob *self = user_data; if (self->priv->selected == NULL) return; self->priv->centered = FALSE; self->priv->selected->transformation.y = TO_PIXELS (gtk_adjustment_get_value (adjustment)) / self->priv->max_image_height; gth_image_print_job_update_preview (self); } static void width_adjustment_value_changed_cb (GtkAdjustment *adjustment, gpointer user_data) { GthImagePrintJob *self = user_data; if (self->priv->selected == NULL) return; gth_image_print_job_set_selected_zoom (self, TO_PIXELS (gtk_adjustment_get_value (adjustment)) / self->priv->selected->maximized_box.width); } static void height_adjustment_value_changed_cb (GtkAdjustment *adjustment, gpointer user_data) { GthImagePrintJob *self = user_data; if (self->priv->selected == NULL) return; gth_image_print_job_set_selected_zoom (self, TO_PIXELS (gtk_adjustment_get_value (adjustment)) / self->priv->selected->maximized_box.height); } static void position_combobox_changed_cb (GtkComboBox *combo_box, gpointer user_data) { GthImagePrintJob *self = user_data; if (self->priv->selected == NULL) return; if (gtk_combo_box_get_active (combo_box) == 0) { self->priv->centered = TRUE; center_image (self); gth_image_print_job_update_preview (self); } else { self->priv->centered = FALSE; } } static char * image_scale_format_value_cb (GtkScale *scale, double value, gpointer user_data) { return g_strdup_printf ("%0.0f%%", value * 100.0); } static GObject * operation_create_custom_widget_cb (GtkPrintOperation *operation, gpointer user_data) { GthImagePrintJob *self = user_data; self->priv->builder = _gtk_builder_new_from_file ("print-layout.ui", "image_print"); self->priv->caption_chooser = gth_metadata_chooser_new (GTH_METADATA_ALLOW_IN_PRINT, TRUE); gtk_widget_show (self->priv->caption_chooser); gtk_container_add (GTK_CONTAINER (GET_WIDGET ("caption_scrolledwindow")), self->priv->caption_chooser); gth_metadata_chooser_set_selection (GTH_METADATA_CHOOSER (self->priv->caption_chooser), self->priv->caption_attributes); gtk_combo_box_set_active (GTK_COMBO_BOX (GET_WIDGET ("unit_combobox")), self->priv->unit); gtk_spin_button_set_value (GTK_SPIN_BUTTON (GET_WIDGET ("rows_spinbutton")), self->priv->n_rows); gtk_spin_button_set_value (GTK_SPIN_BUTTON (GET_WIDGET ("columns_spinbutton")), self->priv->n_columns); gtk_combo_box_set_active (GTK_COMBO_BOX (GET_WIDGET ("unit_combobox")), g_settings_get_enum (self->priv->settings, PREF_IMAGE_PRINT_UNIT)); g_signal_connect (GET_WIDGET ("preview_drawingarea"), "draw", G_CALLBACK (preview_draw_cb), self); g_signal_connect (GET_WIDGET ("preview_drawingarea"), "motion-notify-event", G_CALLBACK (preview_motion_notify_event_cb), self); g_signal_connect (GET_WIDGET ("preview_drawingarea"), "leave-notify-event", G_CALLBACK (preview_leave_notify_event_cb), self); g_signal_connect (GET_WIDGET ("preview_drawingarea"), "button-press-event", G_CALLBACK (preview_button_press_event_cb), self); g_signal_connect (GET_WIDGET ("rows_spinbutton"), "value-changed", G_CALLBACK (rows_spinbutton_changed_cb), self); g_signal_connect (GET_WIDGET ("columns_spinbutton"), "value-changed", G_CALLBACK (columns_spinbutton_changed_cb), self); g_signal_connect (GET_WIDGET ("next_page_button"), "clicked", G_CALLBACK (next_page_button_clicked_cb), self); g_signal_connect (GET_WIDGET ("prev_page_button"), "clicked", G_CALLBACK (prev_page_button_clicked_cb), self); g_signal_connect (self->priv->caption_chooser, "changed", G_CALLBACK (caption_chooser_changed_cb), self); g_signal_connect (GET_WIDGET ("unit_combobox"), "changed", G_CALLBACK (unit_combobox_changed_cb), self); g_signal_connect (GET_WIDGET ("header_entry"), "changed", G_CALLBACK (header_entry_changed_cb), self); g_signal_connect (GET_WIDGET ("footer_entry"), "changed", G_CALLBACK (footer_entry_changed_cb), self); g_signal_connect (GET_WIDGET ("edit_header_button"), "clicked", G_CALLBACK (edit_header_button_clicked_cb), self); g_signal_connect (GET_WIDGET ("edit_footer_button"), "clicked", G_CALLBACK (edit_footer_button_clicked_cb), self); self->priv->rotation_combobox_changed_event = g_signal_connect (GET_WIDGET ("rotation_combobox"), "changed", G_CALLBACK (rotation_combobox_changed_cb), self); self->priv->scale_adjustment_value_changed_event = g_signal_connect (GET_WIDGET ("scale_adjustment"), "value-changed", G_CALLBACK (scale_adjustment_value_changed_cb), self); g_signal_connect (GET_WIDGET ("image_scale"), "format-value", G_CALLBACK (image_scale_format_value_cb), self); self->priv->left_adjustment_value_changed_event = g_signal_connect (GET_WIDGET ("left_adjustment"), "value-changed", G_CALLBACK (left_adjustment_value_changed_cb), self); self->priv->top_adjustment_value_changed_event = g_signal_connect (GET_WIDGET ("top_adjustment"), "value-changed", G_CALLBACK (top_adjustment_value_changed_cb), self); self->priv->width_adjustment_value_changed_event = g_signal_connect (GET_WIDGET ("width_adjustment"), "value-changed", G_CALLBACK (width_adjustment_value_changed_cb), self); self->priv->height_adjustment_value_changed_event = g_signal_connect (GET_WIDGET ("height_adjustment"), "value-changed", G_CALLBACK (height_adjustment_value_changed_cb), self); self->priv->position_combobox_changed_event = g_signal_connect (GET_WIDGET ("position_combobox"), "changed", G_CALLBACK (position_combobox_changed_cb), self); if (self->priv->page_setup != NULL) { int i; gtk_widget_set_size_request (GET_WIDGET ("preview_drawingarea"), gtk_page_setup_get_paper_width (self->priv->page_setup, GTK_UNIT_MM), gtk_page_setup_get_paper_height (self->priv->page_setup, GTK_UNIT_MM)); for (i = 0; i < self->priv->n_images; i++) gth_image_info_reset (self->priv->images[i]); gth_image_print_job_update_preview (self); unit_combobox_changed_cb (GTK_COMBO_BOX (GET_WIDGET ("unit_combobox")), self); } return gtk_builder_get_object (self->priv->builder, "print_layout"); } static void operation_update_custom_widget_cb (GtkPrintOperation *operation, GtkWidget *widget, GtkPageSetup *setup, GtkPrintSettings *settings, gpointer user_data) { GthImagePrintJob *self = user_data; int i; _g_object_unref (self->priv->page_setup); self->priv->page_setup = NULL; if (setup == NULL) return; self->priv->page_setup = gtk_page_setup_copy (setup); self->priv->dpi = gtk_print_settings_get_resolution (settings); gtk_widget_set_size_request (GET_WIDGET ("preview_drawingarea"), gtk_page_setup_get_paper_width (setup, GTK_UNIT_MM), gtk_page_setup_get_paper_height (setup, GTK_UNIT_MM)); gtk_entry_set_text (GTK_ENTRY (GET_WIDGET ("header_entry")), self->priv->header_template); gtk_entry_set_text (GTK_ENTRY (GET_WIDGET ("footer_entry")), self->priv->footer_template); for (i = 0; i < self->priv->n_images; i++) gth_image_info_reset (self->priv->images[i]); gth_image_print_job_update_preview (self); } static void operation_custom_widget_apply_cb (GtkPrintOperation *operation, GtkWidget *widget, gpointer user_data) { GthImagePrintJob *self = user_data; g_settings_set_int (self->priv->settings, PREF_IMAGE_PRINT_N_ROWS, self->priv->n_rows); g_settings_set_int (self->priv->settings, PREF_IMAGE_PRINT_N_COLUMNS, self->priv->n_columns); g_settings_set_enum (self->priv->settings, PREF_IMAGE_PRINT_UNIT, gtk_combo_box_get_active (GTK_COMBO_BOX (GET_WIDGET ("unit_combobox")))); g_settings_set_string (self->priv->settings, PREF_IMAGE_PRINT_HEADER, gtk_entry_get_text (GTK_ENTRY (GET_WIDGET ("header_entry")))); g_settings_set_string (self->priv->settings, PREF_IMAGE_PRINT_FOOTER, gtk_entry_get_text (GTK_ENTRY (GET_WIDGET ("footer_entry")))); } static void print_operation_begin_print_cb (GtkPrintOperation *operation, GtkPrintContext *context, gpointer user_data) { GthImagePrintJob *self = user_data; GtkPrintSettings *settings; GFile *file; char *filename; PangoLayout *pango_layout; _g_object_unref (self->priv->page_setup); self->priv->page_setup = gtk_page_setup_copy (gtk_print_context_get_page_setup (context)); settings = gtk_print_operation_get_print_settings (operation); self->priv->dpi = gtk_print_settings_get_resolution (settings); /* save the page setup */ file = gth_user_dir_get_file_for_write (GTH_DIR_CONFIG, PIX_DIR, "page_setup", NULL); filename = g_file_get_path (file); gtk_page_setup_to_file (self->priv->page_setup, filename, NULL); g_free (filename); g_object_unref (file); self->priv->printing = TRUE; pango_layout = gtk_print_context_create_pango_layout (context); gth_image_print_job_update_layout_info (self, gtk_print_context_get_width (context), gtk_print_context_get_height (context), gtk_page_setup_get_orientation (self->priv->page_setup), pango_layout, FALSE); gtk_print_operation_set_n_pages (operation, self->priv->n_pages); g_object_unref (pango_layout); } static void print_operation_draw_page_cb (GtkPrintOperation *operation, GtkPrintContext *context, int page_nr, gpointer user_data) { GthImagePrintJob *self = user_data; cairo_t *cr; PangoLayout *pango_layout; GtkPageSetup *setup; cr = gtk_print_context_get_cairo_context (context); pango_layout = gtk_print_context_create_pango_layout (context); setup = gtk_print_context_get_page_setup (context); self->priv->current_page = page_nr; update_header_and_footer_texts (self); gth_image_print_job_update_page_layout (self, page_nr, gtk_print_context_get_width (context), gtk_print_context_get_height (context), gtk_page_setup_get_orientation (setup), pango_layout, FALSE); gth_image_print_job_paint (self, cr, pango_layout, 0, 0, page_nr, FALSE); g_object_unref (pango_layout); } static void print_operation_done_cb (GtkPrintOperation *operation, GtkPrintOperationResult result, gpointer user_data) { GthImagePrintJob *self = user_data; if (result == GTK_PRINT_OPERATION_RESULT_ERROR) { GError *error = NULL; gtk_print_operation_get_error (self->priv->print_operation, &error); _gtk_error_dialog_from_gerror_show (GTK_WINDOW (self->priv->browser), _("Could not print"), error); g_clear_error (&error); return; } else if (result == GTK_PRINT_OPERATION_RESULT_APPLY) { GtkPrintSettings *settings; GFile *file; char *filename; settings = gtk_print_operation_get_print_settings (operation); file = gth_user_dir_get_file_for_write (GTH_DIR_CONFIG, PIX_DIR, "print_settings", NULL); filename = g_file_get_path (file); gtk_print_settings_to_file (settings, filename, NULL); g_free (filename); g_object_unref (file); } g_object_unref (self); } GthImagePrintJob * gth_image_print_job_new (GList *file_data_list, GthFileData *current, cairo_surface_t *current_image, const char *event_name, GError **error) { GthImagePrintJob *self; GList *scan; int n; self = g_object_new (GTH_TYPE_IMAGE_PRINT_JOB, NULL); self->priv->n_images = g_list_length (file_data_list); self->priv->images = g_new (GthImageInfo *, self->priv->n_images + 1); for (scan = file_data_list, n = 0; scan; scan = scan->next) { GthFileData *file_data = scan->data; if (_g_mime_type_is_image (gth_file_data_get_mime_type (file_data))) { GthImageInfo *image_info; image_info = gth_image_info_new (file_data); if ((current_image != NULL) && g_file_equal (file_data->file, current->file)) gth_image_info_set_image (image_info, current_image); self->priv->images[n++] = image_info; } } self->priv->images[n] = NULL; self->priv->n_images = n; self->priv->event_name = g_strdup (event_name); self->priv->image_width = 0; self->priv->image_height = 0; if (self->priv->n_images == 0) { if (error != NULL) *error = g_error_new_literal (GTH_ERROR, GTH_ERROR_GENERIC, _("No valid file selected.")); g_object_unref (self); return NULL; } self->priv->print_operation = gtk_print_operation_new (); gtk_print_operation_set_allow_async (self->priv->print_operation, TRUE); gtk_print_operation_set_custom_tab_label (self->priv->print_operation, _("Images")); gtk_print_operation_set_embed_page_setup (self->priv->print_operation, TRUE); gtk_print_operation_set_show_progress (self->priv->print_operation, TRUE); g_signal_connect (self->priv->print_operation, "create-custom-widget", G_CALLBACK (operation_create_custom_widget_cb), self); g_signal_connect (self->priv->print_operation, "update-custom-widget", G_CALLBACK (operation_update_custom_widget_cb), self); g_signal_connect (self->priv->print_operation, "custom-widget-apply", G_CALLBACK (operation_custom_widget_apply_cb), self); g_signal_connect (self->priv->print_operation, "begin_print", G_CALLBACK (print_operation_begin_print_cb), self); g_signal_connect (self->priv->print_operation, "draw_page", G_CALLBACK (print_operation_draw_page_cb), self); g_signal_connect (self->priv->print_operation, "done", G_CALLBACK (print_operation_done_cb), self); return self; } static void _gth_image_print_job_set_output_uri (GthImagePrintJob *self, GtkPrintSettings *settings) { char *basename; const char *default_dir; const char *ext; char *path; char *uri; if (self->priv->n_images == 1) basename = _g_path_remove_extension (g_file_info_get_name (self->priv->images[0]->file_data->info)); else basename = g_strdup (_g_file_info_get_edit_name (gth_browser_get_location_data (self->priv->browser)->info)); default_dir = g_get_user_special_dir (G_USER_DIRECTORY_PICTURES); if (default_dir == NULL) default_dir = g_get_home_dir (); ext = gtk_print_settings_get (settings, GTK_PRINT_SETTINGS_OUTPUT_FILE_FORMAT); if (ext == NULL) { gtk_print_settings_set (settings, GTK_PRINT_SETTINGS_OUTPUT_FILE_FORMAT, "pdf"); ext = "pdf"; } path = g_strconcat (default_dir, G_DIR_SEPARATOR_S, basename, ".", ext, NULL); uri = g_filename_to_uri (path, NULL, NULL); if (uri != NULL) gtk_print_settings_set (settings, GTK_PRINT_SETTINGS_OUTPUT_URI, uri); g_free (uri); g_free (path); g_free (basename); } static void load_image_info_task_completed_cb (GthTask *task, GError *error, gpointer user_data) { GthImagePrintJob *self = user_data; int n_loaded_images; GthImageInfo **loaded_images; int i, j; GtkPrintOperationResult result; GFile *file; char *filename; GtkPrintSettings *settings; if (error != NULL) { g_object_unref (self); return; } n_loaded_images = 0; for (i = 0; i < self->priv->n_images; i++) { GthImageInfo *image_info = self->priv->images[i]; if (image_info->thumbnail == NULL) { gth_image_info_unref (self->priv->images[i]); self->priv->images[i] = NULL; } else n_loaded_images += 1; } if (n_loaded_images == 0) { _gtk_error_dialog_show (GTK_WINDOW (self->priv->browser), _("Could not print"), "%s", _("No suitable loader available for this file type")); g_object_unref (self); return; } loaded_images = g_new (GthImageInfo *, n_loaded_images + 1); for (i = 0, j = 0; i < self->priv->n_images; i++) { if (self->priv->images[i] != NULL) { loaded_images[j] = self->priv->images[i]; j += 1; } } loaded_images[j] = NULL; g_free (self->priv->images); self->priv->images = loaded_images; self->priv->n_images = n_loaded_images; file = gth_user_dir_get_file_for_read (GTH_DIR_CONFIG, PIX_DIR, "print_settings", NULL); filename = g_file_get_path (file); settings = gtk_print_settings_new_from_file (filename, NULL); if (settings != NULL) { _gth_image_print_job_set_output_uri (self, settings); gtk_print_operation_set_print_settings (self->priv->print_operation, settings); } g_free (filename); g_object_unref (file); file = gth_user_dir_get_file_for_read (GTH_DIR_CONFIG, PIX_DIR, "page_setup", NULL); filename = g_file_get_path (file); self->priv->page_setup = gtk_page_setup_new_from_file (filename, NULL); if (self->priv->page_setup != NULL) gtk_print_operation_set_default_page_setup (self->priv->print_operation, self->priv->page_setup); g_free (filename); g_object_unref (file); result = gtk_print_operation_run (self->priv->print_operation, self->priv->action, GTK_WINDOW (self->priv->browser), &error); if (result == GTK_PRINT_OPERATION_RESULT_ERROR) { _gtk_error_dialog_from_gerror_show (GTK_WINDOW (self->priv->browser), _("Could not print"), error); g_clear_error (&error); } _g_object_unref (settings); } void gth_image_print_job_run (GthImagePrintJob *self, GtkPrintOperationAction action, GthBrowser *browser) { g_return_if_fail (self->priv->task == NULL); self->priv->action = action; self->priv->browser = browser; self->priv->task = gth_load_image_info_task_new (self->priv->images, self->priv->n_images, self->priv->caption_attributes); g_signal_connect (self->priv->task, "completed", G_CALLBACK (load_image_info_task_completed_cb), self); gth_browser_exec_task (browser, self->priv->task, GTH_TASK_FLAGS_DEFAULT); }