]> git.street.me.uk Git - andy/viking.git/blob - src/print-preview.c
[QA] Fix memory leak in sorting layers.
[andy/viking.git] / src / print-preview.c
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 2 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, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  */
18 /*
19  * Modified for viking by Quy Tonthat <qtonthat@gmail.com>
20  */
21
22 #include <gtk/gtk.h>
23
24 #include "print-preview.h"
25
26
27 #define DRAWING_AREA_SIZE 200
28
29
30 enum
31 {
32   OFFSETS_CHANGED,
33   LAST_SIGNAL
34 };
35
36
37 static void      vik_print_preview_finalize         (GObject          *object);
38
39 static void      vik_print_preview_size_allocate    (GtkWidget        *widget,
40                                                       GtkAllocation    *allocation,
41                                                       VikPrintPreview *preview);
42 static void      vik_print_preview_realize          (GtkWidget        *widget);
43 static gboolean  vik_print_preview_event            (GtkWidget        *widget,
44                                                       GdkEvent         *event,
45                                                       VikPrintPreview *preview);
46
47 static gboolean  vik_print_preview_expose_event     (GtkWidget        *widget,
48                                                       GdkEventExpose   *eevent,
49                                                       VikPrintPreview *preview);
50
51 static gdouble   vik_print_preview_get_scale        (VikPrintPreview *preview);
52
53 static void      vik_print_preview_get_page_margins (VikPrintPreview *preview,
54                                                       gdouble          *left_margin,
55                                                       gdouble          *right_margin,
56                                                       gdouble          *top_margin,
57                                                       gdouble          *bottom_margin);
58
59 static void      print_preview_queue_draw            (VikPrintPreview *preview);
60
61
62 G_DEFINE_TYPE (VikPrintPreview, vik_print_preview, GTK_TYPE_ASPECT_FRAME)
63
64 #define parent_class vik_print_preview_parent_class
65
66 static guint vik_print_preview_signals[LAST_SIGNAL] = { 0 };
67
68
69 #define g_marshal_value_peek_double(v)   (v)->data[0].v_double
70
71 static void
72 marshal_VOID__DOUBLE_DOUBLE (GClosure     *closure,
73                              GValue       *return_value,
74                              guint         n_param_values,
75                              const GValue *param_values,
76                              gpointer      invocation_hint,
77                              gpointer      marshal_data)
78 {
79   typedef void (*GMarshalFunc_VOID__DOUBLE_DOUBLE) (gpointer     data1,
80                                                     gdouble      arg_1,
81                                                     gdouble      arg_2,
82                                                     gpointer     data2);
83   register GMarshalFunc_VOID__DOUBLE_DOUBLE callback;
84   register GCClosure *cc = (GCClosure*) closure;
85   register gpointer data1, data2;
86
87   g_return_if_fail (n_param_values == 3);
88
89   if (G_CCLOSURE_SWAP_DATA (closure))
90     {
91       data1 = closure->data;
92       data2 = g_value_peek_pointer (param_values + 0);
93     }
94   else
95     {
96       data1 = g_value_peek_pointer (param_values + 0);
97       data2 = closure->data;
98     }
99
100   callback = (GMarshalFunc_VOID__DOUBLE_DOUBLE) (marshal_data ?
101                                                  marshal_data : cc->callback);
102
103   callback (data1,
104             g_marshal_value_peek_double (param_values + 1),
105             g_marshal_value_peek_double (param_values + 2),
106             data2);
107 }
108
109 static void
110 vik_print_preview_class_init (VikPrintPreviewClass *klass)
111 {
112   GObjectClass *object_class = G_OBJECT_CLASS (klass);
113
114   vik_print_preview_signals[OFFSETS_CHANGED] =
115     g_signal_new ("offsets-changed",
116                   G_TYPE_FROM_CLASS (klass),
117                   G_SIGNAL_RUN_FIRST,
118                   G_STRUCT_OFFSET (VikPrintPreviewClass, offsets_changed),
119                   NULL, NULL,
120                   marshal_VOID__DOUBLE_DOUBLE,
121                   G_TYPE_NONE, 2,
122                   G_TYPE_DOUBLE,
123                   G_TYPE_DOUBLE);
124
125   object_class->finalize = vik_print_preview_finalize;
126
127   klass->offsets_changed = NULL;
128 }
129
130 static void
131 vik_print_preview_init (VikPrintPreview *preview)
132 {
133   preview->page               = NULL;
134   preview->pixbuf             = NULL;
135   preview->dragging           = FALSE;
136   preview->image_offset_x     = 0.0;
137   preview->image_offset_y     = 0.0;
138   preview->image_offset_x_max = 0.0;
139   preview->image_offset_y_max = 0.0;
140   preview->image_xres         = 230.0; // 1.0
141   preview->image_yres         = 230.0;
142   preview->use_full_page      = FALSE;
143
144   preview->area = gtk_drawing_area_new();
145   gtk_container_add (GTK_CONTAINER (preview), preview->area);
146   gtk_widget_show (preview->area);
147
148   gtk_widget_add_events (GTK_WIDGET (preview->area), GDK_BUTTON_PRESS_MASK);
149
150   g_signal_connect (preview->area, "size-allocate",
151                     G_CALLBACK (vik_print_preview_size_allocate),
152                     preview);
153   g_signal_connect (preview->area, "realize",
154                     G_CALLBACK (vik_print_preview_realize),
155                     NULL);
156   g_signal_connect (preview->area, "event",
157                     G_CALLBACK (vik_print_preview_event),
158                     preview);
159   g_signal_connect (preview->area, "expose-event",
160                     G_CALLBACK (vik_print_preview_expose_event),
161                     preview);
162 }
163
164
165 static void
166 vik_print_preview_finalize (GObject *object)
167 {
168   VikPrintPreview *preview = VIK_PRINT_PREVIEW (object);
169
170   if (preview->drawable)
171     {
172       preview->drawable = NULL;
173     }
174
175   if (preview->pixbuf)
176     {
177       g_object_unref (preview->pixbuf);
178       preview->pixbuf = NULL;
179     }
180
181   if (preview->page)
182     {
183       g_object_unref (preview->page);
184       preview->page = NULL;
185     }
186
187   G_OBJECT_CLASS (vik_print_preview_parent_class)->finalize (object);
188 }
189
190 /**
191  * vik_print_preview_new:
192  * @page: page setup
193  * @drawable_id: the drawable to print
194  *
195  * Creates a new #VikPrintPreview widget.
196  *
197  * Return value: the new #VikPrintPreview widget.
198  **/
199 GtkWidget *
200 vik_print_preview_new (GtkPageSetup *page,
201                         GdkDrawable        *drawable)
202 {
203   VikPrintPreview *preview;
204   gfloat            ratio;
205
206   preview = g_object_new (VIK_TYPE_PRINT_PREVIEW, NULL);
207
208   preview->drawable = drawable;
209
210   if (page != NULL)
211     preview->page = gtk_page_setup_copy (page);
212   else
213     preview->page = gtk_page_setup_new ();
214
215   ratio = (gtk_page_setup_get_paper_width (preview->page, GTK_UNIT_POINTS) /
216            gtk_page_setup_get_paper_height (preview->page, GTK_UNIT_POINTS));
217
218   gtk_aspect_frame_set (GTK_ASPECT_FRAME (preview), 0.5, 0.5, ratio, FALSE);
219
220   gtk_widget_set_size_request (preview->area,
221                                DRAWING_AREA_SIZE, DRAWING_AREA_SIZE);
222
223   return GTK_WIDGET (preview);
224 }
225
226 /**
227  * vik_print_preview_set_image_dpi:
228  * @preview: a #VikPrintPreview.
229  * @xres: the X resolution
230  * @yres: the Y resolution
231  *
232  * Sets the resolution of the image/drawable displayed by the
233  * #VikPrintPreview.
234  **/
235 void
236 vik_print_preview_set_image_dpi (VikPrintPreview *preview,
237                                   gdouble           xres,
238                                   gdouble           yres)
239 {
240   g_return_if_fail (VIK_IS_PRINT_PREVIEW (preview));
241
242   if (preview->image_xres != xres || preview->image_yres != yres)
243     {
244       preview->image_xres = xres;
245       preview->image_yres = yres;
246
247       print_preview_queue_draw (preview);
248     }
249 }
250
251 /**
252  * vik_print_preview_set_page_setup:
253  * @preview: a #VikPrintPreview.
254  * @page: the page setup to use
255  *
256  * Sets the page setup to use by the #VikPrintPreview.
257  **/
258 void
259 vik_print_preview_set_page_setup (VikPrintPreview *preview,
260                                    GtkPageSetup     *page)
261 {
262   gfloat ratio;
263
264   if (preview->page)
265     g_object_unref (preview->page);
266
267   preview->page = gtk_page_setup_copy (page);
268
269   ratio = (gtk_page_setup_get_paper_width (page, GTK_UNIT_POINTS) /
270            gtk_page_setup_get_paper_height (page, GTK_UNIT_POINTS));
271
272   gtk_aspect_frame_set (GTK_ASPECT_FRAME (preview), 0.5, 0.5, ratio, FALSE);
273
274   print_preview_queue_draw (preview);
275 }
276
277 /**
278  * vik_print_preview_set_image_offsets:
279  * @preview: a #VikPrintPreview.
280  * @offset_x: the X offset
281  * @offset_y: the Y offset
282  *
283  * Sets the offsets of the image/drawable displayed by the #VikPrintPreview.
284  * It does not emit the "offsets-changed" signal.
285  **/
286 void
287 vik_print_preview_set_image_offsets (VikPrintPreview *preview,
288                                       gdouble           offset_x,
289                                       gdouble           offset_y)
290 {
291   g_return_if_fail (VIK_IS_PRINT_PREVIEW (preview));
292
293   preview->image_offset_x = offset_x;
294   preview->image_offset_y = offset_y;
295
296   print_preview_queue_draw (preview);
297 }
298
299 /**
300  * vik_print_preview_set_image_offsets_max:
301  * @preview: a #VikPrintPreview.
302  * @offset_x_max: the maximum X offset allowed
303  * @offset_y_max: the maximum Y offset allowed
304  *
305  * Sets the maximum offsets of the image/drawable displayed by the
306  * #VikPrintPreview.  It does not emit the "offsets-changed" signal.
307  **/
308 void
309 vik_print_preview_set_image_offsets_max (VikPrintPreview *preview,
310                                           gdouble           offset_x_max,
311                                           gdouble           offset_y_max)
312 {
313   g_return_if_fail (VIK_IS_PRINT_PREVIEW (preview));
314
315   preview->image_offset_x_max = offset_x_max;
316   preview->image_offset_y_max = offset_y_max;
317
318   print_preview_queue_draw (preview);
319 }
320
321 /**
322  * vik_print_preview_set_use_full_page:
323  * @preview: a #VikPrintPreview.
324  * @full_page: TRUE to ignore the page margins
325  *
326  * If @full_page is TRUE, the page margins are ignored and the full page
327  * can be used to setup printing.
328  **/
329 void
330 vik_print_preview_set_use_full_page (VikPrintPreview *preview,
331                                       gboolean          full_page)
332 {
333   g_return_if_fail (VIK_IS_PRINT_PREVIEW (preview));
334
335   preview->use_full_page = full_page;
336
337   print_preview_queue_draw (preview);
338 }
339
340 static void
341 vik_print_preview_realize (GtkWidget *widget)
342 {
343   GdkCursor *cursor;
344
345   cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
346                                        GDK_FLEUR);
347   gdk_window_set_cursor (gtk_widget_get_window(widget), cursor);
348   gdk_cursor_unref (cursor);
349 }
350
351 static gboolean
352 vik_print_preview_event (GtkWidget        *widget,
353                           GdkEvent         *event,
354                           VikPrintPreview *preview)
355 {
356   static gdouble orig_offset_x = 0.0;
357   static gdouble orig_offset_y = 0.0;
358   static gint    start_x       = 0;
359   static gint    start_y       = 0;
360
361   gdouble        offset_x;
362   gdouble        offset_y;
363   gdouble        scale;
364
365   switch (event->type)
366     {
367     case GDK_BUTTON_PRESS:
368       gdk_pointer_grab (gtk_widget_get_window(widget), FALSE,
369                         (GDK_BUTTON1_MOTION_MASK |
370                          GDK_BUTTON_RELEASE_MASK),
371                         NULL, NULL, event->button.time);
372
373       orig_offset_x = preview->image_offset_x;
374       orig_offset_y = preview->image_offset_y;
375
376       start_x = event->button.x;
377       start_y = event->button.y;
378
379       preview->dragging = TRUE;
380       break;
381
382     case GDK_MOTION_NOTIFY:
383       scale = vik_print_preview_get_scale (preview);
384
385       offset_x = (orig_offset_x + (event->motion.x - start_x) / scale);
386       offset_y = (orig_offset_y + (event->motion.y - start_y) / scale);
387
388       offset_x = CLAMP (offset_x, 0, preview->image_offset_x_max);
389       offset_y = CLAMP (offset_y, 0, preview->image_offset_y_max);
390
391       if (preview->image_offset_x != offset_x ||
392           preview->image_offset_y != offset_y)
393         {
394           vik_print_preview_set_image_offsets (preview, offset_x, offset_y);
395
396           g_signal_emit (preview,
397                          vik_print_preview_signals[OFFSETS_CHANGED], 0,
398                          preview->image_offset_x, preview->image_offset_y);
399         }
400       break;
401
402     case GDK_BUTTON_RELEASE:
403       gdk_display_pointer_ungrab (gtk_widget_get_display (widget),
404                                   event->button.time);
405       start_x = start_y = 0;
406       preview->dragging = FALSE;
407
408       print_preview_queue_draw (preview);
409       break;
410
411     default:
412       break;
413     }
414
415   return FALSE;
416 }
417
418 static GdkPixbuf *get_thumbnail(GdkDrawable *drawable, gint thumb_width, gint thumb_height)
419 {
420   gint width, height;
421   GdkPixbuf *pixbuf;
422   GdkPixbuf *thumbnail;
423
424   gdk_drawable_get_size(drawable, &width, &height);
425   pixbuf = gdk_pixbuf_get_from_drawable(NULL, drawable,
426                                            NULL, 0, 0, 0, 0, width, height);
427   thumbnail = gdk_pixbuf_scale_simple(pixbuf, thumb_width, thumb_height,
428                                       GDK_INTERP_BILINEAR);
429   g_object_unref(pixbuf);
430   return thumbnail;
431 }
432
433 static gboolean
434 vik_print_preview_expose_event (GtkWidget        *widget,
435                                  GdkEventExpose   *eevent,
436                                  VikPrintPreview *preview)
437 {
438   gdouble  paper_width;
439   gdouble  paper_height;
440   gdouble  left_margin;
441   gdouble  right_margin;
442   gdouble  top_margin;
443   gdouble  bottom_margin;
444   gdouble  scale;
445   cairo_t *cr;
446
447   paper_width = gtk_page_setup_get_paper_width (preview->page,
448                                                 GTK_UNIT_POINTS);
449   paper_height = gtk_page_setup_get_paper_height (preview->page,
450                                                   GTK_UNIT_POINTS);
451   vik_print_preview_get_page_margins (preview,
452                                        &left_margin,
453                                        &right_margin,
454                                        &top_margin,
455                                        &bottom_margin);
456
457   cr = gdk_cairo_create (gtk_widget_get_window(widget));
458
459   scale = vik_print_preview_get_scale (preview);
460
461   /* draw background */
462   cairo_scale (cr, scale, scale);
463   gdk_cairo_set_source_color (cr, &gtk_widget_get_style(widget)->white);
464   cairo_rectangle (cr, 0, 0, paper_width, paper_height);
465   cairo_fill (cr);
466
467   /* draw page_margins */
468   gdk_cairo_set_source_color (cr, &gtk_widget_get_style(widget)->black);
469   cairo_rectangle (cr,
470                    left_margin,
471                    top_margin,
472                    paper_width - left_margin - right_margin,
473                    paper_height - top_margin - bottom_margin);
474   cairo_stroke (cr);
475
476   if (preview->dragging)
477     {
478       gint width, height;
479       gdk_drawable_get_size(preview->drawable, &width, &height);
480       cairo_rectangle (cr,
481                        left_margin + preview->image_offset_x,
482                        top_margin  + preview->image_offset_y,
483                        (gdouble) width  * 72.0 / preview->image_xres,
484                        (gdouble) height * 72.0 / preview->image_yres);
485       cairo_stroke (cr);
486     }
487   else
488     {
489       GdkDrawable *drawable = preview->drawable;
490
491       /* draw image */
492       cairo_translate (cr,
493                        left_margin + preview->image_offset_x,
494                        top_margin  + preview->image_offset_y);
495
496       if (preview->pixbuf == NULL)
497         {
498           gint width  = MIN (widget->allocation.width, 1024);
499           gint height = MIN (widget->allocation.height, 1024);
500
501           preview->pixbuf = get_thumbnail(drawable, width, height);
502         }
503
504       if (preview->pixbuf != NULL)
505         {
506           gint width, height;
507           gdk_drawable_get_size(drawable, &width, &height);
508
509           gdouble scale_x = ((gdouble) width /
510                              gdk_pixbuf_get_width (preview->pixbuf));
511           gdouble scale_y = ((gdouble) height /
512                              gdk_pixbuf_get_height (preview->pixbuf));
513
514           scale_x = scale_x * 72.0 / preview->image_xres;
515           scale_y = scale_y * 72.0 / preview->image_yres;
516
517           cairo_scale (cr, scale_x, scale_y);
518
519           gdk_cairo_set_source_pixbuf (cr, preview->pixbuf, 0, 0);
520
521           cairo_paint (cr);
522         }
523     }
524
525   cairo_destroy (cr);
526
527   return FALSE;
528 }
529
530 static gdouble
531 vik_print_preview_get_scale (VikPrintPreview* preview)
532 {
533   gdouble scale_x;
534   gdouble scale_y;
535
536   scale_x = ((gdouble) preview->area->allocation.width /
537              gtk_page_setup_get_paper_width (preview->page, GTK_UNIT_POINTS));
538
539   scale_y = ((gdouble) preview->area->allocation.height /
540              gtk_page_setup_get_paper_height (preview->page, GTK_UNIT_POINTS));
541
542   return MIN (scale_x, scale_y);
543 }
544
545 static void
546 vik_print_preview_size_allocate (GtkWidget        *widget,
547                                   GtkAllocation    *allocation,
548                                   VikPrintPreview *preview)
549 {
550   if (preview->pixbuf != NULL)
551     {
552       g_object_unref (preview->pixbuf);
553       preview->pixbuf = NULL;
554     }
555 }
556
557 static void
558 vik_print_preview_get_page_margins (VikPrintPreview *preview,
559                                      gdouble          *left_margin,
560                                      gdouble          *right_margin,
561                                      gdouble          *top_margin,
562                                      gdouble          *bottom_margin)
563 {
564   if (preview->use_full_page)
565     {
566       *left_margin   = 0.0;
567       *right_margin  = 0.0;
568       *top_margin    = 0.0;
569       *bottom_margin = 0.0;
570     }
571   else
572     {
573       *left_margin   = gtk_page_setup_get_left_margin (preview->page,
574                                                        GTK_UNIT_POINTS);
575       *right_margin  = gtk_page_setup_get_right_margin (preview->page,
576                                                         GTK_UNIT_POINTS);
577       *top_margin    = gtk_page_setup_get_top_margin (preview->page,
578                                                       GTK_UNIT_POINTS);
579       *bottom_margin = gtk_page_setup_get_bottom_margin (preview->page,
580                                                          GTK_UNIT_POINTS);
581     }
582 }
583
584 static void
585 print_preview_queue_draw (VikPrintPreview *preview)
586 {
587   gtk_widget_queue_draw (GTK_WIDGET (preview->area));
588 }