gtk2/gtk/a11y/gtktextviewaccessible.c
Samuel Thibault a1f206b802 gtktextviewaccessible: update cursor position after text suppression
delete_range_cb is set to be called before the text suppression done by
the gtktextlayout (otherwise it does not work properly). But at that
point the cursor position is not yet up to date.  We thus need to move
the accessibility cursor notification to after the actual text
suppression, by using another callback.

This fixes cursor position in brltty screen reading.

(cherry picked from commit fa6994d033)
2018-04-04 18:26:55 +02:00

1862 lines
58 KiB
C

/* GTK+ - accessibility implementations
* Copyright 2001, 2002, 2003 Sun Microsystems Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <sys/types.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <string.h>
#include <stdlib.h>
#include <glib-object.h>
#include <glib/gstdio.h>
#include <gtk/gtk.h>
#include "gtktextviewaccessibleprivate.h"
#include "gtktextbufferprivate.h"
#include "gtktextviewprivate.h"
#include "gtk/gtkwidgetprivate.h"
struct _GtkTextViewAccessiblePrivate
{
gint insert_offset;
gint selection_bound;
};
static void insert_text_cb (GtkTextBuffer *buffer,
GtkTextIter *arg1,
gchar *arg2,
gint arg3,
gpointer user_data);
static void delete_range_cb (GtkTextBuffer *buffer,
GtkTextIter *arg1,
GtkTextIter *arg2,
gpointer user_data);
static void delete_range_after_cb (GtkTextBuffer *buffer,
GtkTextIter *arg1,
GtkTextIter *arg2,
gpointer user_data);
static void mark_set_cb (GtkTextBuffer *buffer,
GtkTextIter *arg1,
GtkTextMark *arg2,
gpointer user_data);
static void atk_editable_text_interface_init (AtkEditableTextIface *iface);
static void atk_text_interface_init (AtkTextIface *iface);
G_DEFINE_TYPE_WITH_CODE (GtkTextViewAccessible, gtk_text_view_accessible, GTK_TYPE_CONTAINER_ACCESSIBLE,
G_ADD_PRIVATE (GtkTextViewAccessible)
G_IMPLEMENT_INTERFACE (ATK_TYPE_EDITABLE_TEXT, atk_editable_text_interface_init)
G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT, atk_text_interface_init))
static void
gtk_text_view_accessible_initialize (AtkObject *obj,
gpointer data)
{
ATK_OBJECT_CLASS (gtk_text_view_accessible_parent_class)->initialize (obj, data);
obj->role = ATK_ROLE_TEXT;
}
static void
gtk_text_view_accessible_notify_gtk (GObject *obj,
GParamSpec *pspec)
{
AtkObject *atk_obj;
atk_obj = gtk_widget_get_accessible (GTK_WIDGET (obj));
if (!strcmp (pspec->name, "editable"))
{
gboolean editable;
editable = gtk_text_view_get_editable (GTK_TEXT_VIEW (obj));
atk_object_notify_state_change (atk_obj, ATK_STATE_EDITABLE, editable);
}
else
GTK_WIDGET_ACCESSIBLE_CLASS (gtk_text_view_accessible_parent_class)->notify_gtk (obj, pspec);
}
static AtkStateSet*
gtk_text_view_accessible_ref_state_set (AtkObject *accessible)
{
AtkStateSet *state_set;
GtkWidget *widget;
state_set = ATK_OBJECT_CLASS (gtk_text_view_accessible_parent_class)->ref_state_set (accessible);
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
if (widget == NULL)
{
atk_state_set_add_state (state_set, ATK_STATE_DEFUNCT);
return state_set;
}
if (gtk_text_view_get_editable (GTK_TEXT_VIEW (widget)))
atk_state_set_add_state (state_set, ATK_STATE_EDITABLE);
atk_state_set_add_state (state_set, ATK_STATE_MULTI_LINE);
return state_set;
}
static void
gtk_text_view_accessible_change_buffer (GtkTextViewAccessible *accessible,
GtkTextBuffer *old_buffer,
GtkTextBuffer *new_buffer)
{
if (old_buffer)
{
g_signal_handlers_disconnect_matched (old_buffer, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, accessible);
g_signal_emit_by_name (accessible,
"text-changed::delete",
0,
gtk_text_buffer_get_char_count (old_buffer));
}
if (new_buffer)
{
g_signal_connect_after (new_buffer, "insert-text", G_CALLBACK (insert_text_cb), accessible);
g_signal_connect (new_buffer, "delete-range", G_CALLBACK (delete_range_cb), accessible);
g_signal_connect_after (new_buffer, "delete-range", G_CALLBACK (delete_range_after_cb), accessible);
g_signal_connect_after (new_buffer, "mark-set", G_CALLBACK (mark_set_cb), accessible);
g_signal_emit_by_name (accessible,
"text-changed::insert",
0,
gtk_text_buffer_get_char_count (new_buffer));
}
}
static void
gtk_text_view_accessible_widget_set (GtkAccessible *accessible)
{
gtk_text_view_accessible_change_buffer (GTK_TEXT_VIEW_ACCESSIBLE (accessible),
NULL,
gtk_text_view_get_buffer (GTK_TEXT_VIEW (gtk_accessible_get_widget (accessible))));
}
static void
gtk_text_view_accessible_widget_unset (GtkAccessible *accessible)
{
gtk_text_view_accessible_change_buffer (GTK_TEXT_VIEW_ACCESSIBLE (accessible),
gtk_text_view_get_buffer (GTK_TEXT_VIEW (gtk_accessible_get_widget (accessible))),
NULL);
}
static void
gtk_text_view_accessible_class_init (GtkTextViewAccessibleClass *klass)
{
AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
GtkAccessibleClass *accessible_class = GTK_ACCESSIBLE_CLASS (klass);
GtkWidgetAccessibleClass *widget_class = (GtkWidgetAccessibleClass*)klass;
accessible_class->widget_set = gtk_text_view_accessible_widget_set;
accessible_class->widget_unset = gtk_text_view_accessible_widget_unset;
class->ref_state_set = gtk_text_view_accessible_ref_state_set;
class->initialize = gtk_text_view_accessible_initialize;
widget_class->notify_gtk = gtk_text_view_accessible_notify_gtk;
}
static void
gtk_text_view_accessible_init (GtkTextViewAccessible *accessible)
{
accessible->priv = gtk_text_view_accessible_get_instance_private (accessible);
}
static gchar *
gtk_text_view_accessible_get_text (AtkText *text,
gint start_offset,
gint end_offset)
{
GtkTextView *view;
GtkTextBuffer *buffer;
GtkTextIter start, end;
GtkWidget *widget;
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
if (widget == NULL)
return NULL;
view = GTK_TEXT_VIEW (widget);
buffer = gtk_text_view_get_buffer (view);
gtk_text_buffer_get_iter_at_offset (buffer, &start, start_offset);
gtk_text_buffer_get_iter_at_offset (buffer, &end, end_offset);
return gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
}
static gchar *
gtk_text_view_accessible_get_text_after_offset (AtkText *text,
gint offset,
AtkTextBoundary boundary_type,
gint *start_offset,
gint *end_offset)
{
GtkWidget *widget;
GtkTextView *view;
GtkTextBuffer *buffer;
GtkTextIter pos;
GtkTextIter start, end;
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
if (widget == NULL)
return NULL;
view = GTK_TEXT_VIEW (widget);
buffer = gtk_text_view_get_buffer (view);
gtk_text_buffer_get_iter_at_offset (buffer, &pos, offset);
start = end = pos;
if (boundary_type == ATK_TEXT_BOUNDARY_LINE_START)
{
gtk_text_view_forward_display_line (view, &end);
start = end;
gtk_text_view_forward_display_line (view, &end);
}
else if (boundary_type == ATK_TEXT_BOUNDARY_LINE_END)
{
gtk_text_view_forward_display_line_end (view, &end);
start = end;
gtk_text_view_forward_display_line (view, &end);
gtk_text_view_forward_display_line_end (view, &end);
}
else
_gtk_text_buffer_get_text_after (buffer, boundary_type, &pos, &start, &end);
*start_offset = gtk_text_iter_get_offset (&start);
*end_offset = gtk_text_iter_get_offset (&end);
return gtk_text_buffer_get_slice (buffer, &start, &end, FALSE);
}
static gchar *
gtk_text_view_accessible_get_text_at_offset (AtkText *text,
gint offset,
AtkTextBoundary boundary_type,
gint *start_offset,
gint *end_offset)
{
GtkWidget *widget;
GtkTextView *view;
GtkTextBuffer *buffer;
GtkTextIter pos;
GtkTextIter start, end;
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
if (widget == NULL)
return NULL;
view = GTK_TEXT_VIEW (widget);
buffer = gtk_text_view_get_buffer (view);
gtk_text_buffer_get_iter_at_offset (buffer, &pos, offset);
start = end = pos;
if (boundary_type == ATK_TEXT_BOUNDARY_LINE_START)
{
gtk_text_view_backward_display_line_start (view, &start);
gtk_text_view_forward_display_line (view, &end);
}
else if (boundary_type == ATK_TEXT_BOUNDARY_LINE_END)
{
gtk_text_view_backward_display_line_start (view, &start);
if (!gtk_text_iter_is_start (&start))
{
gtk_text_view_backward_display_line (view, &start);
gtk_text_view_forward_display_line_end (view, &start);
}
gtk_text_view_forward_display_line_end (view, &end);
}
else
_gtk_text_buffer_get_text_at (buffer, boundary_type, &pos, &start, &end);
*start_offset = gtk_text_iter_get_offset (&start);
*end_offset = gtk_text_iter_get_offset (&end);
return gtk_text_buffer_get_slice (buffer, &start, &end, FALSE);
}
static gchar *
gtk_text_view_accessible_get_text_before_offset (AtkText *text,
gint offset,
AtkTextBoundary boundary_type,
gint *start_offset,
gint *end_offset)
{
GtkWidget *widget;
GtkTextView *view;
GtkTextBuffer *buffer;
GtkTextIter pos;
GtkTextIter start, end;
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
if (widget == NULL)
return NULL;
view = GTK_TEXT_VIEW (widget);
buffer = gtk_text_view_get_buffer (view);
gtk_text_buffer_get_iter_at_offset (buffer, &pos, offset);
start = end = pos;
if (boundary_type == ATK_TEXT_BOUNDARY_LINE_START)
{
gtk_text_view_backward_display_line_start (view, &start);
end = start;
gtk_text_view_backward_display_line (view, &start);
gtk_text_view_backward_display_line_start (view, &start);
}
else if (boundary_type == ATK_TEXT_BOUNDARY_LINE_END)
{
gtk_text_view_backward_display_line_start (view, &start);
if (!gtk_text_iter_is_start (&start))
{
gtk_text_view_backward_display_line (view, &start);
end = start;
gtk_text_view_forward_display_line_end (view, &end);
if (!gtk_text_iter_is_start (&start))
{
if (gtk_text_view_backward_display_line (view, &start))
gtk_text_view_forward_display_line_end (view, &start);
else
gtk_text_iter_set_offset (&start, 0);
}
}
else
end = start;
}
else
_gtk_text_buffer_get_text_before (buffer, boundary_type, &pos, &start, &end);
*start_offset = gtk_text_iter_get_offset (&start);
*end_offset = gtk_text_iter_get_offset (&end);
return gtk_text_buffer_get_slice (buffer, &start, &end, FALSE);
}
static gunichar
gtk_text_view_accessible_get_character_at_offset (AtkText *text,
gint offset)
{
GtkWidget *widget;
GtkTextIter start, end;
GtkTextBuffer *buffer;
gchar *string;
gunichar unichar;
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
if (widget == NULL)
return '\0';
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
if (offset >= gtk_text_buffer_get_char_count (buffer))
return '\0';
gtk_text_buffer_get_iter_at_offset (buffer, &start, offset);
end = start;
gtk_text_iter_forward_char (&end);
string = gtk_text_buffer_get_slice (buffer, &start, &end, FALSE);
unichar = g_utf8_get_char (string);
g_free (string);
return unichar;
}
static gint
gtk_text_view_accessible_get_character_count (AtkText *text)
{
GtkWidget *widget;
GtkTextBuffer *buffer;
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
if (widget == NULL)
return 0;
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
return gtk_text_buffer_get_char_count (buffer);
}
static gint
get_insert_offset (GtkTextBuffer *buffer)
{
GtkTextMark *insert;
GtkTextIter iter;
insert = gtk_text_buffer_get_insert (buffer);
gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert);
return gtk_text_iter_get_offset (&iter);
}
static gint
gtk_text_view_accessible_get_caret_offset (AtkText *text)
{
GtkWidget *widget;
GtkTextBuffer *buffer;
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
if (widget == NULL)
return 0;
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
return get_insert_offset (buffer);
}
static gboolean
gtk_text_view_accessible_set_caret_offset (AtkText *text,
gint offset)
{
GtkTextView *view;
GtkWidget *widget;
GtkTextBuffer *buffer;
GtkTextIter iter;
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
if (widget == NULL)
return FALSE;
view = GTK_TEXT_VIEW (widget);
buffer = gtk_text_view_get_buffer (view);
gtk_text_buffer_get_iter_at_offset (buffer, &iter, offset);
gtk_text_buffer_place_cursor (buffer, &iter);
gtk_text_view_scroll_to_iter (view, &iter, 0, FALSE, 0, 0);
return TRUE;
}
static gint
gtk_text_view_accessible_get_offset_at_point (AtkText *text,
gint x,
gint y,
AtkCoordType coords)
{
GtkTextView *view;
GtkTextIter iter;
gint x_widget, y_widget, x_surface, y_surface, buff_x, buff_y;
GtkWidget *widget;
GdkSurface *surface;
GdkRectangle rect;
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
if (widget == NULL)
return -1;
view = GTK_TEXT_VIEW (widget);
surface = gtk_widget_get_surface (widget);
gdk_surface_get_origin (surface, &x_widget, &y_widget);
if (coords == ATK_XY_SCREEN)
{
x = x - x_widget;
y = y - y_widget;
}
else if (coords == ATK_XY_WINDOW)
{
surface = gdk_surface_get_toplevel (surface);
gdk_surface_get_origin (surface, &x_surface, &y_surface);
x = x - x_widget + x_surface;
y = y - y_widget + y_surface;
}
else
return -1;
gtk_text_view_window_to_buffer_coords (view, GTK_TEXT_WINDOW_WIDGET,
x, y, &buff_x, &buff_y);
gtk_text_view_get_visible_rect (view, &rect);
/* Clamp point to visible rectangle */
buff_x = CLAMP (buff_x, rect.x, rect.x + rect.width - 1);
buff_y = CLAMP (buff_y, rect.y, rect.y + rect.height - 1);
gtk_text_view_get_iter_at_location (view, &iter, buff_x, buff_y);
/* The iter at a location sometimes points to the next character.
* See bug 111031. We work around that
*/
gtk_text_view_get_iter_location (view, &iter, &rect);
if (buff_x < rect.x)
gtk_text_iter_backward_char (&iter);
return gtk_text_iter_get_offset (&iter);
}
static void
gtk_text_view_accessible_get_character_extents (AtkText *text,
gint offset,
gint *x,
gint *y,
gint *width,
gint *height,
AtkCoordType coords)
{
GtkTextView *view;
GtkTextBuffer *buffer;
GtkTextIter iter;
GtkWidget *widget;
GdkRectangle rectangle;
GdkSurface *surface;
gint x_widget, y_widget, x_surface, y_surface;
*x = 0;
*y = 0;
*width = 0;
*height = 0;
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
if (widget == NULL)
return;
view = GTK_TEXT_VIEW (widget);
buffer = gtk_text_view_get_buffer (view);
gtk_text_buffer_get_iter_at_offset (buffer, &iter, offset);
gtk_text_view_get_iter_location (view, &iter, &rectangle);
surface = gtk_widget_get_surface (widget);
if (surface == NULL)
return;
gdk_surface_get_origin (surface, &x_widget, &y_widget);
*height = rectangle.height;
*width = rectangle.width;
gtk_text_view_buffer_to_surface_coords (view, GTK_TEXT_WINDOW_WIDGET,
rectangle.x, rectangle.y, x, y);
if (coords == ATK_XY_WINDOW)
{
surface = gdk_surface_get_toplevel (surface);
gdk_surface_get_origin (surface, &x_surface, &y_surface);
*x += x_widget - x_surface;
*y += y_widget - y_surface;
}
else if (coords == ATK_XY_SCREEN)
{
*x += x_widget;
*y += y_widget;
}
else
{
*x = 0;
*y = 0;
*height = 0;
*width = 0;
}
}
static AtkAttributeSet *
add_text_attribute (AtkAttributeSet *attributes,
AtkTextAttribute attr,
gchar *value)
{
AtkAttribute *at;
at = g_new (AtkAttribute, 1);
at->name = g_strdup (atk_text_attribute_get_name (attr));
at->value = value;
return g_slist_prepend (attributes, at);
}
static AtkAttributeSet *
add_text_int_attribute (AtkAttributeSet *attributes,
AtkTextAttribute attr,
gint i)
{
gchar *value;
value = g_strdup (atk_text_attribute_get_value (attr, i));
return add_text_attribute (attributes, attr, value);
}
static AtkAttributeSet *
gtk_text_view_accessible_get_run_attributes (AtkText *text,
gint offset,
gint *start_offset,
gint *end_offset)
{
GtkTextView *view;
GtkTextBuffer *buffer;
GtkWidget *widget;
GtkTextIter iter;
AtkAttributeSet *attrib_set = NULL;
GSList *tags, *temp_tags;
gdouble scale = 1;
gboolean val_set = FALSE;
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
if (widget == NULL)
return NULL;
view = GTK_TEXT_VIEW (widget);
buffer = gtk_text_view_get_buffer (view);
gtk_text_buffer_get_iter_at_offset (buffer, &iter, offset);
gtk_text_iter_forward_to_tag_toggle (&iter, NULL);
*end_offset = gtk_text_iter_get_offset (&iter);
gtk_text_iter_backward_to_tag_toggle (&iter, NULL);
*start_offset = gtk_text_iter_get_offset (&iter);
gtk_text_buffer_get_iter_at_offset (buffer, &iter, offset);
tags = gtk_text_iter_get_tags (&iter);
tags = g_slist_reverse (tags);
temp_tags = tags;
while (temp_tags && !val_set)
{
GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
g_object_get (tag, "style-set", &val_set, NULL);
if (val_set)
{
PangoStyle style;
g_object_get (tag, "style", &style, NULL);
attrib_set = add_text_int_attribute (attrib_set, ATK_TEXT_ATTR_STYLE, style);
}
temp_tags = temp_tags->next;
}
val_set = FALSE;
temp_tags = tags;
while (temp_tags && !val_set)
{
GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
g_object_get (tag, "variant-set", &val_set, NULL);
if (val_set)
{
PangoVariant variant;
g_object_get (tag, "variant", &variant, NULL);
attrib_set = add_text_int_attribute (attrib_set, ATK_TEXT_ATTR_VARIANT, variant);
}
temp_tags = temp_tags->next;
}
val_set = FALSE;
temp_tags = tags;
while (temp_tags && !val_set)
{
GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
g_object_get (tag, "stretch-set", &val_set, NULL);
if (val_set)
{
PangoStretch stretch;
g_object_get (tag, "stretch", &stretch, NULL);
attrib_set = add_text_int_attribute (attrib_set, ATK_TEXT_ATTR_STRETCH, stretch);
}
temp_tags = temp_tags->next;
}
val_set = FALSE;
temp_tags = tags;
while (temp_tags && !val_set)
{
GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
g_object_get (tag, "justification-set", &val_set, NULL);
if (val_set)
{
GtkJustification justification;
g_object_get (tag, "justification", &justification, NULL);
attrib_set = add_text_int_attribute (attrib_set, ATK_TEXT_ATTR_JUSTIFICATION, justification);
}
temp_tags = temp_tags->next;
}
val_set = FALSE;
temp_tags = tags;
while (temp_tags && !val_set)
{
GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
GtkTextDirection direction;
g_object_get (tag, "direction", &direction, NULL);
if (direction != GTK_TEXT_DIR_NONE)
{
val_set = TRUE;
attrib_set = add_text_int_attribute (attrib_set, ATK_TEXT_ATTR_DIRECTION, direction);
}
temp_tags = temp_tags->next;
}
val_set = FALSE;
temp_tags = tags;
while (temp_tags && !val_set)
{
GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
g_object_get (tag, "wrap-mode-set", &val_set, NULL);
if (val_set)
{
GtkWrapMode wrap_mode;
g_object_get (tag, "wrap-mode", &wrap_mode, NULL);
attrib_set = add_text_int_attribute (attrib_set, ATK_TEXT_ATTR_WRAP_MODE, wrap_mode);
}
temp_tags = temp_tags->next;
}
val_set = FALSE;
temp_tags = tags;
while (temp_tags && !val_set)
{
GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
g_object_get (tag, "foreground-set", &val_set, NULL);
if (val_set)
{
GdkRGBA *rgba;
gchar *value;
g_object_get (tag, "foreground-rgba", &rgba, NULL);
value = g_strdup_printf ("%u,%u,%u",
(guint) rgba->red * 65535,
(guint) rgba->green * 65535,
(guint) rgba->blue * 65535);
gdk_rgba_free (rgba);
attrib_set = add_text_attribute (attrib_set, ATK_TEXT_ATTR_FG_COLOR, value);
}
temp_tags = temp_tags->next;
}
val_set = FALSE;
temp_tags = tags;
while (temp_tags && !val_set)
{
GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
g_object_get (tag, "background-set", &val_set, NULL);
if (val_set)
{
GdkRGBA *rgba;
gchar *value;
g_object_get (tag, "background-rgba", &rgba, NULL);
value = g_strdup_printf ("%u,%u,%u",
(guint) rgba->red * 65535,
(guint) rgba->green * 65535,
(guint) rgba->blue * 65535);
gdk_rgba_free (rgba);
attrib_set = add_text_attribute (attrib_set, ATK_TEXT_ATTR_BG_COLOR, value);
}
temp_tags = temp_tags->next;
}
val_set = FALSE;
temp_tags = tags;
while (temp_tags && !val_set)
{
GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
g_object_get (tag, "family-set", &val_set, NULL);
if (val_set)
{
gchar *value;
g_object_get (tag, "family", &value, NULL);
attrib_set = add_text_attribute (attrib_set, ATK_TEXT_ATTR_FAMILY_NAME, value);
}
temp_tags = temp_tags->next;
}
val_set = FALSE;
temp_tags = tags;
while (temp_tags && !val_set)
{
GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
g_object_get (tag, "language-set", &val_set, NULL);
if (val_set)
{
gchar *value;
g_object_get (tag, "language", &value, NULL);
attrib_set = add_text_attribute (attrib_set, ATK_TEXT_ATTR_LANGUAGE, value);
}
temp_tags = temp_tags->next;
}
val_set = FALSE;
temp_tags = tags;
while (temp_tags && !val_set)
{
GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
g_object_get (tag, "weight-set", &val_set, NULL);
if (val_set)
{
gint weight;
gchar *value;
g_object_get (tag, "weight", &weight, NULL);
value = g_strdup_printf ("%d", weight);
attrib_set = add_text_attribute (attrib_set, ATK_TEXT_ATTR_WEIGHT, value);
}
temp_tags = temp_tags->next;
}
val_set = FALSE;
/* scale is special as the effective value is the product
* of all specified values
*/
temp_tags = tags;
while (temp_tags)
{
GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
gboolean scale_set;
g_object_get (tag, "scale-set", &scale_set, NULL);
if (scale_set)
{
gdouble font_scale;
g_object_get (tag, "scale", &font_scale, NULL);
val_set = TRUE;
scale *= font_scale;
}
temp_tags = temp_tags->next;
}
if (val_set)
{
gchar *value;
value = g_strdup_printf ("%g", scale);
attrib_set = add_text_attribute (attrib_set, ATK_TEXT_ATTR_SCALE, value);
}
val_set = FALSE;
temp_tags = tags;
while (temp_tags && !val_set)
{
GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
g_object_get (tag, "size-set", &val_set, NULL);
if (val_set)
{
gint size;
gchar *value;
g_object_get (tag, "size", &size, NULL);
value = g_strdup_printf ("%i", size);
attrib_set = add_text_attribute (attrib_set, ATK_TEXT_ATTR_SIZE, value);
}
temp_tags = temp_tags->next;
}
val_set = FALSE;
temp_tags = tags;
while (temp_tags && !val_set)
{
GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
g_object_get (tag, "strikethrough-set", &val_set, NULL);
if (val_set)
{
gboolean strikethrough;
g_object_get (tag, "strikethrough", &strikethrough, NULL);
attrib_set = add_text_int_attribute (attrib_set, ATK_TEXT_ATTR_STRIKETHROUGH, strikethrough);
}
temp_tags = temp_tags->next;
}
val_set = FALSE;
temp_tags = tags;
while (temp_tags && !val_set)
{
GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
g_object_get (tag, "underline-set", &val_set, NULL);
if (val_set)
{
PangoUnderline underline;
g_object_get (tag, "underline", &underline, NULL);
attrib_set = add_text_int_attribute (attrib_set, ATK_TEXT_ATTR_UNDERLINE, underline);
}
temp_tags = temp_tags->next;
}
val_set = FALSE;
temp_tags = tags;
while (temp_tags && !val_set)
{
GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
g_object_get (tag, "rise-set", &val_set, NULL);
if (val_set)
{
gint rise;
gchar *value;
g_object_get (tag, "rise", &rise, NULL);
value = g_strdup_printf ("%i", rise);
attrib_set = add_text_attribute (attrib_set, ATK_TEXT_ATTR_RISE, value);
}
temp_tags = temp_tags->next;
}
val_set = FALSE;
temp_tags = tags;
while (temp_tags && !val_set)
{
GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
g_object_get (tag, "background-full-height-set", &val_set, NULL);
if (val_set)
{
gboolean bg_full_height;
g_object_get (tag, "background-full-height", &bg_full_height, NULL);
attrib_set = add_text_int_attribute (attrib_set, ATK_TEXT_ATTR_BG_FULL_HEIGHT, bg_full_height);
}
temp_tags = temp_tags->next;
}
val_set = FALSE;
temp_tags = tags;
while (temp_tags && !val_set)
{
GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
g_object_get (tag, "pixels-inside-wrap-set", &val_set, NULL);
if (val_set)
{
gint pixels;
gchar *value;
g_object_get (tag, "pixels-inside-wrap", &pixels, NULL);
value = g_strdup_printf ("%i", pixels);
attrib_set = add_text_attribute (attrib_set, ATK_TEXT_ATTR_PIXELS_INSIDE_WRAP, value);
}
temp_tags = temp_tags->next;
}
val_set = FALSE;
temp_tags = tags;
while (temp_tags && !val_set)
{
GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
g_object_get (tag, "pixels-below-lines-set", &val_set, NULL);
if (val_set)
{
gint pixels;
gchar *value;
g_object_get (tag, "pixels-below-lines", &pixels, NULL);
value = g_strdup_printf ("%i", pixels);
attrib_set = add_text_attribute (attrib_set, ATK_TEXT_ATTR_PIXELS_BELOW_LINES, value);
}
temp_tags = temp_tags->next;
}
val_set = FALSE;
temp_tags = tags;
while (temp_tags && !val_set)
{
GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
g_object_get (tag, "pixels-above-lines-set", &val_set, NULL);
if (val_set)
{
gint pixels;
gchar *value;
g_object_get (tag, "pixels-above-lines", &pixels, NULL);
value = g_strdup_printf ("%i", pixels);
attrib_set = add_text_attribute (attrib_set, ATK_TEXT_ATTR_PIXELS_ABOVE_LINES, value);
}
temp_tags = temp_tags->next;
}
val_set = FALSE;
temp_tags = tags;
while (temp_tags && !val_set)
{
GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
g_object_get (tag, "editable-set", &val_set, NULL);
if (val_set)
{
gboolean editable;
g_object_get (tag, "editable", &editable, NULL);
attrib_set = add_text_int_attribute (attrib_set, ATK_TEXT_ATTR_EDITABLE, editable);
}
temp_tags = temp_tags->next;
}
val_set = FALSE;
temp_tags = tags;
while (temp_tags && !val_set)
{
GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
g_object_get (tag, "invisible-set", &val_set, NULL);
if (val_set)
{
gboolean invisible;
g_object_get (tag, "invisible", &invisible, NULL);
attrib_set = add_text_int_attribute (attrib_set, ATK_TEXT_ATTR_INVISIBLE, invisible);
}
temp_tags = temp_tags->next;
}
val_set = FALSE;
temp_tags = tags;
while (temp_tags && !val_set)
{
GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
g_object_get (tag, "indent-set", &val_set, NULL);
if (val_set)
{
gint indent;
gchar *value;
g_object_get (tag, "indent", &indent, NULL);
value = g_strdup_printf ("%i", indent);
attrib_set = add_text_attribute (attrib_set, ATK_TEXT_ATTR_INDENT, value);
}
temp_tags = temp_tags->next;
}
val_set = FALSE;
temp_tags = tags;
while (temp_tags && !val_set)
{
GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
g_object_get (tag, "right-margin-set", &val_set, NULL);
if (val_set)
{
gint margin;
gchar *value;
g_object_get (tag, "right-margin", &margin, NULL);
value = g_strdup_printf ("%i", margin);
attrib_set = add_text_attribute (attrib_set, ATK_TEXT_ATTR_RIGHT_MARGIN, value);
}
temp_tags = temp_tags->next;
}
val_set = FALSE;
temp_tags = tags;
while (temp_tags && !val_set)
{
GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
g_object_get (tag, "left-margin-set", &val_set, NULL);
if (val_set)
{
gint margin;
gchar *value;
g_object_get (tag, "left-margin", &margin, NULL);
value = g_strdup_printf ("%i", margin);
attrib_set = add_text_attribute (attrib_set, ATK_TEXT_ATTR_LEFT_MARGIN, value);
}
temp_tags = temp_tags->next;
}
val_set = FALSE;
g_slist_free (tags);
return attrib_set;
}
static AtkAttributeSet *
gtk_text_view_accessible_get_default_attributes (AtkText *text)
{
GtkTextView *view;
GtkWidget *widget;
GtkTextAttributes *text_attrs;
AtkAttributeSet *attributes;
PangoFontDescription *font;
gchar *value;
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
if (widget == NULL)
return NULL;
view = GTK_TEXT_VIEW (widget);
text_attrs = gtk_text_view_get_default_attributes (view);
attributes = NULL;
font = text_attrs->font;
if (font)
{
attributes = add_text_int_attribute (attributes, ATK_TEXT_ATTR_STYLE,
pango_font_description_get_style (font));
attributes = add_text_int_attribute (attributes, ATK_TEXT_ATTR_VARIANT,
pango_font_description_get_variant (font));
attributes = add_text_int_attribute (attributes, ATK_TEXT_ATTR_STRETCH,
pango_font_description_get_stretch (font));
value = g_strdup (pango_font_description_get_family (font));
attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_FAMILY_NAME, value);
value = g_strdup_printf ("%d", pango_font_description_get_weight (font));
attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_WEIGHT, value);
value = g_strdup_printf ("%i", pango_font_description_get_size (font) / PANGO_SCALE);
attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_SIZE, value);
}
attributes = add_text_int_attribute (attributes, ATK_TEXT_ATTR_JUSTIFICATION, text_attrs->justification);
attributes = add_text_int_attribute (attributes, ATK_TEXT_ATTR_DIRECTION, text_attrs->direction);
attributes = add_text_int_attribute (attributes, ATK_TEXT_ATTR_WRAP_MODE, text_attrs->wrap_mode);
attributes = add_text_int_attribute (attributes, ATK_TEXT_ATTR_EDITABLE, text_attrs->editable);
attributes = add_text_int_attribute (attributes, ATK_TEXT_ATTR_INVISIBLE, text_attrs->invisible);
attributes = add_text_int_attribute (attributes, ATK_TEXT_ATTR_BG_FULL_HEIGHT, text_attrs->bg_full_height);
attributes = add_text_int_attribute (attributes, ATK_TEXT_ATTR_STRIKETHROUGH,
text_attrs->appearance.strikethrough);
attributes = add_text_int_attribute (attributes, ATK_TEXT_ATTR_UNDERLINE,
text_attrs->appearance.underline);
value = g_strdup_printf ("%u,%u,%u",
(guint)(text_attrs->appearance.bg_rgba->red * 65535),
(guint)(text_attrs->appearance.bg_rgba->green * 65535),
(guint)(text_attrs->appearance.bg_rgba->blue * 65535));
attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_BG_COLOR, value);
value = g_strdup_printf ("%u,%u,%u",
(guint)(text_attrs->appearance.fg_rgba->red),
(guint)(text_attrs->appearance.fg_rgba->green),
(guint)(text_attrs->appearance.fg_rgba->blue));
attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_FG_COLOR, value);
value = g_strdup_printf ("%g", text_attrs->font_scale);
attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_SCALE, value);
value = g_strdup ((gchar *)(text_attrs->language));
attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_LANGUAGE, value);
value = g_strdup_printf ("%i", text_attrs->appearance.rise);
attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_RISE, value);
value = g_strdup_printf ("%i", text_attrs->pixels_inside_wrap);
attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_PIXELS_INSIDE_WRAP, value);
value = g_strdup_printf ("%i", text_attrs->pixels_below_lines);
attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_PIXELS_BELOW_LINES, value);
value = g_strdup_printf ("%i", text_attrs->pixels_above_lines);
attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_PIXELS_ABOVE_LINES, value);
value = g_strdup_printf ("%i", text_attrs->indent);
attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_INDENT, value);
value = g_strdup_printf ("%i", text_attrs->left_margin);
attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_LEFT_MARGIN, value);
value = g_strdup_printf ("%i", text_attrs->right_margin);
attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_RIGHT_MARGIN, value);
gtk_text_attributes_unref (text_attrs);
return attributes;
}
static gint
gtk_text_view_accessible_get_n_selections (AtkText *text)
{
GtkWidget *widget;
GtkTextBuffer *buffer;
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
if (widget == NULL)
return 0;
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
if (gtk_text_buffer_get_selection_bounds (buffer, NULL, NULL))
return 1;
return 0;
}
static gchar *
gtk_text_view_accessible_get_selection (AtkText *atk_text,
gint selection_num,
gint *start_pos,
gint *end_pos)
{
GtkTextView *view;
GtkWidget *widget;
GtkTextBuffer *buffer;
GtkTextIter start, end;
gchar *text;
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_text));
if (widget == NULL)
return NULL;
if (selection_num != 0)
return NULL;
view = GTK_TEXT_VIEW (widget);
buffer = gtk_text_view_get_buffer (view);
if (gtk_text_buffer_get_selection_bounds (buffer, &start, &end))
text = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
else
text = NULL;
*start_pos = gtk_text_iter_get_offset (&start);
*end_pos = gtk_text_iter_get_offset (&end);
return text;
}
static gboolean
gtk_text_view_accessible_add_selection (AtkText *text,
gint start_pos,
gint end_pos)
{
GtkWidget *widget;
GtkTextBuffer *buffer;
GtkTextIter start, end;
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
if (widget == NULL)
return FALSE;
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
if (!gtk_text_buffer_get_selection_bounds (buffer, NULL, NULL))
{
gtk_text_buffer_get_iter_at_offset (buffer, &start, start_pos);
gtk_text_buffer_get_iter_at_offset (buffer, &end, end_pos);
gtk_text_buffer_select_range (buffer, &end, &start);
return TRUE;
}
else
return FALSE;
}
static gboolean
gtk_text_view_accessible_remove_selection (AtkText *text,
gint selection_num)
{
GtkWidget *widget;
GtkTextBuffer *buffer;
GtkTextMark *insert;
GtkTextIter iter;
GtkTextIter start, end;
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
if (widget == NULL)
return FALSE;
if (selection_num != 0)
return FALSE;
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
if (gtk_text_buffer_get_selection_bounds (buffer, &start, &end))
{
insert = gtk_text_buffer_get_insert (buffer);
gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert);
gtk_text_buffer_place_cursor (buffer, &iter);
return TRUE;
}
else
return FALSE;
}
static gboolean
gtk_text_view_accessible_set_selection (AtkText *text,
gint selection_num,
gint start_pos,
gint end_pos)
{
GtkWidget *widget;
GtkTextBuffer *buffer;
GtkTextIter start, end;
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
if (widget == NULL)
return FALSE;
if (selection_num != 0)
return FALSE;
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
if (gtk_text_buffer_get_selection_bounds (buffer, &start, &end))
{
gtk_text_buffer_get_iter_at_offset (buffer, &start, start_pos);
gtk_text_buffer_get_iter_at_offset (buffer, &end, end_pos);
gtk_text_buffer_select_range (buffer, &end, &start);
return TRUE;
}
else
return FALSE;
}
static void
atk_text_interface_init (AtkTextIface *iface)
{
iface->get_text = gtk_text_view_accessible_get_text;
iface->get_text_after_offset = gtk_text_view_accessible_get_text_after_offset;
iface->get_text_at_offset = gtk_text_view_accessible_get_text_at_offset;
iface->get_text_before_offset = gtk_text_view_accessible_get_text_before_offset;
iface->get_character_at_offset = gtk_text_view_accessible_get_character_at_offset;
iface->get_character_count = gtk_text_view_accessible_get_character_count;
iface->get_caret_offset = gtk_text_view_accessible_get_caret_offset;
iface->set_caret_offset = gtk_text_view_accessible_set_caret_offset;
iface->get_offset_at_point = gtk_text_view_accessible_get_offset_at_point;
iface->get_character_extents = gtk_text_view_accessible_get_character_extents;
iface->get_n_selections = gtk_text_view_accessible_get_n_selections;
iface->get_selection = gtk_text_view_accessible_get_selection;
iface->add_selection = gtk_text_view_accessible_add_selection;
iface->remove_selection = gtk_text_view_accessible_remove_selection;
iface->set_selection = gtk_text_view_accessible_set_selection;
iface->get_run_attributes = gtk_text_view_accessible_get_run_attributes;
iface->get_default_attributes = gtk_text_view_accessible_get_default_attributes;
}
/* atkeditabletext.h */
static gboolean
gtk_text_view_accessible_set_run_attributes (AtkEditableText *text,
AtkAttributeSet *attributes,
gint start_offset,
gint end_offset)
{
GtkTextView *view;
GtkTextBuffer *buffer;
GtkWidget *widget;
GtkTextTag *tag;
GtkTextIter start;
GtkTextIter end;
gint j;
GdkRGBA *color;
gchar** RGB_vals;
GSList *l;
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
if (widget == NULL)
return FALSE;
view = GTK_TEXT_VIEW (widget);
if (!gtk_text_view_get_editable (view))
return FALSE;
buffer = gtk_text_view_get_buffer (view);
if (attributes == NULL)
return FALSE;
gtk_text_buffer_get_iter_at_offset (buffer, &start, start_offset);
gtk_text_buffer_get_iter_at_offset (buffer, &end, end_offset);
tag = gtk_text_buffer_create_tag (buffer, NULL, NULL);
for (l = attributes; l; l = l->next)
{
gchar *name;
gchar *value;
AtkAttribute *at;
at = l->data;
name = at->name;
value = at->value;
if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_LEFT_MARGIN)))
g_object_set (G_OBJECT (tag), "left-margin", atoi (value), NULL);
else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_RIGHT_MARGIN)))
g_object_set (G_OBJECT (tag), "right-margin", atoi (value), NULL);
else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_INDENT)))
g_object_set (G_OBJECT (tag), "indent", atoi (value), NULL);
else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_PIXELS_ABOVE_LINES)))
g_object_set (G_OBJECT (tag), "pixels-above-lines", atoi (value), NULL);
else if (!strcmp(name, atk_text_attribute_get_name (ATK_TEXT_ATTR_PIXELS_BELOW_LINES)))
g_object_set (G_OBJECT (tag), "pixels-below-lines", atoi (value), NULL);
else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_PIXELS_INSIDE_WRAP)))
g_object_set (G_OBJECT (tag), "pixels-inside-wrap", atoi (value), NULL);
else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_SIZE)))
g_object_set (G_OBJECT (tag), "size", atoi (value), NULL);
else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_RISE)))
g_object_set (G_OBJECT (tag), "rise", atoi (value), NULL);
else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_WEIGHT)))
g_object_set (G_OBJECT (tag), "weight", atoi (value), NULL);
else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_BG_FULL_HEIGHT)))
{
g_object_set (G_OBJECT (tag), "bg-full-height",
(strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_BG_FULL_HEIGHT, 0))),
NULL);
}
else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_LANGUAGE)))
g_object_set (G_OBJECT (tag), "language", value, NULL);
else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_FAMILY_NAME)))
g_object_set (G_OBJECT (tag), "family", value, NULL);
else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_EDITABLE)))
{
g_object_set (G_OBJECT (tag), "editable",
(strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_EDITABLE, 0))),
NULL);
}
else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_INVISIBLE)))
{
g_object_set (G_OBJECT (tag), "invisible",
(strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_EDITABLE, 0))),
NULL);
}
else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_UNDERLINE)))
{
for (j = 0; j < 3; j++)
{
if (!strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_UNDERLINE, j)))
{
g_object_set (G_OBJECT (tag), "underline", j, NULL);
break;
}
}
}
else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_STRIKETHROUGH)))
{
g_object_set (G_OBJECT (tag), "strikethrough",
(strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_STRIKETHROUGH, 0))),
NULL);
}
else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_BG_COLOR)))
{
RGB_vals = g_strsplit (value, ",", 3);
color = g_malloc (sizeof (GdkRGBA));
color->red = atoi (RGB_vals[0]) / 65535.0;
color->green = atoi (RGB_vals[1]) / 65535.0;
color->blue = atoi (RGB_vals[2]) / 65535.0;
g_object_set (G_OBJECT (tag), "background-rgba", color, NULL);
}
else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_FG_COLOR)))
{
RGB_vals = g_strsplit (value, ",", 3);
color = g_malloc (sizeof (GdkRGBA));
color->red = atoi (RGB_vals[0]) / 65535.0;
color->green = atoi (RGB_vals[1]) / 65535.0;
color->blue = atoi (RGB_vals[2]) / 65535.0;
g_object_set (G_OBJECT (tag), "foreground-rgba", color, NULL);
}
else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_STRETCH)))
{
for (j = 0; j < 9; j++)
{
if (!strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_STRETCH, j)))
{
g_object_set (G_OBJECT (tag), "stretch", j, NULL);
break;
}
}
}
else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_JUSTIFICATION)))
{
for (j = 0; j < 4; j++)
{
if (!strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_JUSTIFICATION, j)))
{
g_object_set (G_OBJECT (tag), "justification", j, NULL);
break;
}
}
}
else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_DIRECTION)))
{
for (j = 0; j < 3; j++)
{
if (!strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_DIRECTION, j)))
{
g_object_set (G_OBJECT (tag), "direction", j, NULL);
break;
}
}
}
else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_VARIANT)))
{
for (j = 0; j < 2; j++)
{
if (!strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_VARIANT, j)))
{
g_object_set (G_OBJECT (tag), "variant", j, NULL);
break;
}
}
}
else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_WRAP_MODE)))
{
for (j = 0; j < 3; j++)
{
if (!strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_WRAP_MODE, j)))
{
g_object_set (G_OBJECT (tag), "wrap-mode", j, NULL);
break;
}
}
}
else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_STYLE)))
{
for (j = 0; j < 3; j++)
{
if (!strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_STYLE, j)))
{
g_object_set (G_OBJECT (tag), "style", j, NULL);
break;
}
}
}
else
return FALSE;
}
gtk_text_buffer_apply_tag (buffer, tag, &start, &end);
return TRUE;
}
static void
gtk_text_view_accessible_set_text_contents (AtkEditableText *text,
const gchar *string)
{
GtkTextView *view;
GtkWidget *widget;
GtkTextBuffer *buffer;
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
if (widget == NULL)
return;
view = GTK_TEXT_VIEW (widget);
if (!gtk_text_view_get_editable (view))
return;
buffer = gtk_text_view_get_buffer (view);
gtk_text_buffer_set_text (buffer, string, -1);
}
static void
gtk_text_view_accessible_insert_text (AtkEditableText *text,
const gchar *string,
gint length,
gint *position)
{
GtkTextView *view;
GtkWidget *widget;
GtkTextBuffer *buffer;
GtkTextIter iter;
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
if (widget == NULL)
return;
view = GTK_TEXT_VIEW (widget);
if (!gtk_text_view_get_editable (view))
return;
buffer = gtk_text_view_get_buffer (view);
gtk_text_buffer_get_iter_at_offset (buffer, &iter, *position);
gtk_text_buffer_insert (buffer, &iter, string, length);
}
static void
gtk_text_view_accessible_copy_text (AtkEditableText *text,
gint start_pos,
gint end_pos)
{
GtkWidget *widget;
GtkTextBuffer *buffer;
GtkTextIter start, end;
gchar *str;
GdkClipboard *clipboard;
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
if (widget == NULL)
return;
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
gtk_text_buffer_get_iter_at_offset (buffer, &start, start_pos);
gtk_text_buffer_get_iter_at_offset (buffer, &end, end_pos);
str = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
clipboard = gtk_widget_get_clipboard (widget);
gdk_clipboard_set_text (clipboard, str);
}
static void
gtk_text_view_accessible_cut_text (AtkEditableText *text,
gint start_pos,
gint end_pos)
{
GtkTextView *view;
GtkWidget *widget;
GtkTextBuffer *buffer;
GtkTextIter start, end;
gchar *str;
GdkClipboard *clipboard;
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
if (widget == NULL)
return;
view = GTK_TEXT_VIEW (widget);
if (!gtk_text_view_get_editable (view))
return;
buffer = gtk_text_view_get_buffer (view);
gtk_text_buffer_get_iter_at_offset (buffer, &start, start_pos);
gtk_text_buffer_get_iter_at_offset (buffer, &end, end_pos);
str = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
clipboard = gtk_widget_get_clipboard (widget);
gdk_clipboard_set_text (clipboard, str);
gtk_text_buffer_delete (buffer, &start, &end);
}
static void
gtk_text_view_accessible_delete_text (AtkEditableText *text,
gint start_pos,
gint end_pos)
{
GtkTextView *view;
GtkWidget *widget;
GtkTextBuffer *buffer;
GtkTextIter start_itr;
GtkTextIter end_itr;
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
if (widget == NULL)
return;
view = GTK_TEXT_VIEW (widget);
if (!gtk_text_view_get_editable (view))
return;
buffer = gtk_text_view_get_buffer (view);
gtk_text_buffer_get_iter_at_offset (buffer, &start_itr, start_pos);
gtk_text_buffer_get_iter_at_offset (buffer, &end_itr, end_pos);
gtk_text_buffer_delete (buffer, &start_itr, &end_itr);
}
typedef struct
{
GtkTextBuffer* buffer;
gint position;
} PasteData;
static void
paste_received (GObject *clipboard,
GAsyncResult *result,
gpointer data)
{
PasteData* paste = data;
GtkTextIter pos_itr;
char *text;
text = gdk_clipboard_read_text_finish (GDK_CLIPBOARD (clipboard), result, NULL);
if (text)
{
gtk_text_buffer_get_iter_at_offset (paste->buffer, &pos_itr, paste->position);
gtk_text_buffer_insert (paste->buffer, &pos_itr, text, -1);
g_free (text);
}
g_object_unref (paste->buffer);
}
static void
gtk_text_view_accessible_paste_text (AtkEditableText *text,
gint position)
{
GtkTextView *view;
GtkWidget *widget;
GtkTextBuffer *buffer;
PasteData paste;
GdkClipboard *clipboard;
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
if (widget == NULL)
return;
view = GTK_TEXT_VIEW (widget);
if (!gtk_text_view_get_editable (view))
return;
buffer = gtk_text_view_get_buffer (view);
paste.buffer = buffer;
paste.position = position;
g_object_ref (paste.buffer);
clipboard = gtk_widget_get_clipboard (widget);
gdk_clipboard_read_text_async (clipboard, NULL, paste_received, &paste);
}
static void
atk_editable_text_interface_init (AtkEditableTextIface *iface)
{
iface->set_text_contents = gtk_text_view_accessible_set_text_contents;
iface->insert_text = gtk_text_view_accessible_insert_text;
iface->copy_text = gtk_text_view_accessible_copy_text;
iface->cut_text = gtk_text_view_accessible_cut_text;
iface->delete_text = gtk_text_view_accessible_delete_text;
iface->paste_text = gtk_text_view_accessible_paste_text;
iface->set_run_attributes = gtk_text_view_accessible_set_run_attributes;
}
/* Callbacks */
static void
gtk_text_view_accessible_update_cursor (GtkTextViewAccessible *accessible,
GtkTextBuffer * buffer)
{
int prev_insert_offset, prev_selection_bound;
int insert_offset, selection_bound;
GtkTextIter iter;
prev_insert_offset = accessible->priv->insert_offset;
prev_selection_bound = accessible->priv->selection_bound;
gtk_text_buffer_get_iter_at_mark (buffer, &iter, gtk_text_buffer_get_insert (buffer));
insert_offset = gtk_text_iter_get_offset (&iter);
gtk_text_buffer_get_iter_at_mark (buffer, &iter, gtk_text_buffer_get_selection_bound (buffer));
selection_bound = gtk_text_iter_get_offset (&iter);
if (prev_insert_offset == insert_offset && prev_selection_bound == selection_bound)
return;
accessible->priv->insert_offset = insert_offset;
accessible->priv->selection_bound = selection_bound;
if (prev_insert_offset != insert_offset)
g_signal_emit_by_name (accessible, "text-caret-moved", insert_offset);
if (prev_insert_offset != prev_selection_bound || insert_offset != selection_bound)
g_signal_emit_by_name (accessible, "text-selection-changed");
}
static void
insert_text_cb (GtkTextBuffer *buffer,
GtkTextIter *iter,
gchar *text,
gint len,
gpointer data)
{
GtkTextViewAccessible *accessible = data;
gint position;
gint length;
position = gtk_text_iter_get_offset (iter);
length = g_utf8_strlen (text, len);
g_signal_emit_by_name (accessible, "text-changed::insert", position - length, length);
gtk_text_view_accessible_update_cursor (accessible, buffer);
}
static void
delete_range_cb (GtkTextBuffer *buffer,
GtkTextIter *start,
GtkTextIter *end,
gpointer data)
{
GtkTextViewAccessible *accessible = data;
gint offset, length;
offset = gtk_text_iter_get_offset (start);
length = gtk_text_iter_get_offset (end) - offset;
g_signal_emit_by_name (accessible,
"text-changed::delete",
offset,
length);
}
static void
delete_range_after_cb (GtkTextBuffer *buffer,
GtkTextIter *start,
GtkTextIter *end,
gpointer data)
{
GtkTextViewAccessible *accessible = data;
gtk_text_view_accessible_update_cursor (accessible, buffer);
}
static void
mark_set_cb (GtkTextBuffer *buffer,
GtkTextIter *location,
GtkTextMark *mark,
gpointer data)
{
GtkTextViewAccessible *accessible = data;
/*
* Only generate the signal for the "insert" mark, which
* represents the cursor.
*/
if (mark == gtk_text_buffer_get_insert (buffer))
{
gtk_text_view_accessible_update_cursor (accessible, buffer);
}
else if (mark == gtk_text_buffer_get_selection_bound (buffer))
{
gtk_text_view_accessible_update_cursor (accessible, buffer);
}
}
void
_gtk_text_view_accessible_set_buffer (GtkTextView *textview,
GtkTextBuffer *old_buffer)
{
GtkTextViewAccessible *accessible;
g_return_if_fail (GTK_IS_TEXT_VIEW (textview));
g_return_if_fail (old_buffer == NULL || GTK_IS_TEXT_BUFFER (old_buffer));
accessible = GTK_TEXT_VIEW_ACCESSIBLE (_gtk_widget_peek_accessible (GTK_WIDGET (textview)));
if (accessible == NULL)
return;
gtk_text_view_accessible_change_buffer (accessible,
old_buffer,
gtk_text_view_get_buffer (textview));
}