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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 fevent->focus_change.type = GDK_FOCUS_CHANGE;
88 fevent->focus_change.window = gtk_widget_get_window (widget);
89 fevent->focus_change.in = in;
90 if (fevent->focus_change.window)
91 g_object_ref (fevent->focus_change.window);
93 gtk_widget_send_focus_change (widget, fevent);
95 gdk_event_free (fevent);
99 get_screen_dpi (EvAnnotationWindow *window)
103 screen = gtk_window_get_screen (GTK_WINDOW (window));
104 return ev_document_misc_get_screen_dpi (screen);
108 ev_annotation_window_get_icon_size (void)
110 static GtkIconSize icon_size = 0;
112 if (G_UNLIKELY (icon_size == 0))
113 icon_size = gtk_icon_size_register ("ev-icon-size-annot-window", 8, 8);
119 ev_annotation_window_sync_contents (EvAnnotationWindow *window)
122 GtkTextIter start, end;
123 GtkTextBuffer *buffer;
124 EvAnnotation *annot = window->annotation;
126 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (window->text_view));
127 gtk_text_buffer_get_bounds (buffer, &start, &end);
128 contents = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
129 ev_annotation_set_contents (annot, contents);
134 ev_annotation_window_set_color (EvAnnotationWindow *window,
137 GtkRcStyle *rc_style;
142 #if !GTK_CHECK_VERSION (2, 90, 8)
143 /* Allocate these colors */
144 gdk_colormap_alloc_color (gtk_widget_get_colormap (GTK_WIDGET (window)),
145 &gcolor, FALSE, TRUE);
148 /* Apply colors to style */
149 rc_style = gtk_widget_get_modifier_style (GTK_WIDGET (window));
150 rc_style->base[GTK_STATE_NORMAL] = gcolor;
151 rc_style->bg[GTK_STATE_PRELIGHT] = gcolor;
152 rc_style->bg[GTK_STATE_NORMAL] = gcolor;
153 rc_style->bg[GTK_STATE_ACTIVE] = gcolor;
154 rc_style->color_flags[GTK_STATE_PRELIGHT] = GTK_RC_BG;
155 rc_style->color_flags[GTK_STATE_NORMAL] = GTK_RC_BG | GTK_RC_BASE;
156 rc_style->color_flags[GTK_STATE_ACTIVE] = GTK_RC_BG;
158 /* Apply the style to the widgets */
159 g_object_ref (rc_style);
160 gtk_widget_modify_style (GTK_WIDGET (window), rc_style);
161 gtk_widget_modify_style (window->close_button, rc_style);
162 gtk_widget_modify_style (window->resize_se, rc_style);
163 gtk_widget_modify_style (window->resize_sw, rc_style);
164 g_object_unref (rc_style);
168 ev_annotation_window_label_changed (EvAnnotationMarkup *annot,
170 EvAnnotationWindow *window)
172 const gchar *label = ev_annotation_markup_get_label (annot);
174 gtk_window_set_title (GTK_WINDOW (window), label);
175 gtk_label_set_text (GTK_LABEL (window->title), label);
179 ev_annotation_window_color_changed (EvAnnotation *annot,
181 EvAnnotationWindow *window)
185 ev_annotation_get_color (annot, &color);
186 ev_annotation_window_set_color (window, &color);
190 ev_annotation_window_dispose (GObject *object)
192 EvAnnotationWindow *window = EV_ANNOTATION_WINDOW (object);
194 if (window->annotation) {
195 ev_annotation_window_sync_contents (window);
196 g_object_unref (window->annotation);
197 window->annotation = NULL;
200 (* G_OBJECT_CLASS (ev_annotation_window_parent_class)->dispose) (object);
204 ev_annotation_window_set_property (GObject *object,
209 EvAnnotationWindow *window = EV_ANNOTATION_WINDOW (object);
212 case PROP_ANNOTATION:
213 window->annotation = g_value_dup_object (value);
216 window->parent = g_value_get_object (value);
219 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
224 ev_annotation_window_resize (EvAnnotationWindow *window,
225 GdkEventButton *event,
228 if (event->type == GDK_BUTTON_PRESS && event->button == 1) {
229 gtk_window_begin_resize_drag (GTK_WINDOW (window),
230 window->resize_sw == ebox ?
231 GDK_WINDOW_EDGE_SOUTH_WEST :
232 GDK_WINDOW_EDGE_SOUTH_EAST,
233 event->button, event->x_root,
234 event->y_root, event->time);
242 ev_annotation_window_set_resize_cursor (GtkWidget *widget,
243 EvAnnotationWindow *window)
245 GdkWindow *gdk_window = gtk_widget_get_window (widget);
250 if (gtk_widget_is_sensitive (widget)) {
251 GdkDisplay *display = gtk_widget_get_display (widget);
254 cursor = gdk_cursor_new_for_display (display,
255 widget == window->resize_sw ?
256 GDK_BOTTOM_LEFT_CORNER :
257 GDK_BOTTOM_RIGHT_CORNER);
258 gdk_window_set_cursor (gdk_window, cursor);
259 gdk_cursor_unref (cursor);
261 gdk_window_set_cursor (gdk_window, NULL);
266 text_view_button_press (GtkWidget *widget,
267 GdkEventButton *event,
268 EvAnnotationWindow *window)
270 ev_annotation_window_grab_focus (window);
276 ev_annotation_window_close (EvAnnotationWindow *window)
278 gtk_widget_hide (GTK_WIDGET (window));
279 g_signal_emit (window, signals[CLOSED], 0);
283 ev_annotation_window_init (EvAnnotationWindow *window)
285 GtkWidget *vbox, *hbox;
289 gtk_widget_set_can_focus (GTK_WIDGET (window), TRUE);
291 vbox = gtk_vbox_new (FALSE, 0);
294 hbox = gtk_hbox_new (FALSE, 0);
296 icon = gtk_image_new (); /* FIXME: use the annot icon */
297 gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
298 gtk_widget_show (icon);
300 window->title = gtk_label_new (NULL);
301 gtk_box_pack_start (GTK_BOX (hbox), window->title, TRUE, TRUE, 0);
302 gtk_widget_show (window->title);
304 window->close_button = gtk_button_new ();
305 gtk_button_set_relief (GTK_BUTTON (window->close_button), GTK_RELIEF_NONE);
306 gtk_container_set_border_width (GTK_CONTAINER (window->close_button), 0);
307 g_signal_connect_swapped (window->close_button, "clicked",
308 G_CALLBACK (ev_annotation_window_close),
310 icon = gtk_image_new_from_stock (EV_STOCK_CLOSE, EV_ICON_SIZE_ANNOT_WINDOW);
311 gtk_container_add (GTK_CONTAINER (window->close_button), icon);
312 gtk_widget_show (icon);
314 gtk_box_pack_start (GTK_BOX (hbox), window->close_button, FALSE, FALSE, 0);
315 gtk_widget_show (window->close_button);
317 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
318 gtk_widget_show (hbox);
321 swindow = gtk_scrolled_window_new (NULL, NULL);
322 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swindow),
323 GTK_POLICY_AUTOMATIC,
324 GTK_POLICY_AUTOMATIC);
325 window->text_view = gtk_text_view_new ();
326 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (window->text_view), GTK_WRAP_WORD);
327 g_signal_connect (window->text_view, "button_press_event",
328 G_CALLBACK (text_view_button_press),
330 gtk_container_add (GTK_CONTAINER (swindow), window->text_view);
331 gtk_widget_show (window->text_view);
333 gtk_box_pack_start (GTK_BOX (vbox), swindow, TRUE, TRUE, 0);
334 gtk_widget_show (swindow);
337 hbox = gtk_hbox_new (FALSE, 0);
339 window->resize_sw = gtk_event_box_new ();
340 gtk_widget_add_events (window->resize_sw, GDK_BUTTON_PRESS_MASK);
341 g_signal_connect_swapped (window->resize_sw, "button-press-event",
342 G_CALLBACK (ev_annotation_window_resize),
344 g_signal_connect (window->resize_sw, "realize",
345 G_CALLBACK (ev_annotation_window_set_resize_cursor),
348 icon = gtk_image_new_from_stock (EV_STOCK_RESIZE_SW, EV_ICON_SIZE_ANNOT_WINDOW);
349 gtk_container_add (GTK_CONTAINER (window->resize_sw), icon);
350 gtk_widget_show (icon);
351 gtk_box_pack_start (GTK_BOX (hbox), window->resize_sw, FALSE, FALSE, 0);
352 gtk_widget_show (window->resize_sw);
354 window->resize_se = gtk_event_box_new ();
355 gtk_widget_add_events (window->resize_se, GDK_BUTTON_PRESS_MASK);
356 g_signal_connect_swapped (window->resize_se, "button-press-event",
357 G_CALLBACK (ev_annotation_window_resize),
359 g_signal_connect (window->resize_se, "realize",
360 G_CALLBACK (ev_annotation_window_set_resize_cursor),
363 icon = gtk_image_new_from_stock (EV_STOCK_RESIZE_SE, EV_ICON_SIZE_ANNOT_WINDOW);
364 gtk_container_add (GTK_CONTAINER (window->resize_se), icon);
365 gtk_widget_show (icon);
366 gtk_box_pack_end (GTK_BOX (hbox), window->resize_se, FALSE, FALSE, 0);
367 gtk_widget_show (window->resize_se);
369 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
370 gtk_widget_show (hbox);
372 gtk_container_add (GTK_CONTAINER (window), vbox);
373 gtk_container_set_border_width (GTK_CONTAINER (window), 0);
374 gtk_widget_show (vbox);
376 gtk_widget_add_events (GTK_WIDGET (window),
377 GDK_BUTTON_PRESS_MASK |
379 gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
381 gtk_container_set_border_width (GTK_CONTAINER (window), 2);
383 gtk_window_set_accept_focus (GTK_WINDOW (window), TRUE);
384 gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
385 gtk_window_set_skip_taskbar_hint (GTK_WINDOW (window), TRUE);
386 gtk_window_set_skip_pager_hint (GTK_WINDOW (window), TRUE);
387 gtk_window_set_resizable (GTK_WINDOW (window), TRUE);
391 ev_annotation_window_constructor (GType type,
392 guint n_construct_properties,
393 GObjectConstructParam *construct_params)
396 EvAnnotationWindow *window;
398 EvAnnotationMarkup *markup;
399 const gchar *contents;
405 object = G_OBJECT_CLASS (ev_annotation_window_parent_class)->constructor (type,
406 n_construct_properties,
408 window = EV_ANNOTATION_WINDOW (object);
409 annot = window->annotation;
410 markup = EV_ANNOTATION_MARKUP (annot);
412 gtk_window_set_transient_for (GTK_WINDOW (window), window->parent);
413 gtk_window_set_destroy_with_parent (GTK_WINDOW (window), FALSE);
415 label = ev_annotation_markup_get_label (markup);
416 window->is_open = ev_annotation_markup_get_popup_is_open (markup);
417 ev_annotation_markup_get_rectangle (markup, &window->rect);
419 rect = &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));
427 ev_annotation_get_color (annot, &color);
428 ev_annotation_window_set_color (window, &color);
429 gtk_widget_set_name (GTK_WIDGET (window), ev_annotation_get_name (annot));
430 gtk_window_set_title (GTK_WINDOW (window), label);
431 gtk_label_set_text (GTK_LABEL (window->title), label);
433 contents = ev_annotation_get_contents (annot);
435 GtkTextBuffer *buffer;
437 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (window->text_view));
438 gtk_text_buffer_set_text (buffer, contents, -1);
441 g_signal_connect (annot, "notify::label",
442 G_CALLBACK (ev_annotation_window_label_changed),
444 g_signal_connect (annot, "notify::color",
445 G_CALLBACK (ev_annotation_window_color_changed),
452 ev_annotation_window_button_press_event (GtkWidget *widget,
453 GdkEventButton *event)
455 EvAnnotationWindow *window = EV_ANNOTATION_WINDOW (widget);
457 if (event->type == GDK_BUTTON_PRESS && event->button == 1) {
458 window->in_move = TRUE;
459 window->x = event->x_root - event->x;
460 window->y = event->y_root - event->y;
461 gtk_window_begin_move_drag (GTK_WINDOW (widget),
473 ev_annotation_window_configure_event (GtkWidget *widget,
474 GdkEventConfigure *event)
476 EvAnnotationWindow *window = EV_ANNOTATION_WINDOW (widget);
478 if (window->in_move &&
479 (window->x != event->x || window->y != event->y)) {
480 window->x = event->x;
481 window->y = event->y;
484 return GTK_WIDGET_CLASS (ev_annotation_window_parent_class)->configure_event (widget, event);
488 ev_annotation_window_focus_in_event (GtkWidget *widget,
489 GdkEventFocus *event)
491 EvAnnotationWindow *window = EV_ANNOTATION_WINDOW (widget);
493 if (window->in_move) {
494 if (window->orig_x != window->x || window->orig_y != window->y) {
495 window->orig_x = window->x;
496 window->orig_y = window->y;
497 g_signal_emit (window, signals[MOVED], 0, window->x, window->y);
499 window->in_move = FALSE;
506 ev_annotation_window_focus_out_event (GtkWidget *widget,
507 GdkEventFocus *event)
509 EvAnnotationWindow *window = EV_ANNOTATION_WINDOW (widget);
511 ev_annotation_window_sync_contents (window);
517 ev_annotation_window_class_init (EvAnnotationWindowClass *klass)
519 GObjectClass *g_object_class = G_OBJECT_CLASS (klass);
520 GtkWidgetClass *gtk_widget_class = GTK_WIDGET_CLASS (klass);
522 g_object_class->constructor = ev_annotation_window_constructor;
523 g_object_class->set_property = ev_annotation_window_set_property;
524 g_object_class->dispose = ev_annotation_window_dispose;
526 gtk_widget_class->button_press_event = ev_annotation_window_button_press_event;
527 gtk_widget_class->configure_event = ev_annotation_window_configure_event;
528 gtk_widget_class->focus_in_event = ev_annotation_window_focus_in_event;
529 gtk_widget_class->focus_out_event = ev_annotation_window_focus_out_event;
531 g_object_class_install_property (g_object_class,
533 g_param_spec_object ("annotation",
535 "The annotation associated to the window",
536 EV_TYPE_ANNOTATION_MARKUP,
538 G_PARAM_CONSTRUCT_ONLY));
539 g_object_class_install_property (g_object_class,
541 g_param_spec_object ("parent",
546 G_PARAM_CONSTRUCT_ONLY));
548 g_signal_new ("closed",
549 G_TYPE_FROM_CLASS (g_object_class),
550 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
551 G_STRUCT_OFFSET (EvAnnotationWindowClass, closed),
553 g_cclosure_marshal_VOID__VOID,
554 G_TYPE_NONE, 0, G_TYPE_NONE);
556 g_signal_new ("moved",
557 G_TYPE_FROM_CLASS (g_object_class),
558 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
559 G_STRUCT_OFFSET (EvAnnotationWindowClass, moved),
561 ev_view_marshal_VOID__INT_INT,
563 G_TYPE_INT, G_TYPE_INT);
568 ev_annotation_window_new (EvAnnotation *annot,
573 g_return_val_if_fail (EV_IS_ANNOTATION_MARKUP (annot), NULL);
574 g_return_val_if_fail (GTK_IS_WINDOW (parent), NULL);
576 window = g_object_new (EV_TYPE_ANNOTATION_WINDOW,
584 ev_annotation_window_get_annotation (EvAnnotationWindow *window)
586 g_return_val_if_fail (EV_IS_ANNOTATION_WINDOW (window), NULL);
588 return window->annotation;
592 ev_annotation_window_set_annotation (EvAnnotationWindow *window,
595 g_return_if_fail (EV_IS_ANNOTATION_WINDOW (window));
596 g_return_if_fail (EV_IS_ANNOTATION (annot));
598 if (annot == window->annotation)
601 g_object_unref (window->annotation);
602 window->annotation = g_object_ref (annot);
603 ev_annotation_window_sync_contents (window);
604 g_object_notify (G_OBJECT (window), "annotation");
608 ev_annotation_window_is_open (EvAnnotationWindow *window)
610 g_return_val_if_fail (EV_IS_ANNOTATION_WINDOW (window), FALSE);
612 return window->is_open;
616 ev_annotation_window_get_rectangle (EvAnnotationWindow *window,
619 g_return_if_fail (EV_IS_ANNOTATION_WINDOW (window));
620 g_return_if_fail (rect != NULL);
622 *rect = window->rect;
626 ev_annotation_window_set_rectangle (EvAnnotationWindow *window,
627 const EvRectangle *rect)
629 g_return_if_fail (EV_IS_ANNOTATION_WINDOW (window));
630 g_return_if_fail (rect != NULL);
632 window->rect = *rect;
636 ev_annotation_window_grab_focus (EvAnnotationWindow *window)
638 g_return_if_fail (EV_IS_ANNOTATION_WINDOW (window));
640 if (!gtk_widget_has_focus (window->text_view)) {
641 gtk_widget_grab_focus (GTK_WIDGET (window));
642 send_focus_change (window->text_view, TRUE);
647 ev_annotation_window_ungrab_focus (EvAnnotationWindow *window)
649 g_return_if_fail (EV_IS_ANNOTATION_WINDOW (window));
651 if (gtk_widget_has_focus (window->text_view)) {
652 send_focus_change (window->text_view, FALSE);
655 ev_annotation_window_sync_contents (window);