/* This file is part of the GtkHTML library. * * Copyright 2002 Ximian, Inc. * * Author: Radek Doulik * * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include "gtkhtml.h" #include "htmlengine.h" #include "htmlengine-edit.h" #include "htmlengine-edit-cursor.h" #include "htmlengine-edit-movement.h" #include "htmlengine-edit-cut-and-paste.h" #include "htmlinterval.h" #include "htmlselection.h" #include "htmltext.h" #include "htmltextslave.h" #include "object.h" #include "html.h" #include "text.h" #include "hyperlink.h" static void html_a11y_text_class_init (HTMLA11YTextClass *klass); static void html_a11y_text_init (HTMLA11YText *a11y_text); static void atk_component_interface_init (AtkComponentIface *iface); static void atk_text_interface_init (AtkTextIface *iface); static void html_a11y_text_get_extents (AtkComponent *component, gint *x, gint *y, gint *width, gint *height, AtkCoordType coord_type); static void html_a11y_text_get_size (AtkComponent *component, gint *width, gint *height); static gboolean html_a11y_text_grab_focus (AtkComponent *comp); static gchar * html_a11y_text_get_text (AtkText *text, gint start_offset, gint end_offset); static gchar * html_a11y_text_get_text_after_offset (AtkText *text, gint offset, AtkTextBoundary boundary_type, gint *start_offset, gint *end_offset); static gchar * html_a11y_text_get_text_at_offset (AtkText *text, gint offset, AtkTextBoundary boundary_type, gint *start_offset, gint *end_offset); static gunichar html_a11y_text_get_character_at_offset (AtkText *text, gint offset); static gchar * html_a11y_text_get_text_before_offset (AtkText *text, gint offset, AtkTextBoundary boundary_type, gint *start_offset, gint *end_offset); static gint html_a11y_text_get_character_count (AtkText *text); static gint html_a11y_text_get_n_selections (AtkText *text); static gchar *html_a11y_text_get_selection (AtkText *text, gint selection_num, gint *start_offset, gint *end_offset); static gboolean html_a11y_text_add_selection (AtkText *text, gint start_offset, gint end_offset); static gboolean html_a11y_text_remove_selection (AtkText *text, gint selection_num); static gboolean html_a11y_text_set_selection (AtkText *text, gint selection_num, gint start_offset, gint end_offset); static gint html_a11y_text_get_caret_offset (AtkText *text); static gboolean html_a11y_text_set_caret_offset (AtkText *text, gint offset); static void html_a11y_text_get_character_extents (AtkText *text, gint offset, gint *x, gint *y, gint *width, gint *height, AtkCoordType coords); static gint html_a11y_text_get_offset_at_point (AtkText *text, gint x, gint y, AtkCoordType coords); /* Hyperlink interface */ static void atk_hyper_text_interface_init (AtkHypertextIface *iface); static AtkHyperlink * html_a11y_text_get_link (AtkHypertext *hypertext, gint link_index); static gint html_a11y_text_get_n_links (AtkHypertext *hypertext); static gint html_a11y_text_get_link_index (AtkHypertext *hypertext, gint char_index); /* Editable text interface. */ static void atk_editable_text_interface_init (AtkEditableTextIface *iface); static void html_a11y_text_set_text_contents (AtkEditableText *text, const gchar *string); static void html_a11y_text_insert_text (AtkEditableText *text, const gchar *string, gint length, gint *position); static void html_a11y_text_copy_text (AtkEditableText *text, gint start_pos, gint end_pos); static void html_a11y_text_cut_text (AtkEditableText *text, gint start_pos, gint end_pos); static void html_a11y_text_delete_text (AtkEditableText *text, gint start_pos, gint end_pos); static void html_a11y_text_paste_text (AtkEditableText *text, gint position); static AtkStateSet* html_a11y_text_ref_state_set (AtkObject *accessible); static AtkAttributeSet* html_a11y_text_get_run_attributes (AtkText *text, gint offset, gint *start_offset, gint *end_offset); static AtkObjectClass *parent_class = NULL; static gint get_n_actions (AtkAction *action) { return 1; } static G_CONST_RETURN gchar* action_get_name (AtkAction *action, gint i) { if (i == 0) return _("grab focus"); return NULL; } static gboolean do_action (AtkAction * action, gint i) { switch (i) { case 0: return html_a11y_text_grab_focus (ATK_COMPONENT (action)); default: return FALSE; } } static void atk_action_interface_init (AtkActionIface *iface) { g_return_if_fail (iface != NULL); iface->do_action = do_action; iface->get_n_actions = get_n_actions; iface->get_name = action_get_name; } GType html_a11y_text_get_type (void) { static GType type = 0; if (!type) { static const GTypeInfo tinfo = { sizeof (HTMLA11YTextClass), NULL, /* base init */ NULL, /* base finalize */ (GClassInitFunc) html_a11y_text_class_init, /* class init */ NULL, /* class finalize */ NULL, /* class data */ sizeof (HTMLA11YText), /* instance size */ 0, /* nb preallocs */ (GInstanceInitFunc) html_a11y_text_init, /* instance init */ NULL /* value table */ }; static const GInterfaceInfo atk_component_info = { (GInterfaceInitFunc) atk_component_interface_init, (GInterfaceFinalizeFunc) NULL, NULL }; static const GInterfaceInfo atk_text_info = { (GInterfaceInitFunc) atk_text_interface_init, (GInterfaceFinalizeFunc) NULL, NULL }; static const GInterfaceInfo atk_editable_text_info = { (GInterfaceInitFunc) atk_editable_text_interface_init, (GInterfaceFinalizeFunc) NULL, NULL }; static const GInterfaceInfo atk_action_info = { (GInterfaceInitFunc) atk_action_interface_init, (GInterfaceFinalizeFunc) NULL, NULL }; static const GInterfaceInfo atk_hyper_text_info = { (GInterfaceInitFunc) atk_hyper_text_interface_init, (GInterfaceFinalizeFunc) NULL, NULL }; type = g_type_register_static (G_TYPE_HTML_A11Y, "HTMLA11YText", &tinfo, 0); g_type_add_interface_static (type, ATK_TYPE_COMPONENT, &atk_component_info); g_type_add_interface_static (type, ATK_TYPE_TEXT, &atk_text_info); g_type_add_interface_static (type, ATK_TYPE_EDITABLE_TEXT, &atk_editable_text_info); g_type_add_interface_static (type, ATK_TYPE_ACTION, &atk_action_info); g_type_add_interface_static (type, ATK_TYPE_HYPERTEXT, &atk_hyper_text_info); } return type; } static void atk_component_interface_init (AtkComponentIface *iface) { g_return_if_fail (iface != NULL); iface->get_extents = html_a11y_text_get_extents; iface->get_size = html_a11y_text_get_size; iface->grab_focus = html_a11y_text_grab_focus; } static void atk_text_interface_init (AtkTextIface *iface) { g_return_if_fail (iface != NULL); iface->get_text = html_a11y_text_get_text; iface->get_text_after_offset = html_a11y_text_get_text_after_offset; iface->get_text_before_offset = html_a11y_text_get_text_before_offset; iface->get_text_at_offset = html_a11y_text_get_text_at_offset; iface->get_character_at_offset = html_a11y_text_get_character_at_offset; iface->get_character_count = html_a11y_text_get_character_count; iface->get_n_selections = html_a11y_text_get_n_selections; iface->get_selection = html_a11y_text_get_selection; iface->remove_selection = html_a11y_text_remove_selection; iface->set_selection = html_a11y_text_set_selection; iface->add_selection = html_a11y_text_add_selection; iface->get_caret_offset = html_a11y_text_get_caret_offset; iface->set_caret_offset = html_a11y_text_set_caret_offset; iface->get_character_extents = html_a11y_text_get_character_extents; iface->get_offset_at_point = html_a11y_text_get_offset_at_point; iface->get_run_attributes = html_a11y_text_get_run_attributes; } static void html_a11y_text_finalize (GObject *obj) { HTMLA11YText *ato = HTML_A11Y_TEXT (obj); if (ato->util != NULL) { g_object_unref (ato->util); ato->util = NULL; } G_OBJECT_CLASS (parent_class)->finalize (obj); } static void html_a11y_text_initialize (AtkObject *obj, gpointer data) { GtkTextBuffer *buffer; HTMLText *to; HTMLA11YText *ato; /* printf ("html_a11y_text_initialize\n"); */ if (ATK_OBJECT_CLASS (parent_class)->initialize) ATK_OBJECT_CLASS (parent_class)->initialize (obj, data); to = HTML_TEXT (data); ato = HTML_A11Y_TEXT (obj); buffer = gtk_text_buffer_new (NULL); ato->util = gail_text_util_new (); gtk_text_buffer_set_text (buffer, to->text, -1); gail_text_util_buffer_setup (ato->util, buffer); g_object_unref (buffer); } static void html_a11y_text_class_init (HTMLA11YTextClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); AtkObjectClass *atk_class = ATK_OBJECT_CLASS (klass); parent_class = g_type_class_peek_parent (klass); atk_class->initialize = html_a11y_text_initialize; atk_class->ref_state_set = html_a11y_text_ref_state_set; gobject_class->finalize = html_a11y_text_finalize; } static void html_a11y_text_init (HTMLA11YText *a11y_text) { } AtkObject* html_a11y_text_new (HTMLObject *html_obj) { GObject *object; AtkObject *accessible; g_return_val_if_fail (HTML_IS_TEXT (html_obj), NULL); object = g_object_new (G_TYPE_HTML_A11Y_TEXT, NULL); accessible = ATK_OBJECT (object); atk_object_initialize (accessible, html_obj); accessible->role = ATK_ROLE_TEXT; /* printf ("created new html accessible text object\n"); */ return accessible; } /* atkobject.h */ static AtkStateSet* html_a11y_text_ref_state_set (AtkObject *accessible) { AtkStateSet *state_set; GtkHTML * html; GtkHTMLA11Y *html_a11y; state_set = ATK_OBJECT_CLASS (parent_class)->ref_state_set (accessible); html_a11y = html_a11y_get_gtkhtml_parent (HTML_A11Y (accessible)); if (!html_a11y) return state_set; html = GTK_HTML_A11Y_GTKHTML (html_a11y); if (!html || !html->engine) return state_set; if (html_engine_get_editable(html->engine)) atk_state_set_add_state (state_set, ATK_STATE_EDITABLE); atk_state_set_add_state (state_set, ATK_STATE_MULTI_LINE); atk_state_set_add_state (state_set, ATK_STATE_SENSITIVE); atk_state_set_add_state (state_set, ATK_STATE_FOCUSABLE); return state_set; } /* * AtkComponent interface */ static void html_a11y_text_get_extents (AtkComponent *component, gint *x, gint *y, gint *width, gint *height, AtkCoordType coord_type) { HTMLObject *obj = HTML_A11Y_HTML (component); GtkHTMLA11Y *top_html_a11y; HTMLEngine *top_engine; gint min_x, min_y, max_x, max_y; HTMLObject *next; gint sx, sy; g_return_if_fail (obj); top_html_a11y = html_a11y_get_top_gtkhtml_parent (HTML_A11Y (component)); g_return_if_fail (top_html_a11y); if (obj->y ascent) obj->y = obj->ascent; atk_component_get_extents (ATK_COMPONENT (top_html_a11y), x, y, width, height, coord_type); /* we need to get a max rect here */ html_object_calc_abs_position (obj, &min_x, &min_y); max_x = min_x + obj->width; max_y = min_y + obj->descent; min_y -= obj->ascent; next = obj->next; while (next && HTML_IS_TEXT_SLAVE (next)) { html_object_calc_abs_position (next, &sx, &sy); min_x = MIN (min_x, sx); min_y = MIN (min_y, sy - next->ascent); max_x = MAX (max_x, sx + next->width); max_y = MAX (max_y, sy + next->descent); next = next->next; } *x += min_x; *width = max_x - min_x; *y += min_y; *height = max_y - min_y; /* scroll window */ top_engine = GTK_HTML_A11Y_GTKHTML (top_html_a11y)->engine; *x -= top_engine->x_offset; *y -= top_engine->y_offset; } static void html_a11y_text_get_size (AtkComponent *component, gint *width, gint *height) { gint x, y; html_a11y_get_extents (component, &x, &y, width, height, ATK_XY_WINDOW); } static gboolean html_a11y_text_grab_focus (AtkComponent *comp) { GtkHTML *html; html = GTK_HTML_A11Y_GTKHTML (html_a11y_get_gtkhtml_parent (HTML_A11Y (comp))); g_return_val_if_fail (html && html->engine && html_engine_get_editable (html->engine), FALSE); html_engine_jump_to_object (html->engine, HTML_A11Y_HTML (comp), HTML_TEXT (HTML_A11Y_HTML (comp))->text_len); g_signal_emit_by_name (html, "grab_focus"); return TRUE; } /* * AtkText interface */ static gchar * html_a11y_text_get_text (AtkText *text, gint start_offset, gint end_offset) { HTMLText *to = HTML_TEXT (HTML_A11Y_HTML (text)); gchar *str; g_return_val_if_fail (to, NULL); /* printf ("%d - %d\n", start_offset, end_offset); */ if (end_offset == -1) end_offset = to->text_len; if (start_offset > end_offset) return NULL; if (start_offset < 0) return NULL; if (start_offset > to->text_len) return NULL; if (end_offset > to->text_len) return NULL; str = html_text_get_text (to, start_offset); return g_strndup (str, g_utf8_offset_to_pointer (str, end_offset - start_offset) - str); } static gint html_a11y_text_get_caret_offset(AtkText * text) { HTMLObject * p; HTMLEngine * e; GtkHTML * html; g_return_val_if_fail(text, 0); p= HTML_A11Y_HTML(text); g_return_val_if_fail(p && HTML_IS_TEXT(p), 0); html = GTK_HTML_A11Y_GTKHTML(html_a11y_get_gtkhtml_parent(HTML_A11Y(text))); g_return_val_if_fail(html && GTK_IS_HTML(html) && html->engine, 0); e = html_engine_get_top_html_engine(html->engine); g_return_val_if_fail(e && e->cursor && e->cursor->object == p, 0); return e->cursor->offset; } static gboolean html_a11y_text_set_caret_offset(AtkText * text, gint offset) { GtkHTML * html; HTMLEngine * e; HTMLObject * obj = HTML_A11Y_HTML(text); html = GTK_HTML_A11Y_GTKHTML(html_a11y_get_gtkhtml_parent(HTML_A11Y(text))); g_return_val_if_fail(obj && html && html->engine, FALSE); e = html->engine; html_engine_jump_to_object(e, obj, offset); return TRUE; } static gchar * html_a11y_text_get_text_after_offset (AtkText *text, gint offset, AtkTextBoundary boundary_type, gint *start_offset, gint *end_offset) { HTMLText *to = HTML_TEXT (HTML_A11Y_HTML (text)); HTMLTextSlave *start_slave, *end_slave; g_return_val_if_fail (to, NULL); g_return_val_if_fail (start_offset && end_offset, NULL); switch (boundary_type) { case ATK_TEXT_BOUNDARY_LINE_START: end_slave = html_text_get_slave_at_offset (to, NULL, offset); g_return_val_if_fail (end_slave, NULL); start_slave = (HTMLTextSlave *) HTML_OBJECT (end_slave)->next; if (start_slave && HTML_IS_TEXT_SLAVE (start_slave)) { *start_offset = start_slave->posStart; end_slave = (HTMLTextSlave *) HTML_OBJECT (start_slave)->next; if (end_slave && HTML_IS_TEXT_SLAVE (end_slave)) *end_offset = end_slave->posStart; else *end_offset = start_slave->posStart + start_slave->posLen; } else { /* we are on the last line. */ *start_offset = *end_offset = html_a11y_text_get_character_count (text); } return html_a11y_text_get_text (text, *start_offset, *end_offset); case ATK_TEXT_BOUNDARY_LINE_END: start_slave = html_text_get_slave_at_offset (to, NULL, offset); g_return_val_if_fail (start_slave, NULL); *start_offset = start_slave->posStart + start_slave->posLen; end_slave = (HTMLTextSlave *) HTML_OBJECT (start_slave)->next; if (end_slave && HTML_IS_TEXT_SLAVE (end_slave)) *end_offset = end_slave->posStart + end_slave->posLen; else *end_offset = *start_offset; return html_a11y_text_get_text (text, *start_offset, *end_offset); default: gail_text_util_text_setup (HTML_A11Y_TEXT (text)->util, to->text); return gail_text_util_get_text (HTML_A11Y_TEXT (text)->util, NULL, GAIL_AFTER_OFFSET, boundary_type, offset, start_offset, end_offset); } } static gchar * html_a11y_text_get_text_at_offset (AtkText *text, gint offset, AtkTextBoundary boundary_type, gint *start_offset, gint *end_offset) { HTMLText *to = HTML_TEXT (HTML_A11Y_HTML (text)); HTMLTextSlave *start_slave, *end_slave; g_return_val_if_fail (to, NULL); g_return_val_if_fail (start_offset && end_offset, NULL); switch (boundary_type) { case ATK_TEXT_BOUNDARY_LINE_START: start_slave = html_text_get_slave_at_offset (to, NULL, offset); if (start_slave == NULL) { *start_offset = 0; *end_offset = to->text_len; } else { end_slave = (HTMLTextSlave *) HTML_OBJECT (start_slave)->next; if (end_slave && HTML_IS_TEXT_SLAVE (end_slave)) { *end_offset = end_slave->posStart; } else { *end_offset = start_slave->posStart + start_slave->posLen; } *start_offset = start_slave->posStart; } return html_a11y_text_get_text (text, *start_offset, *end_offset); case ATK_TEXT_BOUNDARY_LINE_END: end_slave = html_text_get_slave_at_offset (to, NULL, offset); if (end_slave == NULL) { *start_offset = 0; *end_offset = to->text_len; } else { start_slave = (HTMLTextSlave *) HTML_OBJECT (end_slave)->prev; if (start_slave && HTML_IS_TEXT_SLAVE (start_slave)) { *start_offset = start_slave->posStart + start_slave->posLen; } else { *start_offset = end_slave->posStart; } *end_offset = end_slave->posStart + end_slave->posLen; } return html_a11y_text_get_text (text, *start_offset, *end_offset); default: gail_text_util_text_setup (HTML_A11Y_TEXT (text)->util, to->text); return gail_text_util_get_text (HTML_A11Y_TEXT (text)->util, NULL, GAIL_AT_OFFSET, boundary_type, offset, start_offset, end_offset); } } static gunichar html_a11y_text_get_character_at_offset (AtkText *text, gint offset) { HTMLText *to = HTML_TEXT (HTML_A11Y_HTML (text)); g_return_val_if_fail (to && offset <= to->text_len, 0); return html_text_get_char (to, offset); } static gchar * html_a11y_text_get_text_before_offset (AtkText *text, gint offset, AtkTextBoundary boundary_type, gint *start_offset, gint *end_offset) { HTMLText *to = HTML_TEXT (HTML_A11Y_HTML (text)); HTMLTextSlave *start_slave, *end_slave; g_return_val_if_fail (to, NULL); g_return_val_if_fail (start_offset && end_offset, NULL); switch (boundary_type) { case ATK_TEXT_BOUNDARY_LINE_START: end_slave = html_text_get_slave_at_offset (to, NULL, offset); g_return_val_if_fail (end_slave, NULL); start_slave = (HTMLTextSlave *) HTML_OBJECT (end_slave)->prev; *end_offset = end_slave->posStart; if (start_slave && HTML_IS_TEXT_SLAVE (start_slave)) { *start_offset = start_slave->posStart; } else *start_offset = *end_offset; return html_a11y_text_get_text (text, *start_offset, *end_offset); case ATK_TEXT_BOUNDARY_LINE_END: start_slave = html_text_get_slave_at_offset (to, NULL, offset); g_return_val_if_fail (start_slave, NULL); end_slave = (HTMLTextSlave *) HTML_OBJECT (start_slave)->prev; if (end_slave && HTML_IS_TEXT_SLAVE (end_slave)) { *end_offset = end_slave->posStart + end_slave->posLen; start_slave = (HTMLTextSlave *) HTML_OBJECT (end_slave)->prev; if (start_slave && HTML_IS_TEXT_SLAVE (start_slave)) *start_offset = start_slave->posStart + start_slave->posLen; else *start_offset = end_slave->posStart; } else { *start_offset = *end_offset = 0; /* on the first line */ } return html_a11y_text_get_text (text, *start_offset, *end_offset); default: gail_text_util_text_setup (HTML_A11Y_TEXT (text)->util, to->text); return gail_text_util_get_text (HTML_A11Y_TEXT (text)->util, NULL, GAIL_BEFORE_OFFSET, boundary_type, offset, start_offset, end_offset); } } static gint html_a11y_text_get_character_count (AtkText *text) { HTMLText *to = HTML_TEXT (HTML_A11Y_HTML (text)); g_return_val_if_fail (to, 0); return to->text_len; } static gint html_a11y_text_get_n_selections (AtkText *text) { HTMLObject *to = HTML_A11Y_HTML (text); g_return_val_if_fail (to, 0); return to->selected ? 1 : 0; } static gchar * html_a11y_text_get_selection (AtkText *text, gint selection_num, gint *start_offset, gint *end_offset) { HTMLText *to = HTML_TEXT (HTML_A11Y_HTML (text)); if (!to || !HTML_OBJECT (to)->selected || selection_num > 0) return NULL; *start_offset = to->select_start; *end_offset = to->select_start + to->select_length; return html_a11y_text_get_text (text, *start_offset, *end_offset); } static gboolean html_a11y_text_add_selection (AtkText *text, gint start_offset, gint end_offset) { GtkHTML *html = GTK_HTML_A11Y_GTKHTML (html_a11y_get_gtkhtml_parent (HTML_A11Y (text))); HTMLObject *obj = HTML_A11Y_HTML (text); HTMLInterval *i; g_return_val_if_fail(html && html->engine, FALSE); if (html_engine_is_selection_active (html->engine)) return FALSE; i = html_interval_new (obj, obj, start_offset, end_offset); html_engine_select_interval (html->engine, i); return TRUE; } static gboolean html_a11y_text_remove_selection (AtkText *text, gint selection_num) { GtkHTML *html = GTK_HTML_A11Y_GTKHTML (html_a11y_get_gtkhtml_parent (HTML_A11Y (text))); HTMLObject *obj = HTML_A11Y_HTML (text); if (!obj->selected || selection_num) return FALSE; html_engine_unselect_all (html->engine); return TRUE; } static gboolean html_a11y_text_set_selection (AtkText *text, gint selection_num, gint start_offset, gint end_offset) { if (selection_num) return FALSE; return html_a11y_text_add_selection (text, start_offset, end_offset); } /* Most of the code of the following function is copied from */ /* libgail-util/gailmisc.c gail_misc_layout_get_run_attributes */ static AtkAttributeSet * html_a11y_text_get_run_attributes (AtkText *text, gint offset, gint *start_offset, gint *end_offset) { PangoAttrIterator *iter; PangoAttrList *attr; PangoAttrString *pango_string; PangoAttrInt *pango_int; PangoAttrColor *pango_color; PangoAttrLanguage *pango_lang; PangoAttrFloat *pango_float; gint index, start_index, end_index; gboolean is_next = TRUE; gchar *value = NULL; glong len; gchar *textstring; AtkAttributeSet *attrib_set = NULL; GtkHTMLA11Y *a11y; GtkHTML *html; HTMLEngine *e; HTMLText *t = HTML_TEXT (HTML_A11Y_HTML (text)); g_return_val_if_fail (t, NULL); textstring = t->text; g_return_val_if_fail (textstring, NULL); a11y = html_a11y_get_top_gtkhtml_parent (HTML_A11Y (text)); g_return_val_if_fail (a11y, NULL); html = GTK_HTML_A11Y_GTKHTML (a11y); g_return_val_if_fail (html && GTK_IS_HTML(html) && html->engine, NULL); e = html->engine; attr = html_text_prepare_attrs (t, e->painter); g_return_val_if_fail (attr, NULL); len = g_utf8_strlen (textstring, -1); iter = pango_attr_list_get_iterator (attr); /* Get invariant range offsets */ /* If offset out of range, set offset in range */ if (offset > len) offset = len; else if (offset < 0) offset = 0; index = g_utf8_offset_to_pointer (textstring, offset) - textstring; pango_attr_iterator_range (iter, &start_index, &end_index); while (is_next) { if (index >= start_index && index < end_index) { *start_offset = g_utf8_pointer_to_offset (textstring, textstring + start_index); if (end_index == G_MAXINT) /* Last iterator */ end_index = len; *end_offset = g_utf8_pointer_to_offset (textstring, textstring + end_index); break; } is_next = pango_attr_iterator_next (iter); pango_attr_iterator_range (iter, &start_index, &end_index); } /* Get attributes */ if ((pango_string = (PangoAttrString*) pango_attr_iterator_get (iter, PANGO_ATTR_FAMILY)) != NULL) { value = g_strdup_printf("%s", pango_string->value); attrib_set = gail_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_FAMILY_NAME, value); } if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_STYLE)) != NULL) { attrib_set = gail_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_STYLE, g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_STYLE, pango_int->value))); } if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_WEIGHT)) != NULL) { value = g_strdup_printf("%i", pango_int->value); attrib_set = gail_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_WEIGHT, value); } if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_VARIANT)) != NULL) { attrib_set = gail_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_VARIANT, g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_VARIANT, pango_int->value))); } if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_STRETCH)) != NULL) { attrib_set = gail_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_STRETCH, g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_STRETCH, pango_int->value))); } if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_SIZE)) != NULL) { value = g_strdup_printf("%i", pango_int->value / PANGO_SCALE); attrib_set = gail_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_SIZE, value); } if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_UNDERLINE)) != NULL) { attrib_set = gail_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_UNDERLINE, g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_UNDERLINE, pango_int->value))); } if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_STRIKETHROUGH)) != NULL) { attrib_set = gail_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_STRIKETHROUGH, g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_STRIKETHROUGH, pango_int->value))); } if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_RISE)) != NULL) { value = g_strdup_printf("%i", pango_int->value); attrib_set = gail_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_RISE, value); } if ((pango_lang = (PangoAttrLanguage*) pango_attr_iterator_get (iter, PANGO_ATTR_LANGUAGE)) != NULL) { value = g_strdup( pango_language_to_string( pango_lang->value)); attrib_set = gail_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_LANGUAGE, value); } if ((pango_float = (PangoAttrFloat*) pango_attr_iterator_get (iter, PANGO_ATTR_SCALE)) != NULL) { value = g_strdup_printf("%g", pango_float->value); attrib_set = gail_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_SCALE, value); } if ((pango_color = (PangoAttrColor*) pango_attr_iterator_get (iter, PANGO_ATTR_FOREGROUND)) != NULL) { value = g_strdup_printf ("%u,%u,%u", pango_color->color.red, pango_color->color.green, pango_color->color.blue); attrib_set = gail_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_FG_COLOR, value); } if ((pango_color = (PangoAttrColor*) pango_attr_iterator_get (iter, PANGO_ATTR_BACKGROUND)) != NULL) { value = g_strdup_printf ("%u,%u,%u", pango_color->color.red, pango_color->color.green, pango_color->color.blue); attrib_set = gail_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_BG_COLOR, value); } pango_attr_iterator_destroy (iter); pango_attr_list_unref (attr); return attrib_set; } /* AtkAttributeSet* (* get_default_attributes) (AtkText *text); */ static gint html_a11y_text_get_offset_at_point (AtkText *text, gint x, gint y, AtkCoordType coords) { GtkHTML *top_html; GtkHTMLA11Y *top_a11y; HTMLEngine *top_e; HTMLObject *obj, *return_obj; gint offset = -1; gint html_x, html_y, html_height, html_width; gint text_x, text_y, text_height, text_width; obj = HTML_A11Y_HTML (text); g_return_val_if_fail(obj && html_object_is_text(obj), -1); atk_component_get_extents (ATK_COMPONENT (text), &text_x, &text_y, &text_width, &text_height, coords); /* check whether in range */ if (x < text_x || x > text_x + text_width || y < text_y || y > text_y + text_height) return -1; top_a11y = html_a11y_get_top_gtkhtml_parent (HTML_A11Y (text)); g_return_val_if_fail (top_a11y, -1); top_html = GTK_HTML_A11Y_GTKHTML (top_a11y); g_return_val_if_fail (top_html && GTK_IS_HTML(top_html) && top_html->engine, -1); top_e = top_html->engine; atk_component_get_extents (ATK_COMPONENT (top_a11y), &html_x, &html_y, &html_width, &html_height, coords); x -= html_x; y -= html_y; return_obj = html_engine_get_object_at (top_e, x, y, (guint *) &offset, FALSE); if (obj == return_obj) return offset; else /*since the point is in range, need to return a valid value */ return 0; } static void html_a11y_text_get_character_extents (AtkText *text, gint offset, gint *x, gint *y, gint *width, gint *height, AtkCoordType coords) { HTMLObject *obj; GtkHTML *html; HTMLEngine *e; gint x1, x2, y1, y2; GtkHTMLA11Y *a11y; obj = HTML_A11Y_HTML (text); g_return_if_fail(obj && html_object_is_text(obj)); a11y = html_a11y_get_top_gtkhtml_parent (HTML_A11Y (text)); g_return_if_fail (a11y); html = GTK_HTML_A11Y_GTKHTML (a11y); g_return_if_fail(html && GTK_IS_HTML(html) && html->engine); e = html->engine; atk_component_get_extents (ATK_COMPONENT (a11y), x, y, width, height, coords); html_object_get_cursor (obj, e->painter, offset, &x1, &y1, &x2, &y2); *x += x1; *y += y1; *height = y2 - y1; /* a reasonable guess */ *width = *height / 2; /* scroll window */ *x -= e->x_offset; *y -= e->y_offset; } static void atk_editable_text_interface_init (AtkEditableTextIface *iface) { g_return_if_fail (iface != NULL); iface->set_text_contents = html_a11y_text_set_text_contents; iface->insert_text = html_a11y_text_insert_text; iface->copy_text = html_a11y_text_copy_text; iface->cut_text = html_a11y_text_cut_text; iface->delete_text = html_a11y_text_delete_text; iface->paste_text = html_a11y_text_paste_text; iface->set_run_attributes = NULL; } static void html_a11y_text_set_text_contents (AtkEditableText *text, const gchar *string) { GtkHTML * html; HTMLText *t; /* fprintf(stderr, "atk set text contents called text %p\n", text);*/ g_return_if_fail(string); html = GTK_HTML_A11Y_GTKHTML(html_a11y_get_gtkhtml_parent(HTML_A11Y(text))); g_return_if_fail(html && html->engine && html_engine_get_editable(html->engine)); t = HTML_TEXT(HTML_A11Y_HTML(text)); g_return_if_fail (t); html_engine_hide_cursor (html->engine); html_cursor_jump_to(html->engine->cursor, html->engine, HTML_OBJECT(t), 0); html_engine_set_mark(html->engine); html_cursor_jump_to(html->engine->cursor, html->engine, HTML_OBJECT(t), t->text_len); html_engine_update_selection_if_necessary (html->engine); html_engine_paste_text(html->engine, string, -1); html_engine_show_cursor (html->engine); g_signal_emit_by_name(html, "grab_focus"); } static void html_a11y_text_insert_text (AtkEditableText *text, const gchar *string, gint length, gint *position) { GtkHTML * html; HTMLText *t; /* fprintf(stderr, "atk insert text called \n"); */ g_return_if_fail(string && (length > 0)); t = HTML_TEXT(HTML_A11Y_HTML(text)); g_return_if_fail (t); html = GTK_HTML_A11Y_GTKHTML(html_a11y_get_gtkhtml_parent(HTML_A11Y(text))); g_return_if_fail(html && html->engine && html_engine_get_editable(html->engine)); html_cursor_jump_to(html->engine->cursor, html->engine, HTML_OBJECT(t), *position); html_engine_paste_text(html->engine, string, -1); } static void html_a11y_text_copy_text (AtkEditableText *text, gint start_pos, gint end_pos) { GtkHTML * html; HTMLText *t; /* fprintf(stderr, "atk copy text called \n"); */ html = GTK_HTML_A11Y_GTKHTML(html_a11y_get_gtkhtml_parent(HTML_A11Y(text))); g_return_if_fail(html && html->engine && html_engine_get_editable(html->engine)); t = HTML_TEXT(HTML_A11Y_HTML(text)); g_return_if_fail (t); html_engine_hide_cursor (html->engine); html_cursor_jump_to(html->engine->cursor, html->engine, HTML_OBJECT(t), start_pos); html_engine_set_mark(html->engine); html_cursor_jump_to(html->engine->cursor, html->engine, HTML_OBJECT(t), end_pos); html_engine_update_selection_if_necessary (html->engine); html_engine_copy(html->engine); html_engine_show_cursor (html->engine); } static void html_a11y_text_cut_text (AtkEditableText *text, gint start_pos, gint end_pos) { GtkHTML * html; HTMLText *t; /* fprintf(stderr, "atk cut text called.\n"); */ html = GTK_HTML_A11Y_GTKHTML(html_a11y_get_gtkhtml_parent(HTML_A11Y(text))); g_return_if_fail(html && html->engine && html_engine_get_editable(html->engine)); t = HTML_TEXT(HTML_A11Y_HTML(text)); g_return_if_fail (t); html_engine_hide_cursor (html->engine); html_cursor_jump_to(html->engine->cursor, html->engine, HTML_OBJECT(t), start_pos); html_engine_set_mark(html->engine); html_cursor_jump_to(html->engine->cursor, html->engine, HTML_OBJECT(t), end_pos); html_engine_update_selection_if_necessary (html->engine); html_engine_cut(html->engine); html_engine_show_cursor (html->engine); g_signal_emit_by_name(html, "grab_focus"); } static void html_a11y_text_delete_text (AtkEditableText *text, gint start_pos, gint end_pos) { GtkHTML * html; HTMLText *t; /* fprintf(stderr, "atk delete text called.\n"); */ html = GTK_HTML_A11Y_GTKHTML(html_a11y_get_gtkhtml_parent(HTML_A11Y(text))); g_return_if_fail(html && html->engine && html_engine_get_editable(html->engine)); t = HTML_TEXT(HTML_A11Y_HTML(text)); g_return_if_fail (t); html_cursor_jump_to(html->engine->cursor, html->engine, HTML_OBJECT(t), start_pos); html_engine_delete_n(html->engine, end_pos-start_pos, TRUE); g_signal_emit_by_name(html, "grab_focus"); } static void html_a11y_text_paste_text (AtkEditableText *text, gint position) { GtkHTML * html; HTMLText *t; /* fprintf(stderr, "atk paste text called.\n"); */ html = GTK_HTML_A11Y_GTKHTML(html_a11y_get_gtkhtml_parent(HTML_A11Y(text))); g_return_if_fail(html && html->engine && html_engine_get_editable(html->engine)); t = HTML_TEXT(HTML_A11Y_HTML(text)); g_return_if_fail (t); html_engine_show_cursor (html->engine); html_cursor_jump_to(html->engine->cursor, html->engine, HTML_OBJECT(t), position); html_engine_paste(html->engine); html_engine_show_cursor (html->engine); g_signal_emit_by_name(html, "grab_focus"); } static void atk_hyper_text_interface_init (AtkHypertextIface *iface) { g_return_if_fail (iface != NULL); iface->get_link = html_a11y_text_get_link; iface->get_n_links = html_a11y_text_get_n_links; iface->get_link_index = html_a11y_text_get_link_index; } /* * AtkHyperLink interface */ static AtkHyperlink * html_a11y_text_get_link (AtkHypertext *hypertext, gint link_index) { return html_a11y_hyper_link_new (HTML_A11Y (hypertext), link_index); } static gint html_a11y_text_get_n_links (AtkHypertext *hypertext) { HTMLText *text = HTML_TEXT (HTML_A11Y_HTML (hypertext)); if (!text || ! HTML_IS_TEXT (text)) return 0; return g_slist_length (text->links); } static gint html_a11y_text_get_link_index (AtkHypertext *hypertext, gint char_index) { HTMLObject *obj = HTML_A11Y_HTML (hypertext); Link *link = html_text_get_link_at_offset (HTML_TEXT (obj), char_index); return link ? link->start_offset : -1; }