Evince
Evince is a document viewer capable of displaying multiple and single page document formats like PDF and Postscript.
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
ev-view-presentation.c
Go to the documentation of this file.
1 /* ev-view-presentation.c
2  * this file is part of evince, a gnome document viewer
3  *
4  * Copyright (C) 2010 Carlos Garcia Campos <carlosgc@gnome.org>
5  *
6  * Evince is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * Evince is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  */
20 
21 #include "config.h"
22 
23 #include <stdlib.h>
24 #include <glib/gi18n-lib.h>
25 #include <gdk/gdkkeysyms.h>
26 
27 #include "ev-view-presentation.h"
28 #include "ev-jobs.h"
29 #include "ev-job-scheduler.h"
31 #include "ev-view-cursor.h"
32 #include "ev-page-cache.h"
33 
34 enum {
40 };
41 
42 enum {
47 };
48 
49 typedef enum {
55 
57 {
58  GtkWidget base;
59 
60  guint is_constructing : 1;
61 
62  guint current_page;
63  cairo_surface_t *current_surface;
65  guint rotation;
66  gboolean inverted_colors;
70 
71  /* Cursors */
74 
75  /* Goto Window */
76  GtkWidget *goto_window;
77  GtkWidget *goto_entry;
78 
79  /* Page Transition */
81 
82  /* Animations */
85 
86  /* Links */
88 
92 };
93 
95 {
96  GtkWidgetClass base_class;
97 
98  /* signals */
99  void (* change_page) (EvViewPresentation *pview,
100  GtkScrollType scroll);
101  void (* finished) (EvViewPresentation *pview);
103  EvLinkAction *action);
104 };
105 
106 static guint signals[N_SIGNALS] = { 0 };
107 
109  gdouble x,
110  gdouble y);
111 
112 #define HIDE_CURSOR_TIMEOUT 5
113 
114 G_DEFINE_TYPE (EvViewPresentation, ev_view_presentation, GTK_TYPE_WIDGET)
115 
116 #if !GTK_CHECK_VERSION(3, 20, 0)
117 static GdkRGBA black = { 0., 0., 0., 1. };
118 static GdkRGBA white = { 1., 1., 1., 1. };
119 #endif
120 
121 static void
123 {
124  GtkWidget *widget = GTK_WIDGET (pview);
125 
126  if (pview->state == EV_PRESENTATION_NORMAL)
127  return;
128 
129  pview->state = EV_PRESENTATION_NORMAL;
130 #if GTK_CHECK_VERSION(3, 20, 0)
131  gtk_style_context_remove_class (gtk_widget_get_style_context (widget),
132  "white-mode");
133 #else
134  gdk_window_set_background_rgba (gtk_widget_get_window (widget), &black);
135  gtk_widget_queue_draw (widget);
136 #endif
137 }
138 
139 static void
141 {
142  GtkWidget *widget = GTK_WIDGET (pview);
143 
144  if (pview->state == EV_PRESENTATION_BLACK)
145  return;
146 
147  pview->state = EV_PRESENTATION_BLACK;
148 #if GTK_CHECK_VERSION(3, 20, 0)
149  gtk_style_context_remove_class (gtk_widget_get_style_context (widget),
150  "white-mode");
151 #else
152  gdk_window_set_background_rgba (gtk_widget_get_window (widget), &black);
153  gtk_widget_queue_draw (widget);
154 #endif
155 }
156 
157 static void
159 {
160  GtkWidget *widget = GTK_WIDGET (pview);
161 
162  if (pview->state == EV_PRESENTATION_WHITE)
163  return;
164 
165  pview->state = EV_PRESENTATION_WHITE;
166 #if GTK_CHECK_VERSION(3, 20, 0)
167  gtk_style_context_add_class (gtk_widget_get_style_context (widget),
168  "white-mode");
169 #else
170  gdk_window_set_background_rgba (gtk_widget_get_window (widget), &white);
171  gtk_widget_queue_draw (widget);
172 #endif
173 }
174 
175 static void
177 {
178  GtkWidget *widget = GTK_WIDGET (pview);
179 
180  if (pview->state == EV_PRESENTATION_END)
181  return;
182 
183  pview->state = EV_PRESENTATION_END;
184  gtk_widget_queue_draw (widget);
185 }
186 
187 static void
189  guint page,
190  int *view_width,
191  int *view_height)
192 {
193  gdouble width, height;
194 
195  ev_document_get_page_size (pview->document, page, &width, &height);
196  if (pview->rotation == 90 || pview->rotation == 270) {
197  gdouble tmp;
198 
199  tmp = width;
200  width = height;
201  height = tmp;
202  }
203 
204  if (pview->monitor_width / width < pview->monitor_height / height) {
205  *view_width = pview->monitor_width;
206  *view_height = (int)((pview->monitor_width / width) * height + 0.5);
207  } else {
208  *view_width = (int)((pview->monitor_height / height) * width + 0.5);
209  *view_height = pview->monitor_height;
210  }
211 }
212 
213 static void
215  GdkRectangle *area)
216 {
217  GtkWidget *widget = GTK_WIDGET (pview);
218  GtkAllocation allocation;
219  gint view_width, view_height;
220 
222  &view_width, &view_height);
223 
224  gtk_widget_get_allocation (widget, &allocation);
225 
226  area->x = (MAX (0, allocation.width - view_width)) / 2;
227  area->y = (MAX (0, allocation.height - view_height)) / 2;
228  area->width = view_width;
229  area->height = view_height;
230 }
231 
232 /* Page Transition */
233 static gboolean
235 {
237 
238  return FALSE;
239 }
240 
241 static void
243 {
244  if (pview->trans_timeout_id > 0)
245  g_source_remove (pview->trans_timeout_id);
246  pview->trans_timeout_id = 0;
247 }
248 
249 static void
251 {
252  gdouble duration;
253 
254  if (!EV_IS_DOCUMENT_TRANSITION (pview->document))
255  return;
256 
258 
260  pview->current_page);
261  if (duration >= 0) {
262  pview->trans_timeout_id =
263  g_timeout_add_seconds (duration,
264  (GSourceFunc) transition_next_page,
265  pview);
266  }
267 }
268 
269 /* Animations */
270 static void
272 {
273  if (pview->animation) {
274  g_object_unref (pview->animation);
275  pview->animation = NULL;
276  }
277 }
278 
279 static void
281 {
284  gtk_widget_queue_draw (GTK_WIDGET (pview));
285 }
286 
287 static void
289  gdouble progress)
290 {
291  gtk_widget_queue_draw (GTK_WIDGET (pview));
292 }
293 
294 static cairo_surface_t *
296  EvJob *job)
297 {
298  cairo_surface_t *surface;
299 
300  if (!job)
301  return NULL;
302 
303  surface = EV_JOB_RENDER(job)->surface;
304  if (!surface)
305  return NULL;
306 
307 #ifdef HAVE_HIDPI_SUPPORT
308  {
309  int scale_factor = gtk_widget_get_scale_factor (GTK_WIDGET (pview));
310  cairo_surface_set_device_scale (surface, scale_factor, scale_factor);
311  }
312 #endif
313 
314  return surface;
315 }
316 
317 static void
319  gint new_page)
320 {
321  EvTransitionEffect *effect = NULL;
322  EvJob *job;
323  cairo_surface_t *surface;
324  gint jump;
325 
326  if (!pview->enable_animations)
327  return;
328 
329  if (pview->current_page == new_page)
330  return;
331 
333  new_page);
334  if (!effect)
335  return;
336 
337  pview->animation = ev_transition_animation_new (effect);
338 
339  surface = pview->curr_job ? EV_JOB_RENDER (pview->curr_job)->surface : NULL;
341  surface != NULL ?
342  surface : pview->current_surface);
343 
344  jump = new_page - pview->current_page;
345  if (jump == -1)
346  job = pview->prev_job;
347  else if (jump == 1)
348  job = pview->next_job;
349  else
350  job = NULL;
351  surface = get_surface_from_job (pview, job);
352  if (surface)
354 
355  g_signal_connect_swapped (pview->animation, "frame",
357  pview);
358  g_signal_connect_swapped (pview->animation, "finished",
360  pview);
361 }
362 
363 /* Page Navigation */
364 static void
366  EvViewPresentation *pview)
367 {
368  EvJobRender *job_render = EV_JOB_RENDER (job);
369 
370  if (pview->inverted_colors)
372 
373  if (job != pview->curr_job)
374  return;
375 
376  if (pview->animation) {
378  get_surface_from_job (pview, job));
379  } else {
381  gtk_widget_queue_draw (GTK_WIDGET (pview));
382  }
383 }
384 
385 static EvJob *
387  gint page,
388  EvJobPriority priority)
389 {
390  EvJob *job;
391  int view_width, view_height;
392 
393  if (page < 0 || page >= ev_document_get_n_pages (pview->document))
394  return NULL;
395 
396  ev_view_presentation_get_view_size (pview, page, &view_width, &view_height);
397 #ifdef HAVE_HIDPI_SUPPORT
398  {
399  gint device_scale = gtk_widget_get_scale_factor (GTK_WIDGET (pview));
400  view_width *= device_scale;
401  view_height *= device_scale;
402  }
403 #endif
404  job = ev_job_render_new (pview->document, page, pview->rotation, 0.,
405  view_width, view_height);
406  g_signal_connect (job, "finished",
407  G_CALLBACK (job_finished_cb),
408  pview);
409  ev_job_scheduler_push_job (job, priority);
410 
411  return job;
412 }
413 
414 static void
416  EvJob *job)
417 {
418  if (!job)
419  return;
420 
421  g_signal_handlers_disconnect_by_func (job, job_finished_cb, pview);
422  ev_job_cancel (job);
423  g_object_unref (job);
424 }
425 
426 static void
428 {
429  if (pview->curr_job) {
431  pview->curr_job = NULL;
432  }
433 
434  if (pview->prev_job) {
436  pview->prev_job = NULL;
437  }
438 
439  if (pview->next_job) {
441  pview->next_job = NULL;
442  }
443 }
444 
445 static void
447  guint page)
448 {
449  gint jump;
450 
451  if (page < 0 || page >= ev_document_get_n_pages (pview->document))
452  return;
453 
456 
457  jump = page - pview->current_page;
458 
459  switch (jump) {
460  case 0:
461  if (!pview->curr_job)
463  if (!pview->next_job)
465  if (!pview->prev_job)
467  break;
468  case -1:
470  pview->next_job = pview->curr_job;
471  pview->curr_job = pview->prev_job;
472 
473  if (!pview->curr_job)
475  else
479 
480  break;
481  case 1:
483  pview->prev_job = pview->curr_job;
484  pview->curr_job = pview->next_job;
485 
486  if (!pview->curr_job)
488  else
492 
493  break;
494  case -2:
497  pview->next_job = pview->prev_job;
498 
501  if (!pview->next_job)
503  else
505  break;
506  case 2:
509  pview->prev_job = pview->next_job;
510 
513  if (!pview->prev_job)
515  else
517  break;
518  default:
522 
524  if (jump > 0) {
527  } else {
530  }
531  }
532 
533  if (pview->current_page != page) {
534  pview->current_page = page;
535  g_object_notify (G_OBJECT (pview), "current-page");
536  }
537 
538  if (pview->page_cache)
539  ev_page_cache_set_page_range (pview->page_cache, page, page);
540 
541  if (pview->cursor != EV_VIEW_CURSOR_HIDDEN) {
542  gint x, y;
543 
544  ev_document_misc_get_pointer_position (GTK_WIDGET (pview), &x, &y);
546  }
547 
548  if (EV_JOB_RENDER (pview->curr_job)->surface)
549  gtk_widget_queue_draw (GTK_WIDGET (pview));
550 }
551 
552 static void
554  guint page)
555 {
556  if (pview->current_page == page)
557  return;
558 
559  if (!gtk_widget_get_realized (GTK_WIDGET (pview))) {
560  pview->current_page = page;
561  g_object_notify (G_OBJECT (pview), "current-page");
562  } else {
564  }
565 }
566 
567 void
569 {
570  guint n_pages;
571  gint new_page;
572 
573  switch (pview->state) {
577  case EV_PRESENTATION_END:
578  return;
580  break;
581  }
582 
583  n_pages = ev_document_get_n_pages (pview->document);
584  new_page = pview->current_page + 1;
585 
586  if (new_page == n_pages)
588  else
590 }
591 
592 void
594 {
595  gint new_page = 0;
596 
597  switch (pview->state) {
601  return;
602  case EV_PRESENTATION_END:
603  pview->state = EV_PRESENTATION_NORMAL;
604  new_page = pview->current_page;
605  break;
607  new_page = pview->current_page - 1;
608  break;
609  }
610 
612 }
613 
614 /* Goto Window */
615 #define KEY_IS_NUMERIC(keyval) \
616  ((keyval >= GDK_KEY_0 && keyval <= GDK_KEY_9) || (keyval >= GDK_KEY_KP_0 && keyval <= GDK_KEY_KP_9))
617 
618 /* Cut and paste from gtkwindow.c */
619 static void
620 send_focus_change (GtkWidget *widget,
621  gboolean in)
622 {
623  GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE);
624 
625  fevent->focus_change.type = GDK_FOCUS_CHANGE;
626  fevent->focus_change.window = gtk_widget_get_window (widget);
627  fevent->focus_change.in = in;
628  if (fevent->focus_change.window)
629  g_object_ref (fevent->focus_change.window);
630 
631  gtk_widget_send_focus_change (widget, fevent);
632 
633  gdk_event_free (fevent);
634 }
635 
636 static void
638 {
639  /* send focus-in event */
641  gtk_widget_hide (pview->goto_window);
642  gtk_entry_set_text (GTK_ENTRY (pview->goto_entry), "");
643 }
644 
645 static gboolean
647  GdkEventAny *event,
648  EvViewPresentation *pview)
649 {
651 
652  return TRUE;
653 }
654 
655 static gboolean
657  GdkEventKey *event,
658  EvViewPresentation *pview)
659 {
660  switch (event->keyval) {
661  case GDK_KEY_Escape:
662  case GDK_KEY_Tab:
663  case GDK_KEY_KP_Tab:
664  case GDK_KEY_ISO_Left_Tab:
666  return TRUE;
667  case GDK_KEY_Return:
668  case GDK_KEY_KP_Enter:
669  case GDK_KEY_ISO_Enter:
670  case GDK_KEY_BackSpace:
671  case GDK_KEY_Delete:
672  return FALSE;
673  default:
674  if (!KEY_IS_NUMERIC (event->keyval))
675  return TRUE;
676  }
677 
678  return FALSE;
679 }
680 
681 static gboolean
683  GdkEventButton *event,
684  EvViewPresentation *pview)
685 {
687 
688  return TRUE;
689 }
690 
691 static void
693  EvViewPresentation *pview)
694 {
695  const gchar *text;
696  gint page;
697 
698  text = gtk_entry_get_text (entry);
699  page = atoi (text) - 1;
700 
703 }
704 
705 static void
707 {
708  GtkWidget *frame, *hbox, *label;
709  GtkWindow *toplevel, *goto_window;
710 
711  toplevel = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (pview)));
712 
713  if (pview->goto_window) {
714  goto_window = GTK_WINDOW (pview->goto_window);
715  if (gtk_window_has_group (toplevel))
716  gtk_window_group_add_window (gtk_window_get_group (toplevel), goto_window);
717  else if (gtk_window_has_group (goto_window))
718  gtk_window_group_remove_window (gtk_window_get_group (goto_window), goto_window);
719 
720  return;
721  }
722 
723  pview->goto_window = gtk_window_new (GTK_WINDOW_POPUP);
724  goto_window = GTK_WINDOW (pview->goto_window);
725  gtk_window_set_screen (goto_window, gtk_widget_get_screen (GTK_WIDGET (pview)));
726 
727  if (gtk_window_has_group (toplevel))
728  gtk_window_group_add_window (gtk_window_get_group (toplevel), goto_window);
729 
730  gtk_window_set_modal (goto_window, TRUE);
731 
732  g_signal_connect (pview->goto_window, "delete_event",
734  pview);
735  g_signal_connect (pview->goto_window, "key_press_event",
737  pview);
738  g_signal_connect (pview->goto_window, "button_press_event",
740  pview);
741 
742  frame = gtk_frame_new (NULL);
743  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
744  gtk_container_add (GTK_CONTAINER (pview->goto_window), frame);
745  gtk_widget_show (frame);
746 
747  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
748  gtk_container_set_border_width (GTK_CONTAINER (hbox), 3);
749  gtk_container_add (GTK_CONTAINER (frame), hbox);
750  gtk_widget_show (hbox);
751 
752  label = gtk_label_new (_("Jump to page:"));
753  gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 3);
754  gtk_widget_show (label);
755  gtk_widget_realize (label);
756 
757  pview->goto_entry = gtk_entry_new ();
758  g_signal_connect (pview->goto_entry, "activate",
760  pview);
761  gtk_box_pack_start (GTK_BOX (hbox), pview->goto_entry, TRUE, TRUE, 0);
762  gtk_widget_show (pview->goto_entry);
763  gtk_widget_realize (pview->goto_entry);
764 }
765 
766 static void
768 {
769  GtkWidgetClass *entry_parent_class;
770 
771  entry_parent_class = g_type_class_peek_parent (GTK_ENTRY_GET_CLASS (pview->goto_entry));
772  (entry_parent_class->grab_focus) (pview->goto_entry);
773 
775 }
776 
777 static void
779  GdkEvent *event)
780 {
781  GdkEventKey *new_event;
782  GdkScreen *screen;
783 
784  /* Move goto window off screen */
785  screen = gtk_widget_get_screen (GTK_WIDGET (pview));
786  gtk_window_move (GTK_WINDOW (pview->goto_window),
787  gdk_screen_get_width (screen) + 1,
788  gdk_screen_get_height (screen) + 1);
789  gtk_widget_show (pview->goto_window);
790 
791  new_event = (GdkEventKey *) gdk_event_copy (event);
792  g_object_unref (new_event->window);
793  new_event->window = gtk_widget_get_window (pview->goto_window);
794  if (new_event->window)
795  g_object_ref (new_event->window);
796  gtk_widget_realize (pview->goto_window);
797 
798  gtk_widget_event (pview->goto_window, (GdkEvent *)new_event);
799  gdk_event_free ((GdkEvent *)new_event);
800  gtk_widget_hide (pview->goto_window);
801 }
802 
803 /* Links */
804 static gboolean
806  EvLink *link)
807 {
808  EvLinkAction *action;
809 
810  action = ev_link_get_action (link);
811  if (!action)
812  return FALSE;
813 
814  switch (ev_link_action_get_action_type (action)) {
816  return ev_link_action_get_dest (action) != NULL;
821  return TRUE;
822  default:
823  return FALSE;
824  }
825 
826  return FALSE;
827 }
828 
829 static EvLink *
831  gdouble x,
832  gdouble y)
833 {
834  GdkRectangle page_area;
835  EvMappingList *link_mapping;
836  EvLink *link;
837  gdouble width, height;
838  gdouble new_x, new_y;
839 
840  if (!pview->page_cache)
841  return NULL;
842 
843  ev_document_get_page_size (pview->document, pview->current_page, &width, &height);
844  ev_view_presentation_get_page_area (pview, &page_area);
845  x = (x - page_area.x) / page_area.width;
846  y = (y - page_area.y) / page_area.height;
847  switch (pview->rotation) {
848  case 0:
849  case 360:
850  new_x = width * x;
851  new_y = height * y;
852  break;
853  case 90:
854  new_x = width * y;
855  new_y = height * (1 - x);
856  break;
857  case 180:
858  new_x = width * (1 - x);
859  new_y = height * (1 - y);
860  break;
861  case 270:
862  new_x = width * (1 - y);
863  new_y = height * x;
864  break;
865  default:
866  g_assert_not_reached ();
867  }
868 
869  link_mapping = ev_page_cache_get_link_mapping (pview->page_cache, pview->current_page);
870 
871  link = link_mapping ? ev_mapping_list_get_data (link_mapping, new_x, new_y) : NULL;
872 
873  return link && ev_view_presentation_link_is_supported (pview, link) ? link : NULL;
874 }
875 
876 static void
878  EvLink *link)
879 {
880  EvLinkAction *action;
881 
882  action = ev_link_get_action (link);
883 
884  switch (ev_link_action_get_action_type (action)) {
886  const gchar *name = ev_link_action_get_name (action);
887 
888  if (g_ascii_strcasecmp (name, "FirstPage") == 0) {
890  } else if (g_ascii_strcasecmp (name, "PrevPage") == 0) {
892  } else if (g_ascii_strcasecmp (name, "NextPage") == 0) {
894  } else if (g_ascii_strcasecmp (name, "LastPage") == 0) {
895  gint n_pages;
896 
897  n_pages = ev_document_get_n_pages (pview->document);
898  ev_view_presentation_update_current_page (pview, n_pages - 1);
899  }
900  }
901  break;
902 
904  EvLinkDest *dest;
905  gint page;
906 
907  dest = ev_link_action_get_dest (action);
910  }
911  break;
915  g_signal_emit (pview, signals[SIGNAL_EXTERNAL_LINK], 0, action);
916  break;
917  default:
918  break;
919  }
920 }
921 
922 /* Cursors */
923 static void
925  EvViewCursor view_cursor)
926 {
927  GtkWidget *widget;
928  GdkCursor *cursor;
929 
930  if (pview->cursor == view_cursor)
931  return;
932 
933  widget = GTK_WIDGET (pview);
934  if (!gtk_widget_get_realized (widget))
935  gtk_widget_realize (widget);
936 
937  pview->cursor = view_cursor;
938 
939  cursor = ev_view_cursor_new (gtk_widget_get_display (widget), view_cursor);
940  gdk_window_set_cursor (gtk_widget_get_window (widget), cursor);
941  gdk_flush ();
942  if (cursor)
943  g_object_unref (cursor);
944 }
945 
946 static void
948  gdouble x,
949  gdouble y)
950 {
953  else
955 }
956 
957 static gboolean
959 {
961  pview->hide_cursor_timeout_id = 0;
962 
963  return FALSE;
964 }
965 
966 static void
968 {
969  if (pview->hide_cursor_timeout_id > 0)
970  g_source_remove (pview->hide_cursor_timeout_id);
971  pview->hide_cursor_timeout_id = 0;
972 }
973 
974 static void
976 {
978  pview->hide_cursor_timeout_id =
979  g_timeout_add_seconds (HIDE_CURSOR_TIMEOUT,
980  (GSourceFunc)hide_cursor_timeout_cb,
981  pview);
982 }
983 
984 static void
986  cairo_surface_t *surface)
987 {
988  if (!surface || pview->current_surface == surface)
989  return;
990 
991  cairo_surface_reference (surface);
992  if (pview->current_surface)
993  cairo_surface_destroy (pview->current_surface);
994  pview->current_surface = surface;
995 }
996 
997 static void
999 {
1000  EvViewPresentation *pview = EV_VIEW_PRESENTATION (object);
1001 
1002  if (pview->document) {
1003  g_object_unref (pview->document);
1004  pview->document = NULL;
1005  }
1006 
1011 
1012  if (pview->current_surface) {
1013  cairo_surface_destroy (pview->current_surface);
1014  pview->current_surface = NULL;
1015  }
1016 
1017  if (pview->page_cache) {
1018  g_object_unref (pview->page_cache);
1019  pview->page_cache = NULL;
1020  }
1021 
1022  if (pview->goto_window) {
1023  gtk_widget_destroy (pview->goto_window);
1024  pview->goto_window = NULL;
1025  pview->goto_entry = NULL;
1026  }
1027 
1028  G_OBJECT_CLASS (ev_view_presentation_parent_class)->dispose (object);
1029 }
1030 
1031 static void
1033  gint *minimum,
1034  gint *natural)
1035 {
1036  *minimum = *natural = 0;
1037 }
1038 
1039 static void
1041  gint *minimum,
1042  gint *natural)
1043 {
1044  *minimum = *natural = 0;
1045 }
1046 
1047 static void
1049  cairo_t *cr)
1050 {
1051  GtkWidget *widget = GTK_WIDGET (pview);
1052  PangoLayout *layout;
1053  PangoFontDescription *font_desc;
1054  gchar *markup;
1055  const gchar *text = _("End of presentation. Click to exit.");
1056 
1057  if (pview->state != EV_PRESENTATION_END)
1058  return;
1059 
1060  layout = gtk_widget_create_pango_layout (widget, NULL);
1061  markup = g_strdup_printf ("<span foreground=\"white\">%s</span>", text);
1062  pango_layout_set_markup (layout, markup, -1);
1063  g_free (markup);
1064 
1065  font_desc = pango_font_description_new ();
1066  pango_font_description_set_size (font_desc, 16 * PANGO_SCALE);
1067  pango_layout_set_font_description (layout, font_desc);
1068 
1069  gtk_render_layout (gtk_widget_get_style_context (widget),
1070  cr, 15, 15, layout);
1071 
1072  pango_font_description_free (font_desc);
1073  g_object_unref (layout);
1074 }
1075 
1076 static gboolean
1077 ev_view_presentation_draw (GtkWidget *widget,
1078  cairo_t *cr)
1079 {
1080  EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1081  GdkRectangle page_area;
1082  GdkRectangle overlap;
1083  cairo_surface_t *surface;
1084  GdkRectangle clip_rect;
1085 #if GTK_CHECK_VERSION(3, 20, 0)
1086  GtkStyleContext *context;
1087 
1088  context = gtk_widget_get_style_context (GTK_WIDGET (pview));
1089  gtk_render_background (context, cr,
1090  0, 0,
1091  gtk_widget_get_allocated_width (widget),
1092  gtk_widget_get_allocated_height (widget));
1093 #endif
1094 
1095  if (!gdk_cairo_get_clip_rectangle (cr, &clip_rect))
1096  return FALSE;
1097 
1098  switch (pview->state) {
1099  case EV_PRESENTATION_END:
1101  return FALSE;
1102  case EV_PRESENTATION_BLACK:
1103  case EV_PRESENTATION_WHITE:
1104  return FALSE;
1106  break;
1107  }
1108 
1109  if (pview->animation) {
1111  ev_view_presentation_get_page_area (pview, &page_area);
1112 
1113  cairo_save (cr);
1114 
1115  /* normalize to x=0, y=0 */
1116  cairo_translate (cr, page_area.x, page_area.y);
1117  page_area.x = page_area.y = 0;
1118 
1119  /* Try to fix rounding errors */
1120  page_area.width--;
1121 
1122  ev_transition_animation_paint (pview->animation, cr, page_area);
1123 
1124  cairo_restore (cr);
1125  }
1126 
1127  return TRUE;
1128  }
1129 
1130  surface = get_surface_from_job (pview, pview->curr_job);
1131  if (surface) {
1133  } else if (pview->current_surface) {
1134  surface = pview->current_surface;
1135  } else {
1136  return FALSE;
1137  }
1138 
1139  ev_view_presentation_get_page_area (pview, &page_area);
1140  if (gdk_rectangle_intersect (&page_area, &clip_rect, &overlap)) {
1141  cairo_save (cr);
1142 
1143  /* Try to fix rounding errors. See bug #438760 */
1144  if (overlap.width == page_area.width)
1145  overlap.width--;
1146 
1147  cairo_rectangle (cr, overlap.x, overlap.y, overlap.width, overlap.height);
1148  cairo_set_source_surface (cr, surface, page_area.x, page_area.y);
1149  cairo_fill (cr);
1150 
1151  cairo_restore (cr);
1152  }
1153 
1154  return FALSE;
1155 }
1156 
1157 static gboolean
1159  GdkEventKey *event)
1160 {
1161  EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1162 
1163  if (pview->state == EV_PRESENTATION_END)
1164  return gtk_bindings_activate_event (G_OBJECT (widget), event);
1165 
1166  if (event->state != 0)
1167  return gtk_bindings_activate_event (G_OBJECT (widget), event);
1168 
1169  switch (event->keyval) {
1170  case GDK_KEY_b:
1171  case GDK_KEY_B:
1172  case GDK_KEY_period:
1173  case GDK_KEY_KP_Decimal:
1174  if (pview->state == EV_PRESENTATION_BLACK)
1176  else
1178 
1179  return TRUE;
1180  case GDK_KEY_w:
1181  case GDK_KEY_W:
1182  if (pview->state == EV_PRESENTATION_WHITE)
1184  else
1186 
1187  return TRUE;
1188  case GDK_KEY_Home:
1189  if (pview->state == EV_PRESENTATION_NORMAL) {
1191  return TRUE;
1192  }
1193  break;
1194  case GDK_KEY_End:
1195  if (pview->state == EV_PRESENTATION_NORMAL) {
1196  gint page;
1197 
1198  page = ev_document_get_n_pages (pview->document) - 1;
1200 
1201  return TRUE;
1202  }
1203  break;
1204  default:
1205  break;
1206  }
1207 
1209 
1210  if (ev_document_get_n_pages (pview->document) > 1 && KEY_IS_NUMERIC (event->keyval)) {
1211  gint x, y;
1212 
1214  ev_view_presentation_goto_window_send_key_event (pview, (GdkEvent *)event);
1215  ev_document_misc_get_pointer_position (GTK_WIDGET (pview), &x, &y);
1216  gtk_window_move (GTK_WINDOW (pview->goto_window), x, y);
1217  gtk_widget_show (pview->goto_window);
1219 
1220  return TRUE;
1221  }
1222 
1223  return gtk_bindings_activate_event (G_OBJECT (widget), event);
1224 }
1225 
1226 static gboolean
1228  GdkEventButton *event)
1229 {
1230  EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1231 
1232  switch (event->button) {
1233  case 1: {
1234  EvLink *link;
1235 
1236  if (pview->state == EV_PRESENTATION_END) {
1237  g_signal_emit (pview, signals[FINISHED], 0, NULL);
1238 
1239  return FALSE;
1240  }
1241 
1243  event->x,
1244  event->y);
1245  if (link)
1246  ev_vew_presentation_handle_link (pview, link);
1247  else
1249  }
1250  break;
1251  case 3:
1253  break;
1254  default:
1255  break;
1256  }
1257 
1258  return FALSE;
1259 }
1260 
1261 static gint
1263  GdkEventFocus *event)
1264 {
1265  EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1266 
1267  if (pview->goto_window)
1269 
1270  return FALSE;
1271 }
1272 
1273 static gboolean
1275  GdkEventMotion *event)
1276 {
1277  EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1278 
1280  ev_view_presentation_set_cursor_for_location (pview, event->x, event->y);
1281 
1282  return FALSE;
1283 }
1284 
1285 static void
1287 {
1288  GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (pview));
1289  GdkRectangle monitor;
1290  gint monitor_num;
1291 
1292  monitor_num = gdk_screen_get_monitor_at_window (screen, gtk_widget_get_window (GTK_WIDGET (pview)));
1293  gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1294  pview->monitor_width = monitor.width;
1295  pview->monitor_height = monitor.height;
1296 }
1297 
1298 static gboolean
1299 init_presentation (GtkWidget *widget)
1300 {
1301  EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1302 
1304 
1307 
1308  return FALSE;
1309 }
1310 
1311 static void
1312 ev_view_presentation_realize (GtkWidget *widget)
1313 {
1314  GdkWindow *window;
1315  GdkWindowAttr attributes;
1316  GtkAllocation allocation;
1317 
1318  gtk_widget_set_realized (widget, TRUE);
1319 
1320  attributes.window_type = GDK_WINDOW_CHILD;
1321  attributes.wclass = GDK_INPUT_OUTPUT;
1322  attributes.visual = gtk_widget_get_visual (widget);
1323 
1324  gtk_widget_get_allocation (widget, &allocation);
1325  attributes.x = allocation.x;
1326  attributes.y = allocation.y;
1327  attributes.width = allocation.width;
1328  attributes.height = allocation.height;
1329  attributes.event_mask = GDK_EXPOSURE_MASK |
1330  GDK_BUTTON_PRESS_MASK |
1331  GDK_BUTTON_RELEASE_MASK |
1332  GDK_SCROLL_MASK |
1333  GDK_KEY_PRESS_MASK |
1334  GDK_POINTER_MOTION_MASK |
1335  GDK_POINTER_MOTION_HINT_MASK |
1336  GDK_ENTER_NOTIFY_MASK |
1337  GDK_LEAVE_NOTIFY_MASK;
1338 
1339  window = gdk_window_new (gtk_widget_get_parent_window (widget),
1340  &attributes,
1341  GDK_WA_X | GDK_WA_Y |
1342  GDK_WA_VISUAL);
1343 
1344  gdk_window_set_user_data (window, widget);
1345  gtk_widget_set_window (widget, window);
1346  gtk_style_context_set_background (gtk_widget_get_style_context (widget),
1347  window);
1348 
1349  g_idle_add ((GSourceFunc)init_presentation, widget);
1350 }
1351 
1352 static void
1354  GtkScrollType scroll)
1355 {
1356  switch (scroll) {
1357  case GTK_SCROLL_PAGE_FORWARD:
1359  break;
1360  case GTK_SCROLL_PAGE_BACKWARD:
1362  break;
1363  default:
1364  g_assert_not_reached ();
1365  }
1366 }
1367 
1368 static gboolean
1370  GdkEventScroll *event)
1371 {
1372  EvViewPresentation *pview = EV_VIEW_PRESENTATION (widget);
1373  guint state;
1374 
1375  state = event->state & gtk_accelerator_get_default_mod_mask ();
1376  if (state != 0)
1377  return FALSE;
1378 
1379  switch (event->direction) {
1380  case GDK_SCROLL_DOWN:
1381  case GDK_SCROLL_RIGHT:
1382  ev_view_presentation_change_page (pview, GTK_SCROLL_PAGE_FORWARD);
1383  break;
1384  case GDK_SCROLL_UP:
1385  case GDK_SCROLL_LEFT:
1386  ev_view_presentation_change_page (pview, GTK_SCROLL_PAGE_BACKWARD);
1387  break;
1388  case GDK_SCROLL_SMOOTH:
1389  return FALSE;
1390  }
1391 
1392  return TRUE;
1393 }
1394 
1395 
1396 static void
1397 add_change_page_binding_keypad (GtkBindingSet *binding_set,
1398  guint keyval,
1399  GdkModifierType modifiers,
1400  GtkScrollType scroll)
1401 {
1402  guint keypad_keyval = keyval - GDK_KEY_Left + GDK_KEY_KP_Left;
1403 
1404  gtk_binding_entry_add_signal (binding_set, keyval, modifiers,
1405  "change_page", 1,
1406  GTK_TYPE_SCROLL_TYPE, scroll);
1407  gtk_binding_entry_add_signal (binding_set, keypad_keyval, modifiers,
1408  "change_page", 1,
1409  GTK_TYPE_SCROLL_TYPE, scroll);
1410 }
1411 
1412 static void
1414  guint prop_id,
1415  const GValue *value,
1416  GParamSpec *pspec)
1417 {
1418  EvViewPresentation *pview = EV_VIEW_PRESENTATION (object);
1419 
1420  switch (prop_id) {
1421  case PROP_DOCUMENT:
1422  pview->document = g_value_dup_object (value);
1424  break;
1425  case PROP_CURRENT_PAGE:
1426  ev_view_presentation_set_current_page (pview, g_value_get_uint (value));
1427  break;
1428  case PROP_ROTATION:
1429  ev_view_presentation_set_rotation (pview, g_value_get_uint (value));
1430  break;
1431  case PROP_INVERTED_COLORS:
1432  pview->inverted_colors = g_value_get_boolean (value);
1433  break;
1434  default:
1435  G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1436  }
1437 }
1438 
1439 static void
1441  guint prop_id,
1442  GValue *value,
1443  GParamSpec *pspec)
1444 {
1445  EvViewPresentation *pview = EV_VIEW_PRESENTATION (object);
1446 
1447  switch (prop_id) {
1448  case PROP_CURRENT_PAGE:
1449  g_value_set_uint (value, pview->current_page);
1450  break;
1451  case PROP_ROTATION:
1452  g_value_set_uint (value, ev_view_presentation_get_rotation (pview));
1453  break;
1454  default:
1455  G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1456  }
1457 }
1458 
1459 static void
1461 {
1465 }
1466 
1467 static GObject *
1469  guint n_construct_properties,
1470  GObjectConstructParam *construct_params)
1471 {
1472  GObject *object;
1473  EvViewPresentation *pview;
1474 
1475  object = G_OBJECT_CLASS (ev_view_presentation_parent_class)->constructor (type,
1476  n_construct_properties,
1477  construct_params);
1478  pview = EV_VIEW_PRESENTATION (object);
1479  pview->is_constructing = FALSE;
1480 
1481  if (EV_IS_DOCUMENT_LINKS (pview->document)) {
1482  pview->page_cache = ev_page_cache_new (pview->document);
1484  }
1485 
1486  g_signal_connect (object, "notify::scale-factor",
1487  G_CALLBACK (ev_view_presentation_notify_scale_factor), NULL);
1488 
1489  return object;
1490 }
1491 
1492 static void
1494 {
1495  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
1496  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1497  GtkBindingSet *binding_set;
1498 
1500 
1501  gobject_class->dispose = ev_view_presentation_dispose;
1502 
1503  widget_class->get_preferred_width = ev_view_presentation_get_preferred_width;
1504  widget_class->get_preferred_height = ev_view_presentation_get_preferred_height;
1505  widget_class->realize = ev_view_presentation_realize;
1506  widget_class->draw = ev_view_presentation_draw;
1507  widget_class->key_press_event = ev_view_presentation_key_press_event;
1508  widget_class->button_release_event = ev_view_presentation_button_release_event;
1509  widget_class->focus_out_event = ev_view_presentation_focus_out;
1510  widget_class->motion_notify_event = ev_view_presentation_motion_notify_event;
1511  widget_class->scroll_event = ev_view_presentation_scroll_event;
1512 
1513 #if GTK_CHECK_VERSION(3, 20, 0)
1514  gtk_widget_class_set_css_name (widget_class, "evpresentationview");
1515 #endif
1516 
1517  gobject_class->constructor = ev_view_presentation_constructor;
1518  gobject_class->set_property = ev_view_presentation_set_property;
1519  gobject_class->get_property = ev_view_presentation_get_property;
1520 
1521  g_object_class_install_property (gobject_class,
1522  PROP_DOCUMENT,
1523  g_param_spec_object ("document",
1524  "Document",
1525  "Document",
1527  G_PARAM_WRITABLE |
1528  G_PARAM_CONSTRUCT_ONLY |
1529  G_PARAM_STATIC_STRINGS));
1530  g_object_class_install_property (gobject_class,
1532  g_param_spec_uint ("current-page",
1533  "Current Page",
1534  "The current page",
1535  0, G_MAXUINT, 0,
1536  G_PARAM_READWRITE |
1537  G_PARAM_CONSTRUCT |
1538  G_PARAM_STATIC_STRINGS));
1539  g_object_class_install_property (gobject_class,
1540  PROP_ROTATION,
1541  g_param_spec_uint ("rotation",
1542  "Rotation",
1543  "Current rotation angle",
1544  0, 360, 0,
1545  G_PARAM_READWRITE |
1546  G_PARAM_CONSTRUCT |
1547  G_PARAM_STATIC_STRINGS));
1548  g_object_class_install_property (gobject_class,
1550  g_param_spec_boolean ("inverted-colors",
1551  "Inverted Colors",
1552  "Whether presentation is displayed with inverted colors",
1553  FALSE,
1554  G_PARAM_WRITABLE |
1555  G_PARAM_CONSTRUCT_ONLY |
1556  G_PARAM_STATIC_STRINGS));
1557 
1558  signals[CHANGE_PAGE] =
1559  g_signal_new ("change_page",
1560  G_OBJECT_CLASS_TYPE (gobject_class),
1561  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1562  G_STRUCT_OFFSET (EvViewPresentationClass, change_page),
1563  NULL, NULL,
1564  g_cclosure_marshal_VOID__ENUM,
1565  G_TYPE_NONE, 1,
1566  GTK_TYPE_SCROLL_TYPE);
1567  signals[FINISHED] =
1568  g_signal_new ("finished",
1569  G_OBJECT_CLASS_TYPE (gobject_class),
1570  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1571  G_STRUCT_OFFSET (EvViewPresentationClass, finished),
1572  NULL, NULL,
1573  g_cclosure_marshal_VOID__VOID,
1574  G_TYPE_NONE, 0,
1575  G_TYPE_NONE);
1577  g_signal_new ("external-link",
1578  G_TYPE_FROM_CLASS (gobject_class),
1579  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1580  G_STRUCT_OFFSET (EvViewPresentationClass, external_link),
1581  NULL, NULL,
1582  g_cclosure_marshal_VOID__OBJECT,
1583  G_TYPE_NONE, 1,
1584  G_TYPE_OBJECT);
1585 
1586  binding_set = gtk_binding_set_by_class (klass);
1587  add_change_page_binding_keypad (binding_set, GDK_KEY_Left, 0, GTK_SCROLL_PAGE_BACKWARD);
1588  add_change_page_binding_keypad (binding_set, GDK_KEY_Right, 0, GTK_SCROLL_PAGE_FORWARD);
1589  add_change_page_binding_keypad (binding_set, GDK_KEY_Up, 0, GTK_SCROLL_PAGE_BACKWARD);
1590  add_change_page_binding_keypad (binding_set, GDK_KEY_Down, 0, GTK_SCROLL_PAGE_FORWARD);
1591  gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, 0,
1592  "change_page", 1,
1593  GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1594  gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, GDK_SHIFT_MASK,
1595  "change_page", 1,
1596  GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1597  gtk_binding_entry_add_signal (binding_set, GDK_KEY_BackSpace, 0,
1598  "change_page", 1,
1599  GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1600  gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Down, 0,
1601  "change_page", 1,
1602  GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1603  gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Up, 0,
1604  "change_page", 1,
1605  GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1606  gtk_binding_entry_add_signal (binding_set, GDK_KEY_J, 0,
1607  "change_page", 1,
1608  GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1609  gtk_binding_entry_add_signal (binding_set, GDK_KEY_H, 0,
1610  "change_page", 1,
1611  GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1612  gtk_binding_entry_add_signal (binding_set, GDK_KEY_L, 0,
1613  "change_page", 1,
1614  GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
1615  gtk_binding_entry_add_signal (binding_set, GDK_KEY_K, 0,
1616  "change_page", 1,
1617  GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
1618 }
1619 
1620 #if !GTK_CHECK_VERSION(3, 20, 0)
1621 static void
1623 {
1624  static gsize initialization_value = 0;
1625 
1626  if (g_once_init_enter (&initialization_value)) {
1627  GtkCssProvider *provider;
1628 
1629  provider = gtk_css_provider_new ();
1630  gtk_css_provider_load_from_data (provider,
1631  "EvViewPresentation {\n"
1632  " background-color: black; }",
1633  -1, NULL);
1634  gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
1635  GTK_STYLE_PROVIDER (provider),
1636  GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
1637  g_object_unref (provider);
1638  g_once_init_leave (&initialization_value, 1);
1639  }
1640 }
1641 #endif
1642 
1643 static void
1645 {
1646  gtk_widget_set_can_focus (GTK_WIDGET (pview), TRUE);
1647  pview->is_constructing = TRUE;
1648 #if !GTK_CHECK_VERSION(3, 20, 0)
1650 #endif
1651 }
1652 
1653 GtkWidget *
1655  guint current_page,
1656  guint rotation,
1657  gboolean inverted_colors)
1658 {
1659  g_return_val_if_fail (EV_IS_DOCUMENT (document), NULL);
1660  g_return_val_if_fail (current_page < ev_document_get_n_pages (document), NULL);
1661 
1662  return GTK_WIDGET (g_object_new (EV_TYPE_VIEW_PRESENTATION,
1663  "document", document,
1664  "current_page", current_page,
1665  "rotation", rotation,
1666  "inverted_colors", inverted_colors,
1667  NULL));
1668 }
1669 
1670 guint
1672 {
1673  return pview->current_page;
1674 }
1675 
1676 void
1678  gint rotation)
1679 {
1680  if (rotation >= 360)
1681  rotation -= 360;
1682  else if (rotation < 0)
1683  rotation += 360;
1684 
1685  if (pview->rotation == rotation)
1686  return;
1687 
1688  pview->rotation = rotation;
1689  g_object_notify (G_OBJECT (pview), "rotation");
1690  if (pview->is_constructing)
1691  return;
1692 
1695 }
1696 
1697 guint
1699 {
1700  return pview->rotation;
1701 }