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