From: Carlos Garcia Campos Date: Tue, 20 Jul 2010 15:01:56 +0000 (+0200) Subject: Preliminary support for adding new annotations X-Git-Tag: EVINCE_2_31_6~40 X-Git-Url: https://www.fi.muni.cz/~kas/git//home/kas/public_html/git/?a=commitdiff_plain;h=584f014b63c56fe3770cba9682fc21c31e09a2e9;p=evince.git Preliminary support for adding new annotations At the moment only Text annotations can be added. See bug #168304. --- diff --git a/backend/pdf/ev-poppler.cc b/backend/pdf/ev-poppler.cc index f457d64e..5943a6d7 100644 --- a/backend/pdf/ev-poppler.cc +++ b/backend/pdf/ev-poppler.cc @@ -2543,6 +2543,69 @@ poppler_annot_color_to_gdk_color (PopplerAnnot *poppler_annot, } /* TODO: else use a default color */ } +static EvAnnotationTextIcon +get_annot_text_icon (PopplerAnnotText *poppler_annot) +{ + gchar *icon = poppler_annot_text_get_icon (poppler_annot); + EvAnnotationTextIcon retval; + + if (!icon) + return EV_ANNOTATION_TEXT_ICON_UNKNOWN; + + if (strcmp (icon, POPPLER_ANNOT_TEXT_ICON_NOTE) == 0) + retval = EV_ANNOTATION_TEXT_ICON_NOTE; + else if (strcmp (icon, POPPLER_ANNOT_TEXT_ICON_COMMENT) == 0) + retval = EV_ANNOTATION_TEXT_ICON_COMMENT; + else if (strcmp (icon, POPPLER_ANNOT_TEXT_ICON_KEY) == 0) + retval = EV_ANNOTATION_TEXT_ICON_KEY; + else if (strcmp (icon, POPPLER_ANNOT_TEXT_ICON_HELP) == 0) + retval = EV_ANNOTATION_TEXT_ICON_HELP; + else if (strcmp (icon, POPPLER_ANNOT_TEXT_ICON_NEW_PARAGRAPH) == 0) + retval = EV_ANNOTATION_TEXT_ICON_NEW_PARAGRAPH; + else if (strcmp (icon, POPPLER_ANNOT_TEXT_ICON_PARAGRAPH) == 0) + retval = EV_ANNOTATION_TEXT_ICON_PARAGRAPH; + else if (strcmp (icon, POPPLER_ANNOT_TEXT_ICON_INSERT) == 0) + retval = EV_ANNOTATION_TEXT_ICON_INSERT; + else if (strcmp (icon, POPPLER_ANNOT_TEXT_ICON_CROSS) == 0) + retval = EV_ANNOTATION_TEXT_ICON_CROSS; + else if (strcmp (icon, POPPLER_ANNOT_TEXT_ICON_CIRCLE) == 0) + retval = EV_ANNOTATION_TEXT_ICON_CIRCLE; + else + retval = EV_ANNOTATION_TEXT_ICON_UNKNOWN; + + g_free (icon); + + return retval; +} + +static const gchar * +get_poppler_annot_text_icon (EvAnnotationTextIcon icon) +{ + switch (icon) { + case EV_ANNOTATION_TEXT_ICON_NOTE: + return POPPLER_ANNOT_TEXT_ICON_NOTE; + case EV_ANNOTATION_TEXT_ICON_COMMENT: + return POPPLER_ANNOT_TEXT_ICON_COMMENT; + case EV_ANNOTATION_TEXT_ICON_KEY: + return POPPLER_ANNOT_TEXT_ICON_KEY; + case EV_ANNOTATION_TEXT_ICON_HELP: + return POPPLER_ANNOT_TEXT_ICON_HELP; + case EV_ANNOTATION_TEXT_ICON_NEW_PARAGRAPH: + return POPPLER_ANNOT_TEXT_ICON_NEW_PARAGRAPH; + case EV_ANNOTATION_TEXT_ICON_PARAGRAPH: + return POPPLER_ANNOT_TEXT_ICON_PARAGRAPH; + case EV_ANNOTATION_TEXT_ICON_INSERT: + return POPPLER_ANNOT_TEXT_ICON_INSERT; + case EV_ANNOTATION_TEXT_ICON_CROSS: + return POPPLER_ANNOT_TEXT_ICON_CROSS; + case EV_ANNOTATION_TEXT_ICON_CIRCLE: + return POPPLER_ANNOT_TEXT_ICON_CIRCLE; + case EV_ANNOTATION_TEXT_ICON_UNKNOWN: + default: + return POPPLER_ANNOT_TEXT_ICON_NOTE; + } +} + static EvAnnotation * ev_annot_from_poppler_annot (PopplerAnnot *poppler_annot, EvPage *page) @@ -2560,7 +2623,9 @@ ev_annot_from_poppler_annot (PopplerAnnot *poppler_annot, ev_annot = ev_annotation_text_new (page); ev_annot_text = EV_ANNOTATION_TEXT (ev_annot); - ev_annot_text->is_open = poppler_annot_text_get_is_open (poppler_text); + ev_annotation_text_set_is_open (ev_annot_text, + poppler_annot_text_get_is_open (poppler_text)); + ev_annotation_text_set_icon (ev_annot_text, get_annot_text_icon (poppler_text)); } break; case POPPLER_ANNOT_FILE_ATTACHMENT: { @@ -2615,19 +2680,34 @@ ev_annot_from_poppler_annot (PopplerAnnot *poppler_annot, } if (ev_annot) { - time_t utime; - gchar *modified; + time_t utime; + gchar *modified; + gchar *contents; + gchar *name; + GdkColor color; + + contents = poppler_annot_get_contents (poppler_annot); + if (contents) { + ev_annotation_set_contents (ev_annot, contents); + g_free (contents); + } + + name = poppler_annot_get_name (poppler_annot); + if (name) { + ev_annotation_set_name (ev_annot, name); + g_free (name); + } - ev_annot->contents = poppler_annot_get_contents (poppler_annot); - ev_annot->name = poppler_annot_get_name (poppler_annot); modified = poppler_annot_get_modified (poppler_annot); if (poppler_date_parse (modified, &utime)) { - ev_annot->modified = ev_document_misc_format_date (utime); - g_free (modified); + ev_annotation_set_modified_from_time (ev_annot, utime); } else { - ev_annot->modified = modified; + ev_annotation_set_modified (ev_annot, modified); } - poppler_annot_color_to_gdk_color (poppler_annot, &ev_annot->color); + g_free (modified); + + poppler_annot_color_to_gdk_color (poppler_annot, &color); + ev_annotation_set_color (ev_annot, &color); if (POPPLER_IS_ANNOT_MARKUP (poppler_annot)) { PopplerAnnotMarkup *markup; @@ -2653,13 +2733,10 @@ ev_annot_from_poppler_annot (PopplerAnnot *poppler_annot, g_object_set (ev_annot, "rectangle", &ev_rect, - "is_open", is_open, + "popup_is_open", is_open, "has_popup", TRUE, NULL); } else { - /* FIXME: Use poppler_annot_markup_has_popup() when - * new poppler is released. - */ g_object_set (ev_annot, "has_popup", FALSE, NULL); @@ -2708,7 +2785,7 @@ pdf_document_annotations_get_annotations (EvDocumentAnnotations *document_annota for (list = annots; list; list = list->next) { PopplerAnnotMapping *mapping; - EvMapping *annot_mapping; + EvMapping *annot_mapping; EvAnnotation *ev_annot; mapping = (PopplerAnnotMapping *)list->data; @@ -2720,8 +2797,12 @@ pdf_document_annotations_get_annotations (EvDocumentAnnotations *document_annota i++; /* Make sure annot has a unique name */ - if (!ev_annot->name) - ev_annot->name = g_strdup_printf ("annot-%d-%d", page->index, i); + if (!ev_annotation_get_name (ev_annot)) { + gchar *name = g_strdup_printf ("annot-%d-%d", page->index, i); + + ev_annotation_set_name (ev_annot, name); + g_free (name); + } annot_mapping = g_new (EvMapping, 1); annot_mapping->area.x1 = mapping->area.x1; @@ -2758,10 +2839,115 @@ pdf_document_annotations_get_annotations (EvDocumentAnnotations *document_annota return mapping_list; } +#ifdef HAVE_POPPLER_PAGE_ADD_ANNOT static void -pdf_document_annotations_annotation_set_contents (EvDocumentAnnotations *document, - EvAnnotation *annot, - const gchar *contents) +pdf_document_annotations_add_annotation (EvDocumentAnnotations *document_annotations, + EvAnnotation *annot, + EvRectangle *rect) +{ + PopplerAnnot *poppler_annot; + PdfDocument *pdf_document; + EvPage *page; + PopplerPage *poppler_page; + GList *list = NULL; + EvMappingList *mapping_list; + EvMapping *annot_mapping; + PopplerRectangle poppler_rect; + gdouble height; + PopplerColor poppler_color; + GdkColor color; + time_t utime; + gchar *modified; + gchar *name; + + pdf_document = PDF_DOCUMENT (document_annotations); + page = ev_annotation_get_page (annot); + poppler_page = POPPLER_PAGE (page->backend_page); + + poppler_page_get_size (poppler_page, NULL, &height); + poppler_rect.x1 = rect->x1; + poppler_rect.x2 = rect->x2; + poppler_rect.y1 = height - rect->y2; + poppler_rect.y2 = height - rect->y1; + poppler_annot = poppler_annot_text_new (pdf_document->document, &poppler_rect); + + ev_annotation_get_color (annot, &color); + poppler_color.red = color.red; + poppler_color.green = color.green; + poppler_color.blue = color.blue; + poppler_annot_set_color (poppler_annot, &poppler_color); + + if (EV_IS_ANNOTATION_MARKUP (annot)) { + EvAnnotationMarkup *markup = EV_ANNOTATION_MARKUP (annot); + const gchar *label; + + if (ev_annotation_markup_has_popup (markup)) { + EvRectangle popup_rect; + + ev_annotation_markup_get_rectangle (markup, &popup_rect); + poppler_rect.x1 = popup_rect.x1; + poppler_rect.x2 = popup_rect.x2; + poppler_rect.y1 = height - popup_rect.y2; + poppler_rect.y2 = height - popup_rect.y1; + poppler_annot_markup_set_popup (POPPLER_ANNOT_MARKUP (poppler_annot), &poppler_rect); + poppler_annot_markup_set_popup_is_open (POPPLER_ANNOT_MARKUP (poppler_annot), + ev_annotation_markup_get_popup_is_open (markup)); + } + + label = ev_annotation_markup_get_label (markup); + if (label) + poppler_annot_markup_set_label (POPPLER_ANNOT_MARKUP (poppler_annot), label); + } + + if (EV_IS_ANNOTATION_TEXT (annot)) { + EvAnnotationText *text = EV_ANNOTATION_TEXT (annot); + EvAnnotationTextIcon icon; + + icon = ev_annotation_text_get_icon (text); + poppler_annot_text_set_icon (POPPLER_ANNOT_TEXT (poppler_annot), + get_poppler_annot_text_icon (icon)); + } + poppler_page_add_annot (poppler_page, poppler_annot); + + annot_mapping = g_new (EvMapping, 1); + annot_mapping->area = *rect; + annot_mapping->data = annot; + g_object_set_data_full (G_OBJECT (annot), + "poppler-annot", + g_object_ref (poppler_annot), + (GDestroyNotify) g_object_unref); + + if (pdf_document->annots) { + mapping_list = (EvMappingList *)g_hash_table_lookup (pdf_document->annots, + GINT_TO_POINTER (page->index)); + list = ev_mapping_list_get_list (mapping_list); + name = g_strdup_printf ("annot-%d-%d", page->index, g_list_length (list) + 1); + ev_annotation_set_name (annot, name); + g_free (name); + list = g_list_append (list, annot_mapping); + } else { + pdf_document->annots = g_hash_table_new_full (g_direct_hash, + g_direct_equal, + (GDestroyNotify)NULL, + (GDestroyNotify)ev_mapping_list_unref); + name = g_strdup_printf ("annot-%d-0", page->index); + ev_annotation_set_name (annot, name); + g_free (name); + list = g_list_append (list, annot_mapping); + mapping_list = ev_mapping_list_new (page->index, list, (GDestroyNotify)g_object_unref); + g_hash_table_insert (pdf_document->annots, + GINT_TO_POINTER (page->index), + ev_mapping_list_ref (mapping_list)); + } + + pdf_document->modified = TRUE; +} +#endif /* HAVE_POPPLER_PAGE_ADD_ANNOT */ + +static void +pdf_document_annotations_save_annotation (EvDocumentAnnotations *document_annotations, + EvAnnotation *annot, + EvAnnotationsSaveMask mask) { PopplerAnnot *poppler_annot; @@ -2769,15 +2955,61 @@ pdf_document_annotations_annotation_set_contents (EvDocumentAnnotations *documen if (!poppler_annot) return; - poppler_annot_set_contents (poppler_annot, contents); - PDF_DOCUMENT (document)->modified = TRUE; + if (mask & EV_ANNOTATIONS_SAVE_CONTENTS) + poppler_annot_set_contents (poppler_annot, + ev_annotation_get_contents (annot)); + +#ifdef HAVE_POPPLER_PAGE_ADD_ANNOT + if (mask & EV_ANNOTATIONS_SAVE_COLOR) { + PopplerColor color; + GdkColor ev_color; + + ev_annotation_get_color (annot, &ev_color); + color.red = ev_color.red; + color.green = ev_color.green; + color.blue = ev_color.blue; + poppler_annot_set_color (poppler_annot, &color); + } + + if (EV_IS_ANNOTATION_MARKUP (annot)) { + EvAnnotationMarkup *ev_markup = EV_ANNOTATION_MARKUP (annot); + PopplerAnnotMarkup *markup = POPPLER_ANNOT_MARKUP (poppler_annot); + + if (mask & EV_ANNOTATIONS_SAVE_LABEL) + poppler_annot_markup_set_label (markup, ev_annotation_markup_get_label (ev_markup)); + if (mask & EV_ANNOTATIONS_SAVE_OPACITY) + poppler_annot_markup_set_opacity (markup, ev_annotation_markup_get_opacity (ev_markup)); + if (mask & EV_ANNOTATIONS_SAVE_POPUP_IS_OPEN) + poppler_annot_markup_set_popup_is_open (markup, ev_annotation_markup_get_popup_is_open (ev_markup)); + } + + if (EV_IS_ANNOTATION_TEXT (annot)) { + EvAnnotationText *ev_text = EV_ANNOTATION_TEXT (annot); + PopplerAnnotText *text = POPPLER_ANNOT_TEXT (poppler_annot); + + if (mask & EV_ANNOTATIONS_SAVE_TEXT_IS_OPEN) { + poppler_annot_text_set_is_open (text, + ev_annotation_text_get_is_open (ev_text)); + } + if (mask & EV_ANNOTATIONS_SAVE_TEXT_ICON) { + EvAnnotationTextIcon icon; + + icon = ev_annotation_text_get_icon (ev_text); + poppler_annot_text_set_icon (text, get_poppler_annot_text_icon (icon)); + } + } +#endif /* HAVE_POPPLER_PAGE_ADD_ANNOT */ + PDF_DOCUMENT (document_annotations)->modified = TRUE; } static void pdf_document_document_annotations_iface_init (EvDocumentAnnotationsInterface *iface) { iface->get_annotations = pdf_document_annotations_get_annotations; - iface->annotation_set_contents = pdf_document_annotations_annotation_set_contents; +#ifdef HAVE_POPPLER_PAGE_ADD_ANNOT + iface->add_annotation = pdf_document_annotations_add_annotation; +#endif + iface->save_annotation = pdf_document_annotations_save_annotation; } /* Attachments */ diff --git a/configure.ac b/configure.ac index 6e9d965a..3c015e11 100644 --- a/configure.ac +++ b/configure.ac @@ -482,6 +482,7 @@ if test "x$enable_pdf" = "xyes"; then LIBS="$LIBS $POPPLER_LIBS" AC_CHECK_FUNCS(poppler_page_get_text_layout) AC_CHECK_FUNCS(poppler_page_get_selected_text) + AC_CHECK_FUNCS(poppler_page_add_annot) LIBS=$evince_save_LIBS PKG_CHECK_MODULES(CAIRO_PDF, cairo-pdf, enable_cairo_pdf=yes, enable_cairo_pdf=no) if test x$enable_cairo_pdf = xyes; then diff --git a/data/evince-ui.xml b/data/evince-ui.xml index 962e8b26..0d15aabc 100644 --- a/data/evince-ui.xml +++ b/data/evince-ui.xml @@ -82,6 +82,8 @@ + + diff --git a/libdocument/ev-annotation.c b/libdocument/ev-annotation.c index 21ffc0a1..f7acc0bf 100644 --- a/libdocument/ev-annotation.c +++ b/libdocument/ev-annotation.c @@ -22,19 +22,86 @@ #include "config.h" #include "ev-annotation.h" +#include "ev-document-misc.h" +#include "ev-document-type-builtins.h" +struct _EvAnnotation { + GObject parent; + + EvAnnotationType type; + EvPage *page; + + gchar *contents; + gchar *name; + gchar *modified; + GdkColor color; + +}; + +struct _EvAnnotationClass { + GObjectClass parent_class; +}; + +struct _EvAnnotationMarkupInterface { + GTypeInterface base_iface; +}; + +struct _EvAnnotationText { + EvAnnotation parent; + + gboolean is_open : 1; + EvAnnotationTextIcon icon; +}; + +struct _EvAnnotationTextClass { + EvAnnotationClass parent_class; +}; + +struct _EvAnnotationAttachment { + EvAnnotation parent; + + EvAttachment *attachment; +}; + +struct _EvAnnotationAttachmentClass { + EvAnnotationClass parent_class; +}; static void ev_annotation_markup_default_init (EvAnnotationMarkupInterface *iface); static void ev_annotation_text_markup_iface_init (EvAnnotationMarkupInterface *iface); static void ev_annotation_attachment_markup_iface_init (EvAnnotationMarkupInterface *iface); +/* EvAnnotation */ +enum { + PROP_ANNOT_0, + PROP_ANNOT_PAGE, + PROP_ANNOT_CONTENTS, + PROP_ANNOT_NAME, + PROP_ANNOT_MODIFIED, + PROP_ANNOT_COLOR +}; + +/* EvAnnotationMarkup */ +enum { + PROP_MARKUP_0, + PROP_MARKUP_LABEL, + PROP_MARKUP_OPACITY, + PROP_MARKUP_HAS_POPUP, + PROP_MARKUP_RECTANGLE, + PROP_MARKUP_POPUP_IS_OPEN +}; + +/* EvAnnotationText */ enum { - PROP_0, - PROP_LABEL, - PROP_OPACITY, - PROP_HAS_POPUP, - PROP_RECTANGLE, - PROP_IS_OPEN + PROP_TEXT_0, + PROP_TEXT_ICON, + PROP_TEXT_IS_OPEN +}; + +/* EvAnnotationAttachment */ +enum { + PROP_ATTACHMENT_0, + PROP_ATTACHMENT_ATTACHMENT }; G_DEFINE_ABSTRACT_TYPE (EvAnnotation, ev_annotation, G_TYPE_OBJECT) @@ -82,6 +149,62 @@ ev_annotation_finalize (GObject *object) static void ev_annotation_init (EvAnnotation *annot) { + annot->type = EV_ANNOTATION_TYPE_UNKNOWN; +} + +static void +ev_annotation_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + EvAnnotation *annot = EV_ANNOTATION (object); + + switch (prop_id) { + case PROP_ANNOT_PAGE: + annot->page = g_value_dup_object (value); + break; + case PROP_ANNOT_CONTENTS: + ev_annotation_set_contents (annot, g_value_get_string (value)); + break; + case PROP_ANNOT_NAME: + ev_annotation_set_name (annot, g_value_get_string (value)); + break; + case PROP_ANNOT_MODIFIED: + ev_annotation_set_modified (annot, g_value_get_string (value)); + break; + case PROP_ANNOT_COLOR: + ev_annotation_set_color (annot, g_value_get_pointer (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +ev_annotation_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + EvAnnotation *annot = EV_ANNOTATION (object); + + switch (prop_id) { + case PROP_ANNOT_CONTENTS: + g_value_set_string (value, ev_annotation_get_contents (annot)); + break; + case PROP_ANNOT_NAME: + g_value_set_string (value, ev_annotation_get_name (annot)); + break; + case PROP_ANNOT_MODIFIED: + g_value_set_string (value, ev_annotation_get_modified (annot)); + break; + case PROP_ANNOT_COLOR: + g_value_set_pointer (value, &annot->color); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } } static void @@ -90,6 +213,208 @@ ev_annotation_class_init (EvAnnotationClass *klass) GObjectClass *g_object_class = G_OBJECT_CLASS (klass); g_object_class->finalize = ev_annotation_finalize; + g_object_class->set_property = ev_annotation_set_property; + g_object_class->get_property = ev_annotation_get_property; + + g_object_class_install_property (g_object_class, + PROP_ANNOT_PAGE, + g_param_spec_object ("page", + "Page", + "The page wehere the annotation is", + EV_TYPE_PAGE, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (g_object_class, + PROP_ANNOT_CONTENTS, + g_param_spec_string ("contents", + "Contents", + "The annotation contents", + NULL, + G_PARAM_READWRITE)); + g_object_class_install_property (g_object_class, + PROP_ANNOT_NAME, + g_param_spec_string ("name", + "Name", + "The annotation unique name", + NULL, + G_PARAM_READWRITE)); + g_object_class_install_property (g_object_class, + PROP_ANNOT_MODIFIED, + g_param_spec_string ("modified", + "Modified", + "Last modified date as string", + NULL, + G_PARAM_READWRITE)); + g_object_class_install_property (g_object_class, + PROP_ANNOT_COLOR, + g_param_spec_pointer ("color", + "Color", + "The annotation color", + G_PARAM_READWRITE)); +} + +EvAnnotationType +ev_annotation_get_annotation_type (EvAnnotation *annot) +{ + g_return_val_if_fail (EV_IS_ANNOTATION (annot), 0); + + return annot->type; +} + +EvPage * +ev_annotation_get_page (EvAnnotation *annot) +{ + g_return_val_if_fail (EV_IS_ANNOTATION (annot), NULL); + + return annot->page; +} + +guint +ev_annotation_get_page_index (EvAnnotation *annot) +{ + g_return_val_if_fail (EV_IS_ANNOTATION (annot), 0); + + return annot->page->index; +} + +gboolean +ev_annotation_equal (EvAnnotation *annot, + EvAnnotation *other) +{ + g_return_val_if_fail (EV_IS_ANNOTATION (annot), FALSE); + g_return_val_if_fail (EV_IS_ANNOTATION (other), FALSE); + + return (annot == other || g_strcmp0 (annot->name, other->name) == 0); +} + +const gchar * +ev_annotation_get_contents (EvAnnotation *annot) +{ + g_return_val_if_fail (EV_IS_ANNOTATION (annot), NULL); + + return annot->contents; +} + +gboolean +ev_annotation_set_contents (EvAnnotation *annot, + const gchar *contents) +{ + g_return_val_if_fail (EV_IS_ANNOTATION (annot), FALSE); + + if (g_strcmp0 (annot->contents, contents) == 0) + return FALSE; + + if (annot->contents) + g_free (annot->contents); + annot->contents = contents ? g_strdup (contents) : NULL; + + g_object_notify (G_OBJECT (annot), "contents"); + + return TRUE; +} + +const gchar * +ev_annotation_get_name (EvAnnotation *annot) +{ + g_return_val_if_fail (EV_IS_ANNOTATION (annot), NULL); + + return annot->name; +} + +gboolean +ev_annotation_set_name (EvAnnotation *annot, + const gchar *name) +{ + g_return_val_if_fail (EV_IS_ANNOTATION (annot), FALSE); + + if (g_strcmp0 (annot->name, name) == 0) + return FALSE; + + if (annot->name) + g_free (annot->name); + annot->name = name ? g_strdup (name) : NULL; + + g_object_notify (G_OBJECT (annot), "name"); + + return TRUE; +} + +const gchar * +ev_annotation_get_modified (EvAnnotation *annot) +{ + g_return_val_if_fail (EV_IS_ANNOTATION (annot), NULL); + + return annot->modified; +} + +gboolean +ev_annotation_set_modified (EvAnnotation *annot, + const gchar *modified) +{ + g_return_val_if_fail (EV_IS_ANNOTATION (annot), FALSE); + + if (g_strcmp0 (annot->modified, modified) == 0) + return FALSE; + + if (annot->modified) + g_free (annot->modified); + annot->modified = modified ? g_strdup (modified) : NULL; + + g_object_notify (G_OBJECT (annot), "modified"); + + return TRUE; +} + +gboolean +ev_annotation_set_modified_from_time (EvAnnotation *annot, + GTime utime) +{ + gchar *modified; + + g_return_val_if_fail (EV_IS_ANNOTATION (annot), FALSE); + + modified = ev_document_misc_format_date (utime); + + if (g_strcmp0 (annot->modified, modified) == 0) { + g_free (modified); + return FALSE; + } + + if (annot->modified) + g_free (annot->modified); + annot->modified = modified; + + g_object_notify (G_OBJECT (annot), "modified"); + + return TRUE; +} + +void +ev_annotation_get_color (EvAnnotation *annot, + GdkColor *color) +{ + g_return_if_fail (EV_IS_ANNOTATION (annot)); + + if (color) + *color = annot->color; +} + +gboolean +ev_annotation_set_color (EvAnnotation *annot, + const GdkColor *color) +{ + g_return_val_if_fail (EV_IS_ANNOTATION (annot), FALSE); + + if (annot->color.red == color->red && + annot->color.green == color->green && + annot->color.blue == color->blue) + return FALSE; + + if (color) + annot->color = *color; + + g_object_notify (G_OBJECT (annot), "color"); + + return TRUE; } /* EvAnnotationMarkup */ @@ -97,8 +422,8 @@ typedef struct { gchar *label; gdouble opacity; gboolean has_popup; - gboolean is_open; - EvRectangle *rectangle; + gboolean popup_is_open; + EvRectangle rectangle; } EvAnnotationMarkupProps; static void @@ -119,7 +444,7 @@ ev_annotation_markup_default_init (EvAnnotationMarkupInterface *iface) "Opacity of the markup annotation", 0, G_MAXDOUBLE, - 0, + 1., G_PARAM_READWRITE)); g_object_interface_install_property (iface, g_param_spec_boolean ("has_popup", @@ -136,8 +461,8 @@ ev_annotation_markup_default_init (EvAnnotationMarkupInterface *iface) EV_TYPE_RECTANGLE, G_PARAM_READWRITE)); g_object_interface_install_property (iface, - g_param_spec_boolean ("is_open", - "Is open", + g_param_spec_boolean ("popup_is_open", + "PopupIsOpen", "Whether the popup associated to " "the markup annotation is open", FALSE, @@ -150,7 +475,6 @@ static void ev_annotation_markup_props_free (EvAnnotationMarkupProps *props) { g_free (props->label); - ev_rectangle_free (props->rectangle); g_slice_free (EvAnnotationMarkupProps, props); } @@ -180,27 +504,23 @@ ev_annotation_markup_set_property (GObject *object, const GValue *value, GParamSpec *pspec) { - EvAnnotationMarkupProps *props; - - props = ev_annotation_markup_get_properties (EV_ANNOTATION_MARKUP (object)); + EvAnnotationMarkup *markup = EV_ANNOTATION_MARKUP (object); switch (prop_id) { - case PROP_LABEL: - g_free (props->label); - props->label = g_value_dup_string (value); + case PROP_MARKUP_LABEL: + ev_annotation_markup_set_label (markup, g_value_get_string (value)); break; - case PROP_OPACITY: - props->opacity = g_value_get_double (value); + case PROP_MARKUP_OPACITY: + ev_annotation_markup_set_opacity (markup, g_value_get_double (value)); break; - case PROP_HAS_POPUP: - props->has_popup = g_value_get_boolean (value); + case PROP_MARKUP_HAS_POPUP: + ev_annotation_markup_set_has_popup (markup, g_value_get_boolean (value)); break; - case PROP_RECTANGLE: - ev_rectangle_free (props->rectangle); - props->rectangle = g_value_dup_boxed (value); + case PROP_MARKUP_RECTANGLE: + ev_annotation_markup_set_rectangle (markup, g_value_get_boxed (value)); break; - case PROP_IS_OPEN: - props->is_open = g_value_get_boolean (value); + case PROP_MARKUP_POPUP_IS_OPEN: + ev_annotation_markup_set_popup_is_open (markup, g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -218,20 +538,20 @@ ev_annotation_markup_get_property (GObject *object, props = ev_annotation_markup_get_properties (EV_ANNOTATION_MARKUP (object)); switch (prop_id) { - case PROP_LABEL: + case PROP_MARKUP_LABEL: g_value_set_string (value, props->label); break; - case PROP_OPACITY: + case PROP_MARKUP_OPACITY: g_value_set_double (value, props->opacity); break; - case PROP_HAS_POPUP: + case PROP_MARKUP_HAS_POPUP: g_value_set_boolean (value, props->has_popup); break; - case PROP_RECTANGLE: - g_value_set_boxed (value, props->rectangle); + case PROP_MARKUP_RECTANGLE: + g_value_set_boxed (value, &props->rectangle); break; - case PROP_IS_OPEN: - g_value_set_boolean (value, props->is_open); + case PROP_MARKUP_POPUP_IS_OPEN: + g_value_set_boolean (value, props->popup_is_open); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -244,106 +564,217 @@ ev_annotation_markup_class_install_properties (GObjectClass *klass) klass->set_property = ev_annotation_markup_set_property; klass->get_property = ev_annotation_markup_get_property; - g_object_class_override_property (klass, PROP_LABEL, "label"); - g_object_class_override_property (klass, PROP_OPACITY, "opacity"); - g_object_class_override_property (klass, PROP_HAS_POPUP, "has_popup"); - g_object_class_override_property (klass, PROP_RECTANGLE, "rectangle"); - g_object_class_override_property (klass, PROP_IS_OPEN, "is_open"); + g_object_class_override_property (klass, PROP_MARKUP_LABEL, "label"); + g_object_class_override_property (klass, PROP_MARKUP_OPACITY, "opacity"); + g_object_class_override_property (klass, PROP_MARKUP_HAS_POPUP, "has_popup"); + g_object_class_override_property (klass, PROP_MARKUP_RECTANGLE, "rectangle"); + g_object_class_override_property (klass, PROP_MARKUP_POPUP_IS_OPEN, "popup_is_open"); } -gchar * +const gchar * ev_annotation_markup_get_label (EvAnnotationMarkup *markup) { - gchar *retval; + EvAnnotationMarkupProps *props; g_return_val_if_fail (EV_IS_ANNOTATION_MARKUP (markup), NULL); - g_object_get (G_OBJECT (markup), "label", &retval, NULL); - - return retval; + props = ev_annotation_markup_get_properties (markup); + return props->label; } -void +gboolean ev_annotation_markup_set_label (EvAnnotationMarkup *markup, const gchar *label) { - g_return_if_fail (EV_IS_ANNOTATION_MARKUP (markup)); - g_return_if_fail (label != NULL); + EvAnnotationMarkupProps *props; + + g_return_val_if_fail (EV_IS_ANNOTATION_MARKUP (markup), FALSE); + g_return_val_if_fail (label != NULL, FALSE); + + props = ev_annotation_markup_get_properties (markup); + if (g_strcmp0 (props->label, label) == 0) + return FALSE; - g_object_set (G_OBJECT (markup), "label", label, NULL); + if (props->label) + g_free (props->label); + props->label = g_strdup (label); + + g_object_notify (G_OBJECT (markup), "label"); + + return TRUE; } gdouble ev_annotation_markup_get_opacity (EvAnnotationMarkup *markup) { - gdouble retval; - - g_return_val_if_fail (EV_IS_ANNOTATION_MARKUP (markup), 0.0); + EvAnnotationMarkupProps *props; - g_object_get (G_OBJECT (markup), "opacity", &retval, NULL); + g_return_val_if_fail (EV_IS_ANNOTATION_MARKUP (markup), 1.0); - return retval; + props = ev_annotation_markup_get_properties (markup); + return props->opacity; } -void +gboolean ev_annotation_markup_set_opacity (EvAnnotationMarkup *markup, gdouble opacity) { - g_return_if_fail (EV_IS_ANNOTATION_MARKUP (markup)); + EvAnnotationMarkupProps *props; - g_object_set (G_OBJECT (markup), "opacity", opacity, NULL); + g_return_val_if_fail (EV_IS_ANNOTATION_MARKUP (markup), FALSE); + + props = ev_annotation_markup_get_properties (markup); + if (props->opacity == opacity) + return FALSE; + + props->opacity = opacity; + + g_object_notify (G_OBJECT (markup), "opacity"); + + return TRUE; } gboolean ev_annotation_markup_has_popup (EvAnnotationMarkup *markup) { - gboolean retval; + EvAnnotationMarkupProps *props; g_return_val_if_fail (EV_IS_ANNOTATION_MARKUP (markup), FALSE); - g_object_get (G_OBJECT (markup), "has_popup", &retval, NULL); + props = ev_annotation_markup_get_properties (markup); + return props->has_popup; +} - return retval; +gboolean +ev_annotation_markup_set_has_popup (EvAnnotationMarkup *markup, + gboolean has_popup) +{ + EvAnnotationMarkupProps *props; + + g_return_val_if_fail (EV_IS_ANNOTATION_MARKUP (markup), FALSE); + + props = ev_annotation_markup_get_properties (markup); + if (props->has_popup == has_popup) + return FALSE; + + props->has_popup = has_popup; + + g_object_notify (G_OBJECT (markup), "has-popup"); + + return TRUE; } void ev_annotation_markup_get_rectangle (EvAnnotationMarkup *markup, EvRectangle *ev_rect) { - EvRectangle *r; + EvAnnotationMarkupProps *props; g_return_if_fail (EV_IS_ANNOTATION_MARKUP (markup)); g_return_if_fail (ev_rect != NULL); - g_object_get (G_OBJECT (markup), "rectangle", &r, NULL); - *ev_rect = *r; + props = ev_annotation_markup_get_properties (markup); + *ev_rect = props->rectangle; } gboolean -ev_annotation_markup_get_is_open (EvAnnotationMarkup *markup) +ev_annotation_markup_set_rectangle (EvAnnotationMarkup *markup, + const EvRectangle *ev_rect) { - gboolean retval; + EvAnnotationMarkupProps *props; g_return_val_if_fail (EV_IS_ANNOTATION_MARKUP (markup), FALSE); + g_return_val_if_fail (ev_rect != NULL, FALSE); + + props = ev_annotation_markup_get_properties (markup); + if (props->rectangle.x1 == ev_rect->x1 && + props->rectangle.y1 == ev_rect->y1 && + props->rectangle.x2 == ev_rect->x2 && + props->rectangle.y2 == ev_rect->y2) + return FALSE; + + props->rectangle = *ev_rect; - g_object_get (G_OBJECT (markup), "is_open", &retval, NULL); + g_object_notify (G_OBJECT (markup), "rectangle"); - return retval; + return TRUE; } -void -ev_annotation_markup_set_is_open (EvAnnotationMarkup *markup, - gboolean is_open) +gboolean +ev_annotation_markup_get_popup_is_open (EvAnnotationMarkup *markup) { - g_return_if_fail (EV_IS_ANNOTATION_MARKUP (markup)); + EvAnnotationMarkupProps *props; + + g_return_val_if_fail (EV_IS_ANNOTATION_MARKUP (markup), FALSE); - g_object_set (G_OBJECT (markup), "is_open", is_open, NULL); + props = ev_annotation_markup_get_properties (markup); + return props->popup_is_open; +} + +gboolean +ev_annotation_markup_set_popup_is_open (EvAnnotationMarkup *markup, + gboolean is_open) +{ + EvAnnotationMarkupProps *props; + + g_return_val_if_fail (EV_IS_ANNOTATION_MARKUP (markup), FALSE); + + props = ev_annotation_markup_get_properties (markup); + if (props->popup_is_open == is_open) + return FALSE; + + props->popup_is_open = is_open; + + g_object_notify (G_OBJECT (markup), "popup_is_open"); + + return TRUE; } /* EvAnnotationText */ static void ev_annotation_text_init (EvAnnotationText *annot) { + EV_ANNOTATION (annot)->type = EV_ANNOTATION_TYPE_TEXT; +} + +static void +ev_annotation_text_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + EvAnnotationText *annot = EV_ANNOTATION_TEXT (object); + + switch (prop_id) { + case PROP_TEXT_ICON: + ev_annotation_text_set_icon (annot, g_value_get_enum (value)); + break; + case PROP_TEXT_IS_OPEN: + ev_annotation_text_set_is_open (annot, g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +ev_annotation_text_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + EvAnnotationText *annot = EV_ANNOTATION_TEXT (object); + + switch (prop_id) { + case PROP_TEXT_ICON: + g_value_set_enum (value, annot->icon); + break; + case PROP_TEXT_IS_OPEN: + g_value_set_boolean (value, annot->is_open); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } } static void @@ -351,7 +782,26 @@ ev_annotation_text_class_init (EvAnnotationTextClass *klass) { GObjectClass *g_object_class = G_OBJECT_CLASS (klass); + g_object_class->set_property = ev_annotation_text_set_property; + g_object_class->get_property = ev_annotation_text_get_property; + ev_annotation_markup_class_install_properties (g_object_class); + + g_object_class_install_property (g_object_class, + PROP_TEXT_ICON, + g_param_spec_enum ("icon", + "Icon", + "The icon fo the text annotation", + EV_TYPE_ANNOTATION_TEXT_ICON, + EV_ANNOTATION_TEXT_ICON_NOTE, + G_PARAM_READWRITE)); + g_object_class_install_property (g_object_class, + PROP_TEXT_IS_OPEN, + g_param_spec_boolean ("is_open", + "IsOpen", + "Whether text annot is initially open", + FALSE, + G_PARAM_READWRITE)); } static void @@ -362,12 +812,57 @@ ev_annotation_text_markup_iface_init (EvAnnotationMarkupInterface *iface) EvAnnotation * ev_annotation_text_new (EvPage *page) { - EvAnnotation *annot; + return EV_ANNOTATION (g_object_new (EV_TYPE_ANNOTATION_TEXT, + "page", page, + NULL)); +} - annot = EV_ANNOTATION (g_object_new (EV_TYPE_ANNOTATION_TEXT, NULL)); - annot->page = g_object_ref (page); +EvAnnotationTextIcon +ev_annotation_text_get_icon (EvAnnotationText *text) +{ + g_return_val_if_fail (EV_IS_ANNOTATION_TEXT (text), 0); - return annot; + return text->icon; +} + +gboolean +ev_annotation_text_set_icon (EvAnnotationText *text, + EvAnnotationTextIcon icon) +{ + g_return_val_if_fail (EV_IS_ANNOTATION_TEXT (text), FALSE); + + if (text->icon == icon) + return FALSE; + + text->icon = icon; + + g_object_notify (G_OBJECT (text), "icon"); + + return TRUE; +} + +gboolean +ev_annotation_text_get_is_open (EvAnnotationText *text) +{ + g_return_val_if_fail (EV_IS_ANNOTATION_TEXT (text), FALSE); + + return text->is_open; +} + +gboolean +ev_annotation_text_set_is_open (EvAnnotationText *text, + gboolean is_open) +{ + g_return_val_if_fail (EV_IS_ANNOTATION_TEXT (text), FALSE); + + if (text->is_open == is_open) + return FALSE; + + text->is_open = is_open; + + g_object_notify (G_OBJECT (text), "is_open"); + + return TRUE; } /* EvAnnotationAttachment */ @@ -387,6 +882,41 @@ ev_annotation_attachment_finalize (GObject *object) static void ev_annotation_attachment_init (EvAnnotationAttachment *annot) { + EV_ANNOTATION (annot)->type = EV_ANNOTATION_TYPE_ATTACHMENT; +} + +static void +ev_annotation_attachment_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + EvAnnotationAttachment *annot = EV_ANNOTATION_ATTACHMENT (object); + + switch (prop_id) { + case PROP_ATTACHMENT_ATTACHMENT: + ev_annotation_attachment_set_attachment (annot, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +ev_annotation_attachment_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + EvAnnotationAttachment *annot = EV_ANNOTATION_ATTACHMENT (object); + + switch (prop_id) { + case PROP_ATTACHMENT_ATTACHMENT: + g_value_set_object (value, annot->attachment); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } } static void @@ -394,9 +924,20 @@ ev_annotation_attachment_class_init (EvAnnotationAttachmentClass *klass) { GObjectClass *g_object_class = G_OBJECT_CLASS (klass); + g_object_class->set_property = ev_annotation_attachment_set_property; + g_object_class->get_property = ev_annotation_attachment_get_property; + g_object_class->finalize = ev_annotation_attachment_finalize; + ev_annotation_markup_class_install_properties (g_object_class); - g_object_class->finalize = ev_annotation_attachment_finalize; + g_object_class_install_property (g_object_class, + PROP_ATTACHMENT_ATTACHMENT, + g_param_spec_object ("attachment", + "Attachment", + "The attachment of the annotation", + EV_TYPE_ATTACHMENT, + G_PARAM_CONSTRUCT | + G_PARAM_READWRITE)); } static void @@ -408,13 +949,36 @@ EvAnnotation * ev_annotation_attachment_new (EvPage *page, EvAttachment *attachment) { - EvAnnotation *annot; - g_return_val_if_fail (EV_IS_ATTACHMENT (attachment), NULL); - annot = EV_ANNOTATION (g_object_new (EV_TYPE_ANNOTATION_ATTACHMENT, NULL)); - annot->page = g_object_ref (page); - EV_ANNOTATION_ATTACHMENT (annot)->attachment = g_object_ref (attachment); + return EV_ANNOTATION (g_object_new (EV_TYPE_ANNOTATION_ATTACHMENT, + "page", page, + "attachment", attachment, + NULL)); +} + +EvAttachment * +ev_annotation_attachment_get_attachment (EvAnnotationAttachment *annot) +{ + g_return_val_if_fail (EV_IS_ANNOTATION_ATTACHMENT (annot), NULL); + + return annot->attachment; +} + +gboolean +ev_annotation_attachment_set_attachment (EvAnnotationAttachment *annot, + EvAttachment *attachment) +{ + g_return_val_if_fail (EV_IS_ANNOTATION_ATTACHMENT (annot), FALSE); + + if (annot->attachment == attachment) + return FALSE; + + if (annot->attachment) + g_object_unref (annot->attachment); + annot->attachment = attachment ? g_object_ref (attachment) : NULL; + + g_object_notify (G_OBJECT (annot), "attachment"); - return annot; + return TRUE; } diff --git a/libdocument/ev-annotation.h b/libdocument/ev-annotation.h index de8736e2..554523c1 100644 --- a/libdocument/ev-annotation.h +++ b/libdocument/ev-annotation.h @@ -77,80 +77,84 @@ typedef struct _EvAnnotationTextClass EvAnnotationTextClass; typedef struct _EvAnnotationAttachment EvAnnotationAttachment; typedef struct _EvAnnotationAttachmentClass EvAnnotationAttachmentClass; -struct _EvAnnotation -{ - GObject parent; - - EvPage *page; - gboolean changed; - - gchar *contents; - gchar *name; - gchar *modified; - GdkColor color; - -}; - -struct _EvAnnotationClass -{ - GObjectClass parent_class; -}; - -struct _EvAnnotationMarkupInterface -{ - GTypeInterface base_iface; -}; - -struct _EvAnnotationText -{ - EvAnnotation parent; - - gboolean is_open : 1; -}; - -struct _EvAnnotationTextClass -{ - EvAnnotationClass parent_class; -}; - -struct _EvAnnotationAttachment -{ - EvAnnotation parent; - - EvAttachment *attachment; -}; - -struct _EvAnnotationAttachmentClass -{ - EvAnnotationClass parent_class; -}; +typedef enum { + EV_ANNOTATION_TYPE_UNKNOWN, + EV_ANNOTATION_TYPE_TEXT, + EV_ANNOTATION_TYPE_ATTACHMENT +} EvAnnotationType; + +typedef enum { + EV_ANNOTATION_TEXT_ICON_NOTE, + EV_ANNOTATION_TEXT_ICON_COMMENT, + EV_ANNOTATION_TEXT_ICON_KEY, + EV_ANNOTATION_TEXT_ICON_HELP, + EV_ANNOTATION_TEXT_ICON_NEW_PARAGRAPH, + EV_ANNOTATION_TEXT_ICON_PARAGRAPH, + EV_ANNOTATION_TEXT_ICON_INSERT, + EV_ANNOTATION_TEXT_ICON_CROSS, + EV_ANNOTATION_TEXT_ICON_CIRCLE, + EV_ANNOTATION_TEXT_ICON_UNKNOWN +} EvAnnotationTextIcon; /* EvAnnotation */ -GType ev_annotation_get_type (void) G_GNUC_CONST; +GType ev_annotation_get_type (void) G_GNUC_CONST; +EvAnnotationType ev_annotation_get_annotation_type (EvAnnotation *annot); +EvPage *ev_annotation_get_page (EvAnnotation *annot); +guint ev_annotation_get_page_index (EvAnnotation *annot); +gboolean ev_annotation_equal (EvAnnotation *annot, + EvAnnotation *other); +const gchar *ev_annotation_get_contents (EvAnnotation *annot); +gboolean ev_annotation_set_contents (EvAnnotation *annot, + const gchar *contents); +const gchar *ev_annotation_get_name (EvAnnotation *annot); +gboolean ev_annotation_set_name (EvAnnotation *annot, + const gchar *name); +const gchar *ev_annotation_get_modified (EvAnnotation *annot); +gboolean ev_annotation_set_modified (EvAnnotation *annot, + const gchar *modified); +gboolean ev_annotation_set_modified_from_time (EvAnnotation *annot, + GTime utime); +void ev_annotation_get_color (EvAnnotation *annot, + GdkColor *color); +gboolean ev_annotation_set_color (EvAnnotation *annot, + const GdkColor *color); /* EvAnnotationMarkup */ -GType ev_annotation_markup_get_type (void) G_GNUC_CONST; -gchar *ev_annotation_markup_get_label (EvAnnotationMarkup *markup); -void ev_annotation_markup_set_label (EvAnnotationMarkup *markup, - const gchar *label); -gdouble ev_annotation_markup_get_opacity (EvAnnotationMarkup *markup); -void ev_annotation_markup_set_opacity (EvAnnotationMarkup *markup, - gdouble opacity); -gboolean ev_annotation_markup_has_popup (EvAnnotationMarkup *markup); -void ev_annotation_markup_get_rectangle (EvAnnotationMarkup *markup, - EvRectangle *ev_rect); -gboolean ev_annotation_markup_get_is_open (EvAnnotationMarkup *markup); -void ev_annotation_markup_set_is_open (EvAnnotationMarkup *markup, - gboolean is_open); - -/* EvAnnotationText */ -GType ev_annotation_text_get_type (void) G_GNUC_CONST; -EvAnnotation *ev_annotation_text_new (EvPage *page); +GType ev_annotation_markup_get_type (void) G_GNUC_CONST; +const gchar *ev_annotation_markup_get_label (EvAnnotationMarkup *markup); +gboolean ev_annotation_markup_set_label (EvAnnotationMarkup *markup, + const gchar *label); +gdouble ev_annotation_markup_get_opacity (EvAnnotationMarkup *markup); +gboolean ev_annotation_markup_set_opacity (EvAnnotationMarkup *markup, + gdouble opacity); +gboolean ev_annotation_markup_has_popup (EvAnnotationMarkup *markup); +gboolean ev_annotation_markup_set_has_popup (EvAnnotationMarkup *markup, + gboolean has_popup); +void ev_annotation_markup_get_rectangle (EvAnnotationMarkup *markup, + EvRectangle *ev_rect); +gboolean ev_annotation_markup_set_rectangle (EvAnnotationMarkup *markup, + const EvRectangle *ev_rect); +gboolean ev_annotation_markup_get_popup_is_open (EvAnnotationMarkup *markup); +gboolean ev_annotation_markup_set_popup_is_open (EvAnnotationMarkup *markup, + gboolean is_open); /* EvAnnotationText */ -GType ev_annotation_attachment_get_type (void) G_GNUC_CONST; -EvAnnotation *ev_annotation_attachment_new (EvPage *page, - EvAttachment *attachment); +GType ev_annotation_text_get_type (void) G_GNUC_CONST; +EvAnnotation *ev_annotation_text_new (EvPage *page); +EvAnnotationTextIcon ev_annotation_text_get_icon (EvAnnotationText *text); +gboolean ev_annotation_text_set_icon (EvAnnotationText *text, + EvAnnotationTextIcon icon); +gboolean ev_annotation_text_get_is_open (EvAnnotationText *text); +gboolean ev_annotation_text_set_is_open (EvAnnotationText *text, + gboolean is_open); + +/* EvAnnotationAttachment */ +GType ev_annotation_attachment_get_type (void) G_GNUC_CONST; +EvAnnotation *ev_annotation_attachment_new (EvPage *page, + EvAttachment *attachment); +EvAttachment *ev_annotation_attachment_get_attachment (EvAnnotationAttachment *annot); +gboolean ev_annotation_attachment_set_attachment (EvAnnotationAttachment *annot, + EvAttachment *attachment); G_END_DECLS diff --git a/libdocument/ev-document-annotations.c b/libdocument/ev-document-annotations.c index 6ccdd57d..4bc039eb 100644 --- a/libdocument/ev-document-annotations.c +++ b/libdocument/ev-document-annotations.c @@ -38,11 +38,30 @@ ev_document_annotations_get_annotations (EvDocumentAnnotations *document_annots, } void -ev_document_annotations_annotation_set_contents (EvDocumentAnnotations *document_annots, - EvAnnotation *annot, - const gchar *contents) +ev_document_annotations_save_annotation (EvDocumentAnnotations *document_annots, + EvAnnotation *annot, + EvAnnotationsSaveMask mask) { EvDocumentAnnotationsInterface *iface = EV_DOCUMENT_ANNOTATIONS_GET_IFACE (document_annots); - iface->annotation_set_contents (document_annots, annot, contents); + iface->save_annotation (document_annots, annot, mask); +} + +void +ev_document_annotations_add_annotation (EvDocumentAnnotations *document_annots, + EvAnnotation *annot, + EvRectangle *rect) +{ + EvDocumentAnnotationsInterface *iface = EV_DOCUMENT_ANNOTATIONS_GET_IFACE (document_annots); + + if (iface->add_annotation) + iface->add_annotation (document_annots, annot, rect); +} + +gboolean +ev_document_annotations_can_add_annotation (EvDocumentAnnotations *document_annots) +{ + EvDocumentAnnotationsInterface *iface = EV_DOCUMENT_ANNOTATIONS_GET_IFACE (document_annots); + + return iface->add_annotation != NULL; } diff --git a/libdocument/ev-document-annotations.h b/libdocument/ev-document-annotations.h index 6da49d95..8eb2c7c2 100644 --- a/libdocument/ev-document-annotations.h +++ b/libdocument/ev-document-annotations.h @@ -40,6 +40,28 @@ G_BEGIN_DECLS #define EV_IS_DOCUMENT_ANNOTATIONS_IFACE(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EV_TYPE_DOCUMENT_ANNOTATIONS)) #define EV_DOCUMENT_ANNOTATIONS_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), EV_TYPE_DOCUMENT_ANNOTATIONS, EvDocumentAnnotationsInterface)) +typedef enum { + EV_ANNOTATIONS_SAVE_NONE = 0, + EV_ANNOTATIONS_SAVE_CONTENTS = 1 << 0, + EV_ANNOTATIONS_SAVE_COLOR = 1 << 1, + + /* Markup Annotations */ + EV_ANNOTATIONS_SAVE_LABEL = 1 << 2, + EV_ANNOTATIONS_SAVE_OPACITY = 1 << 3, + EV_ANNOTATIONS_SAVE_POPUP_RECT = 1 << 4, + EV_ANNOTATIONS_SAVE_POPUP_IS_OPEN = 1 << 5, + + /* Text Annotations */ + EV_ANNOTATIONS_SAVE_TEXT_IS_OPEN = 1 << 6, + EV_ANNOTATIONS_SAVE_TEXT_ICON = 1 << 7, + + /* Attachment Annotations */ + EV_ANNOTATIONS_SAVE_ATTACHMENT = 1 << 8, + + /* Save all */ + EV_ANNOTATIONS_SAVE_ALL = (1 << 9) - 1 +} EvAnnotationsSaveMask; + typedef struct _EvDocumentAnnotations EvDocumentAnnotations; typedef struct _EvDocumentAnnotationsInterface EvDocumentAnnotationsInterface; @@ -48,20 +70,26 @@ struct _EvDocumentAnnotationsInterface GTypeInterface base_iface; /* Methods */ - EvMappingList *(* get_annotations) (EvDocumentAnnotations *document_annots, - EvPage *page); - void (* annotation_set_contents) (EvDocumentAnnotations *document_annots, - EvAnnotation *annot, - const gchar *contents); + EvMappingList *(* get_annotations) (EvDocumentAnnotations *document_annots, + EvPage *page); + void (* add_annotation) (EvDocumentAnnotations *document_annots, + EvAnnotation *annot, + EvRectangle *rect); + void (* save_annotation) (EvDocumentAnnotations *document_annots, + EvAnnotation *annot, + EvAnnotationsSaveMask mask); }; -GType ev_document_annotations_get_type (void) G_GNUC_CONST; -EvMappingList *ev_document_annotations_get_annotations (EvDocumentAnnotations *document_annots, - EvPage *page); - -void ev_document_annotations_annotation_set_contents (EvDocumentAnnotations *document_annots, - EvAnnotation *annot, - const gchar *contents); +GType ev_document_annotations_get_type (void) G_GNUC_CONST; +EvMappingList *ev_document_annotations_get_annotations (EvDocumentAnnotations *document_annots, + EvPage *page); +void ev_document_annotations_add_annotation (EvDocumentAnnotations *document_annots, + EvAnnotation *annot, + EvRectangle *rect); +void ev_document_annotations_save_annotation (EvDocumentAnnotations *document_annots, + EvAnnotation *annot, + EvAnnotationsSaveMask mask); +gboolean ev_document_annotations_can_add_annotation (EvDocumentAnnotations *document_annots); G_END_DECLS diff --git a/libview/ev-annotation-window.c b/libview/ev-annotation-window.c index 559b879f..749d1a01 100644 --- a/libview/ev-annotation-window.c +++ b/libview/ev-annotation-window.c @@ -53,7 +53,7 @@ struct _EvAnnotationWindow { GtkWidget *resize_sw; gboolean is_open; - EvRectangle *rect; + EvRectangle rect; gboolean in_move; gint x; @@ -116,7 +116,7 @@ ev_annotation_window_get_icon_size (void) } static void -ev_annotation_window_check_contents_modified (EvAnnotationWindow *window) +ev_annotation_window_sync_contents (EvAnnotationWindow *window) { gchar *contents; GtkTextIter start, end; @@ -126,23 +126,8 @@ ev_annotation_window_check_contents_modified (EvAnnotationWindow *window) buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (window->text_view)); gtk_text_buffer_get_bounds (buffer, &start, &end); contents = gtk_text_buffer_get_text (buffer, &start, &end, FALSE); - - if (contents && annot->contents) { - if (strcasecmp (contents, annot->contents) != 0) { - g_free (annot->contents); - annot->contents = contents; - annot->changed = TRUE; - } else { - g_free (contents); - } - } else if (annot->contents) { - g_free (annot->contents); - annot->contents = NULL; - annot->changed = TRUE; - } else if (contents) { - annot->contents = contents; - annot->changed = TRUE; - } + ev_annotation_set_contents (annot, contents); + g_free (contents); } static void @@ -177,22 +162,39 @@ ev_annotation_window_set_color (EvAnnotationWindow *window, g_object_unref (rc_style); } +static void +ev_annotation_window_label_changed (EvAnnotationMarkup *annot, + GParamSpec *pspec, + EvAnnotationWindow *window) +{ + const gchar *label = ev_annotation_markup_get_label (annot); + + gtk_window_set_title (GTK_WINDOW (window), label); + gtk_label_set_text (GTK_LABEL (window->title), label); +} + +static void +ev_annotation_window_color_changed (EvAnnotation *annot, + GParamSpec *pspec, + EvAnnotationWindow *window) +{ + GdkColor color; + + ev_annotation_get_color (annot, &color); + ev_annotation_window_set_color (window, &color); +} + static void ev_annotation_window_dispose (GObject *object) { EvAnnotationWindow *window = EV_ANNOTATION_WINDOW (object); if (window->annotation) { - ev_annotation_window_check_contents_modified (window); + ev_annotation_window_sync_contents (window); g_object_unref (window->annotation); window->annotation = NULL; } - if (window->rect) { - ev_rectangle_free (window->rect); - window->rect = NULL; - } - (* G_OBJECT_CLASS (ev_annotation_window_parent_class)->dispose) (object); } @@ -391,8 +393,10 @@ ev_annotation_window_constructor (GType type, GObject *object; EvAnnotationWindow *window; EvAnnotation *annot; - gchar *label; - gdouble opacity; + EvAnnotationMarkup *markup; + const gchar *contents; + const gchar *label; + GdkColor color; EvRectangle *rect; gdouble scale; @@ -401,37 +405,44 @@ ev_annotation_window_constructor (GType type, construct_params); window = EV_ANNOTATION_WINDOW (object); annot = window->annotation; + markup = EV_ANNOTATION_MARKUP (annot); gtk_window_set_transient_for (GTK_WINDOW (window), window->parent); gtk_window_set_destroy_with_parent (GTK_WINDOW (window), FALSE); - g_object_get (annot, - "label", &label, - "opacity", &opacity, - "is_open", &window->is_open, - "rectangle", &window->rect, - NULL); - rect = window->rect; + label = ev_annotation_markup_get_label (markup); + window->is_open = ev_annotation_markup_get_popup_is_open (markup); + ev_annotation_markup_get_rectangle (markup, &window->rect); + + rect = &window->rect; /* Rectangle is at doc resolution (72.0) */ scale = get_screen_dpi (window) / 72.0; gtk_window_resize (GTK_WINDOW (window), (gint)((rect->x2 - rect->x1) * scale), (gint)((rect->y2 - rect->y1) * scale)); - ev_annotation_window_set_color (window, &annot->color); - gtk_widget_set_name (GTK_WIDGET (window), annot->name); + + ev_annotation_get_color (annot, &color); + ev_annotation_window_set_color (window, &color); + gtk_widget_set_name (GTK_WIDGET (window), ev_annotation_get_name (annot)); gtk_window_set_title (GTK_WINDOW (window), label); gtk_label_set_text (GTK_LABEL (window->title), label); - gtk_window_set_opacity (GTK_WINDOW (window), opacity); - g_free (label); - if (annot->contents) { + contents = ev_annotation_get_contents (annot); + if (contents) { GtkTextBuffer *buffer; buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (window->text_view)); - gtk_text_buffer_set_text (buffer, annot->contents, -1); + gtk_text_buffer_set_text (buffer, contents, -1); } + g_signal_connect (annot, "notify::label", + G_CALLBACK (ev_annotation_window_label_changed), + window); + g_signal_connect (annot, "notify::color", + G_CALLBACK (ev_annotation_window_color_changed), + window); + return object; } @@ -497,6 +508,8 @@ ev_annotation_window_focus_out_event (GtkWidget *widget, g_signal_emit (window, signals[MOVED], 0, window->x, window->y); } + ev_annotation_window_sync_contents (window); + return FALSE; } @@ -587,7 +600,7 @@ ev_annotation_window_set_annotation (EvAnnotationWindow *window, g_object_unref (window->annotation); window->annotation = g_object_ref (annot); - ev_annotation_window_check_contents_modified (window); + ev_annotation_window_sync_contents (window); g_object_notify (G_OBJECT (window), "annotation"); } @@ -599,22 +612,24 @@ ev_annotation_window_is_open (EvAnnotationWindow *window) return window->is_open; } -const EvRectangle * -ev_annotation_window_get_rectangle (EvAnnotationWindow *window) +void +ev_annotation_window_get_rectangle (EvAnnotationWindow *window, + EvRectangle *rect) { - g_return_val_if_fail (EV_IS_ANNOTATION_WINDOW (window), NULL); + g_return_if_fail (EV_IS_ANNOTATION_WINDOW (window)); + g_return_if_fail (rect != NULL); - return window->rect; + *rect = window->rect; } void ev_annotation_window_set_rectangle (EvAnnotationWindow *window, - EvRectangle *rect) + const EvRectangle *rect) { g_return_if_fail (EV_IS_ANNOTATION_WINDOW (window)); g_return_if_fail (rect != NULL); - *window->rect = *rect; + window->rect = *rect; } void @@ -637,5 +652,5 @@ ev_annotation_window_ungrab_focus (EvAnnotationWindow *window) send_focus_change (window->text_view, FALSE); } - ev_annotation_window_check_contents_modified (window); + ev_annotation_window_sync_contents (window); } diff --git a/libview/ev-annotation-window.h b/libview/ev-annotation-window.h index 697574f7..b9ba4f12 100644 --- a/libview/ev-annotation-window.h +++ b/libview/ev-annotation-window.h @@ -38,18 +38,19 @@ typedef struct _EvAnnotationWindowClass EvAnnotationWindowClass; #define EV_IS_ANNOTATION_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), EV_TYPE_ANNOTATION_WINDOW)) #define EV_ANNOTATION_WINDOW_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS((object), EV_TYPE_ANNOTATION_WINDOW, EvAnnotationWindowClass)) -GType ev_annotation_window_get_type (void) G_GNUC_CONST; -GtkWidget *ev_annotation_window_new (EvAnnotation *annot, - GtkWindow *parent); -EvAnnotation *ev_annotation_window_get_annotation (EvAnnotationWindow *window); -void ev_annotation_window_set_annotation (EvAnnotationWindow *window, - EvAnnotation *annot); -gboolean ev_annotation_window_is_open (EvAnnotationWindow *window); -const EvRectangle *ev_annotation_window_get_rectangle (EvAnnotationWindow *window); -void ev_annotation_window_set_rectangle (EvAnnotationWindow *window, - EvRectangle *rect); -void ev_annotation_window_grab_focus (EvAnnotationWindow *window); -void ev_annotation_window_ungrab_focus (EvAnnotationWindow *window); +GType ev_annotation_window_get_type (void) G_GNUC_CONST; +GtkWidget *ev_annotation_window_new (EvAnnotation *annot, + GtkWindow *parent); +EvAnnotation *ev_annotation_window_get_annotation (EvAnnotationWindow *window); +void ev_annotation_window_set_annotation (EvAnnotationWindow *window, + EvAnnotation *annot); +gboolean ev_annotation_window_is_open (EvAnnotationWindow *window); +void ev_annotation_window_get_rectangle (EvAnnotationWindow *window, + EvRectangle *rect); +void ev_annotation_window_set_rectangle (EvAnnotationWindow *window, + const EvRectangle *rect); +void ev_annotation_window_grab_focus (EvAnnotationWindow *window); +void ev_annotation_window_ungrab_focus (EvAnnotationWindow *window); G_END_DECLS diff --git a/libview/ev-view-cursor.c b/libview/ev-view-cursor.c index 6974d3f7..67cacd73 100644 --- a/libview/ev-view-cursor.c +++ b/libview/ev-view-cursor.c @@ -58,6 +58,9 @@ ev_view_cursor_new (GdkDisplay *display, case EV_VIEW_CURSOR_AUTOSCROLL: cursor = gdk_cursor_new_for_display (display, GDK_DOUBLE_ARROW); break; + case EV_VIEW_CURSOR_ADD: + cursor = gdk_cursor_new_for_display (display, GDK_PLUS); + break; } return cursor; diff --git a/libview/ev-view-cursor.h b/libview/ev-view-cursor.h index 012d5a45..220441c6 100644 --- a/libview/ev-view-cursor.h +++ b/libview/ev-view-cursor.h @@ -35,7 +35,8 @@ typedef enum { EV_VIEW_CURSOR_WAIT, EV_VIEW_CURSOR_HIDDEN, EV_VIEW_CURSOR_DRAG, - EV_VIEW_CURSOR_AUTOSCROLL + EV_VIEW_CURSOR_AUTOSCROLL, + EV_VIEW_CURSOR_ADD } EvViewCursor; GdkCursor *ev_view_cursor_new (GdkDisplay *display, diff --git a/libview/ev-view-private.h b/libview/ev-view-private.h index 57eea303..a3655072 100644 --- a/libview/ev-view-private.h +++ b/libview/ev-view-private.h @@ -185,6 +185,8 @@ struct _EvView { GList *window_children; EvViewWindowChild *window_child_focus; EvMapping *focus_annotation; + gboolean adding_annot; + EvAnnotationType adding_annot_type; /* Synctex */ EvMapping *synctex_result; @@ -208,6 +210,8 @@ struct _EvViewClass { void (*selection_changed) (EvView *view); void (*sync_source) (EvView *view, EvSourceLink *link); + void (*annot_added) (EvView *view, + EvAnnotation *annot); }; void _get_page_size_for_scale_and_rotation (EvDocument *document, diff --git a/libview/ev-view.c b/libview/ev-view.c index eee1b563..471c66ca 100644 --- a/libview/ev-view.c +++ b/libview/ev-view.c @@ -55,6 +55,7 @@ enum { SIGNAL_POPUP_MENU, SIGNAL_SELECTION_CHANGED, SIGNAL_SYNC_SOURCE, + SIGNAL_ANNOT_ADDED, N_SIGNALS }; @@ -1832,6 +1833,12 @@ ev_view_handle_cursor_over_xy (EvView *view, gint x, gint y) if (view->cursor == EV_VIEW_CURSOR_HIDDEN) return; + if (view->adding_annot) { + if (view->cursor != EV_VIEW_CURSOR_ADD) + ev_view_set_cursor (view, EV_VIEW_CURSOR_ADD); + return; + } + if (view->drag_info.in_drag) { if (view->cursor != EV_VIEW_CURSOR_DRAG) ev_view_set_cursor (view, EV_VIEW_CURSOR_DRAG); @@ -1866,7 +1873,8 @@ ev_view_handle_cursor_over_xy (EvView *view, gint x, gint y) if (view->cursor == EV_VIEW_CURSOR_LINK || view->cursor == EV_VIEW_CURSOR_IBEAM || view->cursor == EV_VIEW_CURSOR_DRAG || - view->cursor == EV_VIEW_CURSOR_AUTOSCROLL) + view->cursor == EV_VIEW_CURSOR_AUTOSCROLL || + view->cursor == EV_VIEW_CURSOR_ADD) ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL); } @@ -2494,7 +2502,7 @@ ev_view_find_window_child_for_annot (EvView *view, continue; wannot = ev_annotation_window_get_annotation (EV_ANNOTATION_WINDOW (child->window)); - if (wannot == annot || strcmp (wannot->name, annot->name) == 0) + if (ev_annotation_equal (wannot, annot)) return child; } @@ -2577,18 +2585,55 @@ annotation_window_moved (EvAnnotationWindow *window, } static void -ev_view_annotation_save (EvView *view, - EvAnnotation *annot) +ev_view_annotation_save_contents (EvView *view, + GParamSpec *pspec, + EvAnnotation *annot) { if (!view->document) return; - if (!annot->changed) - return; + ev_document_doc_mutex_lock (); + ev_document_annotations_save_annotation (EV_DOCUMENT_ANNOTATIONS (view->document), + annot, EV_ANNOTATIONS_SAVE_CONTENTS); + ev_document_doc_mutex_unlock (); +} - ev_document_annotations_annotation_set_contents (EV_DOCUMENT_ANNOTATIONS (view->document), - annot, annot->contents); - annot->changed = FALSE; +static GtkWidget * +ev_view_create_annotation_window (EvView *view, + EvAnnotation *annot, + GtkWindow *parent) +{ + GtkWidget *window; + EvRectangle doc_rect; + GdkRectangle view_rect; + guint page; + + window = ev_annotation_window_new (annot, parent); + g_signal_connect (window, "grab_focus", + G_CALLBACK (annotation_window_grab_focus), + view); + g_signal_connect (window, "closed", + G_CALLBACK (annotation_window_closed), + view); + g_signal_connect (window, "moved", + G_CALLBACK (annotation_window_moved), + view); + g_signal_connect_swapped (annot, "notify::contents", + G_CALLBACK (ev_view_annotation_save_contents), + view); + g_object_set_data (G_OBJECT (annot), "popup", window); + + page = ev_annotation_get_page_index (annot); + ev_annotation_window_get_rectangle (EV_ANNOTATION_WINDOW (window), &doc_rect); + doc_rect_to_view_rect (view, page, &doc_rect, &view_rect); + view_rect.x -= view->scroll_x; + view_rect.y -= view->scroll_y; + + ev_view_window_child_put (view, window, page, + view_rect.x, view_rect.y, + doc_rect.x1, doc_rect.y1); + + return window; } static void @@ -2607,8 +2652,6 @@ show_annotation_windows (EvView *view, EvAnnotation *annot; EvViewWindowChild *child; GtkWidget *window; - EvRectangle *doc_rect; - GdkRectangle view_rect; annot = ((EvMapping *)(l->data))->data; @@ -2632,30 +2675,7 @@ show_annotation_windows (EvView *view, g_object_set_data (G_OBJECT (annot), "popup", window); ev_view_window_child_move_with_parent (view, window); } else { - window = ev_annotation_window_new (annot, parent); - g_signal_connect (window, "grab_focus", - G_CALLBACK (annotation_window_grab_focus), - view); - g_signal_connect (window, "closed", - G_CALLBACK (annotation_window_closed), - view); - g_signal_connect (window, "moved", - G_CALLBACK (annotation_window_moved), - view); - g_object_set_data (G_OBJECT (annot), "popup", window); - - doc_rect = (EvRectangle *)ev_annotation_window_get_rectangle (EV_ANNOTATION_WINDOW (window)); - doc_rect_to_view_rect (view, page, doc_rect, &view_rect); - view_rect.x -= view->scroll_x; - view_rect.y -= view->scroll_y; - - ev_view_window_child_put (view, window, page, - view_rect.x, view_rect.y, - doc_rect->x1, doc_rect->y1); - - g_object_weak_ref (G_OBJECT (annot), - (GWeakNotify)ev_view_annotation_save, - view); + ev_view_create_annotation_window (view, annot, parent); } } } @@ -2707,6 +2727,23 @@ ev_view_get_annotation_at_location (EvView *view, return NULL; } +static void +ev_view_annotation_show_popup_window (EvView *view, + GtkWidget *window) +{ + EvViewWindowChild *child; + + if (!window) + return; + + child = ev_view_get_window_child (view, window); + if (!child->visible) { + child->visible = TRUE; + ev_view_window_child_move (view, child, child->x, child->y); + gtk_widget_show (window); + } +} + static void ev_view_handle_annotation (EvView *view, EvAnnotation *annot, @@ -2718,21 +2755,13 @@ ev_view_handle_annotation (EvView *view, GtkWidget *window; window = g_object_get_data (G_OBJECT (annot), "popup"); - if (window) { - EvViewWindowChild *child; - - child = ev_view_get_window_child (view, window); - if (!child->visible) { - child->visible = TRUE; - ev_view_window_child_move (view, child, child->x, child->y); - gtk_widget_show (window); - } - } + ev_view_annotation_show_popup_window (view, window); } if (EV_IS_ANNOTATION_ATTACHMENT (annot)) { - EvAttachment *attachment = EV_ANNOTATION_ATTACHMENT (annot)->attachment; + EvAttachment *attachment; + attachment = ev_annotation_attachment_get_attachment (EV_ANNOTATION_ATTACHMENT (annot)); if (attachment) { GError *error = NULL; @@ -2749,12 +2778,97 @@ ev_view_handle_annotation (EvView *view, } } +static void +ev_view_create_annotation (EvView *view, + EvAnnotationType annot_type, + gint x, + gint y) +{ + EvAnnotation *annot; + GdkPoint point; + GdkRectangle page_area; + GtkBorder border; + EvRectangle doc_rect, popup_rect; + EvPage *page; + GdkColor color = { 0, 65535, 65535, 0 }; + GdkRectangle view_rect; + cairo_region_t *region; + + point.x = x; + point.y = y; + ev_view_get_page_extents (view, view->current_page, &page_area, &border); + view_point_to_doc_point (view, &point, &page_area, + &doc_rect.x1, &doc_rect.y1); + doc_rect.x2 = doc_rect.x1 + 24; + doc_rect.y2 = doc_rect.y1 + 24; + + ev_document_doc_mutex_lock (); + page = ev_document_get_page (view->document, view->current_page); + switch (annot_type) { + case EV_ANNOTATION_TYPE_TEXT: + annot = ev_annotation_text_new (page); + break; + case EV_ANNOTATION_TYPE_ATTACHMENT: + /* TODO */ + g_object_unref (page); + ev_document_doc_mutex_unlock (); + return; + default: + g_assert_not_reached (); + } + g_object_unref (page); + + ev_annotation_set_color (annot, &color); + + if (EV_IS_ANNOTATION_MARKUP (annot)) { + popup_rect.x1 = doc_rect.x2; + popup_rect.x2 = popup_rect.x1 + 200; + popup_rect.y1 = doc_rect.y2; + popup_rect.y2 = popup_rect.y1 + 150; + g_object_set (annot, + "rectangle", &popup_rect, + "has_popup", TRUE, + "popup_is_open", FALSE, + "label", g_get_real_name (), + "opacity", 1.0, + NULL); + } + ev_document_annotations_add_annotation (EV_DOCUMENT_ANNOTATIONS (view->document), + annot, &doc_rect); + ev_document_doc_mutex_unlock (); + + /* If the page didn't have annots, mark the cache as dirty */ + if (!ev_page_cache_get_annot_mapping (view->page_cache, view->current_page)) + ev_page_cache_mark_dirty (view->page_cache, view->current_page); + + if (EV_IS_ANNOTATION_MARKUP (annot)) { + GtkWindow *parent; + GtkWidget *window; + + parent = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view))); + window = ev_view_create_annotation_window (view, annot, parent); + + /* Show the annot window the first time */ + ev_view_annotation_show_popup_window (view, window); + } + + doc_rect_to_view_rect (view, view->current_page, &doc_rect, &view_rect); + view_rect.x -= view->scroll_x; + view_rect.y -= view->scroll_y; + region = cairo_region_create_rectangle (&view_rect); + ev_view_reload_page (view, view->current_page, region); + cairo_region_destroy (region); + + g_signal_emit (view, signals[SIGNAL_ANNOT_ADDED], 0, annot); +} + void ev_view_focus_annotation (EvView *view, EvMapping *annot_mapping) { GdkRectangle view_rect; EvAnnotation *annot; + guint page; if (!EV_IS_DOCUMENT_ANNOTATIONS (view->document)) return; @@ -2765,14 +2879,43 @@ ev_view_focus_annotation (EvView *view, view->focus_annotation = annot_mapping; annot = (EvAnnotation *)annot_mapping->data; - ev_document_model_set_page (view->model, annot->page->index); + page = ev_annotation_get_page_index (annot); + ev_document_model_set_page (view->model, page); - doc_rect_to_view_rect (view, annot->page->index, + doc_rect_to_view_rect (view, page, &annot_mapping->area, &view_rect); ensure_rectangle_is_visible (view, &view_rect); gtk_widget_queue_draw (GTK_WIDGET (view)); } +void +ev_view_begin_add_annotation (EvView *view, + EvAnnotationType annot_type) +{ + if (annot_type == EV_ANNOTATION_TYPE_UNKNOWN) + return; + + if (view->adding_annot) + return; + + view->adding_annot = TRUE; + view->adding_annot_type = annot_type; + ev_view_set_cursor (view, EV_VIEW_CURSOR_ADD); +} + +void +ev_view_cancel_add_annotation (EvView *view) +{ + gint x, y; + + if (!view->adding_annot) + return; + + view->adding_annot = FALSE; + gtk_widget_get_pointer (GTK_WIDGET (view), &x, &y); + ev_view_handle_cursor_over_xy (view, x, y); +} + static gboolean ev_view_synctex_backward_search (EvView *view, gdouble x, @@ -3040,7 +3183,7 @@ ev_view_size_allocate (GtkWidget *widget, child = (EvViewWindowChild *)l->data; - doc_rect = *ev_annotation_window_get_rectangle (EV_ANNOTATION_WINDOW (child->window)); + ev_annotation_window_get_rectangle (EV_ANNOTATION_WINDOW (child->window), &doc_rect); if (child->moved) { doc_rect.x1 = child->orig_x; doc_rect.y1 = child->orig_y; @@ -3311,14 +3454,18 @@ ev_view_query_tooltip (GtkWidget *widget, gchar *text; annot = ev_view_get_annotation_at_location (view, x, y); - if (annot && annot->contents) { - GdkRectangle annot_area; + if (annot) { + const gchar *contents; - get_annot_area (view, x, y, annot, &annot_area); - gtk_tooltip_set_text (tooltip, annot->contents); - gtk_tooltip_set_tip_area (tooltip, &annot_area); + if ((contents = ev_annotation_get_contents (annot))) { + GdkRectangle annot_area; - return TRUE; + get_annot_area (view, x, y, annot, &annot_area); + gtk_tooltip_set_text (tooltip, contents); + gtk_tooltip_set_tip_area (tooltip, &annot_area); + + return TRUE; + } } link = ev_view_get_link_at_location (view, x, y); @@ -3386,13 +3533,15 @@ ev_view_button_press_event (GtkWidget *widget, window = EV_ANNOTATION_WINDOW (view->window_child_focus->window); annot = ev_annotation_window_get_annotation (window); ev_annotation_window_ungrab_focus (window); - ev_view_annotation_save (view, annot); view->window_child_focus = NULL; } view->pressed_button = event->button; view->selection_info.in_drag = FALSE; + if (view->adding_annot) + return FALSE; + if (view->scroll_info.autoscrolling) return TRUE; @@ -3861,6 +4010,19 @@ ev_view_button_release_event (GtkWidget *widget, view->drag_info.in_drag = FALSE; + if (view->adding_annot && view->pressed_button == 1) { + view->adding_annot = FALSE; + ev_view_handle_cursor_over_xy (view, event->x, event->y); + view->pressed_button = -1; + + ev_view_create_annotation (view, + view->adding_annot_type, + event->x + view->scroll_x, + event->y + view->scroll_y); + + return FALSE; + } + if (view->pressed_button == 2) { ev_view_handle_cursor_over_xy (view, event->x, event->y); } @@ -4094,7 +4256,7 @@ focus_annotation (EvView *view, EvMapping *mapping = view->focus_annotation; EvAnnotation *annot = (EvAnnotation *)mapping->data; - if (annot->page->index != page) + if (ev_annotation_get_page_index (annot) != page) return; doc_rect_to_view_rect (view, page, &mapping->area, &rect); @@ -4505,6 +4667,14 @@ ev_view_class_init (EvViewClass *class) g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); + signals[SIGNAL_ANNOT_ADDED] = g_signal_new ("annot-added", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (EvViewClass, annot_added), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + EV_TYPE_ANNOTATION); binding_set = gtk_binding_set_by_class (class); diff --git a/libview/ev-view.h b/libview/ev-view.h index 712d5546..06f5b30d 100644 --- a/libview/ev-view.h +++ b/libview/ev-view.h @@ -103,9 +103,12 @@ gboolean ev_view_get_page_extents (EvView *view, gint page, GdkRectangle *page_area, GtkBorder *border); - -void ev_view_focus_annotation (EvView *view, - EvMapping *annot_mapping); +/* Annotations */ +void ev_view_focus_annotation (EvView *view, + EvMapping *annot_mapping); +void ev_view_begin_add_annotation (EvView *view, + EvAnnotationType annot_type); +void ev_view_cancel_add_annotation (EvView *view); G_END_DECLS diff --git a/po/POTFILES.in b/po/POTFILES.in index b2eac5e1..c93d309f 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -38,6 +38,7 @@ libview/ev-view-accessible.c libview/ev-view-presentation.c libview/ev-view.c shell/eggfindbar.c +shell/ev-annotation-properties-dialog.c shell/ev-application.c shell/ev-history.c shell/ev-keyring.c diff --git a/shell/Makefile.am b/shell/Makefile.am index 523d299f..8336230f 100644 --- a/shell/Makefile.am +++ b/shell/Makefile.am @@ -32,6 +32,8 @@ endif evince_SOURCES= \ eggfindbar.c \ eggfindbar.h \ + ev-annotation-properties-dialog.h \ + ev-annotation-properties-dialog.c \ ev-application.c \ ev-application.h \ ev-file-monitor.h \ diff --git a/shell/ev-annotation-properties-dialog.c b/shell/ev-annotation-properties-dialog.c new file mode 100644 index 00000000..0f5c6e17 --- /dev/null +++ b/shell/ev-annotation-properties-dialog.c @@ -0,0 +1,327 @@ +/* ev-annotation-properties-dialog.c + * this file is part of evince, a gnome document viewer + * + * Copyright (C) 2010 Carlos Garcia Campos + * + * Evince is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Evince is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ + +#include + +#include + +#include "ev-annotation-properties-dialog.h" + +enum { + PROP_0, + PROP_ANNOT_TYPE +}; + +struct _EvAnnotationPropertiesDialog { + GtkDialog base_instance; + + EvAnnotationType annot_type; + EvAnnotation *annot; + + GtkWidget *table; + + GtkWidget *author; + GtkWidget *color; + GtkWidget *opacity; + GtkWidget *popup_state; + + /* Text Annotations */ + GtkWidget *icon; +}; + +struct _EvAnnotationPropertiesDialogClass { + GtkDialogClass base_class; +}; + +G_DEFINE_TYPE (EvAnnotationPropertiesDialog, ev_annotation_properties_dialog, GTK_TYPE_DIALOG) + +static void +ev_annotation_properties_dialog_dispose (GObject *object) +{ + EvAnnotationPropertiesDialog *dialog = EV_ANNOTATION_PROPERTIES_DIALOG (object); + + if (dialog->annot) { + g_object_unref (dialog->annot); + dialog->annot = NULL; + } + + G_OBJECT_CLASS (ev_annotation_properties_dialog_parent_class)->dispose (object); +} + +static void +ev_annotation_properties_dialog_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + EvAnnotationPropertiesDialog *dialog = EV_ANNOTATION_PROPERTIES_DIALOG (object); + + switch (prop_id) { + case PROP_ANNOT_TYPE: + dialog->annot_type = g_value_get_enum (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +ev_annotation_properties_dialog_constructed (GObject *object) +{ + EvAnnotationPropertiesDialog *dialog = EV_ANNOTATION_PROPERTIES_DIALOG (object); + GtkWidget *contant_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); + GtkWidget *table = dialog->table; + GtkWidget *label; + + contant_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); + + switch (dialog->annot_type) { + case EV_ANNOTATION_TYPE_TEXT: + label = gtk_label_new (_("Icon:")); + gtk_misc_set_alignment (GTK_MISC (label), 0., 0.5); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, 5, 6, + GTK_FILL, GTK_FILL, 0, 0); + gtk_widget_show (label); + + dialog->icon = gtk_combo_box_new_text (); + gtk_combo_box_append_text (GTK_COMBO_BOX (dialog->icon), _("Note")); + gtk_combo_box_append_text (GTK_COMBO_BOX (dialog->icon), _("Comment")); + gtk_combo_box_append_text (GTK_COMBO_BOX (dialog->icon), _("Key")); + gtk_combo_box_append_text (GTK_COMBO_BOX (dialog->icon), _("Help")); + gtk_combo_box_append_text (GTK_COMBO_BOX (dialog->icon), _("New Paragraph")); + gtk_combo_box_append_text (GTK_COMBO_BOX (dialog->icon), _("Paragraph")); + gtk_combo_box_append_text (GTK_COMBO_BOX (dialog->icon), _("Insert")); + gtk_combo_box_append_text (GTK_COMBO_BOX (dialog->icon), _("Cross")); + gtk_combo_box_append_text (GTK_COMBO_BOX (dialog->icon), _("Circle")); + gtk_combo_box_append_text (GTK_COMBO_BOX (dialog->icon), _("Unknown")); + gtk_combo_box_set_active (GTK_COMBO_BOX (dialog->icon), 0); + gtk_table_attach (GTK_TABLE (table), dialog->icon, + 1, 2, 5, 6, + GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0); + gtk_widget_show (dialog->icon); + + break; + case EV_ANNOTATION_TYPE_ATTACHMENT: + /* TODO */ + default: + break; + } +} + +static void +ev_annotation_properties_dialog_init (EvAnnotationPropertiesDialog *annot_dialog) +{ + GtkDialog *dialog = GTK_DIALOG (annot_dialog); + GtkWidget *content_area; + GtkWidget *label; + GtkWidget *table; + GtkWidget *hbox; + gchar *markup; + GdkColor color = { 0, 65535, 65535, 0 }; + + gtk_window_set_title (GTK_WINDOW (annot_dialog), _("Annotation Properties")); + gtk_window_set_destroy_with_parent (GTK_WINDOW (annot_dialog), TRUE); + gtk_container_set_border_width (GTK_CONTAINER (annot_dialog), 5); + gtk_dialog_set_has_separator (dialog, FALSE); + gtk_dialog_add_buttons (dialog, + GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, + GTK_STOCK_APPLY, GTK_RESPONSE_APPLY, + NULL); + gtk_dialog_set_default_response (dialog, GTK_RESPONSE_APPLY); + + content_area = gtk_dialog_get_content_area (dialog); + gtk_box_set_spacing (GTK_BOX (content_area), 2); + + table = gtk_table_new (5, 2, FALSE); + annot_dialog->table = table; + gtk_table_set_col_spacings (GTK_TABLE (table), 12); + gtk_table_set_row_spacings (GTK_TABLE (table), 6); + gtk_container_set_border_width (GTK_CONTAINER (table), 12); + gtk_box_pack_start (GTK_BOX (content_area), table, FALSE, FALSE, 0); + gtk_widget_show (table); + + label = gtk_label_new (_("Author:")); + gtk_misc_set_alignment (GTK_MISC (label), 0., 0.5); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1, + GTK_FILL, GTK_FILL, 0, 0); + gtk_widget_show (label); + + annot_dialog->author = gtk_entry_new (); + gtk_entry_set_text (GTK_ENTRY (annot_dialog->author), g_get_real_name ()); + gtk_table_attach (GTK_TABLE (table), annot_dialog->author, + 1, 2, 0, 1, + GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0); + gtk_widget_show (annot_dialog->author); + + label = gtk_label_new (_("Color:")); + gtk_misc_set_alignment (GTK_MISC (label), 0., 0.5); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2, + GTK_FILL, GTK_FILL, 0, 0); + gtk_widget_show (label); + + annot_dialog->color = gtk_color_button_new_with_color (&color); + gtk_table_attach (GTK_TABLE (table), annot_dialog->color, + 1, 2, 1, 2, + GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0); + gtk_widget_show (annot_dialog->color); + + label = gtk_label_new (_("Style:")); + gtk_misc_set_alignment (GTK_MISC (label), 0., 0.5); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, 2, 3, + GTK_FILL, GTK_FILL, 0, 0); + gtk_widget_show (label); + + annot_dialog->opacity = gtk_hscale_new_with_range (0, 100, 5); + gtk_range_set_value (GTK_RANGE (annot_dialog->opacity), 100); + gtk_table_attach (GTK_TABLE (table), annot_dialog->opacity, + 1, 2, 2, 3, + GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0); + gtk_widget_show (annot_dialog->opacity); + + hbox = gtk_hbox_new (FALSE, 6); + + label = gtk_label_new (NULL); + markup = g_strdup_printf ("%s", _("Transparent")); + gtk_label_set_markup (GTK_LABEL (label), markup); + g_free (markup); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + label = gtk_label_new (NULL); + markup = g_strdup_printf ("%s", _("Opaque")); + gtk_label_set_markup (GTK_LABEL (label), markup); + g_free (markup); + gtk_box_pack_end (GTK_BOX (hbox), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + gtk_table_attach (GTK_TABLE (table), hbox, + 1, 2, 3, 4, + GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0); + gtk_widget_show (hbox); + + label = gtk_label_new (_("Initial window state:")); + gtk_misc_set_alignment (GTK_MISC (label), 0., 0.5); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, 4, 5, + GTK_FILL, GTK_FILL, 0, 0); + gtk_widget_show (label); + + annot_dialog->popup_state = gtk_combo_box_new_text (); + gtk_combo_box_append_text (GTK_COMBO_BOX (annot_dialog->popup_state), _("Open")); + gtk_combo_box_append_text (GTK_COMBO_BOX (annot_dialog->popup_state), _("Close")); + gtk_combo_box_set_active (GTK_COMBO_BOX (annot_dialog->popup_state), 1); + gtk_table_attach (GTK_TABLE (table), annot_dialog->popup_state, + 1, 2, 4, 5, + GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0); + gtk_widget_show (annot_dialog->popup_state); +} + +static void +ev_annotation_properties_dialog_class_init (EvAnnotationPropertiesDialogClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = ev_annotation_properties_dialog_dispose; + object_class->constructed = ev_annotation_properties_dialog_constructed; + object_class->set_property = ev_annotation_properties_dialog_set_property; + + g_object_class_install_property (object_class, + PROP_ANNOT_TYPE, + g_param_spec_enum ("annot-type", + "AnnotType", + "The type of annotation", + EV_TYPE_ANNOTATION_TYPE, + EV_ANNOTATION_TYPE_TEXT, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); +} + +GtkWidget * +ev_annotation_properties_dialog_new (EvAnnotationType annot_type) +{ + return GTK_WIDGET (g_object_new (EV_TYPE_ANNOTATION_PROPERTIES_DIALOG, + "annot-type", annot_type, + NULL)); +} + +GtkWidget * +ev_annotation_properties_dialog_new_with_annotation (EvAnnotation *annot) +{ + EvAnnotationPropertiesDialog *dialog; + const gchar *label; + gdouble opacity; + gboolean is_open; + GdkColor color; + + dialog = (EvAnnotationPropertiesDialog *)ev_annotation_properties_dialog_new (ev_annotation_get_annotation_type (annot)); + dialog->annot = g_object_ref (annot); + + label = ev_annotation_markup_get_label (EV_ANNOTATION_MARKUP (annot)); + if (label) + gtk_entry_set_text (GTK_ENTRY (dialog->author), label); + + ev_annotation_get_color (annot, &color); + gtk_color_button_set_color (GTK_COLOR_BUTTON (dialog->color), &color); + + opacity = ev_annotation_markup_get_opacity (EV_ANNOTATION_MARKUP (annot)); + gtk_range_set_value (GTK_RANGE (dialog->opacity), opacity * 100); + + is_open = ev_annotation_markup_get_popup_is_open (EV_ANNOTATION_MARKUP (annot)); + gtk_combo_box_set_active (GTK_COMBO_BOX (dialog->popup_state), + is_open ? 0 : 1); + + if (EV_IS_ANNOTATION_TEXT (annot)) { + EvAnnotationText *annot_text = EV_ANNOTATION_TEXT (annot); + + gtk_combo_box_set_active (GTK_COMBO_BOX (dialog->icon), + ev_annotation_text_get_icon (annot_text)); + } + + return GTK_WIDGET (dialog); +} + +const gchar * +ev_annotation_properties_dialog_get_author (EvAnnotationPropertiesDialog *dialog) +{ + return gtk_entry_get_text (GTK_ENTRY (dialog->author)); +} + +void +ev_annotation_properties_dialog_get_color (EvAnnotationPropertiesDialog *dialog, + GdkColor *color) +{ + gtk_color_button_get_color (GTK_COLOR_BUTTON (dialog->color), color); +} + +gdouble +ev_annotation_properties_dialog_get_opacity (EvAnnotationPropertiesDialog *dialog) +{ + return gtk_range_get_value (GTK_RANGE (dialog->opacity)) / 100; +} + +gboolean +ev_annotation_properties_dialog_get_popup_is_open (EvAnnotationPropertiesDialog *dialog) +{ + return gtk_combo_box_get_active (GTK_COMBO_BOX (dialog->popup_state)) == 0; +} + +EvAnnotationTextIcon +ev_annotation_properties_dialog_get_text_icon (EvAnnotationPropertiesDialog *dialog) +{ + return gtk_combo_box_get_active (GTK_COMBO_BOX (dialog->icon)); +} diff --git a/shell/ev-annotation-properties-dialog.h b/shell/ev-annotation-properties-dialog.h new file mode 100644 index 00000000..e21a8838 --- /dev/null +++ b/shell/ev-annotation-properties-dialog.h @@ -0,0 +1,54 @@ +/* ev-annotation-properties-dialog.h + * this file is part of evince, a gnome document viewer + * + * Copyright (C) 2010 Carlos Garcia Campos + * + * Evince is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Evince is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __EV_ANNOTATION_PROPERTIES_DIALOG_H__ +#define __EV_ANNOTATION_PROPERTIES_DIALOG_H__ + +#include +#include + +#include + +G_BEGIN_DECLS + +#define EV_TYPE_ANNOTATION_PROPERTIES_DIALOG (ev_annotation_properties_dialog_get_type()) +#define EV_ANNOTATION_PROPERTIES_DIALOG(o) (G_TYPE_CHECK_INSTANCE_CAST((o), EV_TYPE_ANNOTATION_PROPERTIES_DIALOG, EvAnnotationPropertiesDialog)) +#define EV_ANNOTATION_PROPERTIES_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), EV_TYPE_ANNOTATION_PROPERTIES_DIALOG, EvAnnotationPropertiesDialogClass)) +#define EV_IS_ANNOTATION_PROPERTIES_DIALOG(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), EV_TYPE_ANNOTATION_PROPERTIES_DIALOG)) +#define EV_IS_ANNOTATION_PROPERTIES_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), EV_TYPE_ANNOTATION_PROPERTIES_DIALOG)) +#define EV_ANNOTATION_PROPERTIES_DIALOG_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), EV_TYPE_ANNOTATION_PROPERTIES_DIALOG, EvAnnotationPropertiesDialogClass)) + +typedef struct _EvAnnotationPropertiesDialog EvAnnotationPropertiesDialog; +typedef struct _EvAnnotationPropertiesDialogClass EvAnnotationPropertiesDialogClass; + +GType ev_annotation_properties_dialog_get_type (void) G_GNUC_CONST; +GtkWidget *ev_annotation_properties_dialog_new (EvAnnotationType annot_type); +GtkWidget *ev_annotation_properties_dialog_new_with_annotation (EvAnnotation *annot); + +const gchar *ev_annotation_properties_dialog_get_author (EvAnnotationPropertiesDialog *dialog); +void ev_annotation_properties_dialog_get_color (EvAnnotationPropertiesDialog *dialog, + GdkColor *color); +gdouble ev_annotation_properties_dialog_get_opacity (EvAnnotationPropertiesDialog *dialog); +gboolean ev_annotation_properties_dialog_get_popup_is_open (EvAnnotationPropertiesDialog *dialog); +EvAnnotationTextIcon ev_annotation_properties_dialog_get_text_icon (EvAnnotationPropertiesDialog *dialog); + +G_END_DECLS + +#endif /* __EV_ANNOTATION_PROPERTIES_DIALOG_H__ */ diff --git a/shell/ev-sidebar-annotations.c b/shell/ev-sidebar-annotations.c index b1f08482..ec544fa5 100644 --- a/shell/ev-sidebar-annotations.c +++ b/shell/ev-sidebar-annotations.c @@ -43,18 +43,25 @@ enum { enum { ANNOT_ACTIVATED, + BEGIN_ANNOT_ADD, + ANNOT_ADD_CANCELLED, N_SIGNALS }; struct _EvSidebarAnnotationsPrivate { - GtkWidget *notebook; - GtkWidget *tree_view; + EvDocument *document; - EvJob *job; - guint selection_changed_id; + GtkWidget *notebook; + GtkWidget *tree_view; + GtkWidget *palette; + GtkToolItem *annot_text_item; + + EvJob *job; + guint selection_changed_id; }; static void ev_sidebar_annotations_page_iface_init (EvSidebarPageInterface *iface); +static void ev_sidebar_annotations_load (EvSidebarAnnotations *sidebar_annots); static guint signals[N_SIGNALS]; @@ -68,6 +75,20 @@ G_DEFINE_TYPE_EXTENDED (EvSidebarAnnotations, #define EV_SIDEBAR_ANNOTATIONS_GET_PRIVATE(object) \ (G_TYPE_INSTANCE_GET_PRIVATE ((object), EV_TYPE_SIDEBAR_ANNOTATIONS, EvSidebarAnnotationsPrivate)) +static void +ev_sidebar_annotations_dispose (GObject *object) +{ + EvSidebarAnnotations *sidebar_annots = EV_SIDEBAR_ANNOTATIONS (object); + EvSidebarAnnotationsPrivate *priv = sidebar_annots->priv; + + if (priv->document) { + g_object_unref (priv->document); + priv->document = NULL; + } + + G_OBJECT_CLASS (ev_sidebar_annotations_parent_class)->dispose (object); +} + static GtkTreeModel * ev_sidebar_annotations_create_simple_model (const gchar *message) { @@ -100,6 +121,7 @@ ev_sidebar_annotations_add_annots_list (EvSidebarAnnotations *ev_annots) GtkCellRenderer *renderer; GtkTreeViewColumn *column; GtkTreeSelection *selection; + GtkWidget *label; swindow = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swindow), @@ -136,8 +158,71 @@ ev_sidebar_annotations_add_annots_list (EvSidebarAnnotations *ev_annots) gtk_container_add (GTK_CONTAINER (swindow), ev_annots->priv->tree_view); gtk_widget_show (ev_annots->priv->tree_view); + label = gtk_label_new (_("List")); + gtk_notebook_append_page (GTK_NOTEBOOK (ev_annots->priv->notebook), + swindow, label); + gtk_widget_show (label); + + gtk_widget_show (swindow); +} + +static void +ev_sidebar_annotations_text_annot_button_toggled (GtkToggleToolButton *toolbutton, + EvSidebarAnnotations *sidebar_annots) +{ + EvAnnotationType annot_type; + + if (!gtk_toggle_tool_button_get_active (toolbutton)) { + g_signal_emit (sidebar_annots, signals[ANNOT_ADD_CANCELLED], 0, NULL); + return; + } + + if (GTK_TOOL_ITEM (toolbutton) == sidebar_annots->priv->annot_text_item) + annot_type = EV_ANNOTATION_TYPE_TEXT; + else + annot_type = EV_ANNOTATION_TYPE_UNKNOWN; + + g_signal_emit (sidebar_annots, signals[BEGIN_ANNOT_ADD], 0, annot_type); +} + +static void +ev_sidebar_annotations_add_annots_palette (EvSidebarAnnotations *ev_annots) +{ + GtkWidget *swindow; + GtkWidget *group; + GtkToolItem *item; + GtkWidget *label; + + swindow = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swindow), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swindow), + GTK_SHADOW_IN); + + ev_annots->priv->palette = gtk_tool_palette_new (); + group = gtk_tool_item_group_new (_("Annotations")); + gtk_container_add (GTK_CONTAINER (ev_annots->priv->palette), group); + + /* FIXME: use a better icon than EDIT */ + item = gtk_toggle_tool_button_new (); + gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (item), GTK_STOCK_EDIT); + gtk_tool_button_set_label (GTK_TOOL_BUTTON (item), _("Text")); + gtk_widget_set_tooltip_text (GTK_WIDGET (item), _("Add text annotation")); + ev_annots->priv->annot_text_item = item; + g_signal_connect (item, "toggled", + G_CALLBACK (ev_sidebar_annotations_text_annot_button_toggled), + ev_annots); + gtk_tool_item_group_insert (GTK_TOOL_ITEM_GROUP (group), item, -1); + gtk_widget_show (GTK_WIDGET (item)); + + gtk_container_add (GTK_CONTAINER (swindow), ev_annots->priv->palette); + gtk_widget_show (ev_annots->priv->palette); + + label = gtk_label_new (_("Add")); gtk_notebook_append_page (GTK_NOTEBOOK (ev_annots->priv->notebook), - swindow, NULL); + swindow, label); + gtk_widget_show (label); + gtk_widget_show (swindow); } @@ -150,6 +235,7 @@ ev_sidebar_annotations_init (EvSidebarAnnotations *ev_annots) gtk_notebook_set_show_tabs (GTK_NOTEBOOK (ev_annots->priv->notebook), FALSE); gtk_notebook_set_show_border (GTK_NOTEBOOK (ev_annots->priv->notebook), FALSE); ev_sidebar_annotations_add_annots_list (ev_annots); + ev_sidebar_annotations_add_annots_palette (ev_annots); gtk_container_add (GTK_CONTAINER (ev_annots), ev_annots->priv->notebook); gtk_widget_show (ev_annots->priv->notebook); } @@ -180,6 +266,7 @@ ev_sidebar_annotations_class_init (EvSidebarAnnotationsClass *klass) GObjectClass *g_object_class = G_OBJECT_CLASS (klass); g_object_class->get_property = ev_sidebar_annotations_get_property; + g_object_class->dispose = ev_sidebar_annotations_dispose; g_type_class_add_private (g_object_class, sizeof (EvSidebarAnnotationsPrivate)); @@ -194,6 +281,24 @@ ev_sidebar_annotations_class_init (EvSidebarAnnotationsClass *klass) g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); + signals[BEGIN_ANNOT_ADD] = + g_signal_new ("begin-annot-add", + G_TYPE_FROM_CLASS (g_object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (EvSidebarAnnotationsClass, begin_annot_add), + NULL, NULL, + g_cclosure_marshal_VOID__ENUM, + G_TYPE_NONE, 1, + EV_TYPE_ANNOTATION_TYPE); + signals[ANNOT_ADD_CANCELLED] = + g_signal_new ("annot-add-cancelled", + G_TYPE_FROM_CLASS (g_object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (EvSidebarAnnotationsClass, annot_add_cancelled), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0, + G_TYPE_NONE); } GtkWidget * @@ -202,6 +307,26 @@ ev_sidebar_annotations_new (void) return GTK_WIDGET (g_object_new (EV_TYPE_SIDEBAR_ANNOTATIONS, NULL)); } +void +ev_sidebar_annotations_annot_added (EvSidebarAnnotations *sidebar_annots, + EvAnnotation *annot) +{ + GtkToggleToolButton *toolbutton; + + if (EV_IS_ANNOTATION_TEXT (annot)) { + toolbutton = GTK_TOGGLE_TOOL_BUTTON (sidebar_annots->priv->annot_text_item); + g_signal_handlers_block_by_func (toolbutton, + ev_sidebar_annotations_text_annot_button_toggled, + sidebar_annots); + gtk_toggle_tool_button_set_active (toolbutton, FALSE); + g_signal_handlers_unblock_by_func (toolbutton, + ev_sidebar_annotations_text_annot_button_toggled, + sidebar_annots); + } + + ev_sidebar_annotations_load (sidebar_annots); +} + static void selection_changed_cb (GtkTreeSelection *selection, EvSidebarAnnotations *sidebar_annots) @@ -278,7 +403,8 @@ job_finished_callback (EvJobAnnots *job, for (ll = ev_mapping_list_get_list (mapping_list); ll; ll = g_list_next (ll)) { EvAnnotation *annot; - gchar *label; + const gchar *label; + const gchar *modified; gchar *markup; GtkTreeIter child_iter; GdkPixbuf *pixbuf = NULL; @@ -288,13 +414,13 @@ job_finished_callback (EvJobAnnots *job, continue; label = ev_annotation_markup_get_label (EV_ANNOTATION_MARKUP (annot)); - if (annot->modified) { + modified = ev_annotation_get_modified (annot); + if (modified) { markup = g_strdup_printf ("%s\n%s", - label, annot->modified); + label, modified); } else { markup = g_strdup_printf ("%s", label); } - g_free (label); if (EV_IS_ANNOTATION_TEXT (annot)) { if (!text_icon) { @@ -342,18 +468,11 @@ job_finished_callback (EvJobAnnots *job, priv->job = NULL; } - static void -ev_sidebar_annotations_document_changed_cb (EvDocumentModel *model, - GParamSpec *pspec, - EvSidebarAnnotations *sidebar_annots) +ev_sidebar_annotations_load (EvSidebarAnnotations *sidebar_annots) { - EvDocument *document = ev_document_model_get_document (model); EvSidebarAnnotationsPrivate *priv = sidebar_annots->priv; - if (!EV_IS_DOCUMENT_ANNOTATIONS (document)) - return; - if (priv->job) { g_signal_handlers_disconnect_by_func (priv->job, job_finished_callback, @@ -361,7 +480,7 @@ ev_sidebar_annotations_document_changed_cb (EvDocumentModel *model, g_object_unref (priv->job); } - priv->job = ev_job_annots_new (document); + priv->job = ev_job_annots_new (priv->document); g_signal_connect (priv->job, "finished", G_CALLBACK (job_finished_callback), sidebar_annots); @@ -369,6 +488,28 @@ ev_sidebar_annotations_document_changed_cb (EvDocumentModel *model, ev_job_scheduler_push_job (priv->job, EV_JOB_PRIORITY_NONE); } +static void +ev_sidebar_annotations_document_changed_cb (EvDocumentModel *model, + GParamSpec *pspec, + EvSidebarAnnotations *sidebar_annots) +{ + EvDocument *document = ev_document_model_get_document (model); + EvSidebarAnnotationsPrivate *priv = sidebar_annots->priv; + gboolean show_tabs; + + if (!EV_IS_DOCUMENT_ANNOTATIONS (document)) + return; + + if (priv->document) + g_object_unref (priv->document); + priv->document = g_object_ref (document); + + show_tabs = ev_document_annotations_can_add_annotation (EV_DOCUMENT_ANNOTATIONS (document)); + gtk_notebook_set_show_tabs (GTK_NOTEBOOK (priv->notebook), show_tabs); + + ev_sidebar_annotations_load (sidebar_annots); +} + /* EvSidebarPageIface */ static void ev_sidebar_annotations_set_model (EvSidebarPage *sidebar_page, diff --git a/shell/ev-sidebar-annotations.h b/shell/ev-sidebar-annotations.h index 8762d787..544fc244 100644 --- a/shell/ev-sidebar-annotations.h +++ b/shell/ev-sidebar-annotations.h @@ -46,13 +46,17 @@ struct _EvSidebarAnnotations { struct _EvSidebarAnnotationsClass { GtkVBoxClass base_class; - void (* annot_activated) (EvSidebarAnnotations *sidebar_annots, - EvMapping *mapping); + void (* annot_activated) (EvSidebarAnnotations *sidebar_annots, + EvMapping *mapping); + void (* begin_annot_add) (EvSidebarAnnotations *sidebar_annots, + EvAnnotationType annot_type); + void (* annot_add_cancelled) (EvSidebarAnnotations *sidebar_annots); }; -GType ev_sidebar_annotations_get_type (void) G_GNUC_CONST; -GtkWidget *ev_sidebar_annotations_new (void); - +GType ev_sidebar_annotations_get_type (void) G_GNUC_CONST; +GtkWidget *ev_sidebar_annotations_new (void); +void ev_sidebar_annotations_annot_added (EvSidebarAnnotations *sidebar_annots, + EvAnnotation *annot); G_END_DECLS #endif /* __EV_SIDEBAR_ANNOTATIONS_H__ */ diff --git a/shell/ev-window.c b/shell/ev-window.c index c7e9c6d8..ece97c74 100644 --- a/shell/ev-window.c +++ b/shell/ev-window.c @@ -95,6 +95,7 @@ #include "ev-window-title.h" #include "ev-print-operation.h" #include "ev-progress-message-area.h" +#include "ev-annotation-properties-dialog.h" #ifdef ENABLE_DBUS #include "ev-media-player-keys.h" @@ -172,9 +173,10 @@ struct _EvWindowPrivate { GtkWidget *fullscreen_toolbar; /* Popup view */ - GtkWidget *view_popup; - EvLink *link; - EvImage *image; + GtkWidget *view_popup; + EvLink *link; + EvImage *image; + EvAnnotation *annot; /* Popup attachment */ GtkWidget *attachment_popup; @@ -317,6 +319,8 @@ static void ev_view_popup_cmd_save_image_as (GtkAction *actio EvWindow *window); static void ev_view_popup_cmd_copy_image (GtkAction *action, EvWindow *window); +static void ev_view_popup_cmd_annot_properties (GtkAction *action, + EvWindow *window); static void ev_attachment_popup_cmd_open_attachment (GtkAction *action, EvWindow *window); static void ev_attachment_popup_cmd_save_attachment_as (GtkAction *action, @@ -3307,6 +3311,8 @@ ev_window_cmd_file_close_window (GtkAction *action, EvWindow *ev_window) ev_document_model_set_page (ev_window->priv->model, current_page); } + /* TODO: warn about form fields, and annots not saved */ + n_print_jobs = ev_window->priv->print_queue ? g_queue_get_length (ev_window->priv->print_queue) : 0; @@ -4536,9 +4542,18 @@ view_menu_annot_popup (EvWindow *ev_window, GtkAction *action; gboolean show_annot = FALSE; + if (ev_window->priv->annot) + g_object_unref (ev_window->priv->annot); + ev_window->priv->annot = (annot) ? g_object_ref (annot) : NULL; + + action = gtk_action_group_get_action (ev_window->priv->view_popup_action_group, + "AnnotProperties"); + gtk_action_set_visible (action, (annot != NULL && EV_IS_ANNOTATION_MARKUP (annot))); + if (annot && EV_IS_ANNOTATION_ATTACHMENT (annot)) { - EvAttachment *attachment = EV_ANNOTATION_ATTACHMENT (annot)->attachment; + EvAttachment *attachment; + attachment = ev_annotation_attachment_get_attachment (EV_ANNOTATION_ATTACHMENT (annot)); if (attachment) { show_annot = TRUE; if (ev_window->priv->attach_list) { @@ -5029,6 +5044,11 @@ ev_window_dispose (GObject *object) priv->image = NULL; } + if (priv->annot) { + g_object_unref (priv->annot); + priv->annot = NULL; + } + if (priv->attach_list) { g_list_foreach (priv->attach_list, (GFunc) g_object_unref, @@ -5338,6 +5358,8 @@ static const GtkActionEntry view_popup_entries [] = { NULL, G_CALLBACK (ev_view_popup_cmd_save_image_as) }, { "CopyImage", NULL, N_("Copy _Image"), NULL, NULL, G_CALLBACK (ev_view_popup_cmd_copy_image) }, + { "AnnotProperties", NULL, N_("Annotation Properties…"), NULL, + NULL, G_CALLBACK (ev_view_popup_cmd_annot_properties) } }; static const GtkActionEntry attachment_popup_entries [] = { @@ -5383,6 +5405,30 @@ sidebar_annots_annot_activated_cb (EvSidebarAnnotations *sidebar_annots, ev_view_focus_annotation (EV_VIEW (window->priv->view), annot_mapping); } +static void +sidebar_annots_begin_annot_add (EvSidebarAnnotations *sidebar_annots, + EvAnnotationType annot_type, + EvWindow *window) +{ + ev_view_begin_add_annotation (EV_VIEW (window->priv->view), annot_type); +} + +static void +view_annot_added (EvView *view, + EvAnnotation *annot, + EvWindow *window) +{ + ev_sidebar_annotations_annot_added (EV_SIDEBAR_ANNOTATIONS (window->priv->sidebar_annots), + annot); +} + +static void +sidebar_annots_annot_add_cancelled (EvSidebarAnnotations *sidebar_annots, + EvWindow *window) +{ + ev_view_cancel_add_annotation (EV_VIEW (window->priv->view)); +} + static void register_custom_actions (EvWindow *window, GtkActionGroup *group) { @@ -5994,6 +6040,67 @@ ev_view_popup_cmd_copy_image (GtkAction *action, EvWindow *window) g_object_unref (pixbuf); } +static void +ev_view_popup_cmd_annot_properties (GtkAction *action, + EvWindow *window) +{ + const gchar *author; + GdkColor color; + gdouble opacity; + gboolean popup_is_open; + EvAnnotationPropertiesDialog *dialog; + EvAnnotation *annot = window->priv->annot; + EvAnnotationsSaveMask mask = EV_ANNOTATIONS_SAVE_NONE; + + if (!annot) + return; + + dialog = EV_ANNOTATION_PROPERTIES_DIALOG (ev_annotation_properties_dialog_new_with_annotation (window->priv->annot)); + if (gtk_dialog_run (GTK_DIALOG (dialog)) != GTK_RESPONSE_APPLY) { + gtk_widget_destroy (GTK_WIDGET (dialog)); + + return; + } + + /* Set annotations changes */ + author = ev_annotation_properties_dialog_get_author (dialog); + if (ev_annotation_markup_set_label (EV_ANNOTATION_MARKUP (annot), author)) + mask |= EV_ANNOTATIONS_SAVE_LABEL; + + ev_annotation_properties_dialog_get_color (dialog, &color); + if (ev_annotation_set_color (annot, &color)) + mask |= EV_ANNOTATIONS_SAVE_COLOR; + + opacity = ev_annotation_properties_dialog_get_opacity (dialog); + if (ev_annotation_markup_set_opacity (EV_ANNOTATION_MARKUP (annot), opacity)) + mask |= EV_ANNOTATIONS_SAVE_OPACITY; + + popup_is_open = ev_annotation_properties_dialog_get_popup_is_open (dialog); + if (ev_annotation_markup_set_popup_is_open (EV_ANNOTATION_MARKUP (annot), popup_is_open)) + mask |= EV_ANNOTATIONS_SAVE_POPUP_IS_OPEN; + + if (EV_IS_ANNOTATION_TEXT (annot)) { + EvAnnotationTextIcon icon; + + icon = ev_annotation_properties_dialog_get_text_icon (dialog); + if (ev_annotation_text_set_icon (EV_ANNOTATION_TEXT (annot), icon)) + mask |= EV_ANNOTATIONS_SAVE_TEXT_ICON; + } + + if (mask != EV_ANNOTATIONS_SAVE_NONE) { + ev_document_doc_mutex_lock (); + ev_document_annotations_save_annotation (EV_DOCUMENT_ANNOTATIONS (window->priv->document), + window->priv->annot, + mask); + ev_document_doc_mutex_unlock (); + + /* FIXME: update annot region only */ + ev_view_reload (EV_VIEW (window->priv->view)); + } + + gtk_widget_destroy (GTK_WIDGET (dialog)); +} + static void ev_attachment_popup_cmd_open_attachment (GtkAction *action, EvWindow *window) { @@ -6553,6 +6660,14 @@ ev_window_init (EvWindow *ev_window) "annot_activated", G_CALLBACK (sidebar_annots_annot_activated_cb), ev_window); + g_signal_connect (sidebar_widget, + "begin_annot_add", + G_CALLBACK (sidebar_annots_begin_annot_add), + ev_window); + g_signal_connect (sidebar_widget, + "annot_add_cancelled", + G_CALLBACK (sidebar_annots_annot_add_cancelled), + ev_window); gtk_widget_show (sidebar_widget); ev_sidebar_add_page (EV_SIDEBAR (ev_window->priv->sidebar), sidebar_widget); @@ -6598,6 +6713,9 @@ ev_window_init (EvWindow *ev_window) g_signal_connect_object (ev_window->priv->view, "selection-changed", G_CALLBACK (view_selection_changed_cb), ev_window, 0); + g_signal_connect_object (ev_window->priv->view, "annot-added", + G_CALLBACK (view_annot_added), + ev_window, 0); #ifdef ENABLE_DBUS g_signal_connect_swapped (ev_window->priv->view, "sync-source", G_CALLBACK (ev_window_sync_source),