[evince] libview: Implement AtkHypertext interface



commit 6c1d1a25c45e102cabb121a0df2254b2b04aba4c
Author: Carlos Garcia Campos <carlosgc gnome org>
Date:   Fri Jan 18 23:16:38 2013 +0100

    libview: Implement AtkHypertext interface
    
    https://bugzilla.gnome.org/show_bug.cgi?id=639403

 libview/Makefile.am          |    2 +
 libview/ev-link-accessible.c |  260 ++++++++++++++++++++++++++++++++++++++++++
 libview/ev-link-accessible.h |   56 +++++++++
 libview/ev-view-accessible.c |  139 +++++++++++++++++++++--
 4 files changed, 449 insertions(+), 8 deletions(-)
---
diff --git a/libview/Makefile.am b/libview/Makefile.am
index a4b849b..54bf8c0 100644
--- a/libview/Makefile.am
+++ b/libview/Makefile.am
@@ -2,6 +2,7 @@ lib_LTLIBRARIES = libevview3.la
 
 NOINST_H_SRC_FILES =			\
 	ev-annotation-window.h		\
+	ev-link-accessible.h		\
 	ev-page-cache.h			\
 	ev-pixbuf-cache.h		\
 	ev-timeline.h			\
@@ -34,6 +35,7 @@ libevview3_la_SOURCES =			\
 	ev-document-model.c		\
 	ev-jobs.c			\
 	ev-job-scheduler.c		\
+	ev-link-accessible.c		\
 	ev-page-cache.c			\
 	ev-pixbuf-cache.c		\
 	ev-print-operation.c	        \
diff --git a/libview/ev-link-accessible.c b/libview/ev-link-accessible.c
new file mode 100644
index 0000000..76a403b
--- /dev/null
+++ b/libview/ev-link-accessible.c
@@ -0,0 +1,260 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
+/* this file is part of evince, a gnome document viewer
+ *
+ *  Copyright (C) 2013 Carlos Garcia Campos <carlosgc gnome org>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <config.h>
+
+#include "ev-link-accessible.h"
+#include "ev-view-private.h"
+
+typedef struct _EvHyperlink      EvHyperlink;
+typedef struct _EvHyperlinkClass EvHyperlinkClass;
+
+struct _EvLinkAccessiblePrivate {
+        EvViewAccessible *view;
+        EvLink           *link;
+        EvRectangle       area;
+
+        EvHyperlink      *hyperlink;
+};
+
+struct _EvHyperlink {
+        AtkHyperlink parent;
+
+        EvLinkAccessible *link_impl;
+};
+
+struct _EvHyperlinkClass {
+        AtkHyperlinkClass parent_class;
+};
+
+#define EV_TYPE_HYPERLINK (ev_hyperlink_get_type ())
+#define EV_HYPERLINK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EV_TYPE_HYPERLINK, EvHyperlink))
+
+static GType ev_hyperlink_get_type              (void);
+
+G_DEFINE_TYPE (EvHyperlink, ev_hyperlink, ATK_TYPE_HYPERLINK)
+
+static gchar *
+ev_hyperlink_get_uri (AtkHyperlink *atk_hyperlink,
+                      gint          i)
+{
+        EvHyperlink             *hyperlink = EV_HYPERLINK (atk_hyperlink);
+        EvLinkAccessiblePrivate *impl_priv;
+        EvLinkAction            *action;
+
+        if (!hyperlink->link_impl)
+                return NULL;
+
+        impl_priv = hyperlink->link_impl->priv;
+        action = ev_link_get_action (impl_priv->link);
+
+        return action ? g_strdup (ev_link_action_get_uri (action)) : NULL;
+}
+
+static gint
+ev_hyperlink_get_n_anchors (AtkHyperlink *atk_hyperlink)
+{
+        return 1;
+}
+
+static gboolean
+ev_hyperlink_is_valid (AtkHyperlink *atk_hyperlink)
+{
+        return TRUE;
+}
+
+static AtkObject *
+ev_hyperlink_get_object (AtkHyperlink *atk_hyperlink,
+                         gint          i)
+{
+        EvHyperlink *hyperlink = EV_HYPERLINK (atk_hyperlink);
+
+        return hyperlink->link_impl ? ATK_OBJECT (hyperlink->link_impl) : NULL;
+}
+
+static gint
+ev_hyperlink_get_start_index (AtkHyperlink *atk_hyperlink)
+{
+        EvHyperlink             *hyperlink = EV_HYPERLINK (atk_hyperlink);
+        EvLinkAccessiblePrivate *impl_priv;
+        GtkWidget               *widget;
+        EvView                  *view;
+        EvRectangle             *areas = NULL;
+        guint                    n_areas = 0;
+        guint                    i;
+
+        if (!hyperlink->link_impl)
+                return -1;
+
+        impl_priv = hyperlink->link_impl->priv;
+        widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (impl_priv->view));
+        if (widget == NULL)
+                /* State is defunct */
+                return -1;
+
+        view = EV_VIEW (widget);
+        if (!view->page_cache)
+                return -1;
+
+        ev_page_cache_get_text_layout (view->page_cache, view->current_page, &areas, &n_areas);
+        if (!areas)
+                return -1;
+
+        for (i = 0; i < n_areas; i++) {
+                EvRectangle *rect = areas + i;
+                gdouble      c_x, c_y;
+
+                c_x = rect->x1 + (rect->x2 - rect->x1) / 2.;
+                c_y = rect->y1 + (rect->y2 - rect->y1) / 2.;
+                if (c_x >= impl_priv->area.x1 && c_x <= impl_priv->area.x2 &&
+                    c_y >= impl_priv->area.y1 && c_y <= impl_priv->area.y2)
+                        return i;
+        }
+
+        return -1;
+}
+
+static gint
+ev_hyperlink_get_end_index (AtkHyperlink *atk_hyperlink)
+{
+        EvHyperlink             *hyperlink = EV_HYPERLINK (atk_hyperlink);
+        EvLinkAccessiblePrivate *impl_priv;
+        GtkWidget               *widget;
+        EvView                  *view;
+        EvRectangle             *areas = NULL;
+        guint                    n_areas = 0;
+        guint                    i;
+
+        if (!hyperlink->link_impl)
+                return -1;
+
+        impl_priv = hyperlink->link_impl->priv;
+        widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (impl_priv->view));
+        if (widget == NULL)
+                /* State is defunct */
+                return -1;
+
+        view = EV_VIEW (widget);
+        if (!view->page_cache)
+                return -1;
+
+        ev_page_cache_get_text_layout (view->page_cache, view->current_page, &areas, &n_areas);
+        if (!areas)
+                return -1;
+
+        for (i = n_areas - 1; i >= 0; i--) {
+                EvRectangle *rect = areas + i;
+                gdouble      c_x, c_y;
+
+                c_x = rect->x1 + (rect->x2 - rect->x1) / 2.;
+                c_y = rect->y1 + (rect->y2 - rect->y1) / 2.;
+                if (c_x >= impl_priv->area.x1 && c_x <= impl_priv->area.x2 &&
+                    c_y >= impl_priv->area.y1 && c_y <= impl_priv->area.y2)
+                        return i;
+        }
+
+        return -1;
+}
+
+static void
+ev_hyperlink_class_init (EvHyperlinkClass *klass)
+{
+        AtkHyperlinkClass *atk_link_class = ATK_HYPERLINK_CLASS (klass);
+
+        atk_link_class->get_uri = ev_hyperlink_get_uri;
+        atk_link_class->get_n_anchors = ev_hyperlink_get_n_anchors;
+        atk_link_class->is_valid = ev_hyperlink_is_valid;
+        atk_link_class->get_object = ev_hyperlink_get_object;
+        atk_link_class->get_start_index = ev_hyperlink_get_start_index;
+        atk_link_class->get_end_index = ev_hyperlink_get_end_index;
+}
+
+static void
+ev_hyperlink_init (EvHyperlink *link)
+{
+}
+
+static void ev_link_accessible_hyperlink_impl_iface_init (AtkHyperlinkImplIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (EvLinkAccessible, ev_link_accessible, ATK_TYPE_OBJECT,
+			 G_IMPLEMENT_INTERFACE (ATK_TYPE_HYPERLINK_IMPL, ev_link_accessible_hyperlink_impl_iface_init))
+
+static void
+ev_link_accessible_finalize (GObject *object)
+{
+        EvLinkAccessible *link = EV_LINK_ACCESSIBLE (object);
+
+        g_clear_object (&link->priv->hyperlink);
+
+        G_OBJECT_CLASS (ev_link_accessible_parent_class)->finalize (object);
+}
+
+static void
+ev_link_accessible_class_init (EvLinkAccessibleClass *klass)
+{
+        GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+        object_class->finalize = ev_link_accessible_finalize;
+
+        g_type_class_add_private (klass, sizeof (EvLinkAccessiblePrivate));
+}
+
+static void
+ev_link_accessible_init (EvLinkAccessible *link)
+{
+        link->priv = G_TYPE_INSTANCE_GET_PRIVATE (link, EV_TYPE_LINK_ACCESSIBLE, EvLinkAccessiblePrivate);
+}
+
+static AtkHyperlink *
+ev_link_accessible_get_hyperlink (AtkHyperlinkImpl *hyperlink_impl)
+{
+        EvLinkAccessible *link = EV_LINK_ACCESSIBLE (hyperlink_impl);
+
+        if (link->priv->hyperlink)
+                return ATK_HYPERLINK (link->priv->hyperlink);
+
+        link->priv->hyperlink = g_object_new (EV_TYPE_HYPERLINK, NULL);
+
+        link->priv->hyperlink->link_impl = link;
+        g_object_add_weak_pointer (G_OBJECT (link), (gpointer *)&link->priv->hyperlink->link_impl);
+
+        return ATK_HYPERLINK (link->priv->hyperlink);
+}
+
+static void
+ev_link_accessible_hyperlink_impl_iface_init (AtkHyperlinkImplIface *iface)
+{
+        iface->get_hyperlink = ev_link_accessible_get_hyperlink;
+}
+
+EvLinkAccessible *
+ev_link_accessible_new (EvViewAccessible *view,
+                        EvLink           *link,
+                        EvRectangle      *area)
+{
+        EvLinkAccessible *atk_link;
+
+        atk_link = g_object_new (EV_TYPE_LINK_ACCESSIBLE, NULL);
+        atk_link->priv->view = view;
+        atk_link->priv->link = g_object_ref (link);
+        atk_link->priv->area = *area;
+
+        return EV_LINK_ACCESSIBLE (atk_link);
+}
diff --git a/libview/ev-link-accessible.h b/libview/ev-link-accessible.h
new file mode 100644
index 0000000..169ff37
--- /dev/null
+++ b/libview/ev-link-accessible.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
+/* this file is part of evince, a gnome document viewer
+ *
+ *  Copyright (C) 2013 Carlos Garcia Campos <carlosgc gnome org>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#if !defined (EVINCE_COMPILATION)
+#error "This is a private header."
+#endif
+
+#ifndef __EV_LINK_ACCESSIBLE_H__
+#define __EV_LINK_ACCESSIBLE_H__
+
+#include <gtk/gtk-a11y.h>
+#include "ev-view-accessible.h"
+#include "ev-link.h"
+
+#define EV_TYPE_LINK_ACCESSIBLE      (ev_link_accessible_get_type ())
+#define EV_LINK_ACCESSIBLE(obj)      (G_TYPE_CHECK_INSTANCE_CAST ((obj), EV_TYPE_LINK_ACCESSIBLE, EvLinkAccessible))
+#define EV_IS_LINK_ACCESSIBLE(obj)   (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EV_TYPE_LINK_ACCESSIBLE))
+
+typedef struct _EvLinkAccessible        EvLinkAccessible;
+typedef struct _EvLinkAccessibleClass   EvLinkAccessibleClass;
+typedef struct _EvLinkAccessiblePrivate EvLinkAccessiblePrivate;
+
+struct _EvLinkAccessible {
+        AtkObject parent;
+
+        EvLinkAccessiblePrivate *priv;
+};
+
+struct _EvLinkAccessibleClass {
+        AtkObjectClass parent_class;
+};
+
+GType             ev_link_accessible_get_type (void);
+EvLinkAccessible *ev_link_accessible_new      (EvViewAccessible *view,
+                                               EvLink           *link,
+                                               EvRectangle      *area);
+
+#endif  /* __EV_LINK_ACCESSIBLE_H__ */
+
diff --git a/libview/ev-view-accessible.c b/libview/ev-view-accessible.c
index c77ad28..35f92bd 100644
--- a/libview/ev-view-accessible.c
+++ b/libview/ev-view-accessible.c
@@ -27,10 +27,12 @@
 #include "ev-selection.h"
 #include "ev-page-cache.h"
 #include "ev-view-accessible.h"
+#include "ev-link-accessible.h"
 #include "ev-view-private.h"
 
-static void ev_view_accessible_text_iface_init   (AtkTextIface *iface);
-static void ev_view_accessible_action_iface_init (AtkActionIface *iface);
+static void ev_view_accessible_text_iface_init      (AtkTextIface      *iface);
+static void ev_view_accessible_action_iface_init    (AtkActionIface    *iface);
+static void ev_view_accessible_hypertext_iface_init (AtkHypertextIface *iface);
 
 enum {
 	ACTION_SCROLL_UP,
@@ -53,17 +55,24 @@ static const gchar *const ev_view_accessible_action_descriptions[] =
 };
 
 struct _EvViewAccessiblePrivate {
-	/* Action */
-	gchar *action_descriptions[LAST_ACTION];
-	guint action_idle_handler;
+	guint         current_page;
+
+	/* AtkAction */
+	gchar        *action_descriptions[LAST_ACTION];
+	guint         action_idle_handler;
 	GtkScrollType idle_scroll;
+
+	/* AtkText */
 	GtkTextBuffer *buffer;
-	guint current_page;
+
+	/* AtkHypertext */
+	GHashTable    *links;
 };
 
 G_DEFINE_TYPE_WITH_CODE (EvViewAccessible, ev_view_accessible, GTK_TYPE_CONTAINER_ACCESSIBLE,
-			 G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT, ev_view_accessible_text_iface_init);
-			 G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION, ev_view_accessible_action_iface_init);
+			 G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT, ev_view_accessible_text_iface_init)
+			 G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION, ev_view_accessible_action_iface_init)
+			 G_IMPLEMENT_INTERFACE (ATK_TYPE_HYPERTEXT, ev_view_accessible_hypertext_iface_init)
 	)
 
 static void
@@ -78,7 +87,10 @@ ev_view_accessible_finalize (GObject *object)
 		g_free (priv->action_descriptions [i]);
 	if (priv->buffer)
 		g_object_unref (priv->buffer);
+	if (priv->links)
+		g_hash_table_destroy (priv->links);
 
+	G_OBJECT_CLASS (ev_view_accessible_parent_class)->finalize (object);
 }
 
 static void
@@ -810,6 +822,117 @@ static void ev_view_accessible_action_iface_init (AtkActionIface * iface)
 	iface->set_description = ev_view_accessible_action_set_description;
 }
 
+static GHashTable *
+ev_view_accessible_get_links (EvViewAccessible *accessible,
+			      EvView           *view)
+{
+	EvViewAccessiblePrivate* priv = accessible->priv;
+
+	if (view->current_page == priv->current_page && priv->links)
+		return priv->links;
+
+	priv->current_page = view->current_page;
+
+	if (priv->links)
+		g_hash_table_destroy (priv->links);
+	priv->links = g_hash_table_new_full (g_direct_hash,
+					     g_direct_equal,
+					     NULL,
+					     (GDestroyNotify)g_object_unref);
+	return priv->links;
+}
+
+static AtkHyperlink *
+ev_view_accessible_get_link (AtkHypertext *hypertext,
+			     gint          link_index)
+{
+	GtkWidget        *widget;
+	EvView           *view;
+	GHashTable       *links;
+	EvMappingList    *link_mapping;
+	gint              n_links;
+	EvMapping        *mapping;
+	EvLinkAccessible *atk_link;
+
+	widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (hypertext));
+	if (widget == NULL)
+		/* State is defunct */
+		return NULL;
+
+	view = EV_VIEW (widget);
+	if (!EV_IS_DOCUMENT_LINKS (view->document))
+		return NULL;
+
+	links = ev_view_accessible_get_links (EV_VIEW_ACCESSIBLE (hypertext), view);
+
+	atk_link = g_hash_table_lookup (links, GINT_TO_POINTER (link_index));
+	if (atk_link)
+		return atk_hyperlink_impl_get_hyperlink (ATK_HYPERLINK_IMPL (atk_link));
+
+	link_mapping = ev_page_cache_get_link_mapping (view->page_cache, view->current_page);
+	if (!link_mapping)
+		return NULL;
+
+	n_links = ev_mapping_list_length (link_mapping);
+	mapping = ev_mapping_list_nth (link_mapping, n_links - link_index - 1);
+	atk_link = ev_link_accessible_new (EV_VIEW_ACCESSIBLE (hypertext),
+					   EV_LINK (mapping->data),
+					   &mapping->area);
+	g_hash_table_insert (links, GINT_TO_POINTER (link_index), atk_link);
+
+	return atk_hyperlink_impl_get_hyperlink (ATK_HYPERLINK_IMPL (atk_link));
+}
+
+static gint
+ev_view_accessible_get_n_links (AtkHypertext *hypertext)
+{
+	GtkWidget     *widget;
+	EvView        *view;
+	EvMappingList *link_mapping;
+
+	widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (hypertext));
+	if (widget == NULL)
+		/* State is defunct */
+		return 0;
+
+	view = EV_VIEW (widget);
+	if (!EV_IS_DOCUMENT_LINKS (view->document))
+		return 0;
+
+	link_mapping = ev_page_cache_get_link_mapping (view->page_cache, view->current_page);
+
+	return link_mapping ? ev_mapping_list_length (link_mapping) : 0;
+}
+
+static gint
+ev_view_accessible_get_link_index (AtkHypertext *hypertext,
+				   gint          offset)
+{
+	guint i;
+
+	for (i = 0; i < ev_view_accessible_get_n_links (hypertext); i++) {
+		AtkHyperlink *hyperlink;
+		gint          start_index, end_index;
+
+		hyperlink = ev_view_accessible_get_link (hypertext, i);
+		start_index = atk_hyperlink_get_start_index (hyperlink);
+		end_index = atk_hyperlink_get_end_index (hyperlink);
+
+		if (start_index <= offset && end_index >= offset)
+			return i;
+	}
+
+	return -1;
+}
+
+static void
+ev_view_accessible_hypertext_iface_init (AtkHypertextIface *iface)
+{
+	iface->get_link = ev_view_accessible_get_link;
+	iface->get_n_links = ev_view_accessible_get_n_links;
+	iface->get_link_index = ev_view_accessible_get_link_index;
+}
+
 AtkObject *
 ev_view_accessible_new (GtkWidget *widget)
 {



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]