[evince] a11y: move AtkText implementation from EvViewAccessible to EvPageAccessible



commit 2a458a9af4881f295263f6443787050fd370e865
Author: Alejandro Piñeiro <apinheiro igalia com>
Date:   Mon Apr 7 17:50:49 2014 +0200

    a11y: move AtkText implementation from EvViewAccessible to EvPageAccessible
    
    https://bugzilla.gnome.org/show_bug.cgi?id=724965

 libview/ev-page-accessible.c |  685 ++++++++++++++++++++++++++++++++++++++-
 libview/ev-page-accessible.h |    2 +
 libview/ev-view-accessible.c |  746 +-----------------------------------------
 3 files changed, 695 insertions(+), 738 deletions(-)
---
diff --git a/libview/ev-page-accessible.c b/libview/ev-page-accessible.c
index c4f4878..7ea4e7f 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, &border, &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 f826ff7..33161f8 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 d0b9784..bfae3fc 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, &border, &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);
-
-}
-
 #if ATK_CHECK_VERSION (2, 11, 3)
 static gint
 ev_view_accessible_get_page_count (AtkDocument *atk_document)
@@ -934,26 +220,6 @@ ev_view_accessible_document_iface_init (AtkDocumentIface *iface)
 #endif
 }
 
-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)
 {
@@ -1172,6 +438,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;
@@ -1182,14 +449,19 @@ ev_view_accessible_cursor_moved (EvView *view,
 #endif
        }
 
-       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



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