summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlejandro PiƱeiro <[email protected]>2014-04-07 17:50:49 +0200
committerraveit65 <[email protected]>2017-11-27 10:13:51 +0100
commitd9b37d653859acc035b8dd9f5f80b58f781ee065 (patch)
tree848b623afaa054449fb2a0a5f0f2b217ac53a5c7
parent148f8e9faef247824949675333f75b3f3a9b7663 (diff)
downloadatril-d9b37d653859acc035b8dd9f5f80b58f781ee065.tar.bz2
atril-d9b37d653859acc035b8dd9f5f80b58f781ee065.tar.xz
a11y: move AtkText implementation from EvViewAccessible to EvPageAccessible
https://bugzilla.gnome.org/show_bug.cgi?id=724965 origin commit: https://git.gnome.org/browse/evince/commit/?h=gnome-3-14&id=2a458a9
-rw-r--r--libview/ev-page-accessible.c685
-rw-r--r--libview/ev-page-accessible.h2
-rw-r--r--libview/ev-view-accessible.c746
3 files changed, 695 insertions, 738 deletions
diff --git a/libview/ev-page-accessible.c b/libview/ev-page-accessible.c
index 83bb80d8..fa1f3e02 100644
--- a/libview/ev-page-accessible.c
+++ b/libview/ev-page-accessible.c
@@ -22,7 +22,9 @@
#include <config.h>
+#include <glib/gi18n-lib.h>
#include "ev-page-accessible.h"
+#include "ev-view-private.h"
struct _EvPageAccessiblePrivate {
EvViewAccessible *view_accessible;
@@ -35,8 +37,10 @@ enum {
PROP_PAGE,
};
+static void ev_page_accessible_text_iface_init (AtkTextIface *iface);
-G_DEFINE_TYPE (EvPageAccessible, ev_page_accessible, ATK_TYPE_OBJECT)
+G_DEFINE_TYPE_WITH_CODE (EvPageAccessible, ev_page_accessible, ATK_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT, ev_page_accessible_text_iface_init))
gint
ev_page_accessible_get_page (EvPageAccessible *page_accessible)
@@ -140,6 +144,666 @@ ev_page_accessible_class_init (EvPageAccessibleClass *klass)
}
+EvView *
+ev_page_accessible_get_view (EvPageAccessible *page_accessible)
+{
+ g_return_val_if_fail (EV_IS_PAGE_ACCESSIBLE (page_accessible), NULL);
+
+ return EV_VIEW (gtk_accessible_get_widget (GTK_ACCESSIBLE (page_accessible->priv->view_accessible)));
+}
+
+/* ATs expect to be able to identify sentence boundaries based on content. Valid,
+ * content-based boundaries may be present at the end of a newline, for instance
+ * at the end of a heading within a document. Thus being able to distinguish hard
+ * returns from soft returns is necessary. However, the text we get from Poppler
+ * for non-tagged PDFs has "\n" inserted at the end of each line resulting in a
+ * broken accessibility implementation w.r.t. sentences.
+ */
+static gboolean
+treat_as_soft_return (EvView *view,
+ gint page,
+ PangoLogAttr *log_attrs,
+ gint offset)
+{
+ EvRectangle *areas = NULL;
+ guint n_areas = 0;
+ gdouble line_spacing, this_line_height, next_word_width;
+ EvRectangle *this_line_start;
+ EvRectangle *this_line_end;
+ EvRectangle *next_line_start;
+ EvRectangle *next_line_end;
+ EvRectangle *next_word_end;
+ gint prev_offset, next_offset;
+
+
+ if (!log_attrs[offset].is_white)
+ return FALSE;
+
+ ev_page_cache_get_text_layout (view->page_cache, page, &areas, &n_areas);
+ if (n_areas <= offset + 1)
+ return FALSE;
+
+ prev_offset = offset - 1;
+ next_offset = offset + 1;
+
+ /* In wrapped text, the character at the start of the next line starts a word.
+ * Examples where this condition might fail include bullets and images. But it
+ * also includes things like "(", so also check the next character.
+ */
+ if (!log_attrs[next_offset].is_word_start &&
+ (next_offset + 1 >= n_areas || !log_attrs[next_offset + 1].is_word_start))
+ return FALSE;
+
+ /* In wrapped text, the chars on either side of the newline have very similar heights.
+ * Examples where this condition might fail include a newline at the end of a heading,
+ * and a newline at the end of a paragraph that is followed by a heading.
+ */
+ this_line_end = areas + prev_offset;
+ next_line_start = areas + next_offset;;
+
+ this_line_height = this_line_end->y2 - this_line_end->y1;
+ if (ABS (this_line_height - (next_line_start->y2 - next_line_start->y1)) > 0.25)
+ return FALSE;
+
+ /* If there is significant white space between this line and the next, odds are this
+ * is not a soft return in wrapped text. Lines within a typical paragraph are at most
+ * double-spaced. If the spacing is more than that, assume a hard return is present.
+ */
+ line_spacing = next_line_start->y1 - this_line_end->y2;
+ if (line_spacing - this_line_height > 1)
+ return FALSE;
+
+ /* Lines within a typical paragraph have *reasonably* similar x1 coordinates. But
+ * we cannot count on them being nearly identical. Examples where indentation can
+ * be present in wrapped text include indenting the first line of the paragraph,
+ * and hanging indents (e.g. in the works cited within an academic paper). So we'll
+ * be somewhat tolerant here.
+ */
+ for ( ; prev_offset > 0 && !log_attrs[prev_offset].is_mandatory_break; prev_offset--);
+ this_line_start = areas + prev_offset;
+ if (ABS (this_line_start->x1 - next_line_start->x1) > 20)
+ return FALSE;
+
+ /* Ditto for x2, but this line might be short due to a wide word on the next line. */
+ for ( ; next_offset < n_areas && !log_attrs[next_offset].is_word_end; next_offset++);
+ next_word_end = areas + next_offset;
+ next_word_width = next_word_end->x2 - next_line_start->x1;
+
+ for ( ; next_offset < n_areas && !log_attrs[next_offset + 1].is_mandatory_break; next_offset++);
+ next_line_end = areas + next_offset;
+ if (next_line_end->x2 - (this_line_end->x2 + next_word_width) > 20)
+ return FALSE;
+
+ return TRUE;
+}
+
+static gchar *
+ev_page_accessible_get_substring (AtkText *text,
+ gint start_offset,
+ gint end_offset)
+{
+ EvPageAccessible *self = EV_PAGE_ACCESSIBLE (text);
+ EvView *view = ev_page_accessible_get_view (self);
+ gchar *substring, *normalized;
+ const gchar* page_text;
+
+ if (!view->page_cache)
+ return NULL;
+
+ page_text = ev_page_cache_get_text (view->page_cache, self->priv->page);
+ start_offset = MAX (0, start_offset);
+ if (end_offset < 0 || end_offset > g_utf8_strlen (page_text, -1))
+ end_offset = strlen (page_text);
+
+ substring = g_utf8_substring (page_text, start_offset, end_offset);
+ normalized = g_utf8_normalize (substring, -1, G_NORMALIZE_NFKC);
+ g_free (substring);
+
+ return normalized;
+}
+
+static gchar *
+ev_page_accessible_get_text (AtkText *text,
+ gint start_pos,
+ gint end_pos)
+{
+ return ev_page_accessible_get_substring (text, start_pos, end_pos);
+}
+
+static gunichar
+ev_page_accessible_get_character_at_offset (AtkText *text,
+ gint offset)
+{
+ gchar *string;
+ gunichar unichar;
+
+ string = ev_page_accessible_get_substring (text, offset, offset + 1);
+ unichar = g_utf8_get_char (string);
+ g_free(string);
+
+ return unichar;
+}
+
+static void
+ev_page_accessible_get_range_for_boundary (AtkText *text,
+ AtkTextBoundary boundary_type,
+ gint offset,
+ gint *start_offset,
+ gint *end_offset)
+{
+ EvPageAccessible *self = EV_PAGE_ACCESSIBLE (text);
+ EvView *view = ev_page_accessible_get_view (self);
+ gint start = 0;
+ gint end = 0;
+ PangoLogAttr *log_attrs = NULL;
+ gulong n_attrs;
+
+ if (!view->page_cache)
+ return;
+
+ ev_page_cache_get_text_log_attrs (view->page_cache, self->priv->page, &log_attrs, &n_attrs);
+ if (!log_attrs)
+ return;
+
+ switch (boundary_type) {
+ case ATK_TEXT_BOUNDARY_CHAR:
+ start = offset;
+ end = offset + 1;
+ break;
+ case ATK_TEXT_BOUNDARY_WORD_START:
+ for (start = offset; start >= 0 && !log_attrs[start].is_word_start; start--);
+ for (end = offset + 1; end <= n_attrs && !log_attrs[end].is_word_start; end++);
+ break;
+ case ATK_TEXT_BOUNDARY_SENTENCE_START:
+ for (start = offset; start >= 0; start--) {
+ if (log_attrs[start].is_mandatory_break && treat_as_soft_return (view, self->priv->page, log_attrs, start - 1))
+ continue;
+ if (log_attrs[start].is_sentence_start)
+ break;
+ }
+ for (end = offset + 1; end <= n_attrs; end++) {
+ if (log_attrs[end].is_mandatory_break && treat_as_soft_return (view, self->priv->page, log_attrs, end - 1))
+ continue;
+ if (log_attrs[end].is_sentence_start)
+ break;
+ }
+ break;
+ case ATK_TEXT_BOUNDARY_LINE_START:
+ for (start = offset; start >= 0 && !log_attrs[start].is_mandatory_break; start--);
+ for (end = offset + 1; end <= n_attrs && !log_attrs[end].is_mandatory_break; end++);
+ break;
+ default:
+ /* The "END" boundary types are deprecated */
+ break;
+ }
+
+ *start_offset = start;
+ *end_offset = end;
+}
+
+static gchar *
+ev_page_accessible_get_text_at_offset (AtkText *text,
+ gint offset,
+ AtkTextBoundary boundary_type,
+ gint *start_offset,
+ gint *end_offset)
+{
+ gchar *retval;
+
+ ev_page_accessible_get_range_for_boundary (text, boundary_type, offset, start_offset, end_offset);
+ retval = ev_page_accessible_get_substring (text, *start_offset, *end_offset);
+
+ /* If newlines appear inside the text of a sentence (i.e. between the start and
+ * end offsets returned by ev_page_accessible_get_substring), it interferes with
+ * the prosody of text-to-speech based-solutions such as a screen reader because
+ * speech synthesizers tend to pause after the newline char as if it were the end
+ * of the sentence.
+ */
+ if (boundary_type == ATK_TEXT_BOUNDARY_SENTENCE_START)
+ g_strdelimit (retval, "\n", ' ');
+
+ return retval;
+}
+
+static gint
+ev_page_accessible_get_caret_offset (AtkText *text)
+{
+ EvPageAccessible *self = EV_PAGE_ACCESSIBLE (text);
+ EvView *view = ev_page_accessible_get_view (self);
+
+ if (self->priv->page == view->cursor_page && view->caret_enabled)
+ return view->cursor_offset;
+
+ return -1;
+}
+
+static gboolean
+ev_page_accessible_set_caret_offset (AtkText *text, gint offset)
+{
+ EvPageAccessible *self = EV_PAGE_ACCESSIBLE (text);
+ EvView *view = ev_page_accessible_get_view (self);
+
+ ev_view_set_caret_cursor_position (view,
+ self->priv->page,
+ offset);
+
+ return TRUE;
+}
+
+static gint
+ev_page_accessible_get_character_count (AtkText *text)
+{
+ EvPageAccessible *self = EV_PAGE_ACCESSIBLE (text);
+ EvView *view = ev_page_accessible_get_view (self);
+ gint retval;
+
+ retval = g_utf8_strlen (ev_page_cache_get_text (view->page_cache, self->priv->page), -1);
+
+ return retval;
+}
+
+static gboolean
+get_selection_bounds (EvView *view,
+ EvViewSelection *selection,
+ gint *start_offset,
+ gint *end_offset)
+{
+ cairo_rectangle_int_t rect;
+ gint start, end;
+
+ if (!selection->covered_region || cairo_region_is_empty (selection->covered_region))
+ return FALSE;
+
+ cairo_region_get_rectangle (selection->covered_region, 0, &rect);
+ start = _ev_view_get_caret_cursor_offset_at_doc_point (view,
+ selection->page,
+ rect.x / view->scale,
+ (rect.y + (rect.height / 2)) / view->scale);
+ if (start == -1)
+ return FALSE;
+
+ cairo_region_get_rectangle (selection->covered_region,
+ cairo_region_num_rectangles (selection->covered_region) - 1,
+ &rect);
+ end = _ev_view_get_caret_cursor_offset_at_doc_point (view,
+ selection->page,
+ (rect.x + rect.width) / view->scale,
+ (rect.y + (rect.height / 2)) / view->scale);
+ if (end == -1)
+ return FALSE;
+
+ *start_offset = start;
+ *end_offset = end;
+
+ return TRUE;
+}
+
+static gint
+ev_page_accessible_get_n_selections (AtkText *text)
+{
+ EvPageAccessible *self = EV_PAGE_ACCESSIBLE (text);
+ EvView *view = ev_page_accessible_get_view (self);
+ gint n_selections = 0;
+ GList *l;
+
+ if (!EV_IS_SELECTION (view->document) || !view->selection_info.selections)
+ return 0;
+
+ for (l = view->selection_info.selections; l != NULL; l = l->next) {
+ EvViewSelection *selection = (EvViewSelection *)l->data;
+
+ if (selection->page != self->priv->page)
+ continue;
+
+ n_selections = 1;
+ break;
+ }
+
+ return n_selections;
+}
+
+static gchar *
+ev_page_accessible_get_selection (AtkText *text,
+ gint selection_num,
+ gint *start_pos,
+ gint *end_pos)
+{
+ EvPageAccessible *self = EV_PAGE_ACCESSIBLE (text);
+ EvView *view = ev_page_accessible_get_view (self);
+ gchar *selected_text = NULL;
+ gchar *normalized_text = NULL;
+ GList *l;
+
+ *start_pos = -1;
+ *end_pos = -1;
+
+ if (selection_num != 0)
+ return NULL;
+
+ if (!EV_IS_SELECTION (view->document) || !view->selection_info.selections)
+ return NULL;
+
+ for (l = view->selection_info.selections; l != NULL; l = l->next) {
+ EvViewSelection *selection = (EvViewSelection *)l->data;
+ gint start, end;
+
+ if (selection->page != self->priv->page)
+ continue;
+
+ if (get_selection_bounds (view, selection, &start, &end) && start != end) {
+ EvPage *page;
+
+ page = ev_document_get_page (view->document, selection->page);
+
+ ev_document_doc_mutex_lock ();
+ selected_text = ev_selection_get_selected_text (EV_SELECTION (view->document),
+ page,
+ selection->style,
+ &(selection->rect));
+
+ ev_document_doc_mutex_unlock ();
+
+ g_object_unref (page);
+
+ *start_pos = start;
+ *end_pos = end;
+ }
+
+ break;
+ }
+
+ if (selected_text) {
+ normalized_text = g_utf8_normalize (selected_text, -1, G_NORMALIZE_NFKC);
+ g_free (selected_text);
+ }
+
+ return normalized_text;
+}
+
+static AtkAttributeSet *
+add_attribute (AtkAttributeSet *attr_set,
+ AtkTextAttribute attr_type,
+ gchar *attr_value)
+{
+ AtkAttribute *attr = g_new (AtkAttribute, 1);
+
+ attr->name = g_strdup (atk_text_attribute_get_name (attr_type));
+ attr->value = attr_value;
+
+ return g_slist_prepend (attr_set, attr);
+}
+
+static AtkAttributeSet *
+get_run_attributes (PangoAttrList *attrs,
+ const gchar *text,
+ gint offset,
+ gint *start_offset,
+ gint *end_offset)
+{
+ AtkAttributeSet *atk_attr_set = NULL;
+ PangoAttrString *pango_string;
+ PangoAttrInt *pango_int;
+ PangoAttrColor *pango_color;
+ PangoAttrIterator *iter;
+ gint i, start, end;
+ gboolean has_attrs = FALSE;
+ glong text_length;
+ gchar *attr_value;
+
+ text_length = g_utf8_strlen (text, -1);
+ if (offset < 0 || offset >= text_length)
+ return NULL;
+
+ /* Check if there are attributes for the offset,
+ * and set the attributes range if positive */
+ iter = pango_attr_list_get_iterator (attrs);
+ i = g_utf8_offset_to_pointer (text, offset) - text;
+
+ do {
+ pango_attr_iterator_range (iter, &start, &end);
+ if (i >= start && i < end) {
+ *start_offset = g_utf8_pointer_to_offset (text, text + start);
+ if (end == G_MAXINT) /* Last iterator */
+ end = text_length;
+ *end_offset = g_utf8_pointer_to_offset (text, text + end);
+ has_attrs = TRUE;
+ }
+ } while (!has_attrs && pango_attr_iterator_next (iter));
+
+ if (!has_attrs) {
+ pango_attr_iterator_destroy (iter);
+ return NULL;
+ }
+
+ /* Create the AtkAttributeSet from the Pango attributes */
+ pango_string = (PangoAttrString *) pango_attr_iterator_get (iter, PANGO_ATTR_FAMILY);
+ if (pango_string) {
+ attr_value = g_strdup (pango_string->value);
+ atk_attr_set = add_attribute (atk_attr_set, ATK_TEXT_ATTR_FAMILY_NAME, attr_value);
+ }
+
+ pango_int = (PangoAttrInt *) pango_attr_iterator_get (iter, PANGO_ATTR_SIZE);
+ if (pango_int) {
+ attr_value = g_strdup_printf ("%i", pango_int->value / PANGO_SCALE);
+ atk_attr_set = add_attribute (atk_attr_set, ATK_TEXT_ATTR_SIZE, attr_value);
+ }
+
+ pango_int = (PangoAttrInt *) pango_attr_iterator_get (iter, PANGO_ATTR_UNDERLINE);
+ if (pango_int) {
+ atk_attr_set = add_attribute (atk_attr_set,
+ ATK_TEXT_ATTR_UNDERLINE,
+ g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_UNDERLINE,
+ pango_int->value)));
+ }
+
+ pango_color = (PangoAttrColor *) pango_attr_iterator_get (iter, PANGO_ATTR_FOREGROUND);
+ if (pango_color) {
+ attr_value = g_strdup_printf ("%u,%u,%u",
+ pango_color->color.red,
+ pango_color->color.green,
+ pango_color->color.blue);
+ atk_attr_set = add_attribute (atk_attr_set, ATK_TEXT_ATTR_FG_COLOR, attr_value);
+ }
+
+ pango_attr_iterator_destroy (iter);
+
+ return atk_attr_set;
+}
+
+static AtkAttributeSet*
+ev_page_accessible_get_run_attributes (AtkText *text,
+ gint offset,
+ gint *start_offset,
+ gint *end_offset)
+{
+ EvPageAccessible *self = EV_PAGE_ACCESSIBLE (text);
+ EvView *view = ev_page_accessible_get_view (self);
+ PangoAttrList *attrs;
+ const gchar *page_text;
+
+ if (offset < 0)
+ return NULL;
+
+ if (!view->page_cache)
+ return NULL;
+
+ page_text = ev_page_cache_get_text (view->page_cache, self->priv->page);
+ if (!page_text)
+ return NULL;
+
+ attrs = ev_page_cache_get_text_attrs (view->page_cache, self->priv->page);
+ if (!attrs)
+ return NULL;
+
+ return get_run_attributes (attrs, page_text, offset, start_offset, end_offset);
+}
+
+static AtkAttributeSet*
+ev_page_accessible_get_default_attributes (AtkText *text)
+{
+ /* No default attributes */
+ return NULL;
+}
+
+static void
+ev_page_accessible_get_character_extents (AtkText *text,
+ gint offset,
+ gint *x,
+ gint *y,
+ gint *width,
+ gint *height,
+ AtkCoordType coords)
+{
+ EvPageAccessible *self = EV_PAGE_ACCESSIBLE (text);
+ EvView *view = ev_page_accessible_get_view (self);
+ GtkWidget *toplevel;
+ EvRectangle *areas = NULL;
+ EvRectangle *doc_rect;
+ guint n_areas = 0;
+ gint x_widget, y_widget;
+ GdkRectangle view_rect;
+
+ if (!view->page_cache)
+ return;
+
+ ev_page_cache_get_text_layout (view->page_cache, self->priv->page, &areas, &n_areas);
+ if (!areas || offset >= n_areas)
+ return;
+
+ doc_rect = areas + offset;
+ _ev_view_transform_doc_rect_to_view_rect (view, self->priv->page, doc_rect, &view_rect);
+ view_rect.x -= view->scroll_x;
+ view_rect.y -= view->scroll_y;
+
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (view));
+ gtk_widget_translate_coordinates (GTK_WIDGET (view), toplevel, 0, 0, &x_widget, &y_widget);
+ view_rect.x += x_widget;
+ view_rect.y += y_widget;
+
+ if (coords == ATK_XY_SCREEN) {
+ gint x_window, y_window;
+
+ gdk_window_get_origin (gtk_widget_get_window (toplevel), &x_window, &y_window);
+ view_rect.x += x_window;
+ view_rect.y += y_window;
+ }
+
+ *x = view_rect.x;
+ *y = view_rect.y;
+ *width = view_rect.width;
+ *height = view_rect.height;
+}
+
+static gint
+ev_page_accessible_get_offset_at_point (AtkText *text,
+ gint x,
+ gint y,
+ AtkCoordType coords)
+{
+ EvPageAccessible *self = EV_PAGE_ACCESSIBLE (text);
+ EvView *view = ev_page_accessible_get_view (self);
+ GtkWidget *toplevel;
+ EvRectangle *areas = NULL;
+ EvRectangle *rect = NULL;
+ guint n_areas = 0;
+ guint i;
+ gint x_widget, y_widget;
+ gint offset=-1;
+ GdkPoint view_point;
+ gdouble doc_x, doc_y;
+ GtkBorder border;
+ GdkRectangle page_area;
+
+ if (!view->page_cache)
+ return -1;
+
+ ev_page_cache_get_text_layout (view->page_cache, self->priv->page, &areas, &n_areas);
+ if (!areas)
+ return -1;
+
+ view_point.x = x;
+ view_point.y = y;
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (self));
+ gtk_widget_translate_coordinates (GTK_WIDGET (self), toplevel, 0, 0, &x_widget, &y_widget);
+ view_point.x -= x_widget;
+ view_point.y -= y_widget;
+
+ if (coords == ATK_XY_SCREEN) {
+ gint x_window, y_window;
+
+ gdk_window_get_origin (gtk_widget_get_window (toplevel), &x_window, &y_window);
+ view_point.x -= x_window;
+ view_point.y -= y_window;
+ }
+
+ ev_view_get_page_extents (view, self->priv->page, &page_area, &border);
+ _ev_view_transform_view_point_to_doc_point (view, &view_point, &page_area, &doc_x, &doc_y);
+
+ for (i = 0; i < n_areas; i++) {
+ rect = areas + i;
+ if (doc_x >= rect->x1 && doc_x <= rect->x2 &&
+ doc_y >= rect->y1 && doc_y <= rect->y2)
+ offset = i;
+ }
+
+ return offset;
+}
+
+/* ATK allows for multiple, non-contiguous selections within a single AtkText
+ * object. Unless and until Evince supports this, selection numbers are ignored.
+ */
+static gboolean
+ev_page_accessible_remove_selection (AtkText *text,
+ gint selection_num)
+{
+ EvPageAccessible *self = EV_PAGE_ACCESSIBLE (text);
+ EvView *view = ev_page_accessible_get_view (self);
+
+ if (ev_view_get_has_selection (view)) {
+ _ev_view_clear_selection (view);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+ev_page_accessible_set_selection (AtkText *text,
+ gint selection_num,
+ gint start_pos,
+ gint end_pos)
+{
+ EvPageAccessible *self = EV_PAGE_ACCESSIBLE (text);
+ EvView *view = ev_page_accessible_get_view (self);
+ EvRectangle *areas = NULL;
+ guint n_areas = 0;
+ GdkRectangle start_rect, end_rect;
+ GdkPoint start_point, end_point;
+
+ ev_page_cache_get_text_layout (view->page_cache, self->priv->page, &areas, &n_areas);
+ if (start_pos < 0 || end_pos >= n_areas)
+ return FALSE;
+
+ _ev_view_transform_doc_rect_to_view_rect (view, self->priv->page, areas + start_pos, &start_rect);
+ _ev_view_transform_doc_rect_to_view_rect (view, self->priv->page, areas + end_pos - 1, &end_rect);
+ start_point.x = start_rect.x;
+ start_point.y = start_rect.y;
+ end_point.x = end_rect.x + end_rect.width;
+ end_point.y = end_rect.y + end_rect.height;
+ _ev_view_set_selection (view, &start_point, &end_point);
+
+ return TRUE;
+}
+
+static gboolean
+ev_page_accessible_add_selection (AtkText *text,
+ gint start_pos,
+ gint end_pos)
+{
+ return ev_page_accessible_set_selection (text, 0, start_pos, end_pos);
+
+}
+
static void
ev_page_accessible_init (EvPageAccessible *page)
{
@@ -148,6 +812,25 @@ ev_page_accessible_init (EvPageAccessible *page)
page->priv = G_TYPE_INSTANCE_GET_PRIVATE (page, EV_TYPE_PAGE_ACCESSIBLE, EvPageAccessiblePrivate);
}
+static void
+ev_page_accessible_text_iface_init (AtkTextIface *iface)
+{
+ iface->get_text = ev_page_accessible_get_text;
+ iface->get_text_at_offset = ev_page_accessible_get_text_at_offset;
+ iface->get_character_at_offset = ev_page_accessible_get_character_at_offset;
+ iface->get_caret_offset = ev_page_accessible_get_caret_offset;
+ iface->set_caret_offset = ev_page_accessible_set_caret_offset;
+ iface->get_character_count = ev_page_accessible_get_character_count;
+ iface->get_n_selections = ev_page_accessible_get_n_selections;
+ iface->get_selection = ev_page_accessible_get_selection;
+ iface->remove_selection = ev_page_accessible_remove_selection;
+ iface->add_selection = ev_page_accessible_add_selection;
+ iface->get_run_attributes = ev_page_accessible_get_run_attributes;
+ iface->get_default_attributes = ev_page_accessible_get_default_attributes;
+ iface->get_character_extents = ev_page_accessible_get_character_extents;
+ iface->get_offset_at_point = ev_page_accessible_get_offset_at_point;
+}
+
EvPageAccessible *
ev_page_accessible_new (EvViewAccessible *view_accessible,
gint page)
diff --git a/libview/ev-page-accessible.h b/libview/ev-page-accessible.h
index f017b34b..e982a157 100644
--- a/libview/ev-page-accessible.h
+++ b/libview/ev-page-accessible.h
@@ -25,6 +25,7 @@
#include <gtk/gtk-a11y.h>
#include "ev-view-accessible.h"
+#include "ev-view.h"
#define EV_TYPE_PAGE_ACCESSIBLE (ev_page_accessible_get_type ())
#define EV_PAGE_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EV_TYPE_PAGE_ACCESSIBLE, EvPageAccessible))
@@ -49,6 +50,7 @@ EvPageAccessible *ev_page_accessible_new (EvViewAccessible *view
gint page);
gint ev_page_accessible_get_page (EvPageAccessible *page_accessible);
EvViewAccessible *ev_page_accessible_get_view_accessible (EvPageAccessible *page_accessible);
+EvView *ev_page_accessible_get_view (EvPageAccessible *page_accessible);
#endif /* __EV_PAGE_ACCESSIBLE_H__ */
diff --git a/libview/ev-view-accessible.c b/libview/ev-view-accessible.c
index 9be52581..afa52ef0 100644
--- a/libview/ev-view-accessible.c
+++ b/libview/ev-view-accessible.c
@@ -30,7 +30,6 @@
#include "ev-view-private.h"
#include "ev-page-accessible.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_hypertext_iface_init (AtkHypertextIface *iface);
static void ev_view_accessible_document_iface_init (AtkDocumentIface *iface);
@@ -72,7 +71,6 @@ struct _EvViewAccessiblePrivate {
};
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_HYPERTEXT, ev_view_accessible_hypertext_iface_init)
G_IMPLEMENT_INTERFACE (ATK_TYPE_DOCUMENT, ev_view_accessible_document_iface_init)
@@ -188,718 +186,6 @@ ev_view_accessible_init (EvViewAccessible *accessible)
accessible->priv = G_TYPE_INSTANCE_GET_PRIVATE (accessible, EV_TYPE_VIEW_ACCESSIBLE, EvViewAccessiblePrivate);
}
-/* ATs expect to be able to identify sentence boundaries based on content. Valid,
- * content-based boundaries may be present at the end of a newline, for instance
- * at the end of a heading within a document. Thus being able to distinguish hard
- * returns from soft returns is necessary. However, the text we get from Poppler
- * for non-tagged PDFs has "\n" inserted at the end of each line resulting in a
- * broken accessibility implementation w.r.t. sentences.
- */
-static gboolean
-treat_as_soft_return (EvView *view,
- PangoLogAttr *log_attrs,
- gint offset)
-{
- EvRectangle *areas = NULL;
- guint n_areas = 0;
- gdouble line_spacing, this_line_height, next_word_width;
- EvRectangle *this_line_start;
- EvRectangle *this_line_end;
- EvRectangle *next_line_start;
- EvRectangle *next_line_end;
- EvRectangle *next_word_end;
- gint prev_offset, next_offset;
-
-
- if (!log_attrs[offset].is_white)
- return FALSE;
-
- ev_page_cache_get_text_layout (view->page_cache, get_relevant_page (view), &areas, &n_areas);
- if (n_areas <= offset + 1)
- return FALSE;
-
- prev_offset = offset - 1;
- next_offset = offset + 1;
-
- /* In wrapped text, the character at the start of the next line starts a word.
- * Examples where this condition might fail include bullets and images. But it
- * also includes things like "(", so also check the next character.
- */
- if (!log_attrs[next_offset].is_word_start &&
- (next_offset + 1 >= n_areas || !log_attrs[next_offset + 1].is_word_start))
- return FALSE;
-
- /* In wrapped text, the chars on either side of the newline have very similar heights.
- * Examples where this condition might fail include a newline at the end of a heading,
- * and a newline at the end of a paragraph that is followed by a heading.
- */
- this_line_end = areas + prev_offset;
- next_line_start = areas + next_offset;;
-
- this_line_height = this_line_end->y2 - this_line_end->y1;
- if (ABS (this_line_height - (next_line_start->y2 - next_line_start->y1)) > 0.25)
- return FALSE;
-
- /* If there is significant white space between this line and the next, odds are this
- * is not a soft return in wrapped text. Lines within a typical paragraph are at most
- * double-spaced. If the spacing is more than that, assume a hard return is present.
- */
- line_spacing = next_line_start->y1 - this_line_end->y2;
- if (line_spacing - this_line_height > 1)
- return FALSE;
-
- /* Lines within a typical paragraph have *reasonably* similar x1 coordinates. But
- * we cannot count on them being nearly identical. Examples where indentation can
- * be present in wrapped text include indenting the first line of the paragraph,
- * and hanging indents (e.g. in the works cited within an academic paper). So we'll
- * be somewhat tolerant here.
- */
- for ( ; prev_offset > 0 && !log_attrs[prev_offset].is_mandatory_break; prev_offset--);
- this_line_start = areas + prev_offset;
- if (ABS (this_line_start->x1 - next_line_start->x1) > 20)
- return FALSE;
-
- /* Ditto for x2, but this line might be short due to a wide word on the next line. */
- for ( ; next_offset < n_areas && !log_attrs[next_offset].is_word_end; next_offset++);
- next_word_end = areas + next_offset;
- next_word_width = next_word_end->x2 - next_line_start->x1;
-
- for ( ; next_offset < n_areas && !log_attrs[next_offset + 1].is_mandatory_break; next_offset++);
- next_line_end = areas + next_offset;
- if (next_line_end->x2 - (this_line_end->x2 + next_word_width) > 20)
- return FALSE;
-
- return TRUE;
-}
-
-static void
-ev_view_accessible_get_range_for_boundary (AtkText *text,
- AtkTextBoundary boundary_type,
- gint offset,
- gint *start_offset,
- gint *end_offset)
-{
- GtkWidget *widget;
- EvView *view;
- gint start = 0;
- gint end = 0;
- PangoLogAttr *log_attrs = NULL;
- gulong n_attrs;
-
- widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
- if (widget == NULL)
- return;
-
- view = EV_VIEW (widget);
- if (!view->page_cache)
- return;
-
- ev_page_cache_get_text_log_attrs (view->page_cache, get_relevant_page (view), &log_attrs, &n_attrs);
- if (!log_attrs)
- return;
-
- switch (boundary_type) {
- case ATK_TEXT_BOUNDARY_CHAR:
- start = offset;
- end = offset + 1;
- break;
- case ATK_TEXT_BOUNDARY_WORD_START:
- for (start = offset; start >= 0 && !log_attrs[start].is_word_start; start--);
- for (end = offset + 1; end <= n_attrs && !log_attrs[end].is_word_start; end++);
- break;
- case ATK_TEXT_BOUNDARY_SENTENCE_START:
- for (start = offset; start >= 0; start--) {
- if (log_attrs[start].is_mandatory_break && treat_as_soft_return (view, log_attrs, start - 1))
- continue;
- if (log_attrs[start].is_sentence_start)
- break;
- }
- for (end = offset + 1; end <= n_attrs; end++) {
- if (log_attrs[end].is_mandatory_break && treat_as_soft_return (view, log_attrs, end - 1))
- continue;
- if (log_attrs[end].is_sentence_start)
- break;
- }
- break;
- case ATK_TEXT_BOUNDARY_LINE_START:
- for (start = offset; start >= 0 && !log_attrs[start].is_mandatory_break; start--);
- for (end = offset + 1; end <= n_attrs && !log_attrs[end].is_mandatory_break; end++);
- break;
- default:
- /* The "END" boundary types are deprecated */
- break;
- }
-
- *start_offset = start;
- *end_offset = end;
-}
-
-static gchar *
-ev_view_accessible_get_substring (AtkText *text,
- gint start_offset,
- gint end_offset)
-{
- GtkWidget *widget;
- EvView *view;
- gchar *substring, *normalized;
- const gchar* page_text;
-
- widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
- if (widget == NULL)
- return NULL;
-
- view = EV_VIEW (widget);
- if (!view->page_cache)
- return NULL;
-
- page_text = ev_page_cache_get_text (view->page_cache, get_relevant_page (view));
- start_offset = MAX (0, start_offset);
- if (end_offset < 0 || end_offset > g_utf8_strlen (page_text, -1))
- end_offset = strlen (page_text);
-
- substring = g_utf8_substring (page_text, start_offset, end_offset);
- normalized = g_utf8_normalize (substring, -1, G_NORMALIZE_NFKC);
- g_free (substring);
-
- return normalized;
-}
-
-static gunichar
-ev_view_accessible_get_character_at_offset (AtkText *text,
- gint offset)
-{
- gchar *string;
- gunichar unichar;
-
- string = ev_view_accessible_get_substring (text, offset, offset + 1);
- unichar = g_utf8_get_char (string);
- g_free(string);
-
- return unichar;
-}
-
-static gchar *
-ev_view_accessible_get_text (AtkText *text,
- gint start_pos,
- gint end_pos)
-{
- return ev_view_accessible_get_substring (text, start_pos, end_pos);
-}
-
-static gchar *
-ev_view_accessible_get_text_at_offset (AtkText *text,
- gint offset,
- AtkTextBoundary boundary_type,
- gint *start_offset,
- gint *end_offset)
-{
- gchar *retval;
-
- ev_view_accessible_get_range_for_boundary (text, boundary_type, offset, start_offset, end_offset);
- retval = ev_view_accessible_get_substring (text, *start_offset, *end_offset);
-
- /* If newlines appear inside the text of a sentence (i.e. between the start and
- * end offsets returned by ev_view_accessible_get_substring), it interferes with
- * the prosody of text-to-speech based-solutions such as a screen reader because
- * speech synthesizers tend to pause after the newline char as if it were the end
- * of the sentence.
- */
- if (boundary_type == ATK_TEXT_BOUNDARY_SENTENCE_START)
- g_strdelimit (retval, "\n", ' ');
-
- return retval;
-}
-
-static gint
-ev_view_accessible_get_character_count (AtkText *text)
-{
- GtkWidget *widget;
- EvView *view;
- gint retval;
-
- widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
- if (widget == NULL)
- /* State is defunct */
- return 0;
-
- view = EV_VIEW (widget);
- retval = g_utf8_strlen (ev_page_cache_get_text (view->page_cache, get_relevant_page (view)), -1);
-
- return retval;
-}
-
-static gint
-ev_view_accessible_get_caret_offset (AtkText *text)
-{
- GtkWidget *widget;
- EvView *view;
-
- widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
- if (widget == NULL)
- /* State is defunct */
- return -1;
-
- view = EV_VIEW (widget);
- if (view->caret_enabled)
- return view->cursor_offset;
-
- return -1;
-}
-
-static gboolean
-ev_view_accessible_set_caret_offset (AtkText *text, gint offset)
-{
- GtkWidget *widget;
- EvView *view;
-
- widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
- if (widget == NULL)
- /* State is defunct */
- return FALSE;
-
- view = EV_VIEW (widget);
- ev_view_set_caret_cursor_position (view,
- view->cursor_page,
- offset);
-
- return TRUE;
-}
-
-static AtkAttributeSet *
-add_attribute (AtkAttributeSet *attr_set,
- AtkTextAttribute attr_type,
- gchar *attr_value)
-{
- AtkAttribute *attr = g_new (AtkAttribute, 1);
-
- attr->name = g_strdup (atk_text_attribute_get_name (attr_type));
- attr->value = attr_value;
-
- return g_slist_prepend (attr_set, attr);
-}
-
-static AtkAttributeSet *
-get_run_attributes (PangoAttrList *attrs,
- const gchar *text,
- gint offset,
- gint *start_offset,
- gint *end_offset)
-{
- AtkAttributeSet *atk_attr_set = NULL;
- PangoAttrString *pango_string;
- PangoAttrInt *pango_int;
- PangoAttrColor *pango_color;
- PangoAttrIterator *iter;
- gint i, start, end;
- gboolean has_attrs = FALSE;
- glong text_length;
- gchar *attr_value;
-
- text_length = g_utf8_strlen (text, -1);
- if (offset < 0 || offset >= text_length)
- return NULL;
-
- /* Check if there are attributes for the offset,
- * and set the attributes range if positive */
- iter = pango_attr_list_get_iterator (attrs);
- i = g_utf8_offset_to_pointer (text, offset) - text;
-
- do {
- pango_attr_iterator_range (iter, &start, &end);
- if (i >= start && i < end) {
- *start_offset = g_utf8_pointer_to_offset (text, text + start);
- if (end == G_MAXINT) /* Last iterator */
- end = text_length;
- *end_offset = g_utf8_pointer_to_offset (text, text + end);
- has_attrs = TRUE;
- }
- } while (!has_attrs && pango_attr_iterator_next (iter));
-
- if (!has_attrs) {
- pango_attr_iterator_destroy (iter);
- return NULL;
- }
-
- /* Create the AtkAttributeSet from the Pango attributes */
- pango_string = (PangoAttrString *) pango_attr_iterator_get (iter, PANGO_ATTR_FAMILY);
- if (pango_string) {
- attr_value = g_strdup (pango_string->value);
- atk_attr_set = add_attribute (atk_attr_set, ATK_TEXT_ATTR_FAMILY_NAME, attr_value);
- }
-
- pango_int = (PangoAttrInt *) pango_attr_iterator_get (iter, PANGO_ATTR_SIZE);
- if (pango_int) {
- attr_value = g_strdup_printf ("%i", pango_int->value / PANGO_SCALE);
- atk_attr_set = add_attribute (atk_attr_set, ATK_TEXT_ATTR_SIZE, attr_value);
- }
-
- pango_int = (PangoAttrInt *) pango_attr_iterator_get (iter, PANGO_ATTR_UNDERLINE);
- if (pango_int) {
- atk_attr_set = add_attribute (atk_attr_set,
- ATK_TEXT_ATTR_UNDERLINE,
- g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_UNDERLINE,
- pango_int->value)));
- }
-
- pango_color = (PangoAttrColor *) pango_attr_iterator_get (iter, PANGO_ATTR_FOREGROUND);
- if (pango_color) {
- attr_value = g_strdup_printf ("%u,%u,%u",
- pango_color->color.red,
- pango_color->color.green,
- pango_color->color.blue);
- atk_attr_set = add_attribute (atk_attr_set, ATK_TEXT_ATTR_FG_COLOR, attr_value);
- }
-
- pango_attr_iterator_destroy (iter);
-
- return atk_attr_set;
-}
-
-static AtkAttributeSet*
-ev_view_accessible_get_run_attributes (AtkText *text,
- gint offset,
- gint *start_offset,
- gint *end_offset)
-{
- EvView *view;
- GtkWidget *widget;
- PangoAttrList *attrs;
- const gchar *page_text;
-
- if (offset < 0)
- return NULL;
-
- widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
- if (!widget)
- return NULL;
-
- view = EV_VIEW (widget);
- if (!view->page_cache)
- return NULL;
-
- page_text = ev_page_cache_get_text (view->page_cache, get_relevant_page (view));
- if (!page_text)
- return NULL;
-
- attrs = ev_page_cache_get_text_attrs (view->page_cache, get_relevant_page (view));
- if (!attrs)
- return NULL;
-
- return get_run_attributes (attrs, page_text, offset, start_offset, end_offset);
-}
-
-static AtkAttributeSet*
-ev_view_accessible_get_default_attributes (AtkText *text)
-{
- GtkWidget *widget;
-
- widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
- if (widget == NULL)
- /* State is defunct */
- return NULL;
- return NULL;
-}
-
-static void
-ev_view_accessible_get_character_extents (AtkText *text,
- gint offset,
- gint *x,
- gint *y,
- gint *width,
- gint *height,
- AtkCoordType coords)
-{
- GtkWidget *widget, *toplevel;
- EvView *view;
- EvRectangle *areas = NULL;
- EvRectangle *doc_rect;
- guint n_areas = 0;
- gint x_widget, y_widget;
- GdkRectangle view_rect;
-
- widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
- if (widget == NULL)
- /* State is defunct */
- return;
-
- view = EV_VIEW (widget);
- if (!view->page_cache)
- return;
-
- ev_page_cache_get_text_layout (view->page_cache, get_relevant_page (view), &areas, &n_areas);
- if (!areas || offset >= n_areas)
- return;
-
- doc_rect = areas + offset;
- _ev_view_transform_doc_rect_to_view_rect (view, get_relevant_page (view), doc_rect, &view_rect);
- view_rect.x -= view->scroll_x;
- view_rect.y -= view->scroll_y;
-
- toplevel = gtk_widget_get_toplevel (widget);
- gtk_widget_translate_coordinates (widget, toplevel, 0, 0, &x_widget, &y_widget);
- view_rect.x += x_widget;
- view_rect.y += y_widget;
-
- if (coords == ATK_XY_SCREEN) {
- gint x_window, y_window;
-
- gdk_window_get_origin (gtk_widget_get_window (toplevel), &x_window, &y_window);
- view_rect.x += x_window;
- view_rect.y += y_window;
- }
-
- *x = view_rect.x;
- *y = view_rect.y;
- *width = view_rect.width;
- *height = view_rect.height;
-}
-
-static gint
-ev_view_accessible_get_offset_at_point (AtkText *text,
- gint x,
- gint y,
- AtkCoordType coords)
-{
- GtkWidget *widget, *toplevel;
- EvView *view;
- EvRectangle *areas = NULL;
- EvRectangle *rect = NULL;
- guint n_areas = 0;
- guint i;
- gint x_widget, y_widget;
- gint offset=-1;
- GdkPoint view_point;
- gdouble doc_x, doc_y;
- GtkBorder border;
- GdkRectangle page_area;
-
- widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
- 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, get_relevant_page (view), &areas, &n_areas);
- if (!areas)
- return -1;
-
- view_point.x = x;
- view_point.y = y;
- toplevel = gtk_widget_get_toplevel (widget);
- gtk_widget_translate_coordinates (widget, toplevel, 0, 0, &x_widget, &y_widget);
- view_point.x -= x_widget;
- view_point.y -= y_widget;
-
- if (coords == ATK_XY_SCREEN) {
- gint x_window, y_window;
-
- gdk_window_get_origin (gtk_widget_get_window (toplevel), &x_window, &y_window);
- view_point.x -= x_window;
- view_point.y -= y_window;
- }
-
- ev_view_get_page_extents (view, get_relevant_page (view), &page_area, &border);
- _ev_view_transform_view_point_to_doc_point (view, &view_point, &page_area, &doc_x, &doc_y);
-
- for (i = 0; i < n_areas; i++) {
- rect = areas + i;
- if (doc_x >= rect->x1 && doc_x <= rect->x2 &&
- doc_y >= rect->y1 && doc_y <= rect->y2)
- offset = i;
- }
-
- return offset;
-}
-
-static gint
-ev_view_accessible_get_n_selections (AtkText *text)
-{
- GtkWidget *widget;
- EvView *view;
-
- widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
- if (widget == NULL)
- /* State is defunct */
- return -1;
-
- view = EV_VIEW (widget);
- if (!EV_IS_SELECTION (view->document) || !view->selection_info.selections)
- return 0;
-
- return 1;
-}
-
-static gboolean
-get_selection_bounds (EvView *view,
- EvViewSelection *selection,
- gint *start_offset,
- gint *end_offset)
-{
- cairo_rectangle_int_t rect;
- gint start, end;
-
- if (!selection->covered_region || cairo_region_is_empty (selection->covered_region))
- return FALSE;
-
- cairo_region_get_rectangle (selection->covered_region, 0, &rect);
- start = _ev_view_get_caret_cursor_offset_at_doc_point (view,
- selection->page,
- rect.x / view->scale,
- (rect.y + (rect.height / 2)) / view->scale);
- if (start == -1)
- return FALSE;
-
- cairo_region_get_rectangle (selection->covered_region,
- cairo_region_num_rectangles (selection->covered_region) - 1,
- &rect);
- end = _ev_view_get_caret_cursor_offset_at_doc_point (view,
- selection->page,
- (rect.x + rect.width) / view->scale,
- (rect.y + (rect.height / 2)) / view->scale);
- if (end == -1)
- return FALSE;
-
- *start_offset = start;
- *end_offset = end;
-
- return TRUE;
-}
-
-static gchar *
-ev_view_accessible_get_selection (AtkText *text,
- gint selection_num,
- gint *start_pos,
- gint *end_pos)
-{
- GtkWidget *widget;
- EvView *view;
- gchar *selected_text = NULL;
- gchar *normalized_text = NULL;
- GList *l;
-
- *start_pos = -1;
- *end_pos = -1;
-
- widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
- if (widget == NULL)
- /* State is defunct */
- return NULL;
-
- if (selection_num != 0)
- return NULL;
-
- view = EV_VIEW (widget);
- if (!EV_IS_SELECTION (view->document) || !view->selection_info.selections)
- return NULL;
-
-
- for (l = view->selection_info.selections; l != NULL; l = l->next) {
- EvViewSelection *selection = (EvViewSelection *)l->data;
- gint start, end;
-
- if (selection->page != get_relevant_page (view))
- continue;
-
- if (get_selection_bounds (view, selection, &start, &end) && start != end) {
- EvPage *page;
-
- page = ev_document_get_page (view->document, selection->page);
-
- ev_document_doc_mutex_lock ();
- selected_text = ev_selection_get_selected_text (EV_SELECTION (view->document),
- page,
- selection->style,
- &(selection->rect));
-
- ev_document_doc_mutex_unlock ();
-
- g_object_unref (page);
-
- *start_pos = start;
- *end_pos = end;
- }
-
- break;
- }
-
- if (selected_text) {
- normalized_text = g_utf8_normalize (selected_text, -1, G_NORMALIZE_NFKC);
- g_free (selected_text);
- }
-
- return normalized_text;
-}
-
-/* ATK allows for multiple, non-contiguous selections within a single AtkText
- * object. Unless and until Evince supports this, selection numbers are ignored.
- */
-static gboolean
-ev_view_accessible_remove_selection (AtkText *text,
- gint selection_num)
-{
- GtkWidget *widget;
- EvView *view;
-
- widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
- if (widget == NULL)
- /* State is defunct */
- return FALSE;
-
- view = EV_VIEW (widget);
- if (ev_view_get_has_selection (view)) {
- _ev_view_clear_selection (view);
- return TRUE;
- }
-
- return FALSE;
-}
-
-static gboolean
-ev_view_accessible_set_selection (AtkText *text,
- gint selection_num,
- gint start_pos,
- gint end_pos)
-{
- GtkWidget *widget;
- EvView *view;
- EvRectangle *areas = NULL;
- guint n_areas = 0;
- GdkRectangle start_rect, end_rect;
- GdkPoint start_point, end_point;
-
- widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
- if (widget == NULL)
- /* State is defunct */
- return FALSE;
-
- view = EV_VIEW (widget);
- ev_page_cache_get_text_layout (view->page_cache, get_relevant_page (view), &areas, &n_areas);
- if (start_pos < 0 || end_pos >= n_areas)
- return FALSE;
-
- _ev_view_transform_doc_rect_to_view_rect (view, get_relevant_page (view), areas + start_pos, &start_rect);
- _ev_view_transform_doc_rect_to_view_rect (view, get_relevant_page (view), areas + end_pos - 1, &end_rect);
- start_point.x = start_rect.x;
- start_point.y = start_rect.y;
- end_point.x = end_rect.x + end_rect.width;
- end_point.y = end_rect.y + end_rect.height;
- _ev_view_set_selection (view, &start_point, &end_point);
-
- return TRUE;
-}
-
-static gboolean
-ev_view_accessible_add_selection (AtkText *text,
- gint start_pos,
- gint end_pos)
-{
- return ev_view_accessible_set_selection (text, 0, start_pos, end_pos);
-
-}
-
static gint
ev_view_accessible_get_page_count (AtkDocument *atk_document)
{
@@ -930,26 +216,6 @@ ev_view_accessible_document_iface_init (AtkDocumentIface *iface)
iface->get_page_count = ev_view_accessible_get_page_count;
}
-static void
-ev_view_accessible_text_iface_init (AtkTextIface * iface)
-{
- iface->get_text = ev_view_accessible_get_text;
- iface->get_character_at_offset = ev_view_accessible_get_character_at_offset;
- iface->get_text_at_offset = ev_view_accessible_get_text_at_offset;
- iface->get_caret_offset = ev_view_accessible_get_caret_offset;
- iface->set_caret_offset = ev_view_accessible_set_caret_offset;
- iface->get_character_count = ev_view_accessible_get_character_count;
- iface->get_n_selections = ev_view_accessible_get_n_selections;
- iface->get_selection = ev_view_accessible_get_selection;
- iface->add_selection = ev_view_accessible_add_selection;
- iface->remove_selection = ev_view_accessible_remove_selection;
- iface->set_selection = ev_view_accessible_set_selection;
- iface->get_run_attributes = ev_view_accessible_get_run_attributes;
- iface->get_default_attributes = ev_view_accessible_get_default_attributes;
- iface->get_character_extents = ev_view_accessible_get_character_extents;
- iface->get_offset_at_point = ev_view_accessible_get_offset_at_point;
-}
-
static gboolean
ev_view_accessible_idle_do_action (gpointer data)
{
@@ -1168,6 +434,7 @@ ev_view_accessible_cursor_moved (EvView *view,
EvViewAccessible *accessible)
{
EvViewAccessiblePrivate* priv = accessible->priv;
+ EvPageAccessible *page_accessible = NULL;
if (priv->previous_cursor_page != page) {
priv->previous_cursor_page = page;
@@ -1176,14 +443,19 @@ ev_view_accessible_cursor_moved (EvView *view,
g_signal_emit_by_name (accessible, "page-changed", page + 1);
}
- g_signal_emit_by_name (accessible, "text-caret-moved", offset);
+ page_accessible = g_ptr_array_index (priv->children, page);
+ g_signal_emit_by_name (page_accessible, "text-caret-moved", offset);
}
static void
ev_view_accessible_selection_changed (EvView *view,
- EvViewAccessible *accessible)
+ EvViewAccessible *view_accessible)
{
- g_signal_emit_by_name (accessible, "text-selection-changed");
+ AtkObject *page_accessible;
+
+ page_accessible = g_ptr_array_index (view_accessible->priv->children,
+ get_relevant_page (view));
+ g_signal_emit_by_name (page_accessible, "text-selection-changed");
}
static void