diff options
-rw-r--r-- | libview/Makefile.am | 4 | ||||
-rw-r--r-- | libview/ev-link-accessible.c | 260 | ||||
-rw-r--r-- | libview/ev-link-accessible.h | 56 | ||||
-rw-r--r-- | libview/ev-view-accessible.c | 139 |
4 files changed, 450 insertions, 9 deletions
diff --git a/libview/Makefile.am b/libview/Makefile.am index 252f9f0e..042016e2 100644 --- a/libview/Makefile.am +++ b/libview/Makefile.am @@ -1,7 +1,8 @@ lib_LTLIBRARIES = libatrilview.la NOINST_H_FILES = \ - ev-annotation-window.h \ + ev-annotation-window.h \ + ev-link-accessible.h \ ev-loading-window.h \ ev-page-cache.h \ ev-pixbuf-cache.h \ @@ -35,6 +36,7 @@ libatrilview_la_SOURCES = \ ev-loading-window.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 00000000..9e2de642 --- /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 atril, a mate document viewer + * + * Copyright (C) 2013 Carlos Garcia Campos <[email protected]> + * + * 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 00000000..899514cf --- /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 atril, a mate document viewer + * + * Copyright (C) 2013 Carlos Garcia Campos <[email protected]> + * + * 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 (ATRIL_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 5a165bd2..e7feda22 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) { |