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 #include <math.h>
26 
27 #include "ev-annotation-window.h"
28 #include "ev-color-contrast.h"
29 #include "ev-stock-icons.h"
30 #include "ev-view-marshal.h"
31 #include "ev-document-misc.h"
32 
33 #if WITH_GSPELL
34 #include <glib/gi18n.h>
35 #include <gspell/gspell.h>
36 #endif
37 
38 enum {
39 	PROP_0,
40 	PROP_ANNOTATION,
41 	PROP_PARENT
42 };
43 
44 enum {
45 	CLOSED,
46 	MOVED,
47 	N_SIGNALS
48 };
49 
50 struct _EvAnnotationWindow {
51 	GtkWindow     base_instance;
52 
53 	EvAnnotation *annotation;
54 	GtkWindow    *parent;
55 
56 	GtkWidget    *title;
57 	GtkWidget    *close_button;
58 	GtkWidget    *text_view;
59 	GtkWidget    *resize_se;
60 	GtkWidget    *resize_sw;
61 
62 	gboolean      is_open;
63 	EvRectangle   rect;
64 
65 	gboolean      in_move;
66 	gint          x;
67 	gint          y;
68 	gint          orig_x;
69 	gint          orig_y;
70 
71 #if WITH_GSPELL
72 	GspellTextView *spellcheck_view;
73 	gboolean      enable_spellchecking;
74 #endif
75 };
76 
77 struct _EvAnnotationWindowClass {
78 	GtkWindowClass base_class;
79 
80 	void (* closed) (EvAnnotationWindow *window);
81 	void (* moved)  (EvAnnotationWindow *window,
82 			 gint                x,
83 			 gint                y);
84 };
85 
86 static guint signals[N_SIGNALS];
87 
G_DEFINE_TYPE(EvAnnotationWindow,ev_annotation_window,GTK_TYPE_WINDOW)88 G_DEFINE_TYPE (EvAnnotationWindow, ev_annotation_window, GTK_TYPE_WINDOW)
89 
90 /* Cut and paste from gtkwindow.c */
91 static void
92 send_focus_change (GtkWidget *widget,
93 		   gboolean   in)
94 {
95 	GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE);
96 
97 	fevent->focus_change.type = GDK_FOCUS_CHANGE;
98 	fevent->focus_change.window = gtk_widget_get_window (widget);
99 	fevent->focus_change.in = in;
100 	if (fevent->focus_change.window)
101 		g_object_ref (fevent->focus_change.window);
102 
103 	gtk_widget_send_focus_change (widget, fevent);
104 
105 	gdk_event_free (fevent);
106 }
107 
108 static void
ev_annotation_window_sync_contents(EvAnnotationWindow * window)109 ev_annotation_window_sync_contents (EvAnnotationWindow *window)
110 {
111 	gchar         *contents;
112 	GtkTextIter    start, end;
113 	GtkTextBuffer *buffer;
114 	EvAnnotation  *annot = window->annotation;
115 
116 	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (window->text_view));
117 	gtk_text_buffer_get_bounds (buffer, &start, &end);
118 	contents = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
119 	ev_annotation_set_contents (annot, contents);
120 	g_free (contents);
121 }
122 
123 static void
ev_annotation_window_set_color(EvAnnotationWindow * window,GdkRGBA * color)124 ev_annotation_window_set_color (EvAnnotationWindow *window,
125 				GdkRGBA            *color)
126 {
127 	GtkCssProvider     *css_provider = gtk_css_provider_new ();
128 	g_autofree char    *rgba_str = gdk_rgba_to_string (color);
129 	g_autofree char    *css_data = NULL;
130 	g_autoptr (GError)  error = NULL;
131 	g_autoptr (GdkRGBA) icon_color = ev_color_contrast_get_best_foreground_color (color);
132 	g_autofree char    *icon_color_str = gdk_rgba_to_string (icon_color);
133 	css_data = g_strdup_printf ("button {border-color: %1$s; color: %2$s; -gtk-icon-shadow:0 0; box-shadow:0 0;}\n\
134 				     button:hover {background: lighter(%1$s); border-color: darker(%1$s);}\n\
135 				     button:active {background: darker(%1$s);}\n\
136 				     evannotationwindow.background, button {background: %1$s}",
137 				    rgba_str, icon_color_str);
138 
139 	gtk_css_provider_load_from_data (css_provider, css_data, strlen (css_data), &error);
140 	if (error != NULL)
141 		g_error ("%s", error->message);
142 
143 	gtk_style_context_add_provider (gtk_widget_get_style_context (GTK_WIDGET (window)),
144 					GTK_STYLE_PROVIDER (css_provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
145 	gtk_style_context_add_provider (gtk_widget_get_style_context (window->close_button),
146 					GTK_STYLE_PROVIDER (css_provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
147 	gtk_style_context_add_class (gtk_widget_get_style_context (window->close_button), "circular");
148 }
149 
150 static void
ev_annotation_window_set_opacity(EvAnnotationWindow * window,gdouble opacity)151 ev_annotation_window_set_opacity (EvAnnotationWindow *window,
152 		                  gdouble             opacity)
153 {
154 	gtk_widget_set_opacity (GTK_WIDGET (window), opacity);
155 	gtk_widget_set_opacity (GTK_WIDGET (window->text_view), opacity);
156 }
157 
158 static void
ev_annotation_window_label_changed(EvAnnotationMarkup * annot,GParamSpec * pspec,EvAnnotationWindow * window)159 ev_annotation_window_label_changed (EvAnnotationMarkup *annot,
160 				    GParamSpec         *pspec,
161 				    EvAnnotationWindow *window)
162 {
163 	const gchar *label = ev_annotation_markup_get_label (annot);
164 
165 	gtk_window_set_title (GTK_WINDOW (window), label);
166 	gtk_label_set_text (GTK_LABEL (window->title), label);
167 }
168 
169 static void
ev_annotation_window_color_changed(EvAnnotation * annot,GParamSpec * pspec,EvAnnotationWindow * window)170 ev_annotation_window_color_changed (EvAnnotation       *annot,
171 				    GParamSpec         *pspec,
172 				    EvAnnotationWindow *window)
173 {
174 	GdkRGBA rgba;
175 
176 	ev_annotation_get_rgba (annot, &rgba);
177 	ev_annotation_window_set_color (window, &rgba);
178 }
179 
180 static void
ev_annotation_window_opacity_changed(EvAnnotation * annot,GParamSpec * pspec,EvAnnotationWindow * window)181 ev_annotation_window_opacity_changed (EvAnnotation       *annot,
182 		                      GParamSpec         *pspec,
183 				      EvAnnotationWindow *window)
184 {
185 	gdouble opacity;
186 
187 	opacity = ev_annotation_markup_get_opacity (EV_ANNOTATION_MARKUP (annot));
188 	ev_annotation_window_set_opacity (window, opacity);
189 }
190 
191 static void
ev_annotation_window_dispose(GObject * object)192 ev_annotation_window_dispose (GObject *object)
193 {
194 	EvAnnotationWindow *window = EV_ANNOTATION_WINDOW (object);
195 
196 	if (window->annotation) {
197 		ev_annotation_window_sync_contents (window);
198 		g_object_unref (window->annotation);
199 		window->annotation = NULL;
200 	}
201 
202 	(* G_OBJECT_CLASS (ev_annotation_window_parent_class)->dispose) (object);
203 }
204 
205 static void
ev_annotation_window_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)206 ev_annotation_window_set_property (GObject      *object,
207 				   guint         prop_id,
208 				   const GValue *value,
209 				   GParamSpec   *pspec)
210 {
211 	EvAnnotationWindow *window = EV_ANNOTATION_WINDOW (object);
212 
213 	switch (prop_id) {
214 	case PROP_ANNOTATION:
215 		window->annotation = g_value_dup_object (value);
216 		break;
217 	case PROP_PARENT:
218 		window->parent = g_value_get_object (value);
219 		break;
220 	default:
221 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
222 	}
223 }
224 
225 static gboolean
ev_annotation_window_resize(EvAnnotationWindow * window,GdkEventButton * event,GtkWidget * ebox)226 ev_annotation_window_resize (EvAnnotationWindow *window,
227 			     GdkEventButton     *event,
228 			     GtkWidget          *ebox)
229 {
230 	if (event->type == GDK_BUTTON_PRESS && event->button == 1) {
231 		gtk_window_begin_resize_drag (GTK_WINDOW (window),
232 					      window->resize_sw == ebox ?
233 					      GDK_WINDOW_EDGE_SOUTH_WEST :
234 					      GDK_WINDOW_EDGE_SOUTH_EAST,
235 					      event->button, event->x_root,
236 					      event->y_root, event->time);
237 		return TRUE;
238 	}
239 
240 	return FALSE;
241 }
242 
243 static void
ev_annotation_window_set_resize_cursor(GtkWidget * widget,EvAnnotationWindow * window)244 ev_annotation_window_set_resize_cursor (GtkWidget          *widget,
245 					EvAnnotationWindow *window)
246 {
247 	GdkWindow *gdk_window = gtk_widget_get_window (widget);
248 
249 	if (!gdk_window)
250 		return;
251 
252 	if (gtk_widget_is_sensitive (widget)) {
253 		GdkDisplay *display = gtk_widget_get_display (widget);
254 		GdkCursor  *cursor;
255 
256 		cursor = gdk_cursor_new_for_display (display,
257 						     widget == window->resize_sw ?
258 						     GDK_BOTTOM_LEFT_CORNER :
259 						     GDK_BOTTOM_RIGHT_CORNER);
260 		gdk_window_set_cursor (gdk_window, cursor);
261 		g_object_unref (cursor);
262 	} else {
263 		gdk_window_set_cursor (gdk_window, NULL);
264 	}
265 }
266 
267 static void
text_view_state_flags_changed(GtkWidget * widget,GtkStateFlags previous_flags)268 text_view_state_flags_changed (GtkWidget     *widget,
269 			       GtkStateFlags  previous_flags)
270 {
271 	GtkStateFlags current_flags = gtk_widget_get_state_flags (widget);
272 
273 	if (current_flags & GTK_STATE_FLAG_BACKDROP)
274 		gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (widget), FALSE);
275 }
276 
277 static void
ev_annotation_window_close(EvAnnotationWindow * window)278 ev_annotation_window_close (EvAnnotationWindow *window)
279 {
280 	gtk_widget_hide (GTK_WIDGET (window));
281 	g_signal_emit (window, signals[CLOSED], 0);
282 }
283 
284 static gboolean
ev_annotation_window_button_press_event(GtkWidget * widget,GdkEventButton * event)285 ev_annotation_window_button_press_event (GtkWidget      *widget,
286 					 GdkEventButton *event)
287 {
288 	EvAnnotationWindow *window = EV_ANNOTATION_WINDOW (widget);
289 
290 	if (event->type == GDK_BUTTON_PRESS && event->button == 1) {
291 		window->in_move = TRUE;
292 		window->x = event->x_root - event->x;
293 		window->y = event->y_root - event->y;
294 		gtk_window_begin_move_drag (GTK_WINDOW (widget),
295 					    event->button,
296 					    event->x_root,
297 					    event->y_root,
298 					    event->time);
299 		return TRUE;
300 	}
301 
302 	return FALSE;
303 }
304 
305 static void
ev_annotation_window_init(EvAnnotationWindow * window)306 ev_annotation_window_init (EvAnnotationWindow *window)
307 {
308 	GtkWidget    *vbox, *hbox;
309 	GtkWidget    *icon;
310 	GtkWidget    *swindow;
311 	GtkWidget    *header;
312 	GtkIconTheme *icon_theme;
313 	GdkPixbuf    *pixbuf;
314 
315 	icon_theme = gtk_icon_theme_get_default ();
316 
317 	gtk_widget_set_can_focus (GTK_WIDGET (window), TRUE);
318 
319 	vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
320 
321 	/* Title bar */
322 	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
323 
324 	icon = gtk_image_new (); /* FIXME: use the annot icon */
325 	gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
326 	gtk_widget_show (icon);
327 
328 	header = gtk_event_box_new ();
329 	gtk_widget_add_events (header, GDK_BUTTON_PRESS_MASK);
330 	g_signal_connect_swapped (header, "button-press-event",
331 				  G_CALLBACK (ev_annotation_window_button_press_event),
332 			          window);
333 
334 	window->title = gtk_label_new (NULL);
335 	gtk_container_add (GTK_CONTAINER (header), window->title);
336 	gtk_widget_show (window->title);
337 
338 	gtk_box_pack_start (GTK_BOX (hbox), header, TRUE, TRUE, 0);
339 	gtk_widget_show (header);
340 
341 	window->close_button = gtk_button_new_from_icon_name ("window-close-symbolic", GTK_ICON_SIZE_BUTTON);
342 	g_signal_connect_swapped (window->close_button, "clicked",
343 				  G_CALLBACK (ev_annotation_window_close),
344 				  window);
345 
346 	gtk_box_pack_start (GTK_BOX (hbox), window->close_button, FALSE, FALSE, 0);
347 	gtk_widget_show (window->close_button);
348 
349 	gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
350 	gtk_widget_show (hbox);
351 
352 	/* Contents */
353 	swindow = gtk_scrolled_window_new (NULL, NULL);
354 	window->text_view = gtk_text_view_new ();
355 
356 #if WITH_GSPELL
357 	window->spellcheck_view = NULL;
358 	window->spellcheck_view = gspell_text_view_get_from_gtk_text_view (GTK_TEXT_VIEW (window->text_view));
359 	gspell_text_view_basic_setup (window->spellcheck_view);
360 #endif
361 
362 	gtk_container_set_border_width (GTK_CONTAINER (window->text_view), 6);
363 	gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (window->text_view), GTK_WRAP_WORD);
364 	g_signal_connect (window->text_view, "state-flags-changed",
365 			  G_CALLBACK (text_view_state_flags_changed),
366 			  window);
367 	gtk_container_add (GTK_CONTAINER (swindow), window->text_view);
368 	gtk_widget_show (window->text_view);
369 
370 	gtk_box_pack_start (GTK_BOX (vbox), swindow, TRUE, TRUE, 0);
371 	gtk_widget_show (swindow);
372 
373 	/* Resize bar */
374 	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
375 
376 	window->resize_sw = gtk_event_box_new ();
377 	gtk_widget_add_events (window->resize_sw, GDK_BUTTON_PRESS_MASK);
378 	g_signal_connect_swapped (window->resize_sw, "button-press-event",
379 				  G_CALLBACK (ev_annotation_window_resize),
380 				  window);
381 	g_signal_connect (window->resize_sw, "realize",
382 			  G_CALLBACK (ev_annotation_window_set_resize_cursor),
383 			  window);
384 
385 	pixbuf = gtk_icon_theme_load_icon (icon_theme, EV_STOCK_RESIZE_SW, 8,
386 					   GTK_ICON_LOOKUP_FORCE_SIZE, NULL);
387 	icon = gtk_image_new_from_pixbuf (pixbuf);
388 	g_object_unref (pixbuf);
389 	gtk_container_add (GTK_CONTAINER (window->resize_sw), icon);
390 	gtk_widget_show (icon);
391 	gtk_box_pack_start (GTK_BOX (hbox), window->resize_sw, FALSE, FALSE, 0);
392 	gtk_widget_show (window->resize_sw);
393 
394 	window->resize_se = gtk_event_box_new ();
395 	gtk_widget_add_events (window->resize_se, GDK_BUTTON_PRESS_MASK);
396 	g_signal_connect_swapped (window->resize_se, "button-press-event",
397 				  G_CALLBACK (ev_annotation_window_resize),
398 				  window);
399 	g_signal_connect (window->resize_se, "realize",
400 			  G_CALLBACK (ev_annotation_window_set_resize_cursor),
401 			  window);
402 
403 	pixbuf = gtk_icon_theme_load_icon (icon_theme, EV_STOCK_RESIZE_SE, 8,
404 					   GTK_ICON_LOOKUP_FORCE_SIZE, NULL);
405 	icon = gtk_image_new_from_pixbuf (pixbuf);
406 	g_object_unref (pixbuf);
407 	gtk_container_add (GTK_CONTAINER (window->resize_se), icon);
408 	gtk_widget_show (icon);
409 	gtk_box_pack_end (GTK_BOX (hbox), window->resize_se, FALSE, FALSE, 0);
410 	gtk_widget_show (window->resize_se);
411 
412 	gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
413 	gtk_widget_show (hbox);
414 
415 	gtk_container_add (GTK_CONTAINER (window), vbox);
416 	gtk_widget_show (vbox);
417 
418 	gtk_widget_add_events (GTK_WIDGET (window),
419 			       GDK_BUTTON_PRESS_MASK |
420 			       GDK_KEY_PRESS_MASK);
421 
422 	gtk_container_set_border_width (GTK_CONTAINER (window), 2);
423 
424 	gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
425 	gtk_window_set_skip_taskbar_hint (GTK_WINDOW (window), TRUE);
426 	gtk_window_set_skip_pager_hint (GTK_WINDOW (window), TRUE);
427 	gtk_window_set_resizable (GTK_WINDOW (window), TRUE);
428 }
429 
430 static GObject *
ev_annotation_window_constructor(GType type,guint n_construct_properties,GObjectConstructParam * construct_params)431 ev_annotation_window_constructor (GType                  type,
432 				  guint                  n_construct_properties,
433 				  GObjectConstructParam *construct_params)
434 {
435 	GObject            *object;
436 	EvAnnotationWindow *window;
437 	EvAnnotation       *annot;
438 	EvAnnotationMarkup *markup;
439 	const gchar        *contents;
440 	const gchar        *label;
441 	GdkRGBA             color;
442 	EvRectangle        *rect;
443 	gdouble             scale;
444 	gdouble             opacity;
445 
446 	object = G_OBJECT_CLASS (ev_annotation_window_parent_class)->constructor (type,
447 										  n_construct_properties,
448 										  construct_params);
449 	window = EV_ANNOTATION_WINDOW (object);
450 	annot = window->annotation;
451 	markup = EV_ANNOTATION_MARKUP (annot);
452 
453 	gtk_window_set_transient_for (GTK_WINDOW (window), window->parent);
454 	gtk_window_set_destroy_with_parent (GTK_WINDOW (window), FALSE);
455 
456 	label = ev_annotation_markup_get_label (markup);
457 	window->is_open = ev_annotation_markup_get_popup_is_open (markup);
458 	ev_annotation_markup_get_rectangle (markup, &window->rect);
459 
460 	rect = &window->rect;
461 
462 	/* Rectangle is at doc resolution (72.0) */
463 	scale = ev_document_misc_get_widget_dpi (GTK_WIDGET (window)) / 72.0;
464 	gtk_window_resize (GTK_WINDOW (window),
465 			   (gint)((rect->x2 - rect->x1) * scale),
466 			   (gint)((rect->y2 - rect->y1) * scale));
467 
468 	ev_annotation_get_rgba (annot, &color);
469 	ev_annotation_window_set_color (window, &color);
470 
471 	opacity = ev_annotation_markup_get_opacity (markup);
472 	ev_annotation_window_set_opacity (window, opacity);
473 
474 	gtk_widget_set_name (GTK_WIDGET (window), ev_annotation_get_name (annot));
475 	gtk_window_set_title (GTK_WINDOW (window), label);
476 	gtk_label_set_text (GTK_LABEL (window->title), label);
477 
478 	contents = ev_annotation_get_contents (annot);
479 	if (contents) {
480 		GtkTextBuffer *buffer;
481 
482 		buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (window->text_view));
483 		gtk_text_buffer_set_text (buffer, contents, -1);
484 	}
485 
486 	g_signal_connect (annot, "notify::label",
487 			  G_CALLBACK (ev_annotation_window_label_changed),
488 			  window);
489 	g_signal_connect (annot, "notify::rgba",
490 			  G_CALLBACK (ev_annotation_window_color_changed),
491 			  window);
492 	g_signal_connect (annot, "notify::opacity",
493 			  G_CALLBACK (ev_annotation_window_opacity_changed),
494 			  window);
495 
496 #if WITH_GSPELL
497         gspell_text_view_set_inline_spell_checking (window->spellcheck_view, ev_annotation_window_get_enable_spellchecking (window));
498 #endif
499 	return object;
500 }
501 
502 static gboolean
ev_annotation_window_configure_event(GtkWidget * widget,GdkEventConfigure * event)503 ev_annotation_window_configure_event (GtkWidget         *widget,
504 				      GdkEventConfigure *event)
505 {
506 	EvAnnotationWindow *window = EV_ANNOTATION_WINDOW (widget);
507 
508 	if (window->in_move &&
509 	    (window->x != event->x || window->y != event->y)) {
510 		window->x = event->x;
511 		window->y = event->y;
512 	}
513 
514 	return GTK_WIDGET_CLASS (ev_annotation_window_parent_class)->configure_event (widget, event);
515 }
516 
517 static gboolean
ev_annotation_window_focus_in_event(GtkWidget * widget,GdkEventFocus * event)518 ev_annotation_window_focus_in_event (GtkWidget     *widget,
519 				     GdkEventFocus *event)
520 {
521 	EvAnnotationWindow *window = EV_ANNOTATION_WINDOW (widget);
522 
523 	if (window->in_move) {
524 		if (window->orig_x != window->x || window->orig_y != window->y) {
525 			window->orig_x = window->x;
526 			window->orig_y = window->y;
527 			g_signal_emit (window, signals[MOVED], 0, window->x, window->y);
528 		}
529 		window->in_move = FALSE;
530 	}
531 
532 	gtk_widget_grab_focus (window->text_view);
533 	send_focus_change (window->text_view, TRUE);
534 	gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (window->text_view), TRUE);
535 
536 	return FALSE;
537 }
538 
539 static gboolean
ev_annotation_window_focus_out_event(GtkWidget * widget,GdkEventFocus * event)540 ev_annotation_window_focus_out_event (GtkWidget     *widget,
541 				      GdkEventFocus *event)
542 {
543 	EvAnnotationWindow *window = EV_ANNOTATION_WINDOW (widget);
544 
545 	ev_annotation_window_sync_contents (window);
546 
547 	return FALSE;
548 }
549 
550 static gboolean
ev_annotation_window_key_press_event(GtkWidget * widget,GdkEventKey * event)551 ev_annotation_window_key_press_event (GtkWidget   *widget,
552                                       GdkEventKey *event)
553 {
554         if (event->keyval == GDK_KEY_Escape) {
555                 ev_annotation_window_close (EV_ANNOTATION_WINDOW (widget));
556                 return TRUE;
557         }
558 
559         return GTK_WIDGET_CLASS (ev_annotation_window_parent_class)->key_press_event (widget, event);
560 }
561 
562 static void
ev_annotation_window_class_init(EvAnnotationWindowClass * klass)563 ev_annotation_window_class_init (EvAnnotationWindowClass *klass)
564 {
565 	GObjectClass   *g_object_class = G_OBJECT_CLASS (klass);
566 	GtkWidgetClass *gtk_widget_class = GTK_WIDGET_CLASS (klass);
567 
568 	g_object_class->constructor = ev_annotation_window_constructor;
569 	g_object_class->set_property = ev_annotation_window_set_property;
570 	g_object_class->dispose = ev_annotation_window_dispose;
571 
572 	gtk_widget_class->configure_event = ev_annotation_window_configure_event;
573 	gtk_widget_class->focus_in_event = ev_annotation_window_focus_in_event;
574 	gtk_widget_class->focus_out_event = ev_annotation_window_focus_out_event;
575         gtk_widget_class->key_press_event = ev_annotation_window_key_press_event;
576 
577 #if GTK_CHECK_VERSION(3, 20, 0)
578 	gtk_widget_class_set_css_name (gtk_widget_class, "evannotationwindow");
579 #endif
580 	g_object_class_install_property (g_object_class,
581 					 PROP_ANNOTATION,
582 					 g_param_spec_object ("annotation",
583 							      "Annotation",
584 							      "The annotation associated to the window",
585 							      EV_TYPE_ANNOTATION_MARKUP,
586 							      G_PARAM_WRITABLE |
587 							      G_PARAM_CONSTRUCT_ONLY |
588                                                               G_PARAM_STATIC_STRINGS));
589 	g_object_class_install_property (g_object_class,
590 					 PROP_PARENT,
591 					 g_param_spec_object ("parent",
592 							      "Parent",
593 							      "The parent window",
594 							      GTK_TYPE_WINDOW,
595 							      G_PARAM_WRITABLE |
596 							      G_PARAM_CONSTRUCT_ONLY |
597                                                               G_PARAM_STATIC_STRINGS));
598 	signals[CLOSED] =
599 		g_signal_new ("closed",
600 			      G_TYPE_FROM_CLASS (g_object_class),
601 			      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
602 			      G_STRUCT_OFFSET (EvAnnotationWindowClass, closed),
603 			      NULL, NULL,
604 			      g_cclosure_marshal_VOID__VOID,
605 			      G_TYPE_NONE, 0, G_TYPE_NONE);
606 	signals[MOVED] =
607 		g_signal_new ("moved",
608 			      G_TYPE_FROM_CLASS (g_object_class),
609 			      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
610 			      G_STRUCT_OFFSET (EvAnnotationWindowClass, moved),
611 			      NULL, NULL,
612 			      ev_view_marshal_VOID__INT_INT,
613 			      G_TYPE_NONE, 2,
614 			      G_TYPE_INT, G_TYPE_INT);
615 }
616 
617 /* Public methods */
618 GtkWidget *
ev_annotation_window_new(EvAnnotation * annot,GtkWindow * parent)619 ev_annotation_window_new (EvAnnotation *annot,
620 			  GtkWindow    *parent)
621 {
622 	GtkWidget *window;
623 
624 	g_return_val_if_fail (EV_IS_ANNOTATION_MARKUP (annot), NULL);
625 	g_return_val_if_fail (GTK_IS_WINDOW (parent), NULL);
626 
627 	window = g_object_new (EV_TYPE_ANNOTATION_WINDOW,
628 			       "annotation", annot,
629 			       "parent", parent,
630 			       NULL);
631 	return window;
632 }
633 
634 EvAnnotation *
ev_annotation_window_get_annotation(EvAnnotationWindow * window)635 ev_annotation_window_get_annotation (EvAnnotationWindow *window)
636 {
637 	g_return_val_if_fail (EV_IS_ANNOTATION_WINDOW (window), NULL);
638 
639 	return window->annotation;
640 }
641 
642 void
ev_annotation_window_set_annotation(EvAnnotationWindow * window,EvAnnotation * annot)643 ev_annotation_window_set_annotation (EvAnnotationWindow *window,
644 				     EvAnnotation       *annot)
645 {
646 	g_return_if_fail (EV_IS_ANNOTATION_WINDOW (window));
647 	g_return_if_fail (EV_IS_ANNOTATION (annot));
648 
649 	if (annot == window->annotation)
650 		return;
651 
652 	g_object_unref (window->annotation);
653 	window->annotation = g_object_ref (annot);
654 	ev_annotation_window_sync_contents (window);
655 	g_object_notify (G_OBJECT (window), "annotation");
656 }
657 
658 gboolean
ev_annotation_window_is_open(EvAnnotationWindow * window)659 ev_annotation_window_is_open (EvAnnotationWindow *window)
660 {
661 	g_return_val_if_fail (EV_IS_ANNOTATION_WINDOW (window), FALSE);
662 
663 	return window->is_open;
664 }
665 
666 void
ev_annotation_window_get_rectangle(EvAnnotationWindow * window,EvRectangle * rect)667 ev_annotation_window_get_rectangle (EvAnnotationWindow *window,
668 				    EvRectangle        *rect)
669 {
670 	g_return_if_fail (EV_IS_ANNOTATION_WINDOW (window));
671 	g_return_if_fail (rect != NULL);
672 
673 	*rect = window->rect;
674 }
675 
676 void
ev_annotation_window_set_rectangle(EvAnnotationWindow * window,const EvRectangle * rect)677 ev_annotation_window_set_rectangle (EvAnnotationWindow *window,
678 				    const EvRectangle  *rect)
679 {
680 	g_return_if_fail (EV_IS_ANNOTATION_WINDOW (window));
681 	g_return_if_fail (rect != NULL);
682 
683 	window->rect = *rect;
684 }
685 
686 void
ev_annotation_window_grab_focus(EvAnnotationWindow * window)687 ev_annotation_window_grab_focus (EvAnnotationWindow *window)
688 {
689 	g_return_if_fail (EV_IS_ANNOTATION_WINDOW (window));
690 
691 	if (!gtk_widget_has_focus (window->text_view)) {
692 		gtk_widget_grab_focus (GTK_WIDGET (window));
693 		send_focus_change (window->text_view, TRUE);
694 	}
695 }
696 
697 void
ev_annotation_window_ungrab_focus(EvAnnotationWindow * window)698 ev_annotation_window_ungrab_focus (EvAnnotationWindow *window)
699 {
700 	g_return_if_fail (EV_IS_ANNOTATION_WINDOW (window));
701 
702 	if (gtk_widget_has_focus (window->text_view)) {
703 		send_focus_change (window->text_view, FALSE);
704 	}
705 
706 	ev_annotation_window_sync_contents (window);
707 }
708 
709 void
ev_annotation_window_set_enable_spellchecking(EvAnnotationWindow * window,gboolean enable_spellchecking)710 ev_annotation_window_set_enable_spellchecking (EvAnnotationWindow *window,
711                                                gboolean enable_spellchecking)
712 {
713         g_return_if_fail (EV_IS_ANNOTATION_WINDOW (window));
714 
715 #if WITH_GSPELL
716         if (enable_spellchecking == ev_annotation_window_get_enable_spellchecking (window))
717                 return;
718 
719         window->enable_spellchecking = enable_spellchecking;
720         gspell_text_view_set_inline_spell_checking (window->spellcheck_view, enable_spellchecking);
721 #endif
722 }
723 
724 gboolean
ev_annotation_window_get_enable_spellchecking(EvAnnotationWindow * window)725 ev_annotation_window_get_enable_spellchecking (EvAnnotationWindow *window)
726 {
727         g_return_val_if_fail (EV_IS_ANNOTATION_WINDOW (window), FALSE);
728 #if WITH_GSPELL
729         return window->enable_spellchecking;
730 #else
731         return FALSE;
732 #endif
733 }
734