/* -*- 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);
}