1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
16  */
17 
18 #include "config.h"
19 
20 #include <libgimp/gimp.h>
21 #include <libgimp/gimpui.h>
22 
23 #include "print-preview.h"
24 
25 
26 enum
27 {
28   OFFSETS_CHANGED,
29   LAST_SIGNAL
30 };
31 
32 
33 #define SIZE_REQUEST  200
34 
35 
36 struct _PrintPreview
37 {
38   GtkEventBox      parent_instance;
39 
40   GdkCursor       *cursor;
41 
42   GtkPageSetup    *page;
43   cairo_surface_t *thumbnail;
44   gboolean         dragging;
45   gboolean         inside;
46 
47   gint32           drawable_id;
48 
49   gdouble          image_offset_x;
50   gdouble          image_offset_y;
51   gdouble          image_offset_x_max;
52   gdouble          image_offset_y_max;
53   gdouble          image_width;
54   gdouble          image_height;
55 
56   gboolean         use_full_page;
57 
58   /* for mouse drags */
59   gdouble          orig_offset_x;
60   gdouble          orig_offset_y;
61   gint             start_x;
62   gint             start_y;
63 };
64 
65 struct _PrintPreviewClass
66 {
67   GtkEventBoxClass  parent_class;
68 
69   void (* offsets_changed)  (PrintPreview *print_preview,
70                              gint          offset_x,
71                              gint          offset_y);
72 };
73 
74 
75 static void      print_preview_finalize             (GObject          *object);
76 
77 static void      print_preview_realize              (GtkWidget        *widget);
78 static void      print_preview_unrealize            (GtkWidget        *widget);
79 static void      print_preview_size_request         (GtkWidget        *widget,
80                                                      GtkRequisition   *requisition);
81 static void      print_preview_size_allocate        (GtkWidget        *widget,
82                                                      GtkAllocation    *allocation);
83 static gboolean  print_preview_expose_event         (GtkWidget        *widget,
84                                                      GdkEventExpose   *event);
85 static gboolean  print_preview_button_press_event   (GtkWidget        *widget,
86                                                      GdkEventButton   *event);
87 static gboolean  print_preview_button_release_event (GtkWidget        *widget,
88                                                      GdkEventButton   *event);
89 static gboolean  print_preview_motion_notify_event  (GtkWidget        *widget,
90                                                      GdkEventMotion   *event);
91 static gboolean  print_preview_leave_notify_event   (GtkWidget        *widget,
92                                                      GdkEventCrossing *event);
93 
94 static gboolean  print_preview_is_inside            (PrintPreview     *preview,
95                                                      gdouble           x,
96                                                      gdouble           y);
97 static void      print_preview_set_inside           (PrintPreview     *preview,
98                                                      gboolean          inside);
99 
100 static gdouble   print_preview_get_scale            (PrintPreview     *preview);
101 
102 static void      print_preview_get_page_size        (PrintPreview     *preview,
103                                                      gdouble          *paper_width,
104                                                      gdouble          *paper_height);
105 static void      print_preview_get_page_margins     (PrintPreview     *preview,
106                                                      gdouble          *left_margin,
107                                                      gdouble          *right_margin,
108                                                      gdouble          *top_margin,
109                                                      gdouble          *bottom_margin);
110 static cairo_surface_t * print_preview_get_thumbnail (gint32           drawable_id,
111                                                       gint             width,
112                                                       gint             height);
113 
114 
115 G_DEFINE_TYPE (PrintPreview, print_preview, GTK_TYPE_EVENT_BOX)
116 
117 #define parent_class print_preview_parent_class
118 
119 static guint print_preview_signals[LAST_SIGNAL] = { 0 };
120 
121 
122 #define g_marshal_value_peek_double(v)   (v)->data[0].v_double
123 
124 static void
marshal_VOID__DOUBLE_DOUBLE(GClosure * closure,GValue * return_value,guint n_param_values,const GValue * param_values,gpointer invocation_hint,gpointer marshal_data)125 marshal_VOID__DOUBLE_DOUBLE (GClosure     *closure,
126                              GValue       *return_value,
127                              guint         n_param_values,
128                              const GValue *param_values,
129                              gpointer      invocation_hint,
130                              gpointer      marshal_data)
131 {
132   typedef void (*GMarshalFunc_VOID__DOUBLE_DOUBLE) (gpointer     data1,
133                                                     gdouble      arg_1,
134                                                     gdouble      arg_2,
135                                                     gpointer     data2);
136   register GMarshalFunc_VOID__DOUBLE_DOUBLE callback;
137   register GCClosure *cc = (GCClosure*) closure;
138   register gpointer data1, data2;
139 
140   g_return_if_fail (n_param_values == 3);
141 
142   if (G_CCLOSURE_SWAP_DATA (closure))
143     {
144       data1 = closure->data;
145       data2 = g_value_peek_pointer (param_values + 0);
146     }
147   else
148     {
149       data1 = g_value_peek_pointer (param_values + 0);
150       data2 = closure->data;
151     }
152 
153   callback = (GMarshalFunc_VOID__DOUBLE_DOUBLE) (marshal_data ?
154                                                  marshal_data : cc->callback);
155 
156   callback (data1,
157             g_marshal_value_peek_double (param_values + 1),
158             g_marshal_value_peek_double (param_values + 2),
159             data2);
160 }
161 
162 static void
print_preview_class_init(PrintPreviewClass * klass)163 print_preview_class_init (PrintPreviewClass *klass)
164 {
165   GObjectClass   *object_class = G_OBJECT_CLASS (klass);
166   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
167 
168   print_preview_signals[OFFSETS_CHANGED] =
169     g_signal_new ("offsets-changed",
170                   G_TYPE_FROM_CLASS (klass),
171                   G_SIGNAL_RUN_FIRST,
172                   G_STRUCT_OFFSET (PrintPreviewClass, offsets_changed),
173                   NULL, NULL,
174                   marshal_VOID__DOUBLE_DOUBLE,
175                   G_TYPE_NONE, 2,
176                   G_TYPE_DOUBLE,
177                   G_TYPE_DOUBLE);
178 
179   object_class->finalize             = print_preview_finalize;
180 
181   widget_class->realize              = print_preview_realize;
182   widget_class->unrealize            = print_preview_unrealize;
183   widget_class->size_request         = print_preview_size_request;
184   widget_class->size_allocate        = print_preview_size_allocate;
185   widget_class->expose_event         = print_preview_expose_event;
186   widget_class->button_press_event   = print_preview_button_press_event;
187   widget_class->button_release_event = print_preview_button_release_event;
188   widget_class->motion_notify_event  = print_preview_motion_notify_event;
189   widget_class->leave_notify_event   = print_preview_leave_notify_event;
190 
191   klass->offsets_changed = NULL;
192 }
193 
194 static void
print_preview_init(PrintPreview * preview)195 print_preview_init (PrintPreview *preview)
196 {
197   gtk_event_box_set_visible_window (GTK_EVENT_BOX (preview), FALSE);
198 
199   gtk_widget_add_events (GTK_WIDGET (preview),
200                          GDK_BUTTON_PRESS_MASK   |
201                          GDK_BUTTON_RELEASE_MASK |
202                          GDK_POINTER_MOTION_MASK);
203 }
204 
205 
206 static void
print_preview_finalize(GObject * object)207 print_preview_finalize (GObject *object)
208 {
209   PrintPreview *preview = PRINT_PREVIEW (object);
210 
211   if (preview->thumbnail)
212     {
213       cairo_surface_destroy (preview->thumbnail);
214       preview->thumbnail = NULL;
215     }
216 
217   if (preview->page)
218     {
219       g_object_unref (preview->page);
220       preview->page = NULL;
221     }
222 
223   G_OBJECT_CLASS (print_preview_parent_class)->finalize (object);
224 }
225 
226 static void
print_preview_realize(GtkWidget * widget)227 print_preview_realize (GtkWidget *widget)
228 {
229   PrintPreview *preview = PRINT_PREVIEW (widget);
230 
231   GTK_WIDGET_CLASS (print_preview_parent_class)->realize (widget);
232 
233   preview->cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
234                                                 GDK_HAND1);
235 }
236 
237 static void
print_preview_unrealize(GtkWidget * widget)238 print_preview_unrealize (GtkWidget *widget)
239 {
240   PrintPreview *preview = PRINT_PREVIEW (widget);
241 
242   if (preview->cursor)
243     gdk_cursor_unref (preview->cursor);
244 
245   GTK_WIDGET_CLASS (print_preview_parent_class)->unrealize (widget);
246 }
247 
248 static void
print_preview_size_request(GtkWidget * widget,GtkRequisition * requisition)249 print_preview_size_request (GtkWidget      *widget,
250                             GtkRequisition *requisition)
251 {
252   PrintPreview *preview = PRINT_PREVIEW (widget);
253   gdouble       paper_width;
254   gdouble       paper_height;
255   gint          border;
256 
257   border = gtk_container_get_border_width (GTK_CONTAINER (widget)) + 1;
258 
259   print_preview_get_page_size (preview, &paper_width, &paper_height);
260 
261   if (paper_width > paper_height)
262     {
263       requisition->height = SIZE_REQUEST;
264       requisition->width  = paper_width * SIZE_REQUEST / paper_height;
265       requisition->width  = MIN (requisition->width, 2 * SIZE_REQUEST);
266     }
267   else
268     {
269       requisition->width  = SIZE_REQUEST;
270       requisition->height = paper_height * SIZE_REQUEST / paper_width;
271       requisition->height = MIN (requisition->height, 2 * SIZE_REQUEST);
272     }
273 
274   requisition->width  += 2 * border;
275   requisition->height += 2 * border;
276 }
277 
278 static void
print_preview_size_allocate(GtkWidget * widget,GtkAllocation * allocation)279 print_preview_size_allocate (GtkWidget     *widget,
280                              GtkAllocation *allocation)
281 {
282   PrintPreview *preview = PRINT_PREVIEW (widget);
283 
284   GTK_WIDGET_CLASS (print_preview_parent_class)->size_allocate (widget,
285                                                                 allocation);
286 
287   if (preview->thumbnail)
288     {
289       cairo_surface_destroy (preview->thumbnail);
290       preview->thumbnail = NULL;
291     }
292 }
293 
294 static gboolean
print_preview_button_press_event(GtkWidget * widget,GdkEventButton * event)295 print_preview_button_press_event (GtkWidget      *widget,
296                                   GdkEventButton *event)
297 {
298   PrintPreview *preview = PRINT_PREVIEW (widget);
299 
300   if (event->type == GDK_BUTTON_PRESS && event->button == 1 && preview->inside)
301     {
302       GdkCursor *cursor;
303 
304       cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
305                                            GDK_FLEUR);
306 
307       if (gdk_pointer_grab (event->window, FALSE,
308                             (GDK_BUTTON1_MOTION_MASK |
309                              GDK_BUTTON_RELEASE_MASK),
310                             NULL, cursor, event->time) == GDK_GRAB_SUCCESS)
311         {
312           preview->orig_offset_x = preview->image_offset_x;
313           preview->orig_offset_y = preview->image_offset_y;
314 
315           preview->start_x = event->x;
316           preview->start_y = event->y;
317 
318           preview->dragging = TRUE;
319         }
320 
321       gdk_cursor_unref (cursor);
322     }
323 
324   return FALSE;
325 }
326 
327 static gboolean
print_preview_button_release_event(GtkWidget * widget,GdkEventButton * event)328 print_preview_button_release_event (GtkWidget      *widget,
329                                     GdkEventButton *event)
330 {
331   PrintPreview *preview = PRINT_PREVIEW (widget);
332 
333   if (preview->dragging)
334     {
335       gdk_display_pointer_ungrab (gtk_widget_get_display (widget),
336                                   event->time);
337       preview->dragging = FALSE;
338 
339       print_preview_set_inside (preview,
340                                 print_preview_is_inside (preview,
341                                                          event->x, event->y));
342     }
343 
344   return FALSE;
345 }
346 
347 static gboolean
print_preview_motion_notify_event(GtkWidget * widget,GdkEventMotion * event)348 print_preview_motion_notify_event (GtkWidget      *widget,
349                                    GdkEventMotion *event)
350 {
351   PrintPreview *preview = PRINT_PREVIEW (widget);
352 
353   if (preview->dragging)
354     {
355       gdouble scale = print_preview_get_scale (preview);
356       gdouble offset_x;
357       gdouble offset_y;
358 
359       offset_x = (preview->orig_offset_x +
360                   (event->x - preview->start_x) / scale);
361       offset_y = (preview->orig_offset_y +
362                   (event->y - preview->start_y) / scale);
363 
364       offset_x = CLAMP (offset_x, 0, preview->image_offset_x_max);
365       offset_y = CLAMP (offset_y, 0, preview->image_offset_y_max);
366 
367       if (preview->image_offset_x != offset_x ||
368           preview->image_offset_y != offset_y)
369         {
370           print_preview_set_image_offsets (preview, offset_x, offset_y);
371 
372           g_signal_emit (preview,
373                          print_preview_signals[OFFSETS_CHANGED], 0,
374                          preview->image_offset_x,
375                          preview->image_offset_y);
376         }
377     }
378   else
379     {
380       print_preview_set_inside (preview,
381                                 print_preview_is_inside (preview,
382                                                          event->x, event->y));
383     }
384 
385   return FALSE;
386 }
387 
388 static gboolean
print_preview_leave_notify_event(GtkWidget * widget,GdkEventCrossing * event)389 print_preview_leave_notify_event (GtkWidget        *widget,
390                                   GdkEventCrossing *event)
391 {
392   PrintPreview *preview = PRINT_PREVIEW (widget);
393 
394   if (event->mode == GDK_CROSSING_NORMAL)
395     print_preview_set_inside (preview, FALSE);
396 
397   return FALSE;
398 }
399 
400 static gboolean
print_preview_expose_event(GtkWidget * widget,GdkEventExpose * event)401 print_preview_expose_event (GtkWidget      *widget,
402                             GdkEventExpose *event)
403 {
404   PrintPreview  *preview = PRINT_PREVIEW (widget);
405   GtkStyle      *style   = gtk_widget_get_style (widget);
406   GtkAllocation  allocation;
407   cairo_t       *cr;
408   gdouble        paper_width;
409   gdouble        paper_height;
410   gdouble        left_margin;
411   gdouble        right_margin;
412   gdouble        top_margin;
413   gdouble        bottom_margin;
414   gdouble        scale;
415   gint           border;
416 
417   gtk_widget_get_allocation (widget, &allocation);
418 
419   border = gtk_container_get_border_width (GTK_CONTAINER (widget)) + 1;
420 
421   print_preview_get_page_size (preview, &paper_width, &paper_height);
422   print_preview_get_page_margins (preview,
423                                   &left_margin, &right_margin,
424                                   &top_margin,  &bottom_margin);
425 
426   scale = print_preview_get_scale (preview);
427 
428   cr = gdk_cairo_create (gtk_widget_get_window (widget));
429 
430   cairo_translate (cr,
431                    allocation.x + border,
432                    allocation.y + border);
433 
434   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
435     {
436       gint width = allocation.width - 2 * border;
437 
438       cairo_translate (cr, width - scale * paper_width, 0);
439     }
440 
441   cairo_set_line_width (cr, 1.0);
442 
443   /* draw page background */
444   cairo_rectangle (cr, 0, 0, scale * paper_width, scale * paper_height);
445 
446   gdk_cairo_set_source_color (cr, &style->black);
447   cairo_stroke_preserve (cr);
448 
449   gdk_cairo_set_source_color (cr, &style->white);
450   cairo_fill (cr);
451 
452   /* draw page_margins */
453   cairo_rectangle (cr,
454                    scale * left_margin,
455                    scale * top_margin,
456                    scale * (paper_width - left_margin - right_margin),
457                    scale * (paper_height - top_margin - bottom_margin));
458 
459   gdk_cairo_set_source_color (cr, &style->mid[gtk_widget_get_state (widget)]);
460   cairo_stroke (cr);
461 
462   cairo_translate (cr,
463                    scale * (left_margin + preview->image_offset_x),
464                    scale * (top_margin  + preview->image_offset_y));
465 
466   if (preview->dragging || preview->inside)
467     {
468       cairo_rectangle (cr,
469                        0, 0,
470                        scale * preview->image_width,
471                        scale * preview->image_height);
472 
473       gdk_cairo_set_source_color (cr, &style->black);
474       cairo_stroke (cr);
475     }
476 
477   if (preview->thumbnail == NULL &&
478       gimp_item_is_valid (preview->drawable_id))
479     {
480       preview->thumbnail =
481         print_preview_get_thumbnail (preview->drawable_id,
482                                      MIN (allocation.width,  1024),
483                                      MIN (allocation.height, 1024));
484     }
485 
486   if (preview->thumbnail != NULL)
487     {
488       gdouble scale_x;
489       gdouble scale_y;
490 
491       scale_x = (preview->image_width  /
492                  cairo_image_surface_get_width (preview->thumbnail));
493       scale_y = (preview->image_height /
494                  cairo_image_surface_get_height (preview->thumbnail));
495 
496       cairo_rectangle (cr, 0, 0, preview->image_width, preview->image_height);
497 
498       cairo_scale (cr, scale_x * scale, scale_y * scale);
499 
500       cairo_set_source_surface (cr, preview->thumbnail, 0, 0);
501       cairo_fill (cr);
502     }
503 
504   cairo_destroy (cr);
505 
506   return FALSE;
507 }
508 
509 /**
510  * print_preview_new:
511  * @page: page setup
512  * @drawable_id: the drawable to print
513  *
514  * Creates a new #PrintPreview widget.
515  *
516  * Return value: the new #PrintPreview widget.
517  **/
518 GtkWidget *
print_preview_new(GtkPageSetup * page,gint32 drawable_id)519 print_preview_new (GtkPageSetup *page,
520                    gint32        drawable_id)
521 {
522   PrintPreview *preview;
523 
524   g_return_val_if_fail (GTK_IS_PAGE_SETUP (page), NULL);
525 
526   preview = g_object_new (PRINT_TYPE_PREVIEW, NULL);
527 
528   preview->drawable_id = drawable_id;
529 
530   print_preview_set_page_setup (preview, page);
531 
532   return GTK_WIDGET (preview);
533 }
534 
535 /**
536  * print_preview_set_image_dpi:
537  * @preview: a #PrintPreview.
538  * @xres: the X resolution
539  * @yres: the Y resolution
540  *
541  * Sets the resolution of the image/drawable displayed by the
542  * #PrintPreview.
543  **/
544 void
print_preview_set_image_dpi(PrintPreview * preview,gdouble xres,gdouble yres)545 print_preview_set_image_dpi (PrintPreview *preview,
546                              gdouble       xres,
547                              gdouble       yres)
548 {
549   gdouble width;
550   gdouble height;
551 
552   g_return_if_fail (PRINT_IS_PREVIEW (preview));
553   g_return_if_fail (xres > 0.0 && yres > 0.0);
554 
555   width  = gimp_drawable_width  (preview->drawable_id) * 72.0 / xres;
556   height = gimp_drawable_height (preview->drawable_id) * 72.0 / yres;
557 
558   if (width != preview->image_width || height != preview->image_height)
559     {
560       preview->image_width  = width;
561       preview->image_height = height;
562 
563       gtk_widget_queue_draw (GTK_WIDGET (preview));
564     }
565 }
566 
567 /**
568  * print_preview_set_page_setup:
569  * @preview: a #PrintPreview.
570  * @page: the page setup to use
571  *
572  * Sets the page setup to use by the #PrintPreview.
573  **/
574 void
print_preview_set_page_setup(PrintPreview * preview,GtkPageSetup * page)575 print_preview_set_page_setup (PrintPreview *preview,
576                               GtkPageSetup *page)
577 {
578   g_return_if_fail (PRINT_IS_PREVIEW (preview));
579   g_return_if_fail (GTK_IS_PAGE_SETUP (page));
580 
581   if (preview->page)
582     g_object_unref (preview->page);
583 
584   preview->page = gtk_page_setup_copy (page);
585 
586   gtk_widget_queue_resize (GTK_WIDGET (preview));
587 }
588 
589 /**
590  * print_preview_set_image_offsets:
591  * @preview: a #PrintPreview.
592  * @offset_x: the X offset
593  * @offset_y: the Y offset
594  *
595  * Sets the offsets of the image/drawable displayed by the #PrintPreview.
596  * It does not emit the "offsets-changed" signal.
597  **/
598 void
print_preview_set_image_offsets(PrintPreview * preview,gdouble offset_x,gdouble offset_y)599 print_preview_set_image_offsets (PrintPreview *preview,
600                                  gdouble       offset_x,
601                                  gdouble       offset_y)
602 {
603   g_return_if_fail (PRINT_IS_PREVIEW (preview));
604 
605   preview->image_offset_x = offset_x;
606   preview->image_offset_y = offset_y;
607 
608   gtk_widget_queue_draw (GTK_WIDGET (preview));
609 }
610 
611 /**
612  * print_preview_set_image_offsets_max:
613  * @preview: a #PrintPreview.
614  * @offset_x_max: the maximum X offset allowed
615  * @offset_y_max: the maximum Y offset allowed
616  *
617  * Sets the maximum offsets of the image/drawable displayed by the
618  * #PrintPreview.  It does not emit the "offsets-changed" signal.
619  **/
620 void
print_preview_set_image_offsets_max(PrintPreview * preview,gdouble offset_x_max,gdouble offset_y_max)621 print_preview_set_image_offsets_max (PrintPreview *preview,
622                                      gdouble       offset_x_max,
623                                      gdouble       offset_y_max)
624 {
625   g_return_if_fail (PRINT_IS_PREVIEW (preview));
626 
627   preview->image_offset_x_max = offset_x_max;
628   preview->image_offset_y_max = offset_y_max;
629 
630   gtk_widget_queue_draw (GTK_WIDGET (preview));
631 }
632 
633 /**
634  * print_preview_set_use_full_page:
635  * @preview: a #PrintPreview.
636  * @full_page: TRUE to ignore the page margins
637  *
638  * If @full_page is TRUE, the page margins are ignored and the full page
639  * can be used to setup printing.
640  **/
641 void
print_preview_set_use_full_page(PrintPreview * preview,gboolean full_page)642 print_preview_set_use_full_page (PrintPreview *preview,
643                                  gboolean      full_page)
644 {
645   g_return_if_fail (PRINT_IS_PREVIEW (preview));
646 
647   preview->use_full_page = full_page;
648 
649   gtk_widget_queue_draw (GTK_WIDGET (preview));
650 }
651 
652 static gboolean
print_preview_is_inside(PrintPreview * preview,gdouble x,gdouble y)653 print_preview_is_inside (PrintPreview *preview,
654                          gdouble       x,
655                          gdouble       y)
656 {
657   GtkWidget     *widget = GTK_WIDGET (preview);
658   GtkAllocation  allocation;
659   gdouble        left_margin;
660   gdouble        right_margin;
661   gdouble        top_margin;
662   gdouble        bottom_margin;
663   gdouble        scale;
664   gint           border;
665 
666   gtk_widget_get_allocation (widget, &allocation);
667 
668   border = gtk_container_get_border_width (GTK_CONTAINER (widget)) + 1;
669 
670   x -= border;
671 
672   scale = print_preview_get_scale (preview);
673 
674   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
675     {
676       gdouble paper_width;
677       gdouble paper_height;
678       gint    width = allocation.width - 2 * border;
679 
680       print_preview_get_page_size (preview, &paper_width, &paper_height);
681 
682       x -= width - scale * paper_width;
683     }
684 
685   print_preview_get_page_margins (preview,
686                                   &left_margin, &right_margin,
687                                   &top_margin,  &bottom_margin);
688 
689   x = x / scale - left_margin;
690   y = y / scale - top_margin;
691 
692   return (x > preview->image_offset_x                        &&
693           x < preview->image_offset_x + preview->image_width &&
694           y > preview->image_offset_y                        &&
695           y < preview->image_offset_y + preview->image_height);
696 }
697 
698 static void
print_preview_set_inside(PrintPreview * preview,gboolean inside)699 print_preview_set_inside (PrintPreview *preview,
700                           gboolean      inside)
701 {
702   if (inside != preview->inside)
703     {
704       GtkWidget *widget = GTK_WIDGET (preview);
705 
706       preview->inside = inside;
707 
708       if (gtk_widget_is_drawable (widget))
709         gdk_window_set_cursor (gtk_widget_get_window (widget),
710                                inside ? preview->cursor : NULL);
711 
712       gtk_widget_queue_draw (widget);
713     }
714 }
715 
716 static gdouble
print_preview_get_scale(PrintPreview * preview)717 print_preview_get_scale (PrintPreview *preview)
718 {
719   GtkWidget     *widget = GTK_WIDGET (preview);
720   GtkAllocation  allocation;
721   gdouble        paper_width;
722   gdouble        paper_height;
723   gdouble        scale_x;
724   gdouble        scale_y;
725   gint           border;
726 
727   gtk_widget_get_allocation (widget, &allocation);
728 
729   border = gtk_container_get_border_width (GTK_CONTAINER (widget)) + 1;
730 
731   print_preview_get_page_size (preview, &paper_width, &paper_height);
732 
733   scale_x = (gdouble) (allocation.width  - 2 * border) / paper_width;
734   scale_y = (gdouble) (allocation.height - 2 * border) / paper_height;
735 
736   return MIN (scale_x, scale_y);
737 }
738 
739 static void
print_preview_get_page_size(PrintPreview * preview,gdouble * paper_width,gdouble * paper_height)740 print_preview_get_page_size (PrintPreview *preview,
741                              gdouble      *paper_width,
742                              gdouble      *paper_height)
743 {
744   *paper_width  = gtk_page_setup_get_paper_width  (preview->page,
745                                                    GTK_UNIT_POINTS);
746   *paper_height = gtk_page_setup_get_paper_height (preview->page,
747                                                    GTK_UNIT_POINTS);
748 }
749 
750 static void
print_preview_get_page_margins(PrintPreview * preview,gdouble * left_margin,gdouble * right_margin,gdouble * top_margin,gdouble * bottom_margin)751 print_preview_get_page_margins (PrintPreview *preview,
752                                 gdouble      *left_margin,
753                                 gdouble      *right_margin,
754                                 gdouble      *top_margin,
755                                 gdouble      *bottom_margin)
756 {
757   if (preview->use_full_page)
758     {
759       *left_margin   = 0.0;
760       *right_margin  = 0.0;
761       *top_margin    = 0.0;
762       *bottom_margin = 0.0;
763     }
764   else
765     {
766       *left_margin   = gtk_page_setup_get_left_margin   (preview->page,
767                                                          GTK_UNIT_POINTS);
768       *right_margin  = gtk_page_setup_get_right_margin  (preview->page,
769                                                          GTK_UNIT_POINTS);
770       *top_margin    = gtk_page_setup_get_top_margin    (preview->page,
771                                                          GTK_UNIT_POINTS);
772       *bottom_margin = gtk_page_setup_get_bottom_margin (preview->page,
773                                                          GTK_UNIT_POINTS);
774     }
775 }
776 
777 
778 /*  This thumbnail code should eventually end up in libgimpui.  */
779 
780 static cairo_surface_t *
print_preview_get_thumbnail(gint32 drawable_id,gint width,gint height)781 print_preview_get_thumbnail (gint32 drawable_id,
782                              gint   width,
783                              gint   height)
784 {
785   cairo_surface_t *surface;
786   cairo_format_t   format;
787   guchar          *data;
788   guchar          *dest;
789   const guchar    *src;
790   gint             src_stride;
791   gint             dest_stride;
792   gint             y;
793   gint             bpp;
794 
795   g_return_val_if_fail (width  > 0 && width  <= 1024, NULL);
796   g_return_val_if_fail (height > 0 && height <= 1024, NULL);
797 
798   data = gimp_drawable_get_thumbnail_data (drawable_id,
799                                            &width, &height, &bpp);
800 
801   switch (bpp)
802     {
803     case 1:
804     case 3:
805       format = CAIRO_FORMAT_RGB24;
806       break;
807 
808     case 2:
809     case 4:
810       format = CAIRO_FORMAT_ARGB32;
811       break;
812 
813     default:
814       g_assert_not_reached ();
815       break;
816     }
817 
818   surface = cairo_image_surface_create (format, width, height);
819 
820   src         = data;
821   src_stride  = width * bpp;
822 
823   dest        = cairo_image_surface_get_data (surface);
824   dest_stride = cairo_image_surface_get_stride (surface);
825 
826   for (y = 0; y < height; y++)
827     {
828       const guchar *s = src;
829       guchar       *d = dest;
830       gint          w = width;
831 
832       switch (bpp)
833         {
834         case 1:
835           while (w--)
836             {
837               GIMP_CAIRO_RGB24_SET_PIXEL (d, s[0], s[0], s[0]);
838               s += 1;
839               d += 4;
840             }
841           break;
842 
843         case 2:
844           while (w--)
845             {
846               GIMP_CAIRO_ARGB32_SET_PIXEL (d, s[0], s[0], s[0], s[1]);
847               s += 2;
848               d += 4;
849             }
850           break;
851 
852         case 3:
853           while (w--)
854             {
855               GIMP_CAIRO_RGB24_SET_PIXEL (d, s[0], s[1], s[2]);
856               s += 3;
857               d += 4;
858             }
859           break;
860 
861         case 4:
862           while (w--)
863             {
864               GIMP_CAIRO_ARGB32_SET_PIXEL (d, s[0], s[1], s[2], s[3]);
865               s += 4;
866               d += 4;
867             }
868           break;
869         }
870 
871       src  += src_stride;
872       dest += dest_stride;
873     }
874 
875   g_free (data);
876 
877   cairo_surface_mark_dirty (surface);
878 
879   return surface;
880 }
881