1 /*
2 * gedit-print-preview.c
3 *
4 * Copyright (C) 2008 Paolo Borelli
5 * Copyright (C) 2015 Sébastien Wilmet
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
18 * along with this program; if not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "gedit-print-preview.h"
22
23 #include <math.h>
24 #include <stdlib.h>
25 #include <glib/gi18n.h>
26 #include <gdk/gdkkeysyms.h>
27 #include <cairo-pdf.h>
28
29 #define PRINTER_DPI (72.0)
30 #define TOOLTIP_THRESHOLD 20
31 #define PAGE_PAD 12
32 #define PAGE_SHADOW_OFFSET 5
33 #define ZOOM_IN_FACTOR (1.2)
34 #define ZOOM_OUT_FACTOR (1.0 / ZOOM_IN_FACTOR)
35
36 struct _GeditPrintPreview
37 {
38 GtkGrid parent_instance;
39
40 GtkPrintOperation *operation;
41 GtkPrintContext *context;
42 GtkPrintOperationPreview *gtk_preview;
43
44 GtkButton *prev_button;
45 GtkButton *next_button;
46 GtkEntry *page_entry;
47 GtkLabel *last_page_label;
48 GtkButton *multi_pages_button;
49 GtkButton *zoom_one_button;
50 GtkButton *zoom_fit_button;
51 GtkButton *zoom_in_button;
52 GtkButton *zoom_out_button;
53 GtkButton *close_button;
54
55 /* The GtkLayout is where the pages are drawn. The layout should have
56 * the focus, because key-press-events and scroll-events are handled on
57 * the layout. It is AFAIK not easily possible to handle those events on
58 * the GeditPrintPreview itself because when a toolbar item has the
59 * focus, some key presses (like the arrows) moves the focus to a
60 * sibling toolbar item instead.
61 */
62 GtkLayout *layout;
63
64 gdouble scale;
65
66 /* multipage support */
67 gint n_columns;
68
69 /* FIXME: handle correctly page selection (e.g. print only
70 * page 1-3, 7 and 12.
71 */
72 guint cur_page; /* starts at 0 */
73
74 gint cursor_x;
75 gint cursor_y;
76
77 guint has_tooltip : 1;
78 };
79
G_DEFINE_TYPE(GeditPrintPreview,gedit_print_preview,GTK_TYPE_GRID)80 G_DEFINE_TYPE (GeditPrintPreview, gedit_print_preview, GTK_TYPE_GRID)
81
82 static void
83 gedit_print_preview_dispose (GObject *object)
84 {
85 GeditPrintPreview *preview = GEDIT_PRINT_PREVIEW (object);
86
87 if (preview->gtk_preview != NULL)
88 {
89 GtkPrintOperationPreview *gtk_preview;
90
91 /* Set preview->gtk_preview to NULL because when calling
92 * end_preview() this dispose() function can be run a second
93 * time.
94 */
95 gtk_preview = preview->gtk_preview;
96 preview->gtk_preview = NULL;
97
98 gtk_print_operation_preview_end_preview (gtk_preview);
99
100 g_object_unref (gtk_preview);
101 }
102
103 g_clear_object (&preview->operation);
104 g_clear_object (&preview->context);
105
106 G_OBJECT_CLASS (gedit_print_preview_parent_class)->dispose (object);
107 }
108
109 static void
gedit_print_preview_grab_focus(GtkWidget * widget)110 gedit_print_preview_grab_focus (GtkWidget *widget)
111 {
112 GeditPrintPreview *preview = GEDIT_PRINT_PREVIEW (widget);
113
114 gtk_widget_grab_focus (GTK_WIDGET (preview->layout));
115 }
116
117 static void
gedit_print_preview_class_init(GeditPrintPreviewClass * klass)118 gedit_print_preview_class_init (GeditPrintPreviewClass *klass)
119 {
120 GObjectClass *object_class = G_OBJECT_CLASS (klass);
121 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
122
123 object_class->dispose = gedit_print_preview_dispose;
124
125 widget_class->grab_focus = gedit_print_preview_grab_focus;
126
127 /* Bind class to template */
128 gtk_widget_class_set_template_from_resource (widget_class,
129 "/org/gnome/gedit/ui/gedit-print-preview.ui");
130 gtk_widget_class_bind_template_child (widget_class, GeditPrintPreview, prev_button);
131 gtk_widget_class_bind_template_child (widget_class, GeditPrintPreview, next_button);
132 gtk_widget_class_bind_template_child (widget_class, GeditPrintPreview, page_entry);
133 gtk_widget_class_bind_template_child (widget_class, GeditPrintPreview, last_page_label);
134 gtk_widget_class_bind_template_child (widget_class, GeditPrintPreview, multi_pages_button);
135 gtk_widget_class_bind_template_child (widget_class, GeditPrintPreview, zoom_one_button);
136 gtk_widget_class_bind_template_child (widget_class, GeditPrintPreview, zoom_fit_button);
137 gtk_widget_class_bind_template_child (widget_class, GeditPrintPreview, zoom_in_button);
138 gtk_widget_class_bind_template_child (widget_class, GeditPrintPreview, zoom_out_button);
139 gtk_widget_class_bind_template_child (widget_class, GeditPrintPreview, close_button);
140 gtk_widget_class_bind_template_child (widget_class, GeditPrintPreview, layout);
141 }
142
143 static gint
get_n_pages(GeditPrintPreview * preview)144 get_n_pages (GeditPrintPreview *preview)
145 {
146 gint n_pages;
147
148 g_object_get (preview->operation, "n-pages", &n_pages, NULL);
149
150 return n_pages;
151 }
152
153 static gdouble
get_screen_dpi(GeditPrintPreview * preview)154 get_screen_dpi (GeditPrintPreview *preview)
155 {
156 GdkScreen *screen;
157 gdouble dpi;
158 static gboolean warning_shown = FALSE;
159
160 screen = gtk_widget_get_screen (GTK_WIDGET (preview));
161
162 if (screen == NULL)
163 {
164 return PRINTER_DPI;
165 }
166
167 dpi = gdk_screen_get_resolution (screen);
168 if (dpi < 30.0 || 600.0 < dpi)
169 {
170 if (!warning_shown)
171 {
172 g_warning ("Invalid the x-resolution for the screen, assuming 96dpi");
173 warning_shown = TRUE;
174 }
175
176 dpi = 96.0;
177 }
178
179 return dpi;
180 }
181
182 /* Get the paper size in points: these must be used only
183 * after the widget has been mapped and the dpi is known.
184 */
185 static gdouble
get_paper_width(GeditPrintPreview * preview)186 get_paper_width (GeditPrintPreview *preview)
187 {
188 GtkPageSetup *page_setup;
189 gdouble paper_width;
190
191 page_setup = gtk_print_context_get_page_setup (preview->context);
192 paper_width = gtk_page_setup_get_paper_width (page_setup, GTK_UNIT_INCH);
193
194 return paper_width * get_screen_dpi (preview);
195 }
196
197 static gdouble
get_paper_height(GeditPrintPreview * preview)198 get_paper_height (GeditPrintPreview *preview)
199 {
200 GtkPageSetup *page_setup;
201 gdouble paper_height;
202
203 page_setup = gtk_print_context_get_page_setup (preview->context);
204 paper_height = gtk_page_setup_get_paper_height (page_setup, GTK_UNIT_INCH);
205
206 return paper_height * get_screen_dpi (preview);
207 }
208
209 /* The tile size is the size in pixels of the area where a page will be
210 * drawn, including the padding. The size is independent of the
211 * orientation.
212 */
213 static void
get_tile_size(GeditPrintPreview * preview,gint * tile_width,gint * tile_height)214 get_tile_size (GeditPrintPreview *preview,
215 gint *tile_width,
216 gint *tile_height)
217 {
218 if (tile_width != NULL)
219 {
220 *tile_width = 2 * PAGE_PAD + round (preview->scale * get_paper_width (preview));
221 }
222
223 if (tile_height != NULL)
224 {
225 *tile_height = 2 * PAGE_PAD + round (preview->scale * get_paper_height (preview));
226 }
227 }
228
229 static void
get_adjustments(GeditPrintPreview * preview,GtkAdjustment ** hadj,GtkAdjustment ** vadj)230 get_adjustments (GeditPrintPreview *preview,
231 GtkAdjustment **hadj,
232 GtkAdjustment **vadj)
233 {
234 *hadj = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (preview->layout));
235 *vadj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (preview->layout));
236 }
237
238 static void
update_layout_size(GeditPrintPreview * preview)239 update_layout_size (GeditPrintPreview *preview)
240 {
241 gint tile_width;
242 gint tile_height;
243
244 get_tile_size (preview, &tile_width, &tile_height);
245
246 /* force size of the drawing area to make the scrolled window work */
247 gtk_layout_set_size (preview->layout,
248 tile_width * preview->n_columns,
249 tile_height);
250
251 gtk_widget_queue_draw (GTK_WIDGET (preview->layout));
252 }
253
254 /* Zoom should always be set with one of these two function
255 * so that the tile size is properly updated.
256 */
257
258 static void
set_zoom_factor(GeditPrintPreview * preview,gdouble zoom)259 set_zoom_factor (GeditPrintPreview *preview,
260 gdouble zoom)
261 {
262 preview->scale = zoom;
263 update_layout_size (preview);
264 }
265
266 static void
set_zoom_fit_to_size(GeditPrintPreview * preview)267 set_zoom_fit_to_size (GeditPrintPreview *preview)
268 {
269 GtkAdjustment *hadj, *vadj;
270 gdouble width, height;
271 gdouble paper_width, paper_height;
272 gdouble zoomx, zoomy;
273
274 get_adjustments (preview, &hadj, &vadj);
275
276 width = gtk_adjustment_get_page_size (hadj);
277 height = gtk_adjustment_get_page_size (vadj);
278
279 width /= preview->n_columns;
280
281 paper_width = get_paper_width (preview);
282 paper_height = get_paper_height (preview);
283
284 zoomx = MAX (1, width - 2 * PAGE_PAD) / paper_width;
285 zoomy = MAX (1, height - 2 * PAGE_PAD) / paper_height;
286
287 set_zoom_factor (preview, zoomx <= zoomy ? zoomx : zoomy);
288 }
289
290 static void
zoom_in(GeditPrintPreview * preview)291 zoom_in (GeditPrintPreview *preview)
292 {
293 set_zoom_factor (preview, preview->scale * ZOOM_IN_FACTOR);
294 }
295
296 static void
zoom_out(GeditPrintPreview * preview)297 zoom_out (GeditPrintPreview *preview)
298 {
299 set_zoom_factor (preview, preview->scale * ZOOM_OUT_FACTOR);
300 }
301
302 static void
goto_page(GeditPrintPreview * preview,gint page)303 goto_page (GeditPrintPreview *preview,
304 gint page)
305 {
306 gchar *page_str;
307 gint n_pages;
308
309 page_str = g_strdup_printf ("%d", page + 1);
310 gtk_entry_set_text (preview->page_entry, page_str);
311 g_free (page_str);
312
313 n_pages = get_n_pages (preview);
314
315 gtk_widget_set_sensitive (GTK_WIDGET (preview->prev_button),
316 page > 0 &&
317 n_pages > 1);
318
319 gtk_widget_set_sensitive (GTK_WIDGET (preview->next_button),
320 page < (n_pages - 1) &&
321 n_pages > 1);
322
323 if (page != preview->cur_page)
324 {
325 preview->cur_page = page;
326 if (n_pages > 0)
327 {
328 gtk_widget_queue_draw (GTK_WIDGET (preview->layout));
329 }
330 }
331 }
332
333 static void
prev_button_clicked(GtkWidget * button,GeditPrintPreview * preview)334 prev_button_clicked (GtkWidget *button,
335 GeditPrintPreview *preview)
336 {
337 GdkEvent *event;
338 gint page;
339
340 event = gtk_get_current_event ();
341
342 if (event->button.state & GDK_SHIFT_MASK)
343 {
344 page = 0;
345 }
346 else
347 {
348 page = preview->cur_page - preview->n_columns;
349 }
350
351 goto_page (preview, MAX (page, 0));
352
353 gtk_widget_grab_focus (GTK_WIDGET (preview->layout));
354
355 gdk_event_free (event);
356 }
357
358 static void
next_button_clicked(GtkWidget * button,GeditPrintPreview * preview)359 next_button_clicked (GtkWidget *button,
360 GeditPrintPreview *preview)
361 {
362 GdkEvent *event;
363 gint page;
364 gint n_pages = get_n_pages (preview);
365
366 event = gtk_get_current_event ();
367
368 if (event->button.state & GDK_SHIFT_MASK)
369 {
370 page = n_pages - 1;
371 }
372 else
373 {
374 page = preview->cur_page + preview->n_columns;
375 }
376
377 goto_page (preview, MIN (page, n_pages - 1));
378
379 gtk_widget_grab_focus (GTK_WIDGET (preview->layout));
380
381 gdk_event_free (event);
382 }
383
384 static void
page_entry_activated(GtkEntry * entry,GeditPrintPreview * preview)385 page_entry_activated (GtkEntry *entry,
386 GeditPrintPreview *preview)
387 {
388 const gchar *text;
389 gint page;
390 gint n_pages = get_n_pages (preview);
391
392 text = gtk_entry_get_text (entry);
393
394 page = CLAMP (atoi (text), 1, n_pages) - 1;
395 goto_page (preview, page);
396
397 gtk_widget_grab_focus (GTK_WIDGET (preview->layout));
398 }
399
400 static void
page_entry_insert_text(GtkEditable * editable,const gchar * text,gint length,gint * position)401 page_entry_insert_text (GtkEditable *editable,
402 const gchar *text,
403 gint length,
404 gint *position)
405 {
406 const gchar *end;
407 const gchar *p;
408
409 end = text + length;
410
411 for (p = text; p < end; p = g_utf8_next_char (p))
412 {
413 if (!g_unichar_isdigit (g_utf8_get_char (p)))
414 {
415 g_signal_stop_emission_by_name (editable, "insert-text");
416 break;
417 }
418 }
419 }
420
421 static gboolean
page_entry_focus_out(GtkEntry * entry,GdkEventFocus * event,GeditPrintPreview * preview)422 page_entry_focus_out (GtkEntry *entry,
423 GdkEventFocus *event,
424 GeditPrintPreview *preview)
425 {
426 const gchar *text;
427 gint page;
428
429 text = gtk_entry_get_text (entry);
430 page = atoi (text) - 1;
431
432 /* Reset the page number only if really needed */
433 if (page != preview->cur_page)
434 {
435 gchar *str;
436
437 str = g_strdup_printf ("%d", preview->cur_page + 1);
438 gtk_entry_set_text (entry, str);
439 g_free (str);
440 }
441
442 return GDK_EVENT_PROPAGATE;
443 }
444
445 static void
on_1x1_clicked(GtkMenuItem * item,GeditPrintPreview * preview)446 on_1x1_clicked (GtkMenuItem *item,
447 GeditPrintPreview *preview)
448 {
449 preview->n_columns = 1;
450 update_layout_size (preview);
451 gtk_widget_grab_focus (GTK_WIDGET (preview->layout));
452 }
453
454 static void
on_1x2_clicked(GtkMenuItem * item,GeditPrintPreview * preview)455 on_1x2_clicked (GtkMenuItem *item,
456 GeditPrintPreview *preview)
457 {
458 preview->n_columns = 2;
459 set_zoom_fit_to_size (preview);
460 gtk_widget_grab_focus (GTK_WIDGET (preview->layout));
461 }
462
463 static void
multi_pages_button_clicked(GtkWidget * button,GeditPrintPreview * preview)464 multi_pages_button_clicked (GtkWidget *button,
465 GeditPrintPreview *preview)
466 {
467 GtkWidget *menu;
468 GtkWidget *item;
469
470 menu = gtk_menu_new ();
471 gtk_widget_show (menu);
472 g_signal_connect (menu,
473 "selection-done",
474 G_CALLBACK (gtk_widget_destroy),
475 NULL);
476
477 item = gtk_menu_item_new_with_label ("1x1");
478 gtk_widget_show (item);
479 gtk_menu_attach (GTK_MENU (menu), item, 0, 1, 0, 1);
480 g_signal_connect (item, "activate", G_CALLBACK (on_1x1_clicked), preview);
481
482 item = gtk_menu_item_new_with_label ("1x2");
483 gtk_widget_show (item);
484 gtk_menu_attach (GTK_MENU (menu), item, 1, 2, 0, 1);
485 g_signal_connect (item, "activate", G_CALLBACK (on_1x2_clicked), preview);
486
487 gtk_menu_popup_at_pointer (GTK_MENU (menu), NULL);
488 }
489
490 static void
zoom_one_button_clicked(GtkWidget * button,GeditPrintPreview * preview)491 zoom_one_button_clicked (GtkWidget *button,
492 GeditPrintPreview *preview)
493 {
494 set_zoom_factor (preview, 1);
495 gtk_widget_grab_focus (GTK_WIDGET (preview->layout));
496 }
497
498 static void
zoom_fit_button_clicked(GtkWidget * button,GeditPrintPreview * preview)499 zoom_fit_button_clicked (GtkWidget *button,
500 GeditPrintPreview *preview)
501 {
502 set_zoom_fit_to_size (preview);
503 gtk_widget_grab_focus (GTK_WIDGET (preview->layout));
504 }
505
506 static void
zoom_in_button_clicked(GtkWidget * button,GeditPrintPreview * preview)507 zoom_in_button_clicked (GtkWidget *button,
508 GeditPrintPreview *preview)
509 {
510 zoom_in (preview);
511 gtk_widget_grab_focus (GTK_WIDGET (preview->layout));
512 }
513
514 static void
zoom_out_button_clicked(GtkWidget * button,GeditPrintPreview * preview)515 zoom_out_button_clicked (GtkWidget *button,
516 GeditPrintPreview *preview)
517 {
518 zoom_out (preview);
519 gtk_widget_grab_focus (GTK_WIDGET (preview->layout));
520 }
521
522 static void
close_button_clicked(GtkWidget * button,GeditPrintPreview * preview)523 close_button_clicked (GtkWidget *button,
524 GeditPrintPreview *preview)
525 {
526 gtk_widget_destroy (GTK_WIDGET (preview));
527 }
528
529 static gboolean
scroll_event_activated(GtkWidget * widget,GdkEventScroll * event,GeditPrintPreview * preview)530 scroll_event_activated (GtkWidget *widget,
531 GdkEventScroll *event,
532 GeditPrintPreview *preview)
533 {
534 if (event->state & GDK_CONTROL_MASK)
535 {
536 if ((event->direction == GDK_SCROLL_UP) ||
537 (event->direction == GDK_SCROLL_SMOOTH &&
538 event->delta_y < 0))
539 {
540 zoom_in (preview);
541 }
542 else if ((event->direction == GDK_SCROLL_DOWN) ||
543 (event->direction == GDK_SCROLL_SMOOTH &&
544 event->delta_y > 0))
545 {
546 zoom_out (preview);
547 }
548
549 return GDK_EVENT_STOP;
550 }
551
552 return GDK_EVENT_PROPAGATE;
553 }
554
555 static gint
get_first_page_displayed(GeditPrintPreview * preview)556 get_first_page_displayed (GeditPrintPreview *preview)
557 {
558 return preview->cur_page - (preview->cur_page % preview->n_columns);
559 }
560
561 /* Returns the page number (starting from 0) or -1 if no page. */
562 static gint
get_page_at_coords(GeditPrintPreview * preview,gint x,gint y)563 get_page_at_coords (GeditPrintPreview *preview,
564 gint x,
565 gint y)
566 {
567 gint tile_width, tile_height;
568 GtkAdjustment *hadj, *vadj;
569 gint col, page;
570
571 get_tile_size (preview, &tile_width, &tile_height);
572
573 if (tile_height <= 0 || tile_width <= 0)
574 {
575 return -1;
576 }
577
578 get_adjustments (preview, &hadj, &vadj);
579
580 x += gtk_adjustment_get_value (hadj);
581 y += gtk_adjustment_get_value (vadj);
582
583 col = x / tile_width;
584
585 if (col >= preview->n_columns || y > tile_height)
586 {
587 return -1;
588 }
589
590 page = get_first_page_displayed (preview) + col;
591
592 if (page >= get_n_pages (preview))
593 {
594 return -1;
595 }
596
597 /* FIXME: we could try to be picky and check if we actually are inside
598 * the page (i.e. not in the padding or shadow).
599 */
600 return page;
601 }
602
603 static gboolean
on_preview_layout_motion_notify(GtkWidget * widget,GdkEvent * event,GeditPrintPreview * preview)604 on_preview_layout_motion_notify (GtkWidget *widget,
605 GdkEvent *event,
606 GeditPrintPreview *preview)
607 {
608 gint temp_x;
609 gint temp_y;
610 gint diff_x;
611 gint diff_y;
612
613 temp_x = ((GdkEventMotion*)event)->x;
614 temp_y = ((GdkEventMotion*)event)->y;
615 diff_x = abs (temp_x - preview->cursor_x);
616 diff_y = abs (temp_y - preview->cursor_y);
617
618 if ((diff_x >= TOOLTIP_THRESHOLD) || (diff_y >= TOOLTIP_THRESHOLD))
619 {
620 preview->has_tooltip = FALSE;
621 preview->cursor_x = temp_x;
622 preview->cursor_y = temp_y;
623 }
624 else
625 {
626 preview->has_tooltip = TRUE;
627 }
628
629 return GDK_EVENT_STOP;
630 }
631
632 static gboolean
preview_layout_query_tooltip(GtkWidget * widget,gint x,gint y,gboolean keyboard_tip,GtkTooltip * tooltip,GeditPrintPreview * preview)633 preview_layout_query_tooltip (GtkWidget *widget,
634 gint x,
635 gint y,
636 gboolean keyboard_tip,
637 GtkTooltip *tooltip,
638 GeditPrintPreview *preview)
639 {
640 if (preview->has_tooltip)
641 {
642 gint page;
643 gchar *tip;
644
645 page = get_page_at_coords (preview, x, y);
646 if (page < 0)
647 {
648 return FALSE;
649 }
650
651 tip = g_strdup_printf (_("Page %d of %d"),
652 page + 1,
653 get_n_pages (preview));
654
655 gtk_tooltip_set_text (tooltip, tip);
656 g_free (tip);
657
658 return TRUE;
659 }
660 else
661 {
662 preview->has_tooltip = TRUE;
663 return FALSE;
664 }
665 }
666
667 static gint
preview_layout_key_press(GtkWidget * widget,GdkEventKey * event,GeditPrintPreview * preview)668 preview_layout_key_press (GtkWidget *widget,
669 GdkEventKey *event,
670 GeditPrintPreview *preview)
671 {
672 GtkAdjustment *hadj, *vadj;
673 gdouble x, y;
674 gdouble hlower, vlower;
675 gdouble hupper, vupper;
676 gdouble visible_width, visible_height;
677 gdouble hstep, vstep;
678 gint n_pages;
679 gboolean do_move = FALSE;
680
681 get_adjustments (preview, &hadj, &vadj);
682
683 x = gtk_adjustment_get_value (hadj);
684 y = gtk_adjustment_get_value (vadj);
685
686 hlower = gtk_adjustment_get_lower (hadj);
687 vlower = gtk_adjustment_get_lower (vadj);
688
689 hupper = gtk_adjustment_get_upper (hadj);
690 vupper = gtk_adjustment_get_upper (vadj);
691
692 visible_width = gtk_adjustment_get_page_size (hadj);
693 visible_height = gtk_adjustment_get_page_size (vadj);
694
695 hstep = 10;
696 vstep = 10;
697
698 n_pages = get_n_pages (preview);
699
700 switch (event->keyval)
701 {
702 case '1':
703 set_zoom_fit_to_size (preview);
704 break;
705
706 case '+':
707 case '=':
708 case GDK_KEY_KP_Add:
709 zoom_in (preview);
710 break;
711
712 case '-':
713 case '_':
714 case GDK_KEY_KP_Subtract:
715 zoom_out (preview);
716 break;
717
718 case GDK_KEY_KP_Right:
719 case GDK_KEY_Right:
720 if (event->state & GDK_SHIFT_MASK)
721 x = hupper - visible_width;
722 else
723 x = MIN (hupper - visible_width, x + hstep);
724 do_move = TRUE;
725 break;
726
727 case GDK_KEY_KP_Left:
728 case GDK_KEY_Left:
729 if (event->state & GDK_SHIFT_MASK)
730 x = hlower;
731 else
732 x = MAX (hlower, x - hstep);
733 do_move = TRUE;
734 break;
735
736 case GDK_KEY_KP_Up:
737 case GDK_KEY_Up:
738 if (event->state & GDK_SHIFT_MASK)
739 goto page_up;
740
741 y = MAX (vlower, y - vstep);
742 do_move = TRUE;
743 break;
744
745 case GDK_KEY_KP_Down:
746 case GDK_KEY_Down:
747 if (event->state & GDK_SHIFT_MASK)
748 goto page_down;
749
750 y = MIN (vupper - visible_height, y + vstep);
751 do_move = TRUE;
752 break;
753
754 case GDK_KEY_KP_Page_Up:
755 case GDK_KEY_Page_Up:
756 case GDK_KEY_Delete:
757 case GDK_KEY_KP_Delete:
758 case GDK_KEY_BackSpace:
759 page_up:
760 if (y <= vlower)
761 {
762 if (preview->cur_page > 0)
763 {
764 goto_page (preview, preview->cur_page - 1);
765 y = (vupper - visible_height);
766 }
767 }
768 else
769 {
770 y = vlower;
771 }
772 do_move = TRUE;
773 break;
774
775 case GDK_KEY_KP_Page_Down:
776 case GDK_KEY_Page_Down:
777 case ' ':
778 page_down:
779 if (y >= (vupper - visible_height))
780 {
781 if (preview->cur_page < n_pages - 1)
782 {
783 goto_page (preview, preview->cur_page + 1);
784 y = vlower;
785 }
786 }
787 else
788 {
789 y = (vupper - visible_height);
790 }
791 do_move = TRUE;
792 break;
793
794 case GDK_KEY_KP_Home:
795 case GDK_KEY_Home:
796 goto_page (preview, 0);
797 y = vlower;
798 do_move = TRUE;
799 break;
800
801 case GDK_KEY_KP_End:
802 case GDK_KEY_End:
803 goto_page (preview, n_pages - 1);
804 y = vlower;
805 do_move = TRUE;
806 break;
807
808 case GDK_KEY_Escape:
809 gtk_widget_destroy (GTK_WIDGET (preview));
810 break;
811
812 case 'p':
813 if (event->state & GDK_MOD1_MASK)
814 {
815 gtk_widget_grab_focus (GTK_WIDGET (preview->page_entry));
816 }
817 break;
818
819 default:
820 return GDK_EVENT_PROPAGATE;
821 }
822
823 if (do_move)
824 {
825 gtk_adjustment_set_value (hadj, x);
826 gtk_adjustment_set_value (vadj, y);
827 }
828
829 return GDK_EVENT_STOP;
830 }
831
832 static void
gedit_print_preview_init(GeditPrintPreview * preview)833 gedit_print_preview_init (GeditPrintPreview *preview)
834 {
835 preview->cur_page = 0;
836 preview->scale = 1.0;
837 preview->n_columns = 1;
838 preview->cursor_x = 0;
839 preview->cursor_y = 0;
840 preview->has_tooltip = TRUE;
841
842 gtk_widget_init_template (GTK_WIDGET (preview));
843
844 g_signal_connect (preview->prev_button,
845 "clicked",
846 G_CALLBACK (prev_button_clicked),
847 preview);
848
849 g_signal_connect (preview->next_button,
850 "clicked",
851 G_CALLBACK (next_button_clicked),
852 preview);
853
854 g_signal_connect (preview->page_entry,
855 "activate",
856 G_CALLBACK (page_entry_activated),
857 preview);
858
859 g_signal_connect (preview->page_entry,
860 "insert-text",
861 G_CALLBACK (page_entry_insert_text),
862 NULL);
863
864 g_signal_connect (preview->page_entry,
865 "focus-out-event",
866 G_CALLBACK (page_entry_focus_out),
867 preview);
868
869 g_signal_connect (preview->multi_pages_button,
870 "clicked",
871 G_CALLBACK (multi_pages_button_clicked),
872 preview);
873
874 g_signal_connect (preview->zoom_one_button,
875 "clicked",
876 G_CALLBACK (zoom_one_button_clicked),
877 preview);
878
879 g_signal_connect (preview->zoom_fit_button,
880 "clicked",
881 G_CALLBACK (zoom_fit_button_clicked),
882 preview);
883
884 g_signal_connect (preview->zoom_in_button,
885 "clicked",
886 G_CALLBACK (zoom_in_button_clicked),
887 preview);
888
889 g_signal_connect (preview->zoom_out_button,
890 "clicked",
891 G_CALLBACK (zoom_out_button_clicked),
892 preview);
893
894 g_signal_connect (preview->close_button,
895 "clicked",
896 G_CALLBACK (close_button_clicked),
897 preview);
898
899 g_signal_connect (preview->layout,
900 "query-tooltip",
901 G_CALLBACK (preview_layout_query_tooltip),
902 preview);
903
904 g_signal_connect (preview->layout,
905 "key-press-event",
906 G_CALLBACK (preview_layout_key_press),
907 preview);
908
909 g_signal_connect (preview->layout,
910 "scroll-event",
911 G_CALLBACK (scroll_event_activated),
912 preview);
913
914 /* hide the tooltip once we move the cursor, since gtk does not do it for us */
915 g_signal_connect (preview->layout,
916 "motion-notify-event",
917 G_CALLBACK (on_preview_layout_motion_notify),
918 preview);
919
920 gtk_widget_grab_focus (GTK_WIDGET (preview->layout));
921 }
922
923 static void
draw_page_content(cairo_t * cr,gint page_number,GeditPrintPreview * preview)924 draw_page_content (cairo_t *cr,
925 gint page_number,
926 GeditPrintPreview *preview)
927 {
928 gdouble dpi;
929
930 /* scale to the desired size */
931 cairo_scale (cr, preview->scale, preview->scale);
932
933 dpi = get_screen_dpi (preview);
934 gtk_print_context_set_cairo_context (preview->context, cr, dpi, dpi);
935
936 gtk_print_operation_preview_render_page (preview->gtk_preview,
937 page_number);
938 }
939
940 /* For the frame, we scale and rotate manually, since
941 * the line width should not depend on the zoom and
942 * the drop shadow should be on the bottom right no matter
943 * the orientation.
944 */
945 static void
draw_page_frame(cairo_t * cr,GeditPrintPreview * preview)946 draw_page_frame (cairo_t *cr,
947 GeditPrintPreview *preview)
948 {
949 gdouble width;
950 gdouble height;
951
952 width = get_paper_width (preview) * preview->scale;
953 height = get_paper_height (preview) * preview->scale;
954
955 /* drop shadow */
956 cairo_set_source_rgb (cr, 0, 0, 0);
957 cairo_rectangle (cr,
958 PAGE_SHADOW_OFFSET, PAGE_SHADOW_OFFSET,
959 width, height);
960 cairo_fill (cr);
961
962 /* page frame */
963 cairo_set_source_rgb (cr, 1, 1, 1);
964 cairo_rectangle (cr,
965 0, 0,
966 width, height);
967 cairo_fill_preserve (cr);
968 cairo_set_source_rgb (cr, 0, 0, 0);
969 cairo_set_line_width (cr, 1);
970 cairo_stroke (cr);
971 }
972
973 static void
draw_page(cairo_t * cr,gdouble x,gdouble y,gint page_number,GeditPrintPreview * preview)974 draw_page (cairo_t *cr,
975 gdouble x,
976 gdouble y,
977 gint page_number,
978 GeditPrintPreview *preview)
979 {
980 cairo_save (cr);
981
982 /* move to the page top left corner */
983 cairo_translate (cr, x + PAGE_PAD, y + PAGE_PAD);
984
985 draw_page_frame (cr, preview);
986 draw_page_content (cr, page_number, preview);
987
988 cairo_restore (cr);
989 }
990
991 static gboolean
preview_draw(GtkWidget * widget,cairo_t * cr,GeditPrintPreview * preview)992 preview_draw (GtkWidget *widget,
993 cairo_t *cr,
994 GeditPrintPreview *preview)
995 {
996 GdkWindow *bin_window;
997 gint tile_width;
998 gint page_num;
999 gint n_pages;
1000 gint col;
1001
1002 bin_window = gtk_layout_get_bin_window (preview->layout);
1003
1004 if (!gtk_cairo_should_draw_window (cr, bin_window))
1005 {
1006 return GDK_EVENT_STOP;
1007 }
1008
1009 cairo_save (cr);
1010
1011 gtk_cairo_transform_to_window (cr, widget, bin_window);
1012
1013 get_tile_size (preview, &tile_width, NULL);
1014 n_pages = get_n_pages (preview);
1015
1016 col = 0;
1017 page_num = get_first_page_displayed (preview);
1018
1019 while (col < preview->n_columns && page_num < n_pages)
1020 {
1021 if (!gtk_print_operation_preview_is_selected (preview->gtk_preview, page_num))
1022 {
1023 page_num++;
1024 continue;
1025 }
1026
1027 draw_page (cr,
1028 col * tile_width,
1029 0,
1030 page_num,
1031 preview);
1032
1033 col++;
1034 page_num++;
1035 }
1036
1037 cairo_restore (cr);
1038
1039 return GDK_EVENT_STOP;
1040 }
1041
1042 static void
init_last_page_label(GeditPrintPreview * preview)1043 init_last_page_label (GeditPrintPreview *preview)
1044 {
1045 gchar *str;
1046
1047 str = g_strdup_printf ("%d", get_n_pages (preview));
1048 gtk_label_set_text (preview->last_page_label, str);
1049 g_free (str);
1050 }
1051
1052 static void
preview_ready(GtkPrintOperationPreview * gtk_preview,GtkPrintContext * context,GeditPrintPreview * preview)1053 preview_ready (GtkPrintOperationPreview *gtk_preview,
1054 GtkPrintContext *context,
1055 GeditPrintPreview *preview)
1056 {
1057 init_last_page_label (preview);
1058 goto_page (preview, 0);
1059
1060 set_zoom_factor (preview, 1.0);
1061
1062 /* let the default gtklayout handler clear the background */
1063 g_signal_connect_after (preview->layout,
1064 "draw",
1065 G_CALLBACK (preview_draw),
1066 preview);
1067
1068 gtk_widget_queue_draw (GTK_WIDGET (preview->layout));
1069 }
1070
1071 /* HACK: we need a dummy surface to paginate... can we use something simpler? */
1072
1073 static cairo_status_t
dummy_write_func(G_GNUC_UNUSED gpointer closure,G_GNUC_UNUSED const guchar * data,G_GNUC_UNUSED guint length)1074 dummy_write_func (G_GNUC_UNUSED gpointer closure,
1075 G_GNUC_UNUSED const guchar *data,
1076 G_GNUC_UNUSED guint length)
1077 {
1078 return CAIRO_STATUS_SUCCESS;
1079 }
1080
1081 static cairo_surface_t *
create_preview_surface_platform(GtkPaperSize * paper_size,gdouble * dpi_x,gdouble * dpi_y)1082 create_preview_surface_platform (GtkPaperSize *paper_size,
1083 gdouble *dpi_x,
1084 gdouble *dpi_y)
1085 {
1086 gdouble width, height;
1087
1088 width = gtk_paper_size_get_width (paper_size, GTK_UNIT_POINTS);
1089 height = gtk_paper_size_get_height (paper_size, GTK_UNIT_POINTS);
1090
1091 *dpi_x = *dpi_y = PRINTER_DPI;
1092
1093 return cairo_pdf_surface_create_for_stream (dummy_write_func, NULL,
1094 width, height);
1095 }
1096
1097 static cairo_surface_t *
create_preview_surface(GeditPrintPreview * preview,gdouble * dpi_x,gdouble * dpi_y)1098 create_preview_surface (GeditPrintPreview *preview,
1099 gdouble *dpi_x,
1100 gdouble *dpi_y)
1101 {
1102 GtkPageSetup *page_setup;
1103 GtkPaperSize *paper_size;
1104
1105 page_setup = gtk_print_context_get_page_setup (preview->context);
1106
1107 /* Note: gtk_page_setup_get_paper_size() swaps width and height for
1108 * landscape.
1109 */
1110 paper_size = gtk_page_setup_get_paper_size (page_setup);
1111
1112 return create_preview_surface_platform (paper_size, dpi_x, dpi_y);
1113 }
1114
1115 GtkWidget *
gedit_print_preview_new(GtkPrintOperation * operation,GtkPrintOperationPreview * gtk_preview,GtkPrintContext * context)1116 gedit_print_preview_new (GtkPrintOperation *operation,
1117 GtkPrintOperationPreview *gtk_preview,
1118 GtkPrintContext *context)
1119 {
1120 GeditPrintPreview *preview;
1121 cairo_surface_t *surface;
1122 cairo_t *cr;
1123 gdouble dpi_x, dpi_y;
1124
1125 g_return_val_if_fail (GTK_IS_PRINT_OPERATION (operation), NULL);
1126 g_return_val_if_fail (GTK_IS_PRINT_OPERATION_PREVIEW (gtk_preview), NULL);
1127
1128 preview = g_object_new (GEDIT_TYPE_PRINT_PREVIEW, NULL);
1129
1130 preview->operation = g_object_ref (operation);
1131 preview->gtk_preview = g_object_ref (gtk_preview);
1132 preview->context = g_object_ref (context);
1133
1134 /* FIXME: is this legal?? */
1135 gtk_print_operation_set_unit (operation, GTK_UNIT_POINTS);
1136
1137 g_signal_connect_object (gtk_preview,
1138 "ready",
1139 G_CALLBACK (preview_ready),
1140 preview,
1141 0);
1142
1143 /* FIXME: we need a cr to paginate... but we can't get the drawing
1144 * area surface because it's not there yet... for now I create
1145 * a dummy pdf surface.
1146 * gtk_print_context_set_cairo_context() should be called in the
1147 * got-page-size handler.
1148 */
1149 surface = create_preview_surface (preview, &dpi_x, &dpi_y);
1150 cr = cairo_create (surface);
1151 gtk_print_context_set_cairo_context (context, cr, dpi_x, dpi_y);
1152 cairo_destroy (cr);
1153 cairo_surface_destroy (surface);
1154
1155 return GTK_WIDGET (preview);
1156 }
1157
1158 /* ex:set ts=8 noet: */
1159