1 /* Eye Of GNOME -- Print Preview Widget
2 *
3 * Copyright (C) 2006-2008 The Free Software Foundation
4 *
5 * Author: Claudio Saavedra <csaavedra@gnome.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 */
21
22 #include <gtk/gtk.h>
23 #include <cairo.h>
24 #include <gdk/gdkkeysyms.h>
25
26 #include "eog-image.h"
27 #include "eog-print-preview.h"
28
29 struct _EogPrintPreviewPrivate {
30 GtkWidget *area;
31 GdkPixbuf *image;
32 GdkPixbuf *image_scaled;
33
34 /* The surface to set to the cairo context, created from the image */
35 cairo_surface_t *surface;
36
37 /* Flag whether we have to create surface */
38 gboolean flag_create_surface;
39
40 /* the alignment of the image in the page */
41 gfloat image_x_align, image_y_align;
42
43 /* real paper size, in inches */
44 gfloat p_width, p_height;
45
46 /* page margins, in inches */
47 gfloat l_margin, r_margin, t_margin, b_margin;
48
49 /* page margins, relatives to the widget size */
50 gint l_rmargin, r_rmargin, t_rmargin, b_rmargin;
51
52 /* image width, relative to the widget size */
53 gint r_width, r_height;
54
55 /* scale of the image, as defined by the user */
56 gfloat i_scale;
57
58 /* scale of the page, relative to the widget size */
59 gfloat p_scale;
60
61 /* whether we are currently grabbing the image */
62 gboolean grabbed;
63
64 /* the last cursor position */
65 gdouble cursorx, cursory;
66
67 /* if we reject to move the image,
68 store the delta here */
69 gdouble r_dx, r_dy;
70 };
71
72 /* Signal IDs */
73 enum {
74 SIGNAL_IMAGE_MOVED,
75 SIGNAL_IMAGE_SCALED,
76 SIGNAL_LAST
77 };
78 static gint preview_signals [SIGNAL_LAST];
79
80 enum {
81 PROP_0,
82 PROP_IMAGE,
83 PROP_IMAGE_X_ALIGN,
84 PROP_IMAGE_Y_ALIGN,
85 PROP_IMAGE_SCALE,
86 PROP_PAPER_WIDTH,
87 PROP_PAPER_HEIGHT,
88 PROP_PAGE_LEFT_MARGIN,
89 PROP_PAGE_RIGHT_MARGIN,
90 PROP_PAGE_TOP_MARGIN,
91 PROP_PAGE_BOTTOM_MARGIN
92 };
93
94 G_DEFINE_TYPE_WITH_PRIVATE (EogPrintPreview, eog_print_preview, GTK_TYPE_ASPECT_FRAME)
95
96 static void eog_print_preview_draw (EogPrintPreview *preview, cairo_t *cr);
97 static void eog_print_preview_finalize (GObject *object);
98 static void update_relative_sizes (EogPrintPreview *preview);
99 static void create_surface (EogPrintPreview *preview);
100 static void create_image_scaled (EogPrintPreview *preview);
101 static gboolean create_surface_when_idle (EogPrintPreview *preview);
102
103 static void
eog_print_preview_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)104 eog_print_preview_get_property (GObject *object,
105 guint prop_id,
106 GValue *value,
107 GParamSpec *pspec)
108 {
109 EogPrintPreviewPrivate *priv = EOG_PRINT_PREVIEW (object)->priv;
110
111 switch (prop_id) {
112 case PROP_IMAGE:
113 g_value_set_object (value, priv->image);
114 break;
115 case PROP_IMAGE_X_ALIGN:
116 g_value_set_float (value, priv->image_x_align);
117 break;
118 case PROP_IMAGE_Y_ALIGN:
119 g_value_set_float (value, priv->image_y_align);
120 break;
121 case PROP_IMAGE_SCALE:
122 g_value_set_float (value, priv->i_scale);
123 break;
124 case PROP_PAPER_WIDTH:
125 g_value_set_float (value, priv->p_width);
126 break;
127 case PROP_PAPER_HEIGHT:
128 g_value_set_float (value, priv->p_height);
129 break;
130 case PROP_PAGE_LEFT_MARGIN:
131 g_value_set_float (value, priv->l_margin);
132 break;
133 case PROP_PAGE_RIGHT_MARGIN:
134 g_value_set_float (value, priv->r_margin);
135 break;
136 case PROP_PAGE_TOP_MARGIN:
137 g_value_set_float (value, priv->t_margin);
138 break;
139 case PROP_PAGE_BOTTOM_MARGIN:
140 g_value_set_float (value, priv->b_margin);
141 break;
142 default:
143 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
144 }
145 }
146
147 static void
eog_print_preview_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)148 eog_print_preview_set_property (GObject *object,
149 guint prop_id,
150 const GValue *value,
151 GParamSpec *pspec)
152 {
153 EogPrintPreviewPrivate *priv = EOG_PRINT_PREVIEW (object)->priv;
154 gboolean paper_size_changed = FALSE;
155
156 switch (prop_id) {
157 case PROP_IMAGE:
158 if (priv->image) {
159 g_object_unref (priv->image);
160 }
161 priv->image = GDK_PIXBUF (g_value_dup_object (value));
162
163 if (priv->image_scaled) {
164 g_object_unref (priv->image_scaled);
165 priv->image_scaled = NULL;
166 }
167
168 priv->flag_create_surface = TRUE;
169 break;
170 case PROP_IMAGE_X_ALIGN:
171 priv->image_x_align = g_value_get_float (value);
172 break;
173 case PROP_IMAGE_Y_ALIGN:
174 priv->image_y_align = g_value_get_float (value);
175 break;
176 case PROP_IMAGE_SCALE:
177 priv->i_scale = g_value_get_float (value);
178 priv->flag_create_surface = TRUE;
179 break;
180 case PROP_PAPER_WIDTH:
181 priv->p_width = g_value_get_float (value);
182 paper_size_changed = TRUE;
183 break;
184 case PROP_PAPER_HEIGHT:
185 priv->p_height = g_value_get_float (value);
186 paper_size_changed = TRUE;
187 break;
188 case PROP_PAGE_LEFT_MARGIN:
189 priv->l_margin = g_value_get_float (value);
190 break;
191 case PROP_PAGE_RIGHT_MARGIN:
192 priv->r_margin = g_value_get_float (value);
193 break;
194 case PROP_PAGE_TOP_MARGIN:
195 priv->t_margin = g_value_get_float (value);
196 break;
197 case PROP_PAGE_BOTTOM_MARGIN:
198 priv->b_margin = g_value_get_float (value);
199 break;
200 default:
201 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
202 }
203
204 if (paper_size_changed) {
205 g_object_set (object,
206 "ratio", priv->p_width/priv->p_height,
207 NULL);
208 }
209
210 update_relative_sizes (EOG_PRINT_PREVIEW (object));
211 gtk_widget_queue_draw (priv->area);
212 }
213
214 static void
eog_print_preview_class_init(EogPrintPreviewClass * klass)215 eog_print_preview_class_init (EogPrintPreviewClass *klass)
216 {
217 GObjectClass *gobject_class;
218
219 gobject_class = (GObjectClass*) klass;
220
221 gobject_class->get_property = eog_print_preview_get_property;
222 gobject_class->set_property = eog_print_preview_set_property;
223 gobject_class->finalize = eog_print_preview_finalize;
224
225 /**
226 * EogPrintPreview:image:
227 *
228 * The "image" property defines the image that is previewed
229 * in the widget.
230 */
231 g_object_class_install_property (gobject_class,
232 PROP_IMAGE,
233 g_param_spec_object ("image",
234 "Image to show in the preview",
235 "",
236 G_TYPE_OBJECT,
237 G_PARAM_READWRITE));
238
239 /**
240 * EogPrintPreview:image-x-align:
241 *
242 * The "image-x-align" property defines the horizontal alignment
243 * of the image in the widget.
244 */
245 g_object_class_install_property (gobject_class,
246 PROP_IMAGE_X_ALIGN,
247 g_param_spec_float ("image-x-align",
248 "Horizontal alignment for the image",
249 "",
250 0,
251 1,
252 0.5,
253 G_PARAM_READWRITE));
254
255 /**
256 * EogPrintPreview:image-y-align:
257 *
258 * The "image-y-align" property defines the horizontal alignment
259 * of the image in the widget.
260 */
261 g_object_class_install_property (gobject_class,
262 PROP_IMAGE_Y_ALIGN,
263 g_param_spec_float ("image-y-align",
264 "Vertical alignment for the image",
265 "",
266 0,
267 1,
268 0.5,
269 G_PARAM_READWRITE));
270
271 /**
272 * EogPrintPreview:image-scale:
273 *
274 * The "image-scale" property defines the scaling of the image
275 * that the user wants for the printing.
276 */
277 g_object_class_install_property (gobject_class,
278 PROP_IMAGE_SCALE,
279 g_param_spec_float ("image-scale",
280 "The scale for the image",
281 "",
282 0,
283 1,
284 1,
285 G_PARAM_READWRITE));
286
287 /**
288 * EogPrintPreview:paper-width:
289 *
290 * The width of the previewed paper, in inches.
291 */
292 g_object_class_install_property (gobject_class,
293 PROP_PAPER_WIDTH,
294 g_param_spec_float ("paper-width",
295 "Real paper width in inches",
296 "",
297 0,
298 100,
299 8.5,
300 G_PARAM_READWRITE));
301
302 /**
303 * EogPrintPreview:paper-height:
304 *
305 * The height of the previewed paper, in inches.
306 */
307 g_object_class_install_property (gobject_class,
308 PROP_PAPER_HEIGHT,
309 g_param_spec_float ("paper-height",
310 "Real paper height in inches",
311 "",
312 0,
313 200,
314 11,
315 G_PARAM_READWRITE));
316
317 /**
318 * EogPrintPreview:page-left-margin:
319 *
320 * The size of the page's left margin, in inches.
321 */
322 g_object_class_install_property (gobject_class,
323 PROP_PAGE_LEFT_MARGIN,
324 g_param_spec_float ("page-left-margin",
325 "Left margin of the page in inches",
326 "",
327 0,
328 100,
329 0.25,
330 G_PARAM_READWRITE));
331
332 /**
333 * EogPrintPreview:page-right-margin:
334 *
335 * The size of the page's right margin, in inches.
336 */
337 g_object_class_install_property (gobject_class,
338 PROP_PAGE_RIGHT_MARGIN,
339 g_param_spec_float ("page-right-margin",
340 "Right margin of the page in inches",
341 "",
342 0,
343 200,
344 0.25,
345 G_PARAM_READWRITE));
346 /**
347 * EogPrintPreview:page-top-margin:
348 *
349 * The size of the page's top margin, in inches.
350 */
351 g_object_class_install_property (gobject_class,
352 PROP_PAGE_TOP_MARGIN,
353 g_param_spec_float ("page-top-margin",
354 "Top margin of the page in inches",
355 "",
356 0,
357 100,
358 0.25,
359 G_PARAM_READWRITE));
360
361 /**
362 * EogPrintPreview:page-bottom-margin:
363 *
364 * The size of the page's bottom margin, in inches.
365 */
366 g_object_class_install_property (gobject_class,
367 PROP_PAGE_BOTTOM_MARGIN,
368 g_param_spec_float ("page-bottom-margin",
369 "Bottom margin of the page in inches",
370 "",
371 0,
372 200,
373 0.56,
374 G_PARAM_READWRITE));
375
376 /**
377 * EogPrintPreview::image-moved:
378 * @preview: the object which received the signal
379 *
380 * The #EogPrintPreview::image-moved signal is emitted when the position
381 * of the image is changed.
382 */
383 preview_signals [SIGNAL_IMAGE_MOVED] =
384 g_signal_new ("image_moved",
385 G_TYPE_FROM_CLASS (gobject_class),
386 G_SIGNAL_RUN_FIRST, 0, NULL, NULL,
387 g_cclosure_marshal_VOID__VOID, G_TYPE_NONE,
388 0, NULL);
389
390 /**
391 * EogPrintPreview::image-scaled:
392 * @preview: the object which received the signal
393 *
394 * The ::image-scaled signal is emitted when the scale of the image is changed.
395 */
396 preview_signals [SIGNAL_IMAGE_SCALED] =
397 g_signal_new ("image_scaled",
398 G_TYPE_FROM_CLASS (gobject_class),
399 G_SIGNAL_RUN_FIRST, 0, NULL, NULL,
400 g_cclosure_marshal_VOID__VOID, G_TYPE_NONE,
401 0, NULL);
402 }
403
404 static void
eog_print_preview_finalize(GObject * object)405 eog_print_preview_finalize (GObject *object)
406 {
407 EogPrintPreviewPrivate *priv;
408
409 priv = EOG_PRINT_PREVIEW (object)->priv;
410
411 if (priv->image) {
412 g_object_unref (priv->image);
413 priv->image = NULL;
414 }
415
416 if (priv->image_scaled) {
417 g_object_unref (priv->image_scaled);
418 priv->image_scaled = NULL;
419 }
420
421 if (priv->surface) {
422 cairo_surface_destroy (priv->surface);
423 priv->surface = NULL;
424 }
425
426 G_OBJECT_CLASS (eog_print_preview_parent_class)->finalize (object);
427 }
428
429 static void
eog_print_preview_init(EogPrintPreview * preview)430 eog_print_preview_init (EogPrintPreview *preview)
431 {
432 EogPrintPreviewPrivate *priv;
433 gfloat ratio;
434
435 priv = preview->priv = eog_print_preview_get_instance_private (preview);
436
437 priv->area = GTK_WIDGET (gtk_drawing_area_new ());
438
439 gtk_container_add (GTK_CONTAINER (preview), priv->area);
440
441 priv->p_width = 8.5;
442 priv->p_height = 11.0;
443
444 ratio = priv->p_width/priv->p_height;
445
446 gtk_aspect_frame_set (GTK_ASPECT_FRAME (preview),
447 0.5, 0.5, ratio, FALSE);
448
449 priv->image = NULL;
450 priv->image_scaled = NULL;
451 priv->image_x_align = 0.5;
452 priv->image_y_align = 0.5;
453 priv->i_scale = 1;
454
455 priv->surface = NULL;
456 priv->flag_create_surface = TRUE;
457
458 priv->p_scale = 0;
459
460 priv->l_margin = 0.25;
461 priv->r_margin = 0.25;
462 priv->t_margin = 0.25;
463 priv->b_margin = 0.56;
464
465 priv->grabbed = FALSE;
466 priv->cursorx = 0;
467 priv->cursory = 0;
468 priv->r_dx = 0;
469 priv->r_dy = 0;
470 }
471
472 static gboolean button_press_event_cb (GtkWidget *widget, GdkEventButton *bev, gpointer user_data);
473 static gboolean button_release_event_cb (GtkWidget *widget, GdkEventButton *bev, gpointer user_data);
474 static gboolean motion_notify_event_cb (GtkWidget *widget, GdkEventMotion *mev, gpointer user_data);
475 static gboolean key_press_event_cb (GtkWidget *widget, GdkEventKey *event, gpointer user_data);
476
477 static gboolean draw_cb (GtkDrawingArea *drawing_area, cairo_t *cr, gpointer user_data);
478 static void size_allocate_cb (GtkWidget *widget, GtkAllocation *allocation, gpointer user_data);
479
480 /**
481 * eog_print_preview_new_with_pixbuf:
482 * @pixbuf: a #GdkPixbuf
483 *
484 * Creates a new #EogPrintPreview widget, and sets the #GdkPixbuf to preview
485 * on it.
486 *
487 * Returns: A new #EogPrintPreview widget.
488 **/
489 GtkWidget *
eog_print_preview_new_with_pixbuf(GdkPixbuf * pixbuf)490 eog_print_preview_new_with_pixbuf (GdkPixbuf *pixbuf)
491 {
492 EogPrintPreview *preview;
493
494 g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
495
496 preview = EOG_PRINT_PREVIEW (eog_print_preview_new ());
497
498 preview->priv->image = g_object_ref (pixbuf);
499
500 update_relative_sizes (preview);
501
502 return GTK_WIDGET (preview);
503 }
504
505 /**
506 * eog_print_preview_new:
507 *
508 * Creates a new #EogPrintPreview widget, setting it to the default values,
509 * and leaving the page empty. You still need to set the #EogPrintPreview:image
510 * property to make it useful.
511 *
512 * Returns: A new and empty #EogPrintPreview widget.
513 **/
514 GtkWidget *
eog_print_preview_new(void)515 eog_print_preview_new (void)
516 {
517 EogPrintPreview *preview;
518 GtkWidget *area;
519
520 preview = g_object_new (EOG_TYPE_PRINT_PREVIEW, NULL);
521
522 area = preview->priv->area;
523
524 gtk_widget_set_events (area,
525 GDK_EXPOSURE_MASK |
526 GDK_POINTER_MOTION_MASK |
527 GDK_BUTTON_PRESS_MASK |
528 GDK_BUTTON_RELEASE_MASK |
529 GDK_SCROLL_MASK |
530 GDK_KEY_PRESS_MASK);
531
532 g_object_set (G_OBJECT (area),
533 "can-focus", TRUE,
534 NULL);
535
536 /* update_relative_sizes (preview); */
537
538 g_signal_connect (G_OBJECT (area), "draw",
539 G_CALLBACK (draw_cb), preview);
540
541 g_signal_connect (G_OBJECT (area), "motion-notify-event",
542 G_CALLBACK (motion_notify_event_cb), preview);
543
544 g_signal_connect (G_OBJECT (area), "button-press-event",
545 G_CALLBACK (button_press_event_cb), preview);
546
547 g_signal_connect (G_OBJECT (area), "button-release-event",
548 G_CALLBACK (button_release_event_cb), preview);
549
550 g_signal_connect (G_OBJECT (area), "key-press-event",
551 G_CALLBACK (key_press_event_cb), preview);
552
553 g_signal_connect (area, "size-allocate",
554 G_CALLBACK (size_allocate_cb), preview);
555
556 return GTK_WIDGET (preview);
557 }
558
559 static gboolean
draw_cb(GtkDrawingArea * drawing_area,cairo_t * cr,gpointer user_data)560 draw_cb (GtkDrawingArea *drawing_area,
561 cairo_t *cr,
562 gpointer user_data)
563 {
564 update_relative_sizes (EOG_PRINT_PREVIEW (user_data));
565
566 eog_print_preview_draw (EOG_PRINT_PREVIEW (user_data), cr);
567
568 if (cairo_status (cr) != CAIRO_STATUS_SUCCESS) {
569 fprintf (stderr, "Cairo is unhappy: %s\n",
570 cairo_status_to_string (cairo_status (cr)));
571 }
572
573 return TRUE;
574 }
575
576 /**
577 * get_current_image_coordinates:
578 * @preview: an #EogPrintPreview
579 * @x0: A pointer where to store the x coordinate.
580 * @y0: A pointer where to store the y coordinate.
581 *
582 * This function returns the current image coordinates, according
583 * with the properties of the given @preview widget.
584 **/
585 static void
get_current_image_coordinates(EogPrintPreview * preview,gint * x0,gint * y0)586 get_current_image_coordinates (EogPrintPreview *preview,
587 gint *x0, gint *y0)
588 {
589 EogPrintPreviewPrivate *priv;
590 GtkAllocation allocation;
591
592 priv = preview->priv;
593 gtk_widget_get_allocation (GTK_WIDGET (priv->area), &allocation);
594
595 *x0 = (gint)((1 - priv->image_x_align)*priv->l_rmargin + priv->image_x_align*(allocation.width - priv->r_rmargin - priv->r_width));
596 *y0 = (gint)((1 - priv->image_y_align)*priv->t_rmargin + priv->image_y_align*(allocation.height - priv->b_rmargin - priv->r_height));
597 }
598
599 /**
600 * press_inside_image_area:
601 * @preview: an #EogPrintPreview
602 * @x: the points x coordinate
603 * @y: the points y coordinate
604 *
605 * Returns whether the given point is inside the image area.
606 *
607 * Returns: %TRUE if the given point is inside of the image area,
608 * %FALSE otherwise.
609 **/
610 static gboolean
press_inside_image_area(EogPrintPreview * preview,guint x,guint y)611 press_inside_image_area (EogPrintPreview *preview,
612 guint x,
613 guint y)
614 {
615 EogPrintPreviewPrivate *priv;
616 const gint xs = (gint) x;
617 const gint ys = (gint) y;
618 gint x0, y0;
619
620 priv = preview->priv;
621 get_current_image_coordinates (preview, &x0, &y0);
622
623 if (xs >= x0 && ys >= y0 &&
624 xs <= x0 + priv->r_width && ys <= y0 + priv->r_height)
625 return TRUE;
626
627 return FALSE;
628 }
629
630 gboolean
eog_print_preview_point_in_image_area(EogPrintPreview * preview,guint x,guint y)631 eog_print_preview_point_in_image_area (EogPrintPreview *preview,
632 guint x,
633 guint y)
634 {
635 g_return_val_if_fail (EOG_IS_PRINT_PREVIEW (preview), FALSE);
636
637 return press_inside_image_area (preview, x, y);
638 }
639
640 static void
create_image_scaled(EogPrintPreview * preview)641 create_image_scaled (EogPrintPreview *preview)
642 {
643 EogPrintPreviewPrivate *priv = preview->priv;
644
645 if (!priv->image_scaled) {
646 gint i_width, i_height;
647 GtkAllocation allocation;
648
649 gtk_widget_get_allocation (priv->area, &allocation);
650 i_width = gdk_pixbuf_get_width (priv->image);
651 i_height = gdk_pixbuf_get_height (priv->image);
652
653 if ((i_width > allocation.width) ||
654 (i_height > allocation.height)) {
655 gdouble scale;
656 scale = MIN ((gdouble) allocation.width/i_width,
657 (gdouble) allocation.height/i_height);
658 scale *= gtk_widget_get_scale_factor (GTK_WIDGET (priv->area));
659 priv->image_scaled = gdk_pixbuf_scale_simple (priv->image,
660 i_width*scale,
661 i_height*scale,
662 GDK_INTERP_TILES);
663 } else {
664 priv->image_scaled = priv->image;
665 g_object_ref (priv->image_scaled);
666 }
667 }
668 }
669
670 static GdkPixbuf *
create_preview_buffer(EogPrintPreview * preview)671 create_preview_buffer (EogPrintPreview *preview)
672 {
673 GdkPixbuf *pixbuf;
674 gint width, height, widget_scale;
675 GdkInterpType type = GDK_INTERP_TILES;
676
677 if (preview->priv->image == NULL) {
678 return NULL;
679 }
680
681 create_image_scaled (preview);
682
683 width = gdk_pixbuf_get_width (preview->priv->image);
684 height = gdk_pixbuf_get_height (preview->priv->image);
685 widget_scale = gtk_widget_get_scale_factor (GTK_WIDGET (preview->priv->area));
686
687 width *= preview->priv->i_scale * preview->priv->p_scale
688 * widget_scale;
689 height *= preview->priv->i_scale * preview->priv->p_scale
690 * widget_scale;
691
692 if (width < 1 || height < 1)
693 return NULL;
694
695 /* to use GDK_INTERP_TILES for small pixbufs is expensive and unnecessary */
696 if (width < 25 || height < 25)
697 type = GDK_INTERP_NEAREST;
698
699 if (preview->priv->image_scaled) {
700 pixbuf = gdk_pixbuf_scale_simple (preview->priv->image_scaled,
701 width, height, type);
702 } else {
703 pixbuf = gdk_pixbuf_scale_simple (preview->priv->image,
704 width, height, type);
705 }
706
707 return pixbuf;
708 }
709
710 static void
create_surface(EogPrintPreview * preview)711 create_surface (EogPrintPreview *preview)
712 {
713 EogPrintPreviewPrivate *priv = preview->priv;
714 GdkPixbuf *pixbuf;
715
716 if (priv->surface) {
717 cairo_surface_destroy (priv->surface);
718 priv->surface = NULL;
719 }
720
721 pixbuf = create_preview_buffer (preview);
722 if (pixbuf) {
723 priv->surface =
724 gdk_cairo_surface_create_from_pixbuf (pixbuf, 0,
725 gtk_widget_get_window (GTK_WIDGET (preview)));
726 g_object_unref (pixbuf);
727 }
728 priv->flag_create_surface = FALSE;
729 }
730
731 static gboolean
create_surface_when_idle(EogPrintPreview * preview)732 create_surface_when_idle (EogPrintPreview *preview)
733 {
734 create_surface (preview);
735
736 return FALSE;
737 }
738
739 static gboolean
button_press_event_cb(GtkWidget * widget,GdkEventButton * event,gpointer user_data)740 button_press_event_cb (GtkWidget *widget,
741 GdkEventButton *event,
742 gpointer user_data)
743 {
744 EogPrintPreview *preview = EOG_PRINT_PREVIEW (user_data);
745
746 preview->priv->cursorx = event->x;
747 preview->priv->cursory = event->y;
748
749 switch (event->button) {
750 case 1:
751 preview->priv->grabbed = press_inside_image_area (preview, event->x, event->y);
752 break;
753 default:
754 break;
755 }
756
757 if (preview->priv->grabbed) {
758 gtk_widget_queue_draw (GTK_WIDGET (preview));
759 }
760
761 gtk_widget_grab_focus (preview->priv->area);
762
763 return FALSE;
764 }
765
766 static gboolean
button_release_event_cb(GtkWidget * widget,GdkEventButton * event,gpointer user_data)767 button_release_event_cb (GtkWidget *widget,
768 GdkEventButton *event,
769 gpointer user_data)
770 {
771 EogPrintPreview *preview = EOG_PRINT_PREVIEW (user_data);
772
773 switch (event->button) {
774 case 1:
775 preview->priv->grabbed = FALSE;
776 preview->priv->r_dx = 0;
777 preview->priv->r_dy = 0;
778 gtk_widget_queue_draw (GTK_WIDGET (preview));
779 break;
780 default:
781 break;
782 }
783 return FALSE;
784 }
785
786 static gboolean
key_press_event_cb(GtkWidget * widget,GdkEventKey * event,gpointer user_data)787 key_press_event_cb (GtkWidget *widget,
788 GdkEventKey *event,
789 gpointer user_data)
790 {
791 gfloat delta, align;
792 gboolean stop_emission = FALSE;
793 const gchar *property;
794
795 delta = 0;
796
797 switch (event->keyval) {
798 case GDK_KEY_Left:
799 property = "image-x-align";
800 delta = -0.01;
801 break;
802 case GDK_KEY_Right:
803 property = "image-x-align";
804 delta = 0.01;
805 break;
806 case GDK_KEY_Up:
807 property = "image-y-align";
808 delta = -0.01;
809 break;
810 case GDK_KEY_Down:
811 property = "image-y-align";
812 delta = 0.01;
813 break;
814 default:
815 break;
816 }
817
818 if (delta != 0) {
819 g_object_get (G_OBJECT (user_data),
820 property, &align,
821 NULL);
822
823 align += delta;
824 align = CLAMP (align, 0, 1);
825 g_object_set (G_OBJECT (user_data),
826 property, align,
827 NULL);
828
829 stop_emission = TRUE;
830 g_signal_emit (G_OBJECT (user_data),
831 preview_signals
832 [SIGNAL_IMAGE_MOVED], 0);
833 }
834
835 return stop_emission;
836 }
837
838 static gboolean
motion_notify_event_cb(GtkWidget * widget,GdkEventMotion * event,gpointer user_data)839 motion_notify_event_cb (GtkWidget *widget,
840 GdkEventMotion *event,
841 gpointer user_data)
842 {
843 EogPrintPreviewPrivate *priv = EOG_PRINT_PREVIEW (user_data)->priv;
844 gdouble dx, dy;
845 GtkAllocation allocation;
846
847 if (priv->grabbed) {
848 dx = event->x - priv->cursorx;
849 dy = event->y - priv->cursory;
850
851 gtk_widget_get_allocation (widget, &allocation);
852
853 /* Make sure the image stays inside the margins */
854
855 priv->image_x_align += (dx + priv->r_dx)/(allocation.width - priv->r_width - priv->l_rmargin - priv->r_rmargin);
856 if (priv->image_x_align < 0. || priv->image_x_align > 1.) {
857 priv->image_x_align = CLAMP (priv->image_x_align, 0., 1.);
858 priv->r_dx += dx;
859 }
860 else
861 priv->r_dx = 0;
862
863 priv->image_y_align += (dy + priv->r_dy)/(allocation.height - priv->r_height - priv->t_rmargin - priv->b_rmargin);
864 if (priv->image_y_align < 0. || priv->image_y_align > 1.) {
865 priv->image_y_align = CLAMP (priv->image_y_align, 0., 1.);
866 priv->r_dy += dy;
867 } else
868 priv->r_dy = 0;
869
870 /* we do this to correctly change the property values */
871 g_object_set (EOG_PRINT_PREVIEW (user_data),
872 "image-x-align", priv->image_x_align,
873 "image-y-align", priv->image_y_align,
874 NULL);
875
876 priv->cursorx = event->x;
877 priv->cursory = event->y;
878
879 g_signal_emit (G_OBJECT (user_data),
880 preview_signals
881 [SIGNAL_IMAGE_MOVED], 0);
882 } else {
883 if (press_inside_image_area (EOG_PRINT_PREVIEW (user_data), event->x, event->y)) {
884 GdkCursor *cursor;
885 cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
886 GDK_FLEUR);
887 gdk_window_set_cursor (gtk_widget_get_window (widget),
888 cursor);
889 g_object_unref (cursor);
890 } else {
891 gdk_window_set_cursor (gtk_widget_get_window (widget),
892 NULL);
893 }
894 }
895 return FALSE;
896 }
897
898 static void
size_allocate_cb(GtkWidget * widget,GtkAllocation * allocation,gpointer user_data)899 size_allocate_cb (GtkWidget *widget,
900 GtkAllocation *allocation,
901 gpointer user_data)
902 {
903 EogPrintPreview *preview;
904
905 preview = EOG_PRINT_PREVIEW (user_data);
906 update_relative_sizes (preview);
907
908 preview->priv->flag_create_surface = TRUE;
909
910 if (preview->priv->image_scaled) {
911 g_object_unref (preview->priv->image_scaled);
912 preview->priv->image_scaled = NULL;
913 }
914
915 g_idle_add ((GSourceFunc) create_surface_when_idle, preview);
916 }
917
918 static void
eog_print_preview_draw(EogPrintPreview * preview,cairo_t * cr)919 eog_print_preview_draw (EogPrintPreview *preview, cairo_t *cr)
920 {
921 EogPrintPreviewPrivate *priv;
922 GtkWidget *area;
923 GtkAllocation allocation;
924 gint x0, y0;
925 gboolean has_focus;
926
927 priv = preview->priv;
928 area = priv->area;
929
930 has_focus = gtk_widget_has_focus (area);
931
932 gtk_widget_get_allocation (area, &allocation);
933
934 /* draw the page */
935 cairo_set_source_rgb (cr, 1., 1., 1.);
936 cairo_rectangle (cr, 0, 0, allocation.width, allocation.height);
937 cairo_fill (cr);
938
939 /* draw the page margins */
940 cairo_set_source_rgb (cr, 0., 0., 0.);
941 cairo_set_line_width (cr, 0.1);
942 cairo_rectangle (cr,
943 priv->l_rmargin, priv->t_rmargin,
944 allocation.width - priv->l_rmargin - priv->r_rmargin,
945 allocation.height - priv->t_rmargin - priv->b_rmargin);
946 cairo_stroke (cr);
947
948 get_current_image_coordinates (preview, &x0, &y0);
949
950 if (priv->flag_create_surface) {
951 create_surface (preview);
952 }
953
954 if (priv->surface) {
955 cairo_set_source_surface (cr, priv->surface, x0, y0);
956 cairo_paint (cr);
957 } else if (priv->image_scaled) {
958 /* just in the remote case we don't have the surface */
959
960 /* adjust (x0, y0) to the new scale */
961 gdouble scale = priv->i_scale * priv->p_scale *
962 gdk_pixbuf_get_width (priv->image) / gdk_pixbuf_get_width (priv->image_scaled);
963 x0 /= scale;
964 y0 /= scale;
965
966 cairo_scale (cr, scale, scale);
967 gdk_cairo_set_source_pixbuf (cr, priv->image_scaled, x0, y0);
968 cairo_paint (cr);
969 } else if (priv->image) {
970 /* just in the remote case we don't have the surface */
971
972 /* adjust (x0, y0) to the new scale */
973 x0 /= priv->i_scale * priv->p_scale;
974 y0 /= priv->i_scale * priv->p_scale;
975
976 cairo_scale (cr, priv->i_scale*priv->p_scale, priv->i_scale*priv->p_scale);
977 gdk_cairo_set_source_pixbuf (cr, priv->image, x0, y0);
978 cairo_paint (cr);
979 }
980
981 if (has_focus) {
982 GtkStyleContext *ctx;
983
984 ctx = gtk_widget_get_style_context (area);
985 gtk_render_focus (ctx, cr, x0, y0,
986 priv->r_width, priv->r_height);
987 }
988 }
989
990 static void
update_relative_sizes(EogPrintPreview * preview)991 update_relative_sizes (EogPrintPreview *preview)
992 {
993 EogPrintPreviewPrivate *priv;
994 GtkAllocation allocation;
995 gint i_width, i_height;
996
997 priv = preview->priv;
998
999 if (priv->image != NULL) {
1000 i_width = gdk_pixbuf_get_width (priv->image);
1001 i_height = gdk_pixbuf_get_height (priv->image);
1002 } else {
1003 i_width = i_height = 0;
1004 }
1005
1006 gtk_widget_get_allocation (priv->area, &allocation);
1007
1008 priv->p_scale = (gfloat) allocation.width / (priv->p_width * 72.0);
1009
1010 priv->r_width = (gint) i_width * priv->i_scale * priv->p_scale;
1011 priv->r_height = (gint) i_height * priv->i_scale * priv->p_scale;
1012
1013 priv->l_rmargin = (gint) (72. * priv->l_margin * priv->p_scale);
1014 priv->r_rmargin = (gint) (72. * priv->r_margin * priv->p_scale);
1015 priv->t_rmargin = (gint) (72. * priv->t_margin * priv->p_scale);
1016 priv->b_rmargin = (gint) (72. * priv->b_margin * priv->p_scale);
1017 }
1018
1019 /**
1020 * eog_print_preview_set_page_margins:
1021 * @preview: a #EogPrintPreview
1022 * @l_margin: Left margin.
1023 * @r_margin: Right margin.
1024 * @t_margin: Top margin.
1025 * @b_margin: Bottom margin.
1026 *
1027 * Manually set the margins, in inches.
1028 **/
1029 void
eog_print_preview_set_page_margins(EogPrintPreview * preview,gfloat l_margin,gfloat r_margin,gfloat t_margin,gfloat b_margin)1030 eog_print_preview_set_page_margins (EogPrintPreview *preview,
1031 gfloat l_margin,
1032 gfloat r_margin,
1033 gfloat t_margin,
1034 gfloat b_margin)
1035 {
1036 g_return_if_fail (EOG_IS_PRINT_PREVIEW (preview));
1037
1038 g_object_set (G_OBJECT(preview),
1039 "page-left-margin", l_margin,
1040 "page-right-margin", r_margin,
1041 "page-top-margin", t_margin,
1042 "page-bottom-margin", r_margin,
1043 NULL);
1044 }
1045
1046 /**
1047 * eog_print_preview_set_from_page_setup:
1048 * @preview: a #EogPrintPreview
1049 * @setup: a #GtkPageSetup to set the properties from
1050 *
1051 * Sets up the page properties from a #GtkPageSetup. Useful when using the
1052 * widget with the GtkPrint API.
1053 **/
1054 void
eog_print_preview_set_from_page_setup(EogPrintPreview * preview,GtkPageSetup * setup)1055 eog_print_preview_set_from_page_setup (EogPrintPreview *preview,
1056 GtkPageSetup *setup)
1057 {
1058 g_return_if_fail (EOG_IS_PRINT_PREVIEW (preview));
1059 g_return_if_fail (GTK_IS_PAGE_SETUP (setup));
1060
1061 g_object_set (G_OBJECT (preview),
1062 "page-left-margin", gtk_page_setup_get_left_margin (setup, GTK_UNIT_INCH),
1063 "page-right-margin", gtk_page_setup_get_right_margin (setup, GTK_UNIT_INCH),
1064 "page-top-margin", gtk_page_setup_get_top_margin (setup, GTK_UNIT_INCH),
1065 "page-bottom-margin", gtk_page_setup_get_bottom_margin (setup, GTK_UNIT_INCH),
1066 "paper-width", gtk_page_setup_get_paper_width (setup, GTK_UNIT_INCH),
1067 "paper-height", gtk_page_setup_get_paper_height (setup, GTK_UNIT_INCH),
1068 NULL);
1069
1070 }
1071
1072 /**
1073 * eog_print_preview_get_image_position:
1074 * @preview: a #EogPrintPreview
1075 * @x: a pointer to a #gdouble, or %NULL to ignore it
1076 * @y: a pointer to a #gdouble, or %NULL to ignore it
1077 *
1078 * Gets current image position in inches, relative to the margins. A
1079 * (0, 0) position is the intersection between the left and top margins.
1080 **/
1081 void
eog_print_preview_get_image_position(EogPrintPreview * preview,gdouble * x,gdouble * y)1082 eog_print_preview_get_image_position (EogPrintPreview *preview,
1083 gdouble *x,
1084 gdouble *y)
1085 {
1086 EogPrintPreviewPrivate *priv;
1087 gdouble width, height;
1088
1089 g_return_if_fail (EOG_IS_PRINT_PREVIEW (preview));
1090
1091 priv = preview->priv;
1092
1093 if (x != NULL) {
1094 width = gdk_pixbuf_get_width (priv->image) * priv->i_scale / 72.;
1095 *x = priv->image_x_align * (priv->p_width - priv->l_margin - priv->r_margin - width);
1096 }
1097 if (y != NULL) {
1098 height = gdk_pixbuf_get_height (priv->image) * priv->i_scale / 72.;
1099 *y = priv->image_y_align * (priv->p_height - priv->t_margin - priv->b_margin - height);
1100 }
1101 }
1102
1103 /**
1104 * eog_print_preview_set_image_position:
1105 * @preview: a #EogPrintPreview
1106 * @x: The X coordinate, in inches, or -1 to ignore it.
1107 * @y: The Y coordinate, in inches, or -1 to ignore it.
1108 *
1109 * Sets the image position. You can pass -1 to one of the coordinates if you
1110 * only want to set the other.
1111 **/
1112 void
eog_print_preview_set_image_position(EogPrintPreview * preview,gdouble x,gdouble y)1113 eog_print_preview_set_image_position (EogPrintPreview *preview,
1114 gdouble x,
1115 gdouble y)
1116 {
1117 EogPrintPreviewPrivate *priv;
1118 gfloat x_align, y_align;
1119 gdouble width, height;
1120
1121 g_return_if_fail (EOG_IS_PRINT_PREVIEW (preview));
1122
1123 priv = preview->priv;
1124
1125 if (x != -1) {
1126 width = gdk_pixbuf_get_width (priv->image) * priv->i_scale / 72.;
1127 x_align = CLAMP (x/(priv->p_width - priv->l_margin - priv->r_margin - width), 0, 1);
1128 g_object_set (preview, "image-x-align", x_align, NULL);
1129 }
1130
1131 if (y != -1) {
1132 height = gdk_pixbuf_get_height (priv->image) * priv->i_scale / 72.;
1133 y_align = CLAMP (y/(priv->p_height - priv->t_margin - priv->b_margin - height), 0, 1);
1134 g_object_set (preview, "image-y-align", y_align, NULL);
1135 }
1136 }
1137
1138 /**
1139 * eog_print_preview_set_scale:
1140 * @preview: a #EogPrintPreview
1141 * @scale: a scale value, between 0 and 1.
1142 *
1143 * Sets the scale for the image.
1144 **/
1145 void
eog_print_preview_set_scale(EogPrintPreview * preview,gfloat scale)1146 eog_print_preview_set_scale (EogPrintPreview *preview,
1147 gfloat scale)
1148 {
1149 g_return_if_fail (EOG_IS_PRINT_PREVIEW (preview));
1150
1151 g_object_set (preview,
1152 "image-scale", scale,
1153 NULL);
1154
1155 g_signal_emit (G_OBJECT (preview),
1156 preview_signals
1157 [SIGNAL_IMAGE_SCALED], 0);
1158
1159 }
1160
1161 /**
1162 * eog_print_preview_get_scale:
1163 * @preview: A #EogPrintPreview.
1164 *
1165 * Gets the scale for the image.
1166 *
1167 * Returns: The scale for the image.
1168 **/
1169 gfloat
eog_print_preview_get_scale(EogPrintPreview * preview)1170 eog_print_preview_get_scale (EogPrintPreview *preview)
1171 {
1172 gfloat scale;
1173
1174 g_return_val_if_fail (EOG_IS_PRINT_PREVIEW (preview), 0);
1175
1176 g_object_get (preview,
1177 "image-scale", &scale,
1178 NULL);
1179
1180 return scale;
1181 }
1182