]> git.street.me.uk Git - andy/viking.git/blame - src/print-preview.c
Fix recent commit: 4dc72a1d407b81853d0093871cff45ef1f47d1b8
[andy/viking.git] / src / print-preview.c
CommitLineData
42f34743
QT
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
30enum
31{
32 OFFSETS_CHANGED,
33 LAST_SIGNAL
34};
35
36
37static void vik_print_preview_finalize (GObject *object);
38
39static void vik_print_preview_size_allocate (GtkWidget *widget,
40 GtkAllocation *allocation,
41 VikPrintPreview *preview);
42static void vik_print_preview_realize (GtkWidget *widget);
43static gboolean vik_print_preview_event (GtkWidget *widget,
44 GdkEvent *event,
45 VikPrintPreview *preview);
46
47static gboolean vik_print_preview_expose_event (GtkWidget *widget,
48 GdkEventExpose *eevent,
49 VikPrintPreview *preview);
50
51static gdouble vik_print_preview_get_scale (VikPrintPreview *preview);
52
53static 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
59static void print_preview_queue_draw (VikPrintPreview *preview);
60
61
62G_DEFINE_TYPE (VikPrintPreview, vik_print_preview, GTK_TYPE_ASPECT_FRAME)
63
64#define parent_class vik_print_preview_parent_class
65
66static guint vik_print_preview_signals[LAST_SIGNAL] = { 0 };
67
68
69#define g_marshal_value_peek_double(v) (v)->data[0].v_double
70
71static void
72marshal_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
109static void
110vik_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
130static void
131vik_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
165static void
166vik_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 **/
199GtkWidget *
200vik_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 **/
235void
236vik_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 **/
258void
259vik_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 **/
286void
287vik_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 **/
308void
309vik_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 **/
329void
330vik_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
340static void
341vik_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);
9b082b39 347 gdk_window_set_cursor (gtk_widget_get_window(widget), cursor);
42f34743
QT
348 gdk_cursor_unref (cursor);
349}
350
351static gboolean
352vik_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:
9b082b39 368 gdk_pointer_grab (gtk_widget_get_window(widget), FALSE,
42f34743
QT
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
418static 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
433static gboolean
434vik_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
9b082b39 457 cr = gdk_cairo_create (gtk_widget_get_window(widget));
42f34743
QT
458
459 scale = vik_print_preview_get_scale (preview);
460
461 /* draw background */
462 cairo_scale (cr, scale, scale);
9b082b39 463 gdk_cairo_set_source_color (cr, &gtk_widget_get_style(widget)->white);
42f34743
QT
464 cairo_rectangle (cr, 0, 0, paper_width, paper_height);
465 cairo_fill (cr);
466
467 /* draw page_margins */
9b082b39 468 gdk_cairo_set_source_color (cr, &gtk_widget_get_style(widget)->black);
42f34743
QT
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 {
90ba111c
RN
498 GtkAllocation allocation;
499 gtk_widget_get_allocation ( widget, &allocation );
500 gint width = MIN (allocation.width, 1024);
501 gint height = MIN (allocation.height, 1024);
42f34743
QT
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
532static gdouble
533vik_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
547static void
548vik_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
559static void
560vik_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
586static void
587print_preview_queue_draw (VikPrintPreview *preview)
588{
589 gtk_widget_queue_draw (GTK_WIDGET (preview->area));
590}