1 /* ev-annotation-window.c
2 * this file is part of evince, a gnome document viewer
4 * Copyright (C) 2009 Carlos Garcia Campos <carlosgc@gnome.org>
5 * Copyright (C) 2007 IƱigo Martinez <inigomartinez@gmail.com>
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.
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.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
26 #include "ev-annotation-window.h"
27 #include "ev-stock-icons.h"
28 #include "ev-view-marshal.h"
29 #include "ev-document-misc.h"
43 struct _EvAnnotationWindow {
44 GtkWindow base_instance;
46 EvAnnotation *annotation;
50 GtkWidget *close_button;
65 struct _EvAnnotationWindowClass {
66 GtkWindowClass base_class;
68 void (* closed) (EvAnnotationWindow *window);
69 void (* moved) (EvAnnotationWindow *window,
74 static guint signals[N_SIGNALS];
76 G_DEFINE_TYPE (EvAnnotationWindow, ev_annotation_window, GTK_TYPE_WINDOW)
78 #define EV_ICON_SIZE_ANNOT_WINDOW (ev_annotation_window_get_icon_size())
80 /* Cut and paste from gtkwindow.c */
82 send_focus_change (GtkWidget *widget,
85 GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE);
87 g_object_ref (widget);
90 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
92 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
94 fevent->focus_change.type = GDK_FOCUS_CHANGE;
95 fevent->focus_change.window = g_object_ref (widget->window);
96 fevent->focus_change.in = in;
98 gtk_widget_event (widget, fevent);
100 g_object_notify (G_OBJECT (widget), "has-focus");
102 g_object_unref (widget);
103 gdk_event_free (fevent);
107 get_screen_dpi (EvAnnotationWindow *window)
111 screen = gtk_window_get_screen (GTK_WINDOW (window));
112 return ev_document_misc_get_screen_dpi (screen);
116 ev_annotation_window_get_icon_size (void)
118 static GtkIconSize icon_size = 0;
120 if (G_UNLIKELY (icon_size == 0))
121 icon_size = gtk_icon_size_register ("ev-icon-size-annot-window", 8, 8);
127 ev_annotation_window_check_contents_modified (EvAnnotationWindow *window)
130 GtkTextIter start, end;
131 GtkTextBuffer *buffer;
132 EvAnnotation *annot = window->annotation;
134 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (window->text_view));
135 gtk_text_buffer_get_bounds (buffer, &start, &end);
136 contents = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
138 if (contents && annot->contents) {
139 if (strcasecmp (contents, annot->contents) != 0) {
140 g_free (annot->contents);
141 annot->contents = contents;
142 annot->changed = TRUE;
146 } else if (annot->contents) {
147 g_free (annot->contents);
148 annot->contents = NULL;
149 annot->changed = TRUE;
150 } else if (contents) {
151 annot->contents = contents;
152 annot->changed = TRUE;
157 ev_annotation_window_set_color (EvAnnotationWindow *window,
160 GtkRcStyle *rc_style;
165 /* Allocate these colors */
166 gdk_colormap_alloc_color (gtk_widget_get_colormap (GTK_WIDGET (window)),
167 &gcolor, FALSE, TRUE);
169 /* Apply colors to style */
170 rc_style = gtk_widget_get_modifier_style (GTK_WIDGET (window));
171 rc_style->base[GTK_STATE_NORMAL] = gcolor;
172 rc_style->bg[GTK_STATE_PRELIGHT] = gcolor;
173 rc_style->bg[GTK_STATE_NORMAL] = gcolor;
174 rc_style->bg[GTK_STATE_ACTIVE] = gcolor;
175 rc_style->color_flags[GTK_STATE_PRELIGHT] = GTK_RC_BG;
176 rc_style->color_flags[GTK_STATE_NORMAL] = GTK_RC_BG | GTK_RC_BASE;
177 rc_style->color_flags[GTK_STATE_ACTIVE] = GTK_RC_BG;
179 /* Apply the style to the widgets */
180 g_object_ref (rc_style);
181 gtk_widget_modify_style (GTK_WIDGET (window), rc_style);
182 gtk_widget_modify_style (window->close_button, rc_style);
183 gtk_widget_modify_style (window->resize_se, rc_style);
184 gtk_widget_modify_style (window->resize_sw, rc_style);
185 g_object_unref (rc_style);
189 ev_annotation_window_dispose (GObject *object)
191 EvAnnotationWindow *window = EV_ANNOTATION_WINDOW (object);
193 if (window->annotation) {
194 ev_annotation_window_check_contents_modified (window);
195 g_object_unref (window->annotation);
196 window->annotation = NULL;
200 ev_rectangle_free (window->rect);
204 (* G_OBJECT_CLASS (ev_annotation_window_parent_class)->dispose) (object);
208 ev_annotation_window_set_property (GObject *object,
213 EvAnnotationWindow *window = EV_ANNOTATION_WINDOW (object);
216 case PROP_ANNOTATION:
217 window->annotation = g_value_dup_object (value);
220 window->parent = g_value_get_object (value);
223 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
228 ev_annotation_window_resize (EvAnnotationWindow *window,
229 GdkEventButton *event,
232 if (event->type == GDK_BUTTON_PRESS && event->button == 1) {
233 gtk_window_begin_resize_drag (GTK_WINDOW (window),
234 window->resize_sw == ebox ?
235 GDK_WINDOW_EDGE_SOUTH_WEST :
236 GDK_WINDOW_EDGE_SOUTH_EAST,
237 event->button, event->x_root,
238 event->y_root, event->time);
246 ev_annotation_window_set_resize_cursor (GtkWidget *widget,
247 EvAnnotationWindow *window)
252 if (GTK_WIDGET_IS_SENSITIVE (widget)) {
253 GdkDisplay *display = gtk_widget_get_display (widget);
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 (widget->window, cursor);
261 gdk_cursor_unref (cursor);
263 gdk_window_set_cursor (widget->window, NULL);
268 text_view_button_press (GtkWidget *widget,
269 GdkEventButton *event,
270 EvAnnotationWindow *window)
272 ev_annotation_window_grab_focus (window);
278 ev_annotation_window_close (EvAnnotationWindow *window)
280 gtk_widget_hide (GTK_WIDGET (window));
281 g_signal_emit (window, signals[CLOSED], 0);
285 ev_annotation_window_init (EvAnnotationWindow *window)
287 GtkWidget *vbox, *hbox;
291 GTK_WIDGET_SET_FLAGS (window, GTK_CAN_FOCUS);
293 vbox = gtk_vbox_new (FALSE, 0);
296 hbox = gtk_hbox_new (FALSE, 0);
298 icon = gtk_image_new (); /* FIXME: use the annot icon */
299 gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
300 gtk_widget_show (icon);
302 window->title = gtk_label_new (NULL);
303 gtk_box_pack_start (GTK_BOX (hbox), window->title, TRUE, TRUE, 0);
304 gtk_widget_show (window->title);
306 window->close_button = gtk_button_new ();
307 gtk_button_set_relief (GTK_BUTTON (window->close_button), GTK_RELIEF_NONE);
308 gtk_container_set_border_width (GTK_CONTAINER (window->close_button), 0);
309 g_signal_connect_swapped (window->close_button, "clicked",
310 G_CALLBACK (ev_annotation_window_close),
312 icon = gtk_image_new_from_stock (EV_STOCK_CLOSE, EV_ICON_SIZE_ANNOT_WINDOW);
313 gtk_container_add (GTK_CONTAINER (window->close_button), icon);
314 gtk_widget_show (icon);
316 gtk_box_pack_start (GTK_BOX (hbox), window->close_button, FALSE, FALSE, 0);
317 gtk_widget_show (window->close_button);
319 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
320 gtk_widget_show (hbox);
323 swindow = gtk_scrolled_window_new (NULL, NULL);
324 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swindow),
325 GTK_POLICY_AUTOMATIC,
326 GTK_POLICY_AUTOMATIC);
327 window->text_view = gtk_text_view_new ();
328 g_signal_connect (window->text_view, "button_press_event",
329 G_CALLBACK (text_view_button_press),
331 gtk_container_add (GTK_CONTAINER (swindow), window->text_view);
332 gtk_widget_show (window->text_view);
334 gtk_box_pack_start (GTK_BOX (vbox), swindow, TRUE, TRUE, 0);
335 gtk_widget_show (swindow);
338 hbox = gtk_hbox_new (FALSE, 0);
340 window->resize_sw = gtk_event_box_new ();
341 gtk_widget_add_events (window->resize_sw, GDK_BUTTON_PRESS_MASK);
342 g_signal_connect_swapped (window->resize_sw, "button-press-event",
343 G_CALLBACK (ev_annotation_window_resize),
345 g_signal_connect (window->resize_sw, "realize",
346 G_CALLBACK (ev_annotation_window_set_resize_cursor),
349 icon = gtk_image_new_from_stock (EV_STOCK_RESIZE_SW, EV_ICON_SIZE_ANNOT_WINDOW);
350 gtk_container_add (GTK_CONTAINER (window->resize_sw), icon);
351 gtk_widget_show (icon);
352 gtk_box_pack_start (GTK_BOX (hbox), window->resize_sw, FALSE, FALSE, 0);
353 gtk_widget_show (window->resize_sw);
355 window->resize_se = gtk_event_box_new ();
356 gtk_widget_add_events (window->resize_se, GDK_BUTTON_PRESS_MASK);
357 g_signal_connect_swapped (window->resize_se, "button-press-event",
358 G_CALLBACK (ev_annotation_window_resize),
360 g_signal_connect (window->resize_se, "realize",
361 G_CALLBACK (ev_annotation_window_set_resize_cursor),
364 icon = gtk_image_new_from_stock (EV_STOCK_RESIZE_SE, EV_ICON_SIZE_ANNOT_WINDOW);
365 gtk_container_add (GTK_CONTAINER (window->resize_se), icon);
366 gtk_widget_show (icon);
367 gtk_box_pack_end (GTK_BOX (hbox), window->resize_se, FALSE, FALSE, 0);
368 gtk_widget_show (window->resize_se);
370 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
371 gtk_widget_show (hbox);
373 gtk_container_add (GTK_CONTAINER (window), vbox);
374 gtk_container_set_border_width (GTK_CONTAINER (window), 0);
375 gtk_widget_show (vbox);
377 gtk_widget_add_events (GTK_WIDGET (window),
378 GDK_BUTTON_PRESS_MASK |
380 gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
382 gtk_container_set_border_width (GTK_CONTAINER (window), 2);
384 gtk_window_set_accept_focus (GTK_WINDOW (window), FALSE);
385 gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
386 gtk_window_set_skip_taskbar_hint (GTK_WINDOW (window), TRUE);
387 gtk_window_set_skip_pager_hint (GTK_WINDOW (window), TRUE);
388 gtk_window_set_resizable (GTK_WINDOW (window), TRUE);
392 ev_annotation_window_constructor (GType type,
393 guint n_construct_properties,
394 GObjectConstructParam *construct_params)
397 EvAnnotationWindow *window;
404 object = G_OBJECT_CLASS (ev_annotation_window_parent_class)->constructor (type,
405 n_construct_properties,
407 window = EV_ANNOTATION_WINDOW (object);
408 annot = window->annotation;
410 gtk_window_set_transient_for (GTK_WINDOW (window), window->parent);
411 gtk_window_set_destroy_with_parent (GTK_WINDOW (window), FALSE);
416 "is_open", &window->is_open,
417 "rectangle", &window->rect,
421 /* Rectangle is at doc resolution (72.0) */
422 scale = get_screen_dpi (window) / 72.0;
423 gtk_window_resize (GTK_WINDOW (window),
424 (gint)((rect->x2 - rect->x1) * scale),
425 (gint)((rect->y2 - rect->y1) * scale));
426 ev_annotation_window_set_color (window, &annot->color);
427 gtk_widget_set_name (GTK_WIDGET (window), annot->name);
428 gtk_window_set_title (GTK_WINDOW (window), label);
429 gtk_label_set_text (GTK_LABEL (window->title), label);
430 gtk_window_set_opacity (GTK_WINDOW (window), opacity);
433 if (annot->contents) {
434 GtkTextBuffer *buffer;
436 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (window->text_view));
437 gtk_text_buffer_set_text (buffer, annot->contents, -1);
444 ev_annotation_window_button_press_event (GtkWidget *widget,
445 GdkEventButton *event)
447 EvAnnotationWindow *window = EV_ANNOTATION_WINDOW (widget);
449 if (event->type == GDK_BUTTON_PRESS && event->button == 1) {
450 window->in_move = TRUE;
451 window->x = event->x;
452 window->y = event->y;
453 gtk_window_begin_move_drag (GTK_WINDOW (widget),
465 ev_annotation_window_configure_event (GtkWidget *widget,
466 GdkEventConfigure *event)
468 EvAnnotationWindow *window = EV_ANNOTATION_WINDOW (widget);
470 if (window->in_move &&
471 (window->x != event->x || window->y != event->y)) {
472 window->x = event->x;
473 window->y = event->y;
476 return GTK_WIDGET_CLASS (ev_annotation_window_parent_class)->configure_event (widget, event);
480 ev_annotation_window_focus_in_event (GtkWidget *widget,
481 GdkEventFocus *event)
483 EvAnnotationWindow *window = EV_ANNOTATION_WINDOW (widget);
485 if (window->in_move) {
486 window->orig_x = window->x;
487 window->orig_y = window->y;
494 ev_annotation_window_focus_out_event (GtkWidget *widget,
495 GdkEventFocus *event)
497 EvAnnotationWindow *window = EV_ANNOTATION_WINDOW (widget);
499 if (window->in_move &&
500 (window->orig_x != window->x || window->orig_y != window->y)) {
501 window->in_move = FALSE;
502 g_signal_emit (window, signals[MOVED], 0, window->x, window->y);
509 ev_annotation_window_class_init (EvAnnotationWindowClass *klass)
511 GObjectClass *g_object_class = G_OBJECT_CLASS (klass);
512 GtkWidgetClass *gtk_widget_class = GTK_WIDGET_CLASS (klass);
514 g_object_class->constructor = ev_annotation_window_constructor;
515 g_object_class->set_property = ev_annotation_window_set_property;
516 g_object_class->dispose = ev_annotation_window_dispose;
518 gtk_widget_class->button_press_event = ev_annotation_window_button_press_event;
519 gtk_widget_class->configure_event = ev_annotation_window_configure_event;
520 gtk_widget_class->focus_in_event = ev_annotation_window_focus_in_event;
521 gtk_widget_class->focus_out_event = ev_annotation_window_focus_out_event;
523 g_object_class_install_property (g_object_class,
525 g_param_spec_object ("annotation",
527 "The annotation associated to the window",
528 EV_TYPE_ANNOTATION_MARKUP,
530 G_PARAM_CONSTRUCT_ONLY));
531 g_object_class_install_property (g_object_class,
533 g_param_spec_object ("parent",
538 G_PARAM_CONSTRUCT_ONLY));
540 g_signal_new ("closed",
541 G_TYPE_FROM_CLASS (g_object_class),
542 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
543 G_STRUCT_OFFSET (EvAnnotationWindowClass, closed),
545 g_cclosure_marshal_VOID__VOID,
546 G_TYPE_NONE, 0, G_TYPE_NONE);
548 g_signal_new ("moved",
549 G_TYPE_FROM_CLASS (g_object_class),
550 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
551 G_STRUCT_OFFSET (EvAnnotationWindowClass, moved),
553 ev_view_marshal_VOID__INT_INT,
555 G_TYPE_INT, G_TYPE_INT);
560 ev_annotation_window_new (EvAnnotation *annot,
565 g_return_val_if_fail (EV_IS_ANNOTATION_MARKUP (annot), NULL);
566 g_return_val_if_fail (GTK_IS_WINDOW (parent), NULL);
568 window = g_object_new (EV_TYPE_ANNOTATION_WINDOW,
576 ev_annotation_window_get_annotation (EvAnnotationWindow *window)
578 g_return_val_if_fail (EV_IS_ANNOTATION_WINDOW (window), NULL);
580 return window->annotation;
584 ev_annotation_window_set_annotation (EvAnnotationWindow *window,
587 g_return_if_fail (EV_IS_ANNOTATION_WINDOW (window));
588 g_return_if_fail (EV_IS_ANNOTATION (annot));
590 if (annot == window->annotation)
593 g_object_unref (window->annotation);
594 window->annotation = g_object_ref (annot);
595 ev_annotation_window_check_contents_modified (window);
596 g_object_notify (G_OBJECT (window), "annotation");
600 ev_annotation_window_is_open (EvAnnotationWindow *window)
602 g_return_val_if_fail (EV_IS_ANNOTATION_WINDOW (window), FALSE);
604 return window->is_open;
608 ev_annotation_window_get_rectangle (EvAnnotationWindow *window)
610 g_return_val_if_fail (EV_IS_ANNOTATION_WINDOW (window), NULL);
616 ev_annotation_window_set_rectangle (EvAnnotationWindow *window,
619 g_return_if_fail (EV_IS_ANNOTATION_WINDOW (window));
620 g_return_if_fail (rect != NULL);
622 *window->rect = *rect;
626 ev_annotation_window_grab_focus (EvAnnotationWindow *window)
628 g_return_if_fail (EV_IS_ANNOTATION_WINDOW (window));
630 if (!GTK_WIDGET_HAS_FOCUS (window->text_view)) {
631 gtk_widget_grab_focus (GTK_WIDGET (window));
632 send_focus_change (window->text_view, TRUE);
637 ev_annotation_window_ungrab_focus (EvAnnotationWindow *window)
639 g_return_if_fail (EV_IS_ANNOTATION_WINDOW (window));
641 if (GTK_WIDGET_HAS_FOCUS (window->text_view)) {
642 send_focus_change (window->text_view, FALSE);
645 ev_annotation_window_check_contents_modified (window);