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-annotation-window.c
Go to the documentation of this file.
1 /* ev-annotation-window.c
2  * this file is part of evince, a gnome document viewer
3  *
4  * Copyright (C) 2009 Carlos Garcia Campos <carlosgc@gnome.org>
5  * Copyright (C) 2007 IƱigo Martinez <inigomartinez@gmail.com>
6  *
7  * Evince is free software; you can redistribute it and/or modify it
8  * 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  * Evince is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * 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, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  */
21 
22 #include "config.h"
23 
24 #include <string.h>
25 
26 #include "ev-annotation-window.h"
27 #include "ev-stock-icons.h"
28 #include "ev-view-marshal.h"
29 #include "ev-document-misc.h"
30 
31 enum {
35 };
36 
37 enum {
41 };
42 
44  GtkWindow base_instance;
45 
47  GtkWindow *parent;
48 
49  GtkWidget *title;
50  GtkWidget *close_button;
51  GtkWidget *text_view;
52  GtkWidget *resize_se;
53  GtkWidget *resize_sw;
54 
55  gboolean is_open;
57 
58  gboolean in_move;
59  gint x;
60  gint y;
61  gint orig_x;
62  gint orig_y;
63 };
64 
66  GtkWindowClass base_class;
67 
70  gint x,
71  gint y);
72 };
73 
74 static guint signals[N_SIGNALS];
75 
76 G_DEFINE_TYPE (EvAnnotationWindow, ev_annotation_window, GTK_TYPE_WINDOW)
77 
78 /* Cut and paste from gtkwindow.c */
79 static void
80 send_focus_change (GtkWidget *widget,
81  gboolean in)
82 {
83  GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE);
84 
85  fevent->focus_change.type = GDK_FOCUS_CHANGE;
86  fevent->focus_change.window = gtk_widget_get_window (widget);
87  fevent->focus_change.in = in;
88  if (fevent->focus_change.window)
89  g_object_ref (fevent->focus_change.window);
90 
91  gtk_widget_send_focus_change (widget, fevent);
92 
93  gdk_event_free (fevent);
94 }
95 
96 static gdouble
98 {
99  GdkScreen *screen;
100 
101  screen = gtk_window_get_screen (GTK_WINDOW (window));
102  return ev_document_misc_get_screen_dpi (screen);
103 }
104 
105 static void
107 {
108  gchar *contents;
109  GtkTextIter start, end;
110  GtkTextBuffer *buffer;
111  EvAnnotation *annot = window->annotation;
112 
113  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (window->text_view));
114  gtk_text_buffer_get_bounds (buffer, &start, &end);
115  contents = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
116  ev_annotation_set_contents (annot, contents);
117  g_free (contents);
118 }
119 
120 static void
122  GdkRGBA *color)
123 {
124  GtkStyleProperties *properties;
125  GtkStyleProvider *provider;
126 
127  properties = gtk_style_properties_new ();
128  gtk_style_properties_set (properties, 0,
129  "background-color", color,
130  NULL);
131 
132  provider = GTK_STYLE_PROVIDER (properties);
133  gtk_style_context_add_provider (gtk_widget_get_style_context (GTK_WIDGET (window)),
134  provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
135  gtk_style_context_add_provider (gtk_widget_get_style_context (window->close_button),
136  provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
137  gtk_style_context_add_provider (gtk_widget_get_style_context (window->resize_se),
138  provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
139  gtk_style_context_add_provider (gtk_widget_get_style_context (window->resize_sw),
140  provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
141  g_object_unref (properties);
142 }
143 
144 static void
146  gdouble opacity)
147 {
148  gtk_widget_set_opacity (GTK_WIDGET (window), opacity);
149  gtk_widget_set_opacity (GTK_WIDGET (window->text_view), opacity);
150 }
151 
152 static void
154  GParamSpec *pspec,
156 {
157  const gchar *label = ev_annotation_markup_get_label (annot);
158 
159  gtk_window_set_title (GTK_WINDOW (window), label);
160  gtk_label_set_text (GTK_LABEL (window->title), label);
161 }
162 
163 static void
165  GParamSpec *pspec,
167 {
168  GdkRGBA rgba;
169 
170  ev_annotation_get_rgba (annot, &rgba);
171  ev_annotation_window_set_color (window, &rgba);
172 }
173 
174 static void
176  GParamSpec *pspec,
178 {
179  gdouble opacity;
180 
182  ev_annotation_window_set_opacity (window, opacity);
183 }
184 
185 static void
187 {
189 
190  if (window->annotation) {
192  g_object_unref (window->annotation);
193  window->annotation = NULL;
194  }
195 
196  (* G_OBJECT_CLASS (ev_annotation_window_parent_class)->dispose) (object);
197 }
198 
199 static void
201  guint prop_id,
202  const GValue *value,
203  GParamSpec *pspec)
204 {
206 
207  switch (prop_id) {
208  case PROP_ANNOTATION:
209  window->annotation = g_value_dup_object (value);
210  break;
211  case PROP_PARENT:
212  window->parent = g_value_get_object (value);
213  break;
214  default:
215  G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
216  }
217 }
218 
219 static gboolean
221  GdkEventButton *event,
222  GtkWidget *ebox)
223 {
224  if (event->type == GDK_BUTTON_PRESS && event->button == 1) {
225  gtk_window_begin_resize_drag (GTK_WINDOW (window),
226  window->resize_sw == ebox ?
227  GDK_WINDOW_EDGE_SOUTH_WEST :
228  GDK_WINDOW_EDGE_SOUTH_EAST,
229  event->button, event->x_root,
230  event->y_root, event->time);
231  return TRUE;
232  }
233 
234  return FALSE;
235 }
236 
237 static void
240 {
241  GdkWindow *gdk_window = gtk_widget_get_window (widget);
242 
243  if (!gdk_window)
244  return;
245 
246  if (gtk_widget_is_sensitive (widget)) {
247  GdkDisplay *display = gtk_widget_get_display (widget);
248  GdkCursor *cursor;
249 
250  cursor = gdk_cursor_new_for_display (display,
251  widget == window->resize_sw ?
252  GDK_BOTTOM_LEFT_CORNER :
253  GDK_BOTTOM_RIGHT_CORNER);
254  gdk_window_set_cursor (gdk_window, cursor);
255  g_object_unref (cursor);
256  } else {
257  gdk_window_set_cursor (gdk_window, NULL);
258  }
259 }
260 
261 static void
262 text_view_state_flags_changed (GtkWidget *widget,
263  GtkStateFlags previous_flags)
264 {
265  GtkStateFlags current_flags = gtk_widget_get_state_flags (widget);
266 
267  if (current_flags & GTK_STATE_FLAG_BACKDROP)
268  gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (widget), FALSE);
269 }
270 
271 static void
273 {
274  gtk_widget_hide (GTK_WIDGET (window));
275  g_signal_emit (window, signals[CLOSED], 0);
276 }
277 
278 static gboolean
280  GdkEventButton *event)
281 {
283 
284  if (event->type == GDK_BUTTON_PRESS && event->button == 1) {
285  window->in_move = TRUE;
286  window->x = event->x_root - event->x;
287  window->y = event->y_root - event->y;
288  gtk_window_begin_move_drag (GTK_WINDOW (widget),
289  event->button,
290  event->x_root,
291  event->y_root,
292  event->time);
293  return TRUE;
294  }
295 
296  return FALSE;
297 }
298 
299 static void
301 {
302  GtkWidget *vbox, *hbox;
303  GtkWidget *icon;
304  GtkWidget *swindow;
305  GtkWidget *header;
306  GtkIconTheme *icon_theme;
307  GdkPixbuf *pixbuf;
308 
309  icon_theme = gtk_icon_theme_get_default ();
310 
311  gtk_widget_set_can_focus (GTK_WIDGET (window), TRUE);
312 
313  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
314 
315  /* Title bar */
316  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
317 
318  icon = gtk_image_new (); /* FIXME: use the annot icon */
319  gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
320  gtk_widget_show (icon);
321 
322  header = gtk_event_box_new ();
323  gtk_widget_add_events (header, GDK_BUTTON_PRESS_MASK);
324  g_signal_connect_swapped (header, "button-press-event",
326  window);
327 
328  window->title = gtk_label_new (NULL);
329  gtk_container_add (GTK_CONTAINER (header), window->title);
330  gtk_widget_show (window->title);
331 
332  gtk_box_pack_start (GTK_BOX (hbox), header, TRUE, TRUE, 0);
333  gtk_widget_show (header);
334 
335  window->close_button = gtk_button_new ();
336  gtk_button_set_relief (GTK_BUTTON (window->close_button), GTK_RELIEF_NONE);
337  gtk_container_set_border_width (GTK_CONTAINER (window->close_button), 0);
338  g_signal_connect_swapped (window->close_button, "clicked",
339  G_CALLBACK (ev_annotation_window_close),
340  window);
341  pixbuf = gtk_icon_theme_load_icon (icon_theme, EV_STOCK_CLOSE, 8,
342  GTK_ICON_LOOKUP_FORCE_SIZE, NULL);
343  icon = gtk_image_new_from_pixbuf (pixbuf);
344  g_object_unref (pixbuf);
345  gtk_container_add (GTK_CONTAINER (window->close_button), icon);
346  gtk_widget_show (icon);
347 
348  gtk_box_pack_start (GTK_BOX (hbox), window->close_button, FALSE, FALSE, 0);
349  gtk_widget_show (window->close_button);
350 
351  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
352  gtk_widget_show (hbox);
353 
354  /* Contents */
355  swindow = gtk_scrolled_window_new (NULL, NULL);
356  window->text_view = gtk_text_view_new ();
357  gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (window->text_view), GTK_WRAP_WORD);
358  g_signal_connect (window->text_view, "state-flags-changed",
359  G_CALLBACK (text_view_state_flags_changed),
360  window);
361  gtk_container_add (GTK_CONTAINER (swindow), window->text_view);
362  gtk_widget_show (window->text_view);
363 
364  gtk_box_pack_start (GTK_BOX (vbox), swindow, TRUE, TRUE, 0);
365  gtk_widget_show (swindow);
366 
367  /* Resize bar */
368  gtk_window_set_has_resize_grip (GTK_WINDOW(window), FALSE);
369  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
370 
371  window->resize_sw = gtk_event_box_new ();
372  gtk_widget_add_events (window->resize_sw, GDK_BUTTON_PRESS_MASK);
373  g_signal_connect_swapped (window->resize_sw, "button-press-event",
374  G_CALLBACK (ev_annotation_window_resize),
375  window);
376  g_signal_connect (window->resize_sw, "realize",
378  window);
379 
380  pixbuf = gtk_icon_theme_load_icon (icon_theme, EV_STOCK_RESIZE_SW, 8,
381  GTK_ICON_LOOKUP_FORCE_SIZE, NULL);
382  icon = gtk_image_new_from_pixbuf (pixbuf);
383  g_object_unref (pixbuf);
384  gtk_container_add (GTK_CONTAINER (window->resize_sw), icon);
385  gtk_widget_show (icon);
386  gtk_box_pack_start (GTK_BOX (hbox), window->resize_sw, FALSE, FALSE, 0);
387  gtk_widget_show (window->resize_sw);
388 
389  window->resize_se = gtk_event_box_new ();
390  gtk_widget_add_events (window->resize_se, GDK_BUTTON_PRESS_MASK);
391  g_signal_connect_swapped (window->resize_se, "button-press-event",
392  G_CALLBACK (ev_annotation_window_resize),
393  window);
394  g_signal_connect (window->resize_se, "realize",
396  window);
397 
398  pixbuf = gtk_icon_theme_load_icon (icon_theme, EV_STOCK_RESIZE_SE, 8,
399  GTK_ICON_LOOKUP_FORCE_SIZE, NULL);
400  icon = gtk_image_new_from_pixbuf (pixbuf);
401  g_object_unref (pixbuf);
402  gtk_container_add (GTK_CONTAINER (window->resize_se), icon);
403  gtk_widget_show (icon);
404  gtk_box_pack_end (GTK_BOX (hbox), window->resize_se, FALSE, FALSE, 0);
405  gtk_widget_show (window->resize_se);
406 
407  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
408  gtk_widget_show (hbox);
409 
410  gtk_container_add (GTK_CONTAINER (window), vbox);
411  gtk_widget_show (vbox);
412 
413  gtk_widget_add_events (GTK_WIDGET (window),
414  GDK_BUTTON_PRESS_MASK |
415  GDK_KEY_PRESS_MASK);
416 
417  gtk_container_set_border_width (GTK_CONTAINER (window), 2);
418 
419  gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
420  gtk_window_set_skip_taskbar_hint (GTK_WINDOW (window), TRUE);
421  gtk_window_set_skip_pager_hint (GTK_WINDOW (window), TRUE);
422  gtk_window_set_resizable (GTK_WINDOW (window), TRUE);
423 }
424 
425 static GObject *
427  guint n_construct_properties,
428  GObjectConstructParam *construct_params)
429 {
430  GObject *object;
432  EvAnnotation *annot;
433  EvAnnotationMarkup *markup;
434  const gchar *contents;
435  const gchar *label;
436  GdkRGBA color;
437  EvRectangle *rect;
438  gdouble scale;
439  gdouble opacity;
440 
441  object = G_OBJECT_CLASS (ev_annotation_window_parent_class)->constructor (type,
442  n_construct_properties,
443  construct_params);
444  window = EV_ANNOTATION_WINDOW (object);
445  annot = window->annotation;
446  markup = EV_ANNOTATION_MARKUP (annot);
447 
448  gtk_window_set_transient_for (GTK_WINDOW (window), window->parent);
449  gtk_window_set_destroy_with_parent (GTK_WINDOW (window), FALSE);
450 
451  label = ev_annotation_markup_get_label (markup);
452  window->is_open = ev_annotation_markup_get_popup_is_open (markup);
453  ev_annotation_markup_get_rectangle (markup, &window->rect);
454 
455  rect = &window->rect;
456 
457  /* Rectangle is at doc resolution (72.0) */
458  scale = get_screen_dpi (window) / 72.0;
459  gtk_window_resize (GTK_WINDOW (window),
460  (gint)((rect->x2 - rect->x1) * scale),
461  (gint)((rect->y2 - rect->y1) * scale));
462 
463  ev_annotation_get_rgba (annot, &color);
464  ev_annotation_window_set_color (window, &color);
465 
466  opacity = ev_annotation_markup_get_opacity (markup);
467  ev_annotation_window_set_opacity (window, opacity);
468 
469  gtk_widget_set_name (GTK_WIDGET (window), ev_annotation_get_name (annot));
470  gtk_window_set_title (GTK_WINDOW (window), label);
471  gtk_label_set_text (GTK_LABEL (window->title), label);
472 
473  contents = ev_annotation_get_contents (annot);
474  if (contents) {
475  GtkTextBuffer *buffer;
476 
477  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (window->text_view));
478  gtk_text_buffer_set_text (buffer, contents, -1);
479  }
480 
481  g_signal_connect (annot, "notify::label",
483  window);
484  g_signal_connect (annot, "notify::rgba",
486  window);
487  g_signal_connect (annot, "notify::opacity",
489  window);
490 
491  return object;
492 }
493 
494 static gboolean
496  GdkEventConfigure *event)
497 {
499 
500  if (window->in_move &&
501  (window->x != event->x || window->y != event->y)) {
502  window->x = event->x;
503  window->y = event->y;
504  }
505 
506  return GTK_WIDGET_CLASS (ev_annotation_window_parent_class)->configure_event (widget, event);
507 }
508 
509 static gboolean
511  GdkEventFocus *event)
512 {
514 
515  if (window->in_move) {
516  if (window->orig_x != window->x || window->orig_y != window->y) {
517  window->orig_x = window->x;
518  window->orig_y = window->y;
519  g_signal_emit (window, signals[MOVED], 0, window->x, window->y);
520  }
521  window->in_move = FALSE;
522  }
523 
524  gtk_widget_grab_focus (window->text_view);
525  send_focus_change (window->text_view, TRUE);
526  gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (window->text_view), TRUE);
527 
528  return FALSE;
529 }
530 
531 static gboolean
533  GdkEventFocus *event)
534 {
536 
538 
539  return FALSE;
540 }
541 
542 static gboolean
544  GdkEventKey *event)
545 {
546  if (event->keyval == GDK_KEY_Escape) {
548  return TRUE;
549  }
550 
551  return GTK_WIDGET_CLASS (ev_annotation_window_parent_class)->key_press_event (widget, event);
552 }
553 
554 static void
556 {
557  GObjectClass *g_object_class = G_OBJECT_CLASS (klass);
558  GtkWidgetClass *gtk_widget_class = GTK_WIDGET_CLASS (klass);
559 
560  g_object_class->constructor = ev_annotation_window_constructor;
561  g_object_class->set_property = ev_annotation_window_set_property;
562  g_object_class->dispose = ev_annotation_window_dispose;
563 
564  gtk_widget_class->configure_event = ev_annotation_window_configure_event;
565  gtk_widget_class->focus_in_event = ev_annotation_window_focus_in_event;
566  gtk_widget_class->focus_out_event = ev_annotation_window_focus_out_event;
567  gtk_widget_class->key_press_event = ev_annotation_window_key_press_event;
568 
569  g_object_class_install_property (g_object_class,
571  g_param_spec_object ("annotation",
572  "Annotation",
573  "The annotation associated to the window",
575  G_PARAM_WRITABLE |
576  G_PARAM_CONSTRUCT_ONLY |
577  G_PARAM_STATIC_STRINGS));
578  g_object_class_install_property (g_object_class,
579  PROP_PARENT,
580  g_param_spec_object ("parent",
581  "Parent",
582  "The parent window",
583  GTK_TYPE_WINDOW,
584  G_PARAM_WRITABLE |
585  G_PARAM_CONSTRUCT_ONLY |
586  G_PARAM_STATIC_STRINGS));
587  signals[CLOSED] =
588  g_signal_new ("closed",
589  G_TYPE_FROM_CLASS (g_object_class),
590  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
591  G_STRUCT_OFFSET (EvAnnotationWindowClass, closed),
592  NULL, NULL,
593  g_cclosure_marshal_VOID__VOID,
594  G_TYPE_NONE, 0, G_TYPE_NONE);
595  signals[MOVED] =
596  g_signal_new ("moved",
597  G_TYPE_FROM_CLASS (g_object_class),
598  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
599  G_STRUCT_OFFSET (EvAnnotationWindowClass, moved),
600  NULL, NULL,
601  ev_view_marshal_VOID__INT_INT,
602  G_TYPE_NONE, 2,
603  G_TYPE_INT, G_TYPE_INT);
604 }
605 
606 /* Public methods */
607 GtkWidget *
609  GtkWindow *parent)
610 {
611  GtkWidget *window;
612 
613  g_return_val_if_fail (EV_IS_ANNOTATION_MARKUP (annot), NULL);
614  g_return_val_if_fail (GTK_IS_WINDOW (parent), NULL);
615 
616  window = g_object_new (EV_TYPE_ANNOTATION_WINDOW,
617  "annotation", annot,
618  "parent", parent,
619  NULL);
620  return window;
621 }
622 
623 EvAnnotation *
625 {
626  g_return_val_if_fail (EV_IS_ANNOTATION_WINDOW (window), NULL);
627 
628  return window->annotation;
629 }
630 
631 void
633  EvAnnotation *annot)
634 {
635  g_return_if_fail (EV_IS_ANNOTATION_WINDOW (window));
636  g_return_if_fail (EV_IS_ANNOTATION (annot));
637 
638  if (annot == window->annotation)
639  return;
640 
641  g_object_unref (window->annotation);
642  window->annotation = g_object_ref (annot);
644  g_object_notify (G_OBJECT (window), "annotation");
645 }
646 
647 gboolean
649 {
650  g_return_val_if_fail (EV_IS_ANNOTATION_WINDOW (window), FALSE);
651 
652  return window->is_open;
653 }
654 
655 void
657  EvRectangle *rect)
658 {
659  g_return_if_fail (EV_IS_ANNOTATION_WINDOW (window));
660  g_return_if_fail (rect != NULL);
661 
662  *rect = window->rect;
663 }
664 
665 void
667  const EvRectangle *rect)
668 {
669  g_return_if_fail (EV_IS_ANNOTATION_WINDOW (window));
670  g_return_if_fail (rect != NULL);
671 
672  window->rect = *rect;
673 }
674 
675 void
677 {
678  g_return_if_fail (EV_IS_ANNOTATION_WINDOW (window));
679 
680  if (!gtk_widget_has_focus (window->text_view)) {
681  gtk_widget_grab_focus (GTK_WIDGET (window));
682  send_focus_change (window->text_view, TRUE);
683  }
684 }
685 
686 void
688 {
689  g_return_if_fail (EV_IS_ANNOTATION_WINDOW (window));
690 
691  if (gtk_widget_has_focus (window->text_view)) {
692  send_focus_change (window->text_view, FALSE);
693  }
694 
696 }