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