From 048d4d7ecbdea03efb4f7ef1d30d87b7be545822 Mon Sep 17 00:00:00 2001 From: raveit65 Date: Thu, 23 Nov 2017 15:41:21 +0100 Subject: Expose links as AtkObject children of the page https://bugzilla.gnome.org/show_bug.cgi?id=728475 origin commit: https://git.gnome.org/browse/evince/commit/?h=gnome-3-14&id=004b199 --- libview/ev-link-accessible.c | 121 ++++++++++++++++++++++++++++++++++++++++++- libview/ev-page-accessible.c | 104 +++++++++++++++++++++++++++++++++++++ 2 files changed, 224 insertions(+), 1 deletion(-) (limited to 'libview') diff --git a/libview/ev-link-accessible.c b/libview/ev-link-accessible.c index d1f91064..a9ee85dc 100644 --- a/libview/ev-link-accessible.c +++ b/libview/ev-link-accessible.c @@ -32,6 +32,8 @@ struct _EvLinkAccessiblePrivate { EvRectangle area; EvHyperlink *hyperlink; + + gchar *name; }; struct _EvHyperlink { @@ -187,10 +189,71 @@ ev_hyperlink_init (EvHyperlink *link) static void ev_link_accessible_hyperlink_impl_iface_init (AtkHyperlinkImplIface *iface); static void ev_link_accessible_action_interface_init (AtkActionIface *iface); +static void ev_link_accessible_component_iface_init (AtkComponentIface *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) - G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION, ev_link_accessible_action_interface_init)) + G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION, ev_link_accessible_action_interface_init) + G_IMPLEMENT_INTERFACE (ATK_TYPE_COMPONENT, ev_link_accessible_component_iface_init)) + +static const gchar * +ev_link_accessible_get_name (AtkObject *atk_object) +{ + EvLinkAccessiblePrivate *priv; + gint start_index; + gint end_index; + + priv = EV_LINK_ACCESSIBLE (atk_object)->priv; + if (priv->name) + return priv->name; + + start_index = ev_hyperlink_get_start_index (ATK_HYPERLINK (priv->hyperlink)); + end_index = ev_hyperlink_get_end_index (ATK_HYPERLINK (priv->hyperlink)); + priv->name = atk_text_get_text (ATK_TEXT (atk_object_get_parent (atk_object)), start_index, end_index); + + return priv->name; +} + +static AtkObject * +ev_link_accessible_get_parent (AtkObject *atk_object) +{ + EvLinkAccessiblePrivate *priv = EV_LINK_ACCESSIBLE (atk_object)->priv; + + return ATK_OBJECT (priv->page); +} + +static AtkStateSet * +ev_link_accessible_ref_state_set (AtkObject *atk_object) +{ + AtkStateSet *state_set; + AtkStateSet *copy_set; + AtkStateSet *page_accessible_state_set; + EvLinkAccessible *self; + EvViewAccessible *view_accessible; + EvView *view; + gint page; + + self = EV_LINK_ACCESSIBLE (atk_object); + state_set = ATK_OBJECT_CLASS (ev_link_accessible_parent_class)->ref_state_set (atk_object); + atk_state_set_clear_states (state_set); + + page_accessible_state_set = atk_object_ref_state_set (ATK_OBJECT (self->priv->page)); + copy_set = atk_state_set_or_sets (state_set, page_accessible_state_set); + + view_accessible = ev_page_accessible_get_view_accessible (self->priv->page); + page = ev_page_accessible_get_page (self->priv->page); + if (!ev_view_accessible_is_doc_rect_showing (view_accessible, page, &self->priv->area)) + atk_state_set_remove_state (copy_set, ATK_STATE_SHOWING); + + view = ev_page_accessible_get_view (self->priv->page); + if (!view->focused_element || view->focused_element->data != self->priv->link) + atk_state_set_remove_state (copy_set, ATK_STATE_FOCUSED); + + g_object_unref (state_set); + g_object_unref (page_accessible_state_set); + + return copy_set; +} static void ev_link_accessible_finalize (GObject *object) @@ -198,6 +261,7 @@ ev_link_accessible_finalize (GObject *object) EvLinkAccessible *link = EV_LINK_ACCESSIBLE (object); g_clear_object (&link->priv->hyperlink); + g_free (link->priv->name); G_OBJECT_CLASS (ev_link_accessible_parent_class)->finalize (object); } @@ -206,15 +270,21 @@ static void ev_link_accessible_class_init (EvLinkAccessibleClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + AtkObjectClass *atk_class = ATK_OBJECT_CLASS (klass); object_class->finalize = ev_link_accessible_finalize; g_type_class_add_private (klass, sizeof (EvLinkAccessiblePrivate)); + + atk_class->get_parent = ev_link_accessible_get_parent; + atk_class->get_name = ev_link_accessible_get_name; + atk_class->ref_state_set = ev_link_accessible_ref_state_set; } static void ev_link_accessible_init (EvLinkAccessible *link) { + atk_object_set_role (ATK_OBJECT (link), ATK_ROLE_LINK); link->priv = G_TYPE_INSTANCE_GET_PRIVATE (link, EV_TYPE_LINK_ACCESSIBLE, EvLinkAccessiblePrivate); } @@ -286,6 +356,55 @@ ev_link_accessible_action_interface_init (AtkActionIface *iface) iface->get_name = ev_link_accessible_action_get_name; } +static void +ev_link_accessible_get_extents (AtkComponent *atk_component, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coord_type) +{ + EvLinkAccessible *self; + EvViewAccessible *view_accessible; + gint page; + EvRectangle atk_rect; + + self = EV_LINK_ACCESSIBLE (atk_component); + view_accessible = ev_page_accessible_get_view_accessible (self->priv->page); + page = ev_page_accessible_get_page (self->priv->page); + _transform_doc_rect_to_atk_rect (view_accessible, page, &self->priv->area, &atk_rect, coord_type); + *x = atk_rect.x1; + *y = atk_rect.y1; + *width = atk_rect.x2 - atk_rect.x1; + *height = atk_rect.y2 - atk_rect.y1; +} + +static gboolean +ev_link_accessible_grab_focus (AtkComponent *atk_component) +{ + EvLinkAccessible *self; + EvView *view; + EvMappingList *link_mapping; + EvMapping *mapping; + gint page; + + self = EV_LINK_ACCESSIBLE (atk_component); + view = ev_page_accessible_get_view (self->priv->page); + page = ev_page_accessible_get_page (self->priv->page); + link_mapping = ev_page_cache_get_link_mapping (view->page_cache, page); + mapping = ev_mapping_list_find (link_mapping, self->priv->link); + _ev_view_set_focused_element (view, mapping, page); + + return TRUE; +} + +static void +ev_link_accessible_component_iface_init (AtkComponentIface *iface) +{ + iface->get_extents = ev_link_accessible_get_extents; + iface->grab_focus = ev_link_accessible_grab_focus; +} + EvLinkAccessible * ev_link_accessible_new (EvPageAccessible *page, EvLink *link, diff --git a/libview/ev-page-accessible.c b/libview/ev-page-accessible.c index 99e970a1..d3c72e3e 100644 --- a/libview/ev-page-accessible.c +++ b/libview/ev-page-accessible.c @@ -31,6 +31,8 @@ struct _EvPageAccessiblePrivate { EvViewAccessible *view_accessible; gint page; GHashTable *links; + GPtrArray *children; + gboolean children_initialized; }; @@ -77,12 +79,87 @@ ev_page_accessible_get_parent (AtkObject *obj) return ATK_OBJECT (self->priv->view_accessible); } +static gint +compare_mappings (EvMapping *a, EvMapping *b) +{ + gdouble dx, dy; + + /* Very rough heuristic for simple, non-tagged PDFs. */ + + dy = a->area.y1 - b->area.y1; + dx = a->area.x1 - b->area.x1; + + return ABS (dy) > 10 ? dy : dx; +} + +static void +ev_page_accessible_initialize_children (EvPageAccessible *self) +{ + EvView *view; + EvMappingList *links; + GList *children = NULL; + GList *list; + + if (self->priv->children_initialized) + return; + + view = ev_page_accessible_get_view (self); + if (!ev_page_cache_is_page_cached (view->page_cache, self->priv->page)) + return; + + self->priv->children_initialized = TRUE; + + links = ev_page_cache_get_link_mapping (view->page_cache, self->priv->page); + if (!links) + return; + + children = g_list_copy (ev_mapping_list_get_list (links)); + + children = g_list_sort (children, (GCompareFunc) compare_mappings); + self->priv->children = g_ptr_array_new_full (g_list_length (children), (GDestroyNotify) g_object_unref); + + for (list = children; list && list->data; list = list->next) { + EvMapping *mapping = list->data; + AtkObject *child = NULL; + + if (links && ev_mapping_list_find (links, mapping->data)) { + EvLinkAccessible *link = ev_link_accessible_new (self, EV_LINK (mapping->data), &mapping->area); + AtkHyperlink *atk_link = atk_hyperlink_impl_get_hyperlink (ATK_HYPERLINK_IMPL (link)); + + child = atk_hyperlink_get_object (atk_link, 0); + } + + if (child) + g_ptr_array_add (self->priv->children, child); + } + + g_list_free (children); +} + +static void +clear_children (EvPageAccessible *self) +{ + gint i; + AtkObject *child; + + if (!self->priv->children) + return; + + for (i = 0; i < self->priv->children->len; i++) { + child = g_ptr_array_index (self->priv->children, i); + atk_object_notify_state_change (child, ATK_STATE_DEFUNCT, TRUE); + } + + g_clear_pointer (&self->priv->children, g_ptr_array_unref); +} + static void ev_page_accessible_finalize (GObject *object) { EvPageAccessiblePrivate *priv = EV_PAGE_ACCESSIBLE (object)->priv; g_clear_pointer (&priv->links, (GDestroyNotify)g_hash_table_destroy); + clear_children (EV_PAGE_ACCESSIBLE (object)); G_OBJECT_CLASS (ev_page_accessible_parent_class)->finalize (object); } @@ -230,6 +307,31 @@ ev_page_accessible_ref_state_set (AtkObject *accessible) return copy_set; } +static gint +ev_page_accessible_get_n_children (AtkObject *accessible) +{ + EvPageAccessible *self; + + self = EV_PAGE_ACCESSIBLE (accessible); + ev_page_accessible_initialize_children (self); + + return self->priv->children == NULL ? 0 : self->priv->children->len; +} + +static AtkObject * +ev_page_accessible_ref_child (AtkObject *accessible, + gint i) +{ + EvPageAccessible *self; + + self = EV_PAGE_ACCESSIBLE (accessible); + ev_page_accessible_initialize_children (self); + + g_return_val_if_fail (i >= 0 || i < self->priv->children->len, NULL); + + return g_object_ref (g_ptr_array_index (self->priv->children, i)); +} + static void ev_page_accessible_class_init (EvPageAccessibleClass *klass) { @@ -241,6 +343,8 @@ ev_page_accessible_class_init (EvPageAccessibleClass *klass) atk_class->get_parent = ev_page_accessible_get_parent; atk_class->ref_relation_set = ev_page_accessible_ref_relation_set; atk_class->ref_state_set = ev_page_accessible_ref_state_set; + atk_class->get_n_children = ev_page_accessible_get_n_children; + atk_class->ref_child = ev_page_accessible_ref_child; g_object_class->get_property = ev_page_accessible_get_property; g_object_class->set_property = ev_page_accessible_set_property; -- cgit v1.2.1