1 /*
2  * Photos - access, organize and share your photos on GNOME
3  * Copyright © 2006 – 2007 The Free Software Foundation
4  * Copyright © 2013 – 2019 Red Hat, Inc.
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 /* Based on code from:
21  *   + Eye of GNOME
22  */
23 
24 
25 #include "config.h"
26 
27 #ifdef HAVE__NL_MEASUREMENT_MEASUREMENT
28 #include <langinfo.h>
29 #endif
30 
31 #include <glib/gi18n.h>
32 #include <glib/gprintf.h>
33 #include <gtk/gtkunixprint.h>
34 
35 #include "photos-gegl.h"
36 #include "photos-print-setup.h"
37 #include "photos-print-preview.h"
38 
39 
40 struct _PhotosPrintSetup
41 {
42   GtkGrid parent_instance;
43   GeglNode *node;
44   GtkPageSetup *page_setup;
45   GtkWidget *left;
46   GtkWidget *right;
47   GtkWidget *top;
48   GtkWidget *bottom;
49   GtkWidget *center;
50   GtkWidget *width;
51   GtkWidget *height;
52   GtkWidget *scaling;
53   GtkWidget *preview;
54   GtkWidget *unit;
55   GtkUnit current_unit;
56 };
57 
58 enum
59 {
60   PROP_0,
61   PROP_NODE,
62   PROP_PAGE_SETUP
63 };
64 
65 
66 G_DEFINE_TYPE (PhotosPrintSetup, photos_print_setup, GTK_TYPE_GRID);
67 
68 
69 enum
70 {
71   CENTER_NONE,
72   CENTER_HORIZONTAL,
73   CENTER_VERTICAL,
74   CENTER_BOTH
75 };
76 
77 enum
78 {
79   CHANGE_HORIZ,
80   CHANGE_VERT
81 };
82 
83 enum
84 {
85   UNIT_INCH,
86   UNIT_MM
87 };
88 
89 #define FACTOR_INCH_TO_MM 25.4
90 #define FACTOR_INCH_TO_PIXEL 72.
91 #define FACTOR_MM_TO_INCH 0.03937007874015748
92 #define FACTOR_MM_TO_PIXEL 2.834645669
93 
94 static void photos_print_setup_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
95 
96 static void on_left_value_changed   (GtkSpinButton *spinbutton, gpointer user_data);
97 static void on_right_value_changed  (GtkSpinButton *spinbutton, gpointer user_data);
98 static void on_top_value_changed    (GtkSpinButton *spinbutton, gpointer user_data);
99 static void on_bottom_value_changed (GtkSpinButton *spinbutton, gpointer user_data);
100 
101 static void on_width_value_changed  (GtkSpinButton *spinbutton, gpointer user_data);
102 static void on_height_value_changed (GtkSpinButton *spinbutton, gpointer user_data);
103 
104 
105 static void
photos_print_setup_block_handlers(PhotosPrintSetup * self)106 photos_print_setup_block_handlers (PhotosPrintSetup *self)
107 {
108   g_signal_handlers_block_by_func (self->left, on_left_value_changed, self);
109   g_signal_handlers_block_by_func (self->right, on_right_value_changed, self);
110   g_signal_handlers_block_by_func (self->width, on_width_value_changed, self);
111   g_signal_handlers_block_by_func (self->top, on_top_value_changed, self);
112   g_signal_handlers_block_by_func (self->bottom, on_bottom_value_changed, self);
113   g_signal_handlers_block_by_func (self->height, on_height_value_changed, self);
114 }
115 
116 
117 static void
photos_print_setup_unblock_handlers(PhotosPrintSetup * self)118 photos_print_setup_unblock_handlers (PhotosPrintSetup *self)
119 {
120   g_signal_handlers_unblock_by_func (self->left, on_left_value_changed, self);
121   g_signal_handlers_unblock_by_func (self->right, on_right_value_changed, self);
122   g_signal_handlers_unblock_by_func (self->width, on_width_value_changed, self);
123   g_signal_handlers_unblock_by_func (self->top, on_top_value_changed, self);
124   g_signal_handlers_unblock_by_func (self->bottom, on_bottom_value_changed, self);
125   g_signal_handlers_unblock_by_func (self->height, on_height_value_changed, self);
126 }
127 
128 
129 static gdouble
get_scale_to_px_factor(PhotosPrintSetup * self)130 get_scale_to_px_factor (PhotosPrintSetup *self)
131 {
132   gdouble factor = 0.;
133 
134   switch (self->current_unit)
135     {
136     case GTK_UNIT_MM:
137       factor = FACTOR_MM_TO_PIXEL;
138       break;
139 
140     case GTK_UNIT_INCH:
141       factor = FACTOR_INCH_TO_PIXEL;
142       break;
143 
144     case GTK_UNIT_NONE:
145     case GTK_UNIT_POINTS:
146     default:
147       g_assert_not_reached ();
148     }
149 
150   return factor;
151 }
152 
153 
154 static gdouble
photos_print_setup_get_max_percentage(PhotosPrintSetup * self)155 photos_print_setup_get_max_percentage (PhotosPrintSetup *self)
156 {
157   GeglRectangle bbox;
158   gdouble height;
159   gdouble page_height;
160   gdouble page_width;
161   gdouble width;
162   gdouble perc;
163 
164   page_width = gtk_page_setup_get_page_width (self->page_setup, GTK_UNIT_INCH);
165   page_height = gtk_page_setup_get_page_height (self->page_setup, GTK_UNIT_INCH);
166   bbox = gegl_node_get_bounding_box (self->node);
167 
168   width  = (gdouble) bbox.width / FACTOR_INCH_TO_PIXEL;
169   height = (gdouble) bbox.height / FACTOR_INCH_TO_PIXEL;
170 
171   if (page_width > width && page_height > height)
172     perc = 1.0;
173   else
174     perc = MIN (page_width / width, page_height / height);
175 
176   return perc;
177 }
178 
179 
180 static void
photos_print_setup_center(gdouble page_width,gdouble width,GtkSpinButton * s_left,GtkSpinButton * s_right)181 photos_print_setup_center (gdouble page_width, gdouble width, GtkSpinButton *s_left, GtkSpinButton *s_right)
182 {
183   gdouble left;
184   gdouble right;
185 
186   left = (page_width - width) / 2;
187   right = page_width - left - width;
188   gtk_spin_button_set_value (s_left, left);
189   gtk_spin_button_set_value (s_right, right);
190 }
191 
192 
193 static void
photos_print_setup_center_changed(GtkComboBox * combobox,gpointer user_data)194 photos_print_setup_center_changed (GtkComboBox *combobox, gpointer user_data)
195 {
196   PhotosPrintSetup *self = PHOTOS_PRINT_SETUP (user_data);
197   gint active;
198 
199   active = gtk_combo_box_get_active (combobox);
200 
201   switch (active)
202     {
203     case CENTER_HORIZONTAL:
204       photos_print_setup_center (gtk_page_setup_get_page_width (self->page_setup, self->current_unit),
205                                  gtk_spin_button_get_value (GTK_SPIN_BUTTON (self->width)),
206                                  GTK_SPIN_BUTTON (self->left),
207                                  GTK_SPIN_BUTTON (self->right));
208       break;
209 
210     case CENTER_VERTICAL:
211       photos_print_setup_center (gtk_page_setup_get_page_height (self->page_setup, self->current_unit),
212                                  gtk_spin_button_get_value (GTK_SPIN_BUTTON (self->height)),
213                                  GTK_SPIN_BUTTON (self->top),
214                                  GTK_SPIN_BUTTON (self->bottom));
215       break;
216 
217     case CENTER_BOTH:
218       photos_print_setup_center (gtk_page_setup_get_page_width (self->page_setup, self->current_unit),
219                                  gtk_spin_button_get_value (GTK_SPIN_BUTTON (self->width)),
220                                  GTK_SPIN_BUTTON (self->left),
221                                  GTK_SPIN_BUTTON (self->right));
222       photos_print_setup_center (gtk_page_setup_get_page_height (self->page_setup, self->current_unit),
223                                  gtk_spin_button_get_value (GTK_SPIN_BUTTON (self->height)),
224                                  GTK_SPIN_BUTTON (self->top),
225                                  GTK_SPIN_BUTTON (self->bottom));
226       break;
227 
228     case CENTER_NONE:
229     default:
230       break;
231     }
232 
233   gtk_combo_box_set_active (combobox, active);
234 }
235 
236 
237 static void
update_image_pos_ranges(PhotosPrintSetup * self,gdouble page_width,gdouble page_height,gdouble width,gdouble height)238 update_image_pos_ranges (PhotosPrintSetup *self,
239 			 gdouble page_width,
240 			 gdouble page_height,
241 			 gdouble width,
242 			 gdouble height)
243 {
244   gtk_spin_button_set_range (GTK_SPIN_BUTTON (self->left), 0, page_width - width);
245   gtk_spin_button_set_range (GTK_SPIN_BUTTON (self->right), 0, page_width - width);
246   gtk_spin_button_set_range (GTK_SPIN_BUTTON (self->top), 0, page_height - height);
247   gtk_spin_button_set_range (GTK_SPIN_BUTTON (self->bottom), 0, page_height - height);
248 }
249 
250 
251 static void
on_scale_changed(GtkRange * range,gpointer user_data)252 on_scale_changed (GtkRange *range, gpointer user_data)
253 {
254   PhotosPrintSetup *self = PHOTOS_PRINT_SETUP (user_data);
255   GeglRectangle bbox;
256   gdouble height;
257   gdouble scale;
258   gdouble width;
259   gdouble left, right, top, bottom;
260   gdouble page_width, page_height;
261   gdouble factor;
262 
263   gtk_combo_box_set_active (GTK_COMBO_BOX (self->center), CENTER_NONE);
264 
265   bbox = gegl_node_get_bounding_box (self->node);
266   factor = get_scale_to_px_factor (self);
267 
268   width = (gdouble) bbox.width / factor;
269   height = (gdouble) bbox.height / factor;
270 
271   left = gtk_spin_button_get_value (GTK_SPIN_BUTTON (self->left));
272   top = gtk_spin_button_get_value (GTK_SPIN_BUTTON (self->top));
273 
274   scale = CLAMP (0.01 * gtk_range_get_value (range), 0, photos_print_setup_get_max_percentage (self));
275 
276   photos_print_preview_set_scale (PHOTOS_PRINT_PREVIEW (self->preview), scale);
277 
278   width  *= scale;
279   height *= scale;
280 
281   page_width = gtk_page_setup_get_page_width (self->page_setup, self->current_unit);
282   page_height = gtk_page_setup_get_page_height (self->page_setup, self->current_unit);
283 
284   update_image_pos_ranges (self, page_width, page_height, width, height);
285 
286   right = page_width - left - width;
287   bottom = page_height - top - height;
288 
289   gtk_spin_button_set_value (GTK_SPIN_BUTTON (self->width), width);
290   gtk_spin_button_set_value (GTK_SPIN_BUTTON (self->height), height);
291   gtk_spin_button_set_value (GTK_SPIN_BUTTON (self->right), right);
292   gtk_spin_button_set_value (GTK_SPIN_BUTTON (self->bottom), bottom);
293 }
294 
295 
296 static gchar *
on_scale_format_value(GtkScale * scale,gdouble value)297 on_scale_format_value (GtkScale *scale, gdouble value)
298 {
299   return g_strdup_printf ("%i%%", (gint) value);
300 }
301 
302 
303 static void
photos_print_setup_position_values_changed(PhotosPrintSetup * self,GtkWidget * w_changed,GtkWidget * w_to_update,GtkWidget * w_size,gdouble total_size,gint change)304 photos_print_setup_position_values_changed (PhotosPrintSetup *self,
305                                             GtkWidget *w_changed,
306                                             GtkWidget *w_to_update,
307                                             GtkWidget *w_size,
308                                             gdouble total_size,
309                                             gint change)
310 {
311   gdouble changed, to_update, size;
312   gdouble pos;
313 
314   size = gtk_spin_button_get_value (GTK_SPIN_BUTTON (w_size));
315   changed = gtk_spin_button_get_value (GTK_SPIN_BUTTON (w_changed));
316 
317   to_update = total_size - changed - size;
318   gtk_spin_button_set_value (GTK_SPIN_BUTTON (w_to_update), to_update);
319   gtk_combo_box_set_active (GTK_COMBO_BOX (self->center), CENTER_NONE);
320 
321   switch (change)
322     {
323     case CHANGE_HORIZ:
324       pos = gtk_spin_button_get_value (GTK_SPIN_BUTTON (self->left));
325       if (self->current_unit == GTK_UNIT_MM)
326         pos *= FACTOR_MM_TO_INCH;
327       photos_print_preview_set_image_position (PHOTOS_PRINT_PREVIEW (self->preview), pos, -1);
328       break;
329 
330     case CHANGE_VERT:
331       pos = gtk_spin_button_get_value (GTK_SPIN_BUTTON (self->top));
332       if (self->current_unit == GTK_UNIT_MM)
333         pos *= FACTOR_MM_TO_INCH;
334       photos_print_preview_set_image_position (PHOTOS_PRINT_PREVIEW (self->preview), -1, pos);
335       break;
336 
337     default:
338       g_assert_not_reached ();
339       break;
340     }
341 }
342 
343 
344 static void
on_left_value_changed(GtkSpinButton * spinbutton,gpointer user_data)345 on_left_value_changed (GtkSpinButton *spinbutton, gpointer user_data)
346 {
347   PhotosPrintSetup *self = PHOTOS_PRINT_SETUP (user_data);
348 
349   photos_print_setup_position_values_changed (self,
350                                               self->left,
351                                               self->right,
352                                               self->width,
353                                               gtk_page_setup_get_page_width (self->page_setup,
354                                                                              self->current_unit),
355                                               CHANGE_HORIZ);
356 }
357 
358 
359 static void
on_right_value_changed(GtkSpinButton * spinbutton,gpointer user_data)360 on_right_value_changed (GtkSpinButton *spinbutton, gpointer user_data)
361 {
362   PhotosPrintSetup *self = PHOTOS_PRINT_SETUP (user_data);
363 
364   photos_print_setup_position_values_changed (self,
365                                               self->right,
366                                               self->left,
367                                               self->width,
368                                               gtk_page_setup_get_page_width (self->page_setup,
369                                                                              self->current_unit),
370                                               CHANGE_HORIZ);
371 }
372 
373 
374 static void
on_top_value_changed(GtkSpinButton * spinbutton,gpointer user_data)375 on_top_value_changed (GtkSpinButton *spinbutton,
376 		      gpointer       user_data)
377 {
378   PhotosPrintSetup *self = PHOTOS_PRINT_SETUP (user_data);
379 
380   photos_print_setup_position_values_changed (self,
381                                               self->top,
382                                               self->bottom,
383                                               self->height,
384                                               gtk_page_setup_get_page_height (self->page_setup,
385                                                                               self->current_unit),
386                                               CHANGE_VERT);
387 }
388 
389 
390 static void
on_bottom_value_changed(GtkSpinButton * spinbutton,gpointer user_data)391 on_bottom_value_changed (GtkSpinButton *spinbutton, gpointer user_data)
392 {
393   PhotosPrintSetup *self = PHOTOS_PRINT_SETUP (user_data);
394 
395   photos_print_setup_position_values_changed (self,
396                                               self->bottom,
397                                               self->top,
398                                               self->height,
399                                               gtk_page_setup_get_page_height (self->page_setup,
400                                                                               self->current_unit),
401                                               CHANGE_VERT);
402 }
403 
404 
405 static void
photos_print_setup_size_changed(PhotosPrintSetup * self,GtkWidget * w_size_x,GtkWidget * w_size_y,GtkWidget * w_margin_x_1,GtkWidget * w_margin_x_2,GtkWidget * w_margin_y_1,GtkWidget * w_margin_y_2,gdouble page_size_x,gdouble page_size_y,gint change)406 photos_print_setup_size_changed (PhotosPrintSetup *self,
407                                  GtkWidget *w_size_x,
408                                  GtkWidget *w_size_y,
409                                  GtkWidget *w_margin_x_1,
410                                  GtkWidget *w_margin_x_2,
411                                  GtkWidget *w_margin_y_1,
412                                  GtkWidget *w_margin_y_2,
413                                  gdouble page_size_x,
414                                  gdouble page_size_y,
415                                  gint change)
416 {
417   GeglRectangle bbox;
418   gdouble margin_x_1, margin_x_2;
419   gdouble margin_y_1, margin_y_2;
420   gdouble orig_size_x = -1, orig_size_y = -1, scale;
421   gdouble size_x, size_y;
422   gdouble factor;
423 
424   size_x = gtk_spin_button_get_value (GTK_SPIN_BUTTON (w_size_x));
425   margin_x_1 = gtk_spin_button_get_value (GTK_SPIN_BUTTON (w_margin_x_1));
426   margin_y_1 = gtk_spin_button_get_value (GTK_SPIN_BUTTON (w_margin_y_1));
427 
428   bbox = gegl_node_get_bounding_box (self->node);
429   factor = get_scale_to_px_factor (self);
430 
431   switch (change)
432     {
433     case CHANGE_HORIZ:
434       orig_size_x = (gdouble) bbox.width / factor;
435       orig_size_y = (gdouble) bbox.height / factor;
436       break;
437 
438     case CHANGE_VERT:
439       orig_size_y = (gdouble) bbox.width / factor;
440       orig_size_x = (gdouble) bbox.height / factor;
441       break;
442 
443     default:
444       g_assert_not_reached ();
445       break;
446     }
447 
448   scale = CLAMP (size_x / orig_size_x, 0, 1);
449 
450   size_y = scale * orig_size_y;
451 
452   margin_x_2 = page_size_x - margin_x_1 - size_x;
453   margin_y_2 = page_size_y - margin_y_1 - size_y;
454 
455   photos_print_preview_set_scale (PHOTOS_PRINT_PREVIEW (self->preview), scale);
456 
457   switch (change)
458     {
459     case CHANGE_HORIZ:
460       update_image_pos_ranges (self, page_size_x, page_size_y, size_x, size_y);
461       break;
462 
463     case CHANGE_VERT:
464       update_image_pos_ranges (self, page_size_y, page_size_x, size_y, size_x);
465       break;
466 
467     default:
468       g_assert_not_reached ();
469       break;
470     }
471 
472   gtk_range_set_value (GTK_RANGE (self->scaling), 100*scale);
473 
474   gtk_spin_button_set_value (GTK_SPIN_BUTTON (w_margin_x_2), margin_x_2);
475   gtk_spin_button_set_value (GTK_SPIN_BUTTON (w_size_y), size_y);
476   gtk_spin_button_set_value (GTK_SPIN_BUTTON (w_margin_y_2), margin_y_2);
477 
478   gtk_combo_box_set_active (GTK_COMBO_BOX (self->center), CENTER_NONE);
479 }
480 
481 static void
on_width_value_changed(GtkSpinButton * spinbutton,gpointer user_data)482 on_width_value_changed (GtkSpinButton *spinbutton, gpointer user_data)
483 {
484   PhotosPrintSetup *self = PHOTOS_PRINT_SETUP (user_data);
485 
486   photos_print_setup_size_changed (self,
487                                    self->width,
488                                    self->height,
489                                    self->left,
490                                    self->right,
491                                    self->top,
492                                    self->bottom,
493                                    gtk_page_setup_get_page_width (self->page_setup, self->current_unit),
494                                    gtk_page_setup_get_page_height (self->page_setup, self->current_unit),
495                                    CHANGE_HORIZ);
496 }
497 
498 
499 static void
on_height_value_changed(GtkSpinButton * spinbutton,gpointer user_data)500 on_height_value_changed (GtkSpinButton *spinbutton, gpointer user_data)
501 {
502   PhotosPrintSetup *self = PHOTOS_PRINT_SETUP (user_data);
503 
504   photos_print_setup_size_changed (self,
505                                    self->height,
506                                    self->width,
507                                    self->top,
508                                    self->bottom,
509                                    self->left,
510                                    self->right,
511                                    gtk_page_setup_get_page_height (self->page_setup, self->current_unit),
512                                    gtk_page_setup_get_page_width (self->page_setup, self->current_unit),
513                                    CHANGE_VERT);
514 }
515 
516 
517 static void
change_unit(GtkSpinButton * spinbutton,gdouble factor,gint digits,gdouble step,gdouble page)518 change_unit (GtkSpinButton *spinbutton, gdouble factor, gint digits, gdouble step, gdouble page)
519 {
520   gdouble value;
521   gdouble range;
522 
523   gtk_spin_button_get_range (spinbutton, NULL, &range);
524   range *= factor;
525 
526   value = gtk_spin_button_get_value (spinbutton);
527   value *= factor;
528 
529   gtk_spin_button_set_range (spinbutton, 0, range);
530   gtk_spin_button_set_value (spinbutton, value);
531   gtk_spin_button_set_digits (spinbutton, digits);
532   gtk_spin_button_set_increments  (spinbutton, step, page);
533 }
534 
535 
536 static void
photos_print_setup_set_scale_unit(PhotosPrintSetup * self,GtkUnit unit)537 photos_print_setup_set_scale_unit (PhotosPrintSetup *self, GtkUnit unit)
538 {
539   gdouble factor;
540   gdouble step, page;
541   gint digits;
542 
543   if (G_UNLIKELY (self->current_unit == unit))
544     return;
545 
546   switch (unit)
547     {
548     case GTK_UNIT_MM:
549       factor = FACTOR_INCH_TO_MM;
550       digits = 0;
551       step = 1;
552       page = 10;
553       break;
554 
555     case GTK_UNIT_INCH:
556       factor = FACTOR_MM_TO_INCH;
557       digits = 2;
558       step = 0.01;
559       page = 0.1;
560       break;
561 
562     case GTK_UNIT_NONE:
563     case GTK_UNIT_POINTS:
564     default:
565       g_assert_not_reached ();
566     }
567 
568   photos_print_setup_block_handlers (self);
569 
570   change_unit (GTK_SPIN_BUTTON (self->width), factor, digits, step, page);
571   change_unit (GTK_SPIN_BUTTON (self->height), factor, digits, step, page);
572   change_unit (GTK_SPIN_BUTTON (self->left), factor, digits, step, page);
573   change_unit (GTK_SPIN_BUTTON (self->right), factor, digits, step, page);
574   change_unit (GTK_SPIN_BUTTON (self->top), factor, digits, step, page);
575   change_unit (GTK_SPIN_BUTTON (self->bottom), factor, digits, step, page);
576 
577   photos_print_setup_unblock_handlers (self);
578 
579   self->current_unit = unit;
580 }
581 
582 
583 static void
on_unit_changed(GtkComboBox * combobox,gpointer user_data)584 on_unit_changed (GtkComboBox *combobox, gpointer user_data)
585 {
586   GtkUnit unit = GTK_UNIT_INCH;
587 
588   switch (gtk_combo_box_get_active (combobox))
589     {
590     case UNIT_INCH:
591       unit = GTK_UNIT_INCH;
592       break;
593 
594     case UNIT_MM:
595       unit = GTK_UNIT_MM;
596       break;
597 
598     default:
599       g_assert_not_reached ();
600     }
601 
602   photos_print_setup_set_scale_unit (PHOTOS_PRINT_SETUP (user_data), unit);
603 }
604 
605 
606 static void
on_preview_pixbuf_moved(PhotosPrintPreview * preview,gpointer user_data)607 on_preview_pixbuf_moved (PhotosPrintPreview *preview, gpointer user_data)
608 {
609   PhotosPrintSetup *self = PHOTOS_PRINT_SETUP (user_data);
610   gdouble x;
611   gdouble y;
612 
613   photos_print_preview_get_image_position (preview, &x, &y);
614 
615   if (self->current_unit == GTK_UNIT_MM)
616     {
617       x *= FACTOR_INCH_TO_MM;
618       y *= FACTOR_INCH_TO_MM;
619     }
620 
621   gtk_spin_button_set_value (GTK_SPIN_BUTTON (self->left), x);
622   gtk_spin_button_set_value (GTK_SPIN_BUTTON (self->top), y);
623 }
624 
625 
626 static gboolean
on_preview_image_scrolled(GtkWidget * widget,GdkEventScroll * event,gpointer user_data)627 on_preview_image_scrolled (GtkWidget *widget, GdkEventScroll *event, gpointer user_data)
628 {
629   PhotosPrintSetup *self = PHOTOS_PRINT_SETUP (user_data);
630   PhotosPrintPreview *preview = PHOTOS_PRINT_PREVIEW (widget);
631   gfloat scale;
632 
633   scale = photos_print_preview_get_scale (preview);
634 
635   if (!photos_print_preview_point_in_image_area (preview, event->x, event->y))
636     return FALSE;
637 
638   switch (event->direction)
639     {
640     case GDK_SCROLL_UP:
641       /* scale up */
642       scale *= 1.1;
643       break;
644 
645     case GDK_SCROLL_DOWN:
646       /* scale down */
647       scale *= 0.9;
648       break;
649 
650     case GDK_SCROLL_LEFT:
651     case GDK_SCROLL_RIGHT:
652     case GDK_SCROLL_SMOOTH:
653     default:
654       return FALSE;
655       break;
656     }
657 
658   gtk_range_set_value (GTK_RANGE (self->scaling), 100*scale);
659 
660   return TRUE;
661 }
662 
663 
664 static gboolean
on_preview_image_key_pressed(GtkWidget * widget,GdkEventKey * event,gpointer user_data)665 on_preview_image_key_pressed (GtkWidget *widget, GdkEventKey *event, gpointer user_data)
666 {
667   PhotosPrintSetup *self = PHOTOS_PRINT_SETUP (user_data);
668   PhotosPrintPreview *preview = PHOTOS_PRINT_PREVIEW (widget);
669   gfloat scale;
670 
671   scale = photos_print_preview_get_scale (preview);
672 
673   switch (event->keyval)
674     {
675     case GDK_KEY_KP_Add:
676     case GDK_KEY_plus:
677       /* scale up */
678       scale *= 1.1;
679       break;
680 
681     case GDK_KEY_KP_Subtract:
682     case GDK_KEY_minus:
683       /* scale down */
684       scale *= 0.9;
685       break;
686 
687     default:
688       return FALSE;
689       break;
690     }
691 
692   gtk_range_set_value (GTK_RANGE (self->scaling), 100 * scale);
693 
694   return TRUE;
695 }
696 
697 
698 /* Function taken from gtkprintunixdialog.c */
699 static GtkWidget *
photos_print_setup_wrap_in_frame(const gchar * label,GtkWidget * child)700 photos_print_setup_wrap_in_frame (const gchar *label, GtkWidget *child)
701 {
702   GtkWidget *frame;
703   GtkWidget *label_widget;
704   gchar *bold_text;
705 
706   label_widget = gtk_label_new ("");
707   gtk_label_set_xalign (GTK_LABEL (label_widget), 0.0);
708   gtk_widget_show (label_widget);
709 
710   bold_text = g_markup_printf_escaped ("<b>%s</b>", label);
711   gtk_label_set_markup (GTK_LABEL (label_widget), bold_text);
712   g_free (bold_text);
713 
714   frame = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
715   gtk_box_pack_start (GTK_BOX (frame), label_widget, FALSE, FALSE, 0);
716 
717   gtk_widget_set_margin_start (child, 12);
718   gtk_widget_set_halign (child, GTK_ALIGN_FILL);
719   gtk_widget_set_valign (child, GTK_ALIGN_FILL);
720 
721   gtk_box_pack_start (GTK_BOX (frame), child, FALSE, FALSE, 0);
722 
723   gtk_widget_show (frame);
724 
725   return frame;
726 }
727 
728 
729 static GtkWidget *
grid_attach_spin_button_with_label(GtkWidget * grid,const gchar * text_label,gint left,gint top)730 grid_attach_spin_button_with_label (GtkWidget *grid, const gchar* text_label, gint left, gint top)
731 {
732   GtkWidget *label;
733   GtkWidget *spin_button;
734 
735   label = gtk_label_new_with_mnemonic (text_label);
736   gtk_label_set_xalign (GTK_LABEL (label), 0.0);
737   spin_button = gtk_spin_button_new_with_range (0, 100, 0.01);
738   gtk_spin_button_set_digits (GTK_SPIN_BUTTON (spin_button), 2);
739   gtk_entry_set_width_chars (GTK_ENTRY (spin_button), 6);
740   gtk_grid_attach (GTK_GRID (grid), label, left, top, 1, 1);
741   gtk_grid_attach_next_to (GTK_GRID (grid), spin_button, label, GTK_POS_RIGHT, 1, 1);
742   gtk_label_set_mnemonic_widget (GTK_LABEL (label), spin_button);
743 
744   return spin_button;
745 }
746 
747 
748 static void
photos_print_setup_set_initial_values(PhotosPrintSetup * self)749 photos_print_setup_set_initial_values (PhotosPrintSetup *self)
750 {
751   GeglRectangle bbox;
752   gdouble page_height;
753   gdouble page_width;
754   gdouble factor;
755   gdouble height;
756   gdouble max_perc;
757   gdouble width;
758 
759   factor = get_scale_to_px_factor (self);
760 
761   bbox = gegl_node_get_bounding_box (self->node);
762   width = (gdouble) bbox.width/factor;
763   height = (gdouble) bbox.height/factor;
764 
765   max_perc = photos_print_setup_get_max_percentage (self);
766 
767   width *= max_perc;
768   height *= max_perc;
769 
770   gtk_range_set_range (GTK_RANGE (self->scaling), 1, 100 * max_perc);
771   gtk_range_set_increments (GTK_RANGE (self->scaling), max_perc, 10 * max_perc);
772   gtk_range_set_value (GTK_RANGE (self->scaling), 100 * max_perc);
773 
774   photos_print_preview_set_scale (PHOTOS_PRINT_PREVIEW (self->preview), max_perc);
775   gtk_spin_button_set_range (GTK_SPIN_BUTTON (self->width), 0, width);
776   gtk_spin_button_set_range (GTK_SPIN_BUTTON (self->height), 0, height);
777   gtk_spin_button_set_value (GTK_SPIN_BUTTON (self->width), width);
778   gtk_spin_button_set_value (GTK_SPIN_BUTTON (self->height), height);
779 
780   gtk_combo_box_set_active (GTK_COMBO_BOX (self->center), CENTER_BOTH);
781 
782   photos_print_setup_center (gtk_page_setup_get_page_width (self->page_setup, self->current_unit),
783                              gtk_spin_button_get_value (GTK_SPIN_BUTTON (self->width)),
784                              GTK_SPIN_BUTTON (self->left), GTK_SPIN_BUTTON (self->right));
785   photos_print_setup_center (gtk_page_setup_get_page_height (self->page_setup, self->current_unit),
786                              gtk_spin_button_get_value (GTK_SPIN_BUTTON (self->height)),
787                              GTK_SPIN_BUTTON (self->top), GTK_SPIN_BUTTON (self->bottom));
788 
789   page_width = gtk_page_setup_get_page_width (self->page_setup, self->current_unit);
790   page_height = gtk_page_setup_get_page_height (self->page_setup, self->current_unit);
791 
792   update_image_pos_ranges (self, page_width, page_height, width, height);
793 }
794 
795 
796 static void
photos_print_setup_constructed(GObject * object)797 photos_print_setup_constructed (GObject *object)
798 {
799   PhotosPrintSetup *self = PHOTOS_PRINT_SETUP (object);
800 
801   G_OBJECT_CLASS (photos_print_setup_parent_class)->constructed (object);
802 
803   photos_print_setup_set_initial_values (self);
804   photos_print_preview_set_from_page_setup (PHOTOS_PRINT_PREVIEW (self->preview), self->page_setup);
805 
806   g_signal_connect (self->left, "value-changed", G_CALLBACK (on_left_value_changed), self);
807   g_signal_connect (self->right, "value-changed", G_CALLBACK (on_right_value_changed), self);
808   g_signal_connect (self->top, "value-changed", G_CALLBACK (on_top_value_changed), self);
809   g_signal_connect (self->bottom, "value-changed", G_CALLBACK (on_bottom_value_changed), self);
810   g_signal_connect (self->width, "value-changed", G_CALLBACK (on_width_value_changed), self);
811   g_signal_connect (self->height, "value-changed", G_CALLBACK (on_height_value_changed), self);
812   g_signal_connect (self->scaling, "value-changed", G_CALLBACK (on_scale_changed), self);
813   g_signal_connect (self->scaling, "format-value", G_CALLBACK (on_scale_format_value), NULL);
814   g_signal_connect (self->preview, "pixbuf-moved", G_CALLBACK (on_preview_pixbuf_moved), self);
815   g_signal_connect (self->preview, "scroll-event", G_CALLBACK (on_preview_image_scrolled), self);
816   g_signal_connect (self->preview, "key-press-event", G_CALLBACK (on_preview_image_key_pressed), self);
817 }
818 
819 
820 static void
photos_print_setup_dispose(GObject * object)821 photos_print_setup_dispose (GObject *object)
822 {
823   PhotosPrintSetup *self = PHOTOS_PRINT_SETUP (object);
824 
825   g_clear_object (&self->node);
826   g_clear_object (&self->page_setup);
827 
828   G_OBJECT_CLASS (photos_print_setup_parent_class)->dispose (object);
829 }
830 
831 
832 static void
photos_print_setup_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)833 photos_print_setup_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
834 {
835   PhotosPrintSetup *self = PHOTOS_PRINT_SETUP (object);
836 
837   switch (prop_id)
838     {
839     case PROP_NODE:
840       {
841         GdkPixbuf *pixbuf;
842 
843         self->node = GEGL_NODE (g_value_dup_object (value));
844         pixbuf = photos_gegl_create_pixbuf_from_node (self->node);
845         if (pixbuf != NULL)
846           {
847             g_object_set (self->preview, "pixbuf", pixbuf, NULL);
848             g_object_unref (pixbuf);
849           }
850       }
851       break;
852 
853     case PROP_PAGE_SETUP:
854       self->page_setup = GTK_PAGE_SETUP (g_value_dup_object (value));
855       break;
856 
857     default:
858       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
859     }
860 }
861 
862 
863 static void
photos_print_setup_init(PhotosPrintSetup * self)864 photos_print_setup_init (PhotosPrintSetup *self)
865 {
866   GtkWidget *frame;
867   GtkWidget *grid;
868   GtkWidget *label;
869   GtkWidget *hscale;
870   GtkWidget *combobox;
871 
872 #ifdef HAVE__NL_MEASUREMENT_MEASUREMENT
873   gchar *locale_scale = NULL;
874 #endif
875 
876   gtk_container_set_border_width (GTK_CONTAINER (self), 12);
877   gtk_orientable_set_orientation (GTK_ORIENTABLE (self), GTK_ORIENTATION_VERTICAL);
878   gtk_grid_set_column_spacing (GTK_GRID (self), 18);
879   gtk_grid_set_row_spacing (GTK_GRID (self), 18);
880 
881   grid = gtk_grid_new ();
882   gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
883   gtk_grid_set_column_spacing (GTK_GRID (grid), 12);
884   frame = photos_print_setup_wrap_in_frame (_("Position"), grid);
885   gtk_grid_attach (GTK_GRID (self), frame, 0, 0, 1, 1);
886 
887   self->left = grid_attach_spin_button_with_label (grid, _("_Left:"), 0, 0);
888   self->right = grid_attach_spin_button_with_label (grid,_("_Right:"), 0, 1);
889   self->top = grid_attach_spin_button_with_label (grid, _("_Top:"), 2, 0);
890   self->bottom = grid_attach_spin_button_with_label (grid, _("_Bottom:"), 2, 1);
891 
892   label = gtk_label_new_with_mnemonic (_("C_enter:"));
893   gtk_label_set_xalign (GTK_LABEL (label), 0.0);
894 
895   combobox = gtk_combo_box_text_new ();
896   gtk_combo_box_text_insert_text (GTK_COMBO_BOX_TEXT (combobox), CENTER_NONE, _("None"));
897   gtk_combo_box_text_insert_text (GTK_COMBO_BOX_TEXT (combobox), CENTER_HORIZONTAL, _("Horizontal"));
898   gtk_combo_box_text_insert_text (GTK_COMBO_BOX_TEXT (combobox), CENTER_VERTICAL, _("Vertical"));
899   gtk_combo_box_text_insert_text (GTK_COMBO_BOX_TEXT (combobox), CENTER_BOTH, _("Both"));
900   gtk_combo_box_set_active (GTK_COMBO_BOX (combobox), CENTER_NONE);
901   /* Attach combobox below right margin spinbutton and span until end */
902   gtk_grid_attach_next_to (GTK_GRID (grid), combobox, self->right, GTK_POS_BOTTOM, 3, 1);
903   /* Attach the label to the left of the combobox */
904   gtk_grid_attach_next_to (GTK_GRID (grid), label, combobox, GTK_POS_LEFT, 1, 1);
905   gtk_label_set_mnemonic_widget (GTK_LABEL (label), combobox);
906   self->center = combobox;
907   g_signal_connect (G_OBJECT (combobox), "changed", G_CALLBACK (photos_print_setup_center_changed), self);
908 
909   grid = gtk_grid_new ();
910   gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
911   gtk_grid_set_column_spacing (GTK_GRID (grid), 12);
912   frame = photos_print_setup_wrap_in_frame (_("Size"), grid);
913   gtk_grid_attach (GTK_GRID (self), frame, 0, 1, 1, 1);
914 
915   self->width = grid_attach_spin_button_with_label (grid, _("_Width:"), 0, 0);
916   self->height = grid_attach_spin_button_with_label (grid, _("_Height:"), 2, 0);
917 
918   label = gtk_label_new_with_mnemonic (_("_Scaling:"));
919   hscale = gtk_scale_new_with_range (GTK_ORIENTATION_HORIZONTAL, 1, 100, 1);
920   gtk_scale_set_value_pos (GTK_SCALE (hscale), GTK_POS_RIGHT);
921   gtk_range_set_value (GTK_RANGE (hscale), 100);
922   gtk_grid_attach_next_to (GTK_GRID (grid), hscale, self->width, GTK_POS_BOTTOM, 3, 1);
923   gtk_grid_attach_next_to (GTK_GRID (grid), label, hscale, GTK_POS_LEFT, 1, 1);
924   gtk_label_set_mnemonic_widget (GTK_LABEL (label), hscale);
925   self->scaling = hscale;
926 
927   label = gtk_label_new_with_mnemonic (_("_Unit:"));
928   gtk_label_set_xalign (GTK_LABEL (label), 0.0);
929 
930   combobox = gtk_combo_box_text_new ();
931   gtk_combo_box_text_insert_text (GTK_COMBO_BOX_TEXT (combobox), UNIT_MM, _("Millimeters"));
932   gtk_combo_box_text_insert_text (GTK_COMBO_BOX_TEXT (combobox), UNIT_INCH, _("Inches"));
933 
934 #ifdef HAVE__NL_MEASUREMENT_MEASUREMENT
935   locale_scale = nl_langinfo (_NL_MEASUREMENT_MEASUREMENT);
936   if (locale_scale && locale_scale[0] == 2)
937     {
938       gtk_combo_box_set_active (GTK_COMBO_BOX (combobox), UNIT_INCH);
939       photos_print_setup_set_scale_unit (self, GTK_UNIT_INCH);
940     }
941   else
942 #endif
943     {
944       gtk_combo_box_set_active (GTK_COMBO_BOX (combobox), UNIT_MM);
945       photos_print_setup_set_scale_unit (self, GTK_UNIT_MM);
946     }
947 
948   gtk_grid_attach_next_to (GTK_GRID (grid), combobox, hscale, GTK_POS_BOTTOM, 3, 1);
949   gtk_grid_attach_next_to (GTK_GRID (grid), label, combobox, GTK_POS_LEFT, 1, 1);
950 
951   gtk_label_set_mnemonic_widget (GTK_LABEL (label), combobox);
952   self->unit = combobox;
953   g_signal_connect (G_OBJECT (combobox), "changed", G_CALLBACK (on_unit_changed), self);
954 
955   self->preview = photos_print_preview_new ();
956 
957   /* FIXME: This shouldn't be set by hand */
958   gtk_widget_set_size_request (self->preview, 250, 250);
959 
960   frame = photos_print_setup_wrap_in_frame (_("Preview"), self->preview);
961   /* The preview widget needs to span the whole grid height */
962   gtk_grid_attach (GTK_GRID (self), frame, 1, 0, 1, 2);
963 
964   gtk_widget_show_all (GTK_WIDGET (self));
965 }
966 
967 
968 static void
photos_print_setup_class_init(PhotosPrintSetupClass * class)969 photos_print_setup_class_init (PhotosPrintSetupClass *class)
970 {
971   GObjectClass *object_class = G_OBJECT_CLASS (class);
972 
973   object_class->constructed = photos_print_setup_constructed;
974   object_class->dispose = photos_print_setup_dispose;
975   object_class->set_property = photos_print_setup_set_property;
976 
977   g_object_class_install_property (object_class,
978                                    PROP_NODE,
979                                    g_param_spec_object ("node",
980                                                         "GeglNode object",
981                                                         "The node corresponding to the item whose printing "
982                                                         "properties will be set up",
983                                                         GEGL_TYPE_NODE,
984                                                         G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE));
985 
986   g_object_class_install_property (object_class,
987                                    PROP_PAGE_SETUP,
988                                    g_param_spec_object ("page-setup",
989                                                         "GtkPageSetup object",
990                                                         "The information for the page where the item will be "
991                                                         "printed",
992                                                         GTK_TYPE_PAGE_SETUP,
993                                                         G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE));
994 }
995 
996 
997 GtkWidget *
photos_print_setup_new(GeglNode * node,GtkPageSetup * page_setup)998 photos_print_setup_new (GeglNode *node, GtkPageSetup *page_setup)
999 {
1000   return g_object_new (PHOTOS_TYPE_PRINT_SETUP, "node", node, "page-setup", page_setup, NULL);
1001 }
1002 
1003 
1004 void
photos_print_setup_get_options(PhotosPrintSetup * self,gdouble * left,gdouble * top,gdouble * scale,GtkUnit * unit)1005 photos_print_setup_get_options (PhotosPrintSetup *self,
1006                                 gdouble *left,
1007                                 gdouble *top,
1008                                 gdouble *scale,
1009                                 GtkUnit *unit)
1010 {
1011   *left = gtk_spin_button_get_value (GTK_SPIN_BUTTON (self->left));
1012   *top = gtk_spin_button_get_value (GTK_SPIN_BUTTON (self->top));
1013   *scale = gtk_range_get_value (GTK_RANGE (self->scaling));
1014   *unit = self->current_unit;
1015 }
1016 
1017 
1018 void
photos_print_setup_update(PhotosPrintSetup * self,GtkPageSetup * page_setup)1019 photos_print_setup_update (PhotosPrintSetup *self, GtkPageSetup *page_setup)
1020 {
1021   gdouble pos_x;
1022   gdouble pos_y;
1023 
1024   self->page_setup = gtk_page_setup_copy (page_setup);
1025 
1026   photos_print_setup_set_initial_values (PHOTOS_PRINT_SETUP (self));
1027 
1028   photos_print_preview_set_from_page_setup (PHOTOS_PRINT_PREVIEW (self->preview), self->page_setup);
1029 
1030   pos_x = gtk_spin_button_get_value (GTK_SPIN_BUTTON (self->left));
1031   pos_y = gtk_spin_button_get_value (GTK_SPIN_BUTTON (self->top));
1032   if (self->current_unit == GTK_UNIT_MM)
1033     {
1034       pos_x *= FACTOR_MM_TO_INCH;
1035       pos_y *= FACTOR_MM_TO_INCH;
1036     }
1037   photos_print_preview_set_image_position (PHOTOS_PRINT_PREVIEW (self->preview), pos_x, pos_y);
1038 }
1039