2008-05-28 Mark Doffman <mark.doffman@codethink.co.uk>
authorMark Doffman <mdoff@silver-wind.(none)>
Wed, 28 May 2008 20:37:20 +0000 (21:37 +0100)
committerMark Doffman <mdoff@silver-wind.(none)>
Wed, 28 May 2008 20:37:20 +0000 (21:37 +0100)
* tests/dummyatk
Rework the LSB dummy implementation, taken from
atk-tests. This is a very simple atk implementation
for the purpose of testing at-spi.

* tests/testapps
Add a test framework for applications used in testing
at-spi. Tests will generally make use of the dummy
atk implmentaion.

31 files changed:
Makefile.am
configure.ac
tests/Makefile.am
tests/dummyatk/Makefile.am [new file with mode: 0644]
tests/dummyatk/my-atk-action.c [new file with mode: 0644]
tests/dummyatk/my-atk-action.h [new file with mode: 0644]
tests/dummyatk/my-atk-component.c [new file with mode: 0644]
tests/dummyatk/my-atk-component.h [new file with mode: 0644]
tests/dummyatk/my-atk-hyperlink.c [new file with mode: 0644]
tests/dummyatk/my-atk-hyperlink.h [new file with mode: 0644]
tests/dummyatk/my-atk-hypertext.c [new file with mode: 0644]
tests/dummyatk/my-atk-hypertext.h [new file with mode: 0644]
tests/dummyatk/my-atk-object.c [new file with mode: 0644]
tests/dummyatk/my-atk-object.h [new file with mode: 0644]
tests/dummyatk/my-atk-streamable-content.c [new file with mode: 0644]
tests/dummyatk/my-atk-streamable-content.h [new file with mode: 0644]
tests/dummyatk/my-atk-text.c [new file with mode: 0644]
tests/dummyatk/my-atk-text.h [new file with mode: 0644]
tests/dummyatk/my-atk-value.c [new file with mode: 0644]
tests/dummyatk/my-atk-value.h [new file with mode: 0644]
tests/dummyatk/my-atk.h [new file with mode: 0644]
tests/dummyatk/resources_storage.c [new file with mode: 0644]
tests/dummyatk/resources_storage.h [new file with mode: 0644]
tests/dummyatk/useful_functions.c [new file with mode: 0644]
tests/dummyatk/useful_functions.h [new file with mode: 0644]
tests/dummyatk/user_marshal.c [new file with mode: 0644]
tests/dummyatk/user_marshal.h [new file with mode: 0644]
tests/testapps/Makefile.am [new file with mode: 0644]
tests/testapps/noop-app.c [new file with mode: 0644]
tests/testapps/object-app.c [new file with mode: 0644]
tests/testapps/test-application.c [new file with mode: 0644]

index d92ac75..edfea24 100644 (file)
@@ -1 +1 @@
-SUBDIRS=xml idl tests tools droute spi-common atk-adaptor registryd
+SUBDIRS=xml idl tests tools droute spi-common atk-adaptor registryd atk-tests
index e4226d0..8b51084 100644 (file)
@@ -18,6 +18,10 @@ PKG_CHECK_MODULES(GOBJ, [gobject-2.0 >= 2.0.0])
 AC_SUBST(GOBJ_LIBS)
 AC_SUBST(GOBJ_CFLAGS)
 
+PKG_CHECK_MODULES(GMODULE, [gmodule-2.0 >= 2.0.0])     
+AC_SUBST(GMODULE_LIBS)
+AC_SUBST(GMODULE_CFLAGS)
+
 PKG_CHECK_MODULES(ATK, [atk >= 1.17.0])
 AC_SUBST(ATK_LIBS)
 AC_SUBST(ATK_CFLAGS)
@@ -110,6 +114,8 @@ AC_CONFIG_FILES([Makefile
                 droute/Makefile
                 spi-common/Makefile
                 registryd/Makefile
-                atk-adaptor/Makefile])
+                atk-adaptor/Makefile
+                tests/dummyatk/Makefile
+                tests/testapps/Makefile])
 
 AC_OUTPUT
index bc906a3..b012e65 100644 (file)
@@ -1,3 +1,5 @@
+SUBDIRS=dummyatk testapps
+
 EXTRA_DIST = \
        AccessibleTree.py      \
        AccessibleTreeCache.py \
diff --git a/tests/dummyatk/Makefile.am b/tests/dummyatk/Makefile.am
new file mode 100644 (file)
index 0000000..397460b
--- /dev/null
@@ -0,0 +1,33 @@
+check_LTLIBRARIES = libdummyatk.la
+
+libdummyatk_la_CFLAGS = $(ATK_CFLAGS)  \
+                       -I$(top_srcdir)
+
+libdummyatk_la_LDFLAGS = -no-undefined \
+                        -module        \
+                        -avoid-version 
+
+libdummyatk_la_LIBADD = $(ATK_LIBS)
+
+libdummyatk_la_SOURCES = my-atk-action.c               \
+                        my-atk-action.h                \
+                        my-atk-component.c             \
+                        my-atk-component.h             \
+                        my-atk-hyperlink.c             \
+                        my-atk-hyperlink.h             \
+                        my-atk-hypertext.c             \
+                        my-atk-hypertext.h             \
+                        my-atk-object.c                \
+                        my-atk-object.h                \
+                        my-atk-streamable-content.c    \
+                        my-atk-streamable-content.h    \
+                        my-atk-text.c                  \
+                        my-atk-text.h                  \
+                        my-atk-value.c                 \
+                        my-atk-value.h                 \
+                        resources_storage.c            \
+                        resources_storage.h            \
+                        useful_functions.c             \
+                        useful_functions.h             \
+                        user_marshal.c                 \
+                        user_marshal.h
diff --git a/tests/dummyatk/my-atk-action.c b/tests/dummyatk/my-atk-action.c
new file mode 100644 (file)
index 0000000..048d0b2
--- /dev/null
@@ -0,0 +1,221 @@
+#include <stdio.h>
+#include <string.h>
+#include <atk/atk.h>
+
+#include "my-atk-action.h"
+
+static GObjectClass *parent_class = NULL;
+//implementaion of the interface
+static gboolean my_atk_action_do_action(AtkAction *action, gint i)
+{
+    MyAtkAction *self = (MyAtkAction*)action;
+    gboolean result = (i>=0) && (i < self->n);
+    self->last_performed_action = result? i : -1;
+    return result;
+}
+static gint my_atk_action_get_n_actions(AtkAction *action)
+{
+    MyAtkAction *self = (MyAtkAction*)action;
+    return self->n;
+}
+static const gchar* my_atk_action_get_description(AtkAction *action, gint i)
+{
+    MyAtkAction *self = (MyAtkAction*)action;
+    if((i>=0) && (i<self->n))
+    {
+         return self->actions[i].description;
+    }
+    else
+    {
+        printf("get_description: Wrong index.\n");
+        return NULL;
+    }
+}
+static const gchar* my_atk_action_get_name(AtkAction *action, gint i)
+{
+    MyAtkAction *self = (MyAtkAction*)action;
+    if((i >= 0) && (i < self->n))
+    {
+         return self->actions[i].name;
+    }
+    else
+    {
+        printf("get_name: Wrong index.\n");
+        return NULL;
+    }
+}
+static const gchar* my_atk_action_get_localized_name(AtkAction *action, gint i)
+{
+    return my_atk_action_get_name(action,i);
+}
+
+static const gchar* my_atk_action_get_keybinding(AtkAction *action, gint i)
+{
+    MyAtkAction *self = (MyAtkAction*)action;
+    if((i >= 0) && (i < self->n))
+    {
+        gchar* keyb = self->actions[i].keybinding;
+        if(keyb == NULL || keybinding_note_define == NULL)
+        {
+            //anywhere(if action has keybinding or not) NULL will return
+            return NULL;
+        }
+        else
+        {
+            //verify, if string mean "no keybinding"
+            return strcmp(keyb, keybinding_note_define) != 0 ? keyb : NULL;
+        }
+    }
+    else
+    {
+        printf("get_keybinding: Wrong index.\n");
+        return NULL;
+    }
+}
+static gboolean my_atk_action_set_description(AtkAction *action, gint i, const gchar *desc)
+{
+    MyAtkAction *self = (MyAtkAction*)action;
+
+    if(!((i >= 0) && (i < self->n)) )
+    {
+        //index out of range, but this is not application error according documentation
+        return FALSE;
+    }
+    //index in correct range
+    if(self->actions[i].description == desc)
+    {
+        //self assignment - return immediately
+        return TRUE;
+    }
+    if(self->actions[i].description != NULL)
+    {
+        //free old value of description if it is not NULL
+        free(self->actions[i].description);
+    }
+    if(desc != NULL)
+    {
+        //dump new value of description if it is not NULL
+        self->actions[i].description = (gchar*)strdup((const char*)desc);
+    }
+    return TRUE;
+}
+//////////
+static void my_atk_action_instance_init(GTypeInstance *instance, gpointer g_class)
+{
+  int i;
+    MyAtkAction *self = (MyAtkAction*)instance;
+    self->n = DEFAULT_NUMBER_ACTIONS;
+    self->actions = g_new(struct OneAction, self->n);
+    if(self->actions == NULL)
+    {
+        self->n = 0;
+        return;
+    }
+    //init fields of action 0 with values which differ from others actions
+    self->actions[0].name = (gchar*)strdup(FIRST_ACTION_NAME);
+    self->actions[0].description = (gchar*)strdup(FIRST_ACTION_DESCRIPTION);
+    self->actions[0].keybinding = (gchar*)strdup(FIRST_ACTION_KEYBINDING);
+
+    for(i = 1; i < self->n; i++)
+    {
+        self->actions[i].name = (gchar*)strdup(DEFAULT_ACTION_NAME);
+        self->actions[i].description = (gchar*)strdup(DEFAULT_ACTION_DESCRIPTION);
+        self->actions[i].keybinding = (gchar*)strdup(DEFAULT_ACTION_KEYBINDING);
+    }
+    self->disposed = FALSE;
+    self->last_performed_action = -1;
+}
+
+static void
+my_atk_action_interface_init(gpointer g_iface, gpointer iface_data)
+{
+    AtkActionIface *klass = (AtkActionIface *)g_iface;
+
+    klass->do_action = my_atk_action_do_action;
+    klass->get_n_actions = my_atk_action_get_n_actions;
+    klass->get_description = my_atk_action_get_description;
+    klass->get_name = my_atk_action_get_name;
+    klass->get_localized_name = my_atk_action_get_localized_name;
+    klass->get_keybinding = my_atk_action_get_keybinding;
+    klass->set_description = my_atk_action_set_description;
+}
+
+static void
+my_atk_action_dispose(GObject *obj)
+{
+    MyAtkAction *self = (MyAtkAction*)obj;
+
+    if(self->disposed)
+    {
+        return;
+    }
+    self->disposed = TRUE;
+
+    G_OBJECT_CLASS(parent_class)->dispose(obj);
+}
+
+static void 
+my_atk_action_finalize(GObject *obj)
+{
+    MyAtkAction *self = (MyAtkAction*)obj;
+  int i;
+
+    for(i = 0; i < self->n; i++)
+    {
+        struct OneAction oneAction = self->actions[i];
+        if(oneAction.name != NULL)
+            free(oneAction.name);
+        if(oneAction.description != NULL)
+            free(oneAction.description);
+        if(oneAction.keybinding != NULL)
+            free(oneAction.keybinding);
+    }
+    if(self->actions != NULL)
+        g_free(self->actions);
+
+    G_OBJECT_CLASS(parent_class)->finalize(obj);
+}
+static void
+my_atk_action_class_init (gpointer g_class, gpointer g_class_data)
+{
+    GObjectClass *gobject_class = G_OBJECT_CLASS(g_class);
+    MyAtkActionClass *klass = MY_ATK_ACTION_CLASS (g_class);
+
+    gobject_class->dispose = my_atk_action_dispose;
+    gobject_class->finalize = my_atk_action_finalize;
+
+    parent_class = g_type_class_peek_parent(klass);
+}
+GType my_atk_action_get_type(void)
+{
+    static GType type = 0;
+    if(type == 0)
+    {
+        static const GTypeInfo info =
+        {
+            sizeof (MyAtkActionClass),
+            NULL,   /* base_init */
+            NULL,   /* base_finalize */
+            my_atk_action_class_init, /* class_init */
+            NULL,   /* class_finalize */
+            NULL,   /* class_data */
+            sizeof (MyAtkAction),
+            0,      /* n_preallocs */
+            my_atk_action_instance_init    /* instance_init */
+        };
+
+        static const GInterfaceInfo iface_info = 
+        {
+            (GInterfaceInitFunc) my_atk_action_interface_init,    /* interface_init */
+            NULL,                                       /* interface_finalize */
+            NULL                                        /* interface_data */
+        };
+        type = g_type_register_static (G_TYPE_OBJECT,
+            "MyAtkAction",
+            &info, 0);
+        g_type_add_interface_static (type,
+            ATK_TYPE_ACTION,
+            &iface_info);
+    }
+    return type;
+}
diff --git a/tests/dummyatk/my-atk-action.h b/tests/dummyatk/my-atk-action.h
new file mode 100644 (file)
index 0000000..dc4da8b
--- /dev/null
@@ -0,0 +1,56 @@
+#ifndef MY_ATK_ACTION_H
+#define MY_ATK_ACTION_H
+//Object, which implement interface AtkAction(all functions)
+#include <glib.h>
+#include <glib-object.h>
+#include <atk/atk.h>
+
+//declarations
+#define MY_TYPE_ATK_ACTION             (my_atk_action_get_type ())
+#define MY_ATK_ACTION(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), MY_TYPE_ATK_ACTION, MyAtkAction))
+#define MY_ATK_ACTION_CLASS(vtable)    (G_TYPE_CHECK_CLASS_CAST ((vtable), MY_TYPE_ATK_ACTION, MyAtkActionClass))
+#define MY_IS_ATK_ACTION(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MY_TYPE_ATK_ACTION))
+#define MY_IS_ATK_ACTION_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), MY_TYPE_ATK_ACTION))
+#define MY_ATK_ACTION_GET_CLASS(inst)  (G_TYPE_INSTANCE_GET_CLASS ((inst), MY_TYPE_ATK_ACTION, MyAtkActionClass))
+
+static const gchar* keybinding_note_define = "none";
+
+#define FIRST_ACTION_NAME "Default action"
+#define FIRST_ACTION_DESCRIPTION "This action will be perfomed by default"
+#define FIRST_ACTION_KEYBINDING "12345"
+
+#define DEFAULT_NUMBER_ACTIONS 10
+#define DEFAULT_ACTION_NAME "Action"
+#define DEFAULT_ACTION_DESCRIPTION "Description of action"
+#define DEFAULT_ACTION_KEYBINDING keybinding_note_define
+
+
+//for external using
+#define LAST_PERFORMED_ACTION(myAtkAction) (MY_ATK_ACTION(myAtkAction)->last_performed_action)
+#define CLEAR_LAST_PERFOMED_ACTION(myAtkAction) (MY_ATK_ACTION(myAtkAction)->last_performed_action = -1
+
+typedef struct _MyAtkAction MyAtkAction;
+typedef struct _MyAtkActionClass MyAtkActionClass;
+
+struct _MyAtkAction
+{
+    GObject parent;
+
+    gboolean disposed;
+    struct OneAction
+    {
+        gchar *name;
+        gchar *description;
+        gchar *keybinding;
+    }*actions;
+    gint n;
+    gint last_performed_action;//this field is changed when perfoms action
+};
+
+struct _MyAtkActionClass
+{
+    GObjectClass parent;
+};
+GType my_atk_action_get_type(void);
+
+#endif /*MY_ATK_ACTION_H*/
diff --git a/tests/dummyatk/my-atk-component.c b/tests/dummyatk/my-atk-component.c
new file mode 100644 (file)
index 0000000..e304c7d
--- /dev/null
@@ -0,0 +1,403 @@
+#include <stdio.h>
+#include <atk/atk.h>
+#include <limits.h>
+
+#include "my-atk-object.h"
+#include "my-atk-component.h"
+
+//*************************implementation***********************
+static MyAtkObjectClass *component_parent_class = NULL;
+//current focus object
+static AtkComponent* focus_object = NULL;
+
+static guint focus_signal_id = 0;
+/*
+ * Because of implementation of AtkUtils, we need to ensure that list of focus_trackers 
+ * is not empty. Otherwise function atk_focus_tracker_notify will not change focus.
+ */
+static guint focus_tracker_id = 0;
+static void my_event_listener(AtkObject* obj)
+{
+    //simply exist for register as focus_tracker
+}
+/*
+ * If this flag is TRUE, then focus cannot be changed until someone clears the flag
+ * via my_atk_component_set_modal(FALSE).
+ */
+static gboolean is_modal = FALSE;
+//for debug
+void print_extent(AtkRectangle *extent)
+{
+    printf("{%d,%d,%d,%d}", extent->x, extent->y, extent->width, extent->height);
+}
+//for internal use
+static void emit_bounds_changed(MyAtkComponent *component)
+{
+    static guint bounds_changed_id = 0;
+    if(bounds_changed_id == 0)
+    {
+        bounds_changed_id = g_signal_lookup("bounds-changed", ATK_TYPE_COMPONENT);
+    }
+    AtkRectangle *param = g_boxed_copy(ATK_TYPE_RECTANGLE, &(component->extent));
+    g_signal_emit(component, bounds_changed_id, 0, param);
+}
+static void change_focus(AtkComponent* component, gboolean is_gain)
+{
+    const gchar* state_name = atk_state_type_get_name(ATK_STATE_FOCUSED);
+    
+    g_signal_emit_by_name(component, "focus-event", is_gain);
+    g_signal_emit_by_name(component, "state-change::focused",
+        state_name, is_gain);
+    
+    AtkObject* parent = atk_object_get_parent((AtkObject*)component);
+    if(parent != NULL)
+    {
+        AtkStateSet* stateSet = atk_object_ref_state_set(parent);
+        if(atk_state_set_contains_state(stateSet, ATK_STATE_MANAGES_DESCENDANTS))
+            g_signal_emit_by_name(parent, "active-descendant-changed",
+                atk_get_focus_object());
+        g_object_unref(stateSet);
+    }
+}
+//implementation of virtual functions
+//******************ref_state_set(AtkObject)*****************************
+static AtkStateSet* my_atk_component_ref_state_set(AtkObject *object)
+{
+    MyAtkComponent *self = (MyAtkComponent*)object;
+    
+    AtkStateSet* result = ((AtkObjectClass*)component_parent_class)->
+        ref_state_set(object);
+    if(self->is_manage_descendants) 
+        atk_state_set_add_state(result, ATK_STATE_MANAGES_DESCENDANTS);
+    return result;
+}
+//******************get_size*******************
+static void my_atk_component_get_size(AtkComponent *component, gint *width, gint *height)
+{
+    g_return_if_fail(MY_IS_ATK_COMPONENT(component));
+    
+    MyAtkComponent *self = MY_ATK_COMPONENT(component);
+    *width = self->extent.width;
+    *height = self->extent.height;
+}
+//*********************get_position*******************
+static void my_atk_component_get_position(AtkComponent *component, gint *x, gint *y, AtkCoordType coord_type)
+{
+    g_return_if_fail(MY_IS_ATK_COMPONENT(component));
+    
+    MyAtkComponent *self = MY_ATK_COMPONENT(component);
+    *x = self->extent.x;
+    *y = self->extent.y;
+    
+//**********************get_extents*******************
+}
+static void my_atk_component_get_extents(AtkComponent *component, gint *x, gint *y,
+    gint *width, gint *height, AtkCoordType coord_type)
+{
+    g_return_if_fail(MY_IS_ATK_COMPONENT(component));
+    
+    MyAtkComponent *self = MY_ATK_COMPONENT(component);
+    *x = self->extent.x;
+    *y = self->extent.y;
+    *width = self->extent.width;
+    *height = self->extent.height;
+}
+
+//**************************set_size*******************
+static gboolean my_atk_component_set_size(AtkComponent *component, gint width, gint height)
+{
+    g_return_val_if_fail(MY_IS_ATK_COMPONENT(component), FALSE);
+    
+    MyAtkComponent *self = MY_ATK_COMPONENT(component);
+    if(self->is_extent_may_changed)
+    {
+        self->extent.width = width;
+        self->extent.height = height;
+    
+        emit_bounds_changed(self);
+        
+        return TRUE;
+    }
+    return FALSE;
+}
+//**************************set_position********************
+static gboolean my_atk_component_set_position(AtkComponent *component,
+    gint x, gint y, AtkCoordType coord_type)
+{
+    g_return_val_if_fail(MY_IS_ATK_COMPONENT(component), FALSE);
+    
+    MyAtkComponent *self = MY_ATK_COMPONENT(component);
+    if(self->is_extent_may_changed)
+    {
+        self->extent.x = x;
+        self->extent.y = y;
+        
+        emit_bounds_changed(self);
+
+        return TRUE;
+    }
+    return FALSE;
+}
+//*************************************set_extents***************
+static gboolean my_atk_component_set_extents(AtkComponent *component,
+    gint x, gint y, gint width, gint height, AtkCoordType coord_type)
+{
+    g_return_val_if_fail(MY_IS_ATK_COMPONENT(component), FALSE);
+    
+    MyAtkComponent *self = MY_ATK_COMPONENT(component);
+    
+    if(self->is_extent_may_changed)
+    {
+        self->extent.x = x;
+        self->extent.y = y;
+        self->extent.width = width;
+        self->extent.height = height;
+        
+        emit_bounds_changed(self);
+        
+        return TRUE;
+    }
+    return FALSE;
+}
+//**************************get_layer****************
+static AtkLayer my_atk_component_get_layer(AtkComponent *component)
+{
+    g_return_val_if_fail(MY_IS_ATK_COMPONENT(component), ATK_LAYER_INVALID);
+    
+    MyAtkComponent *self = MY_ATK_COMPONENT(component);
+    return self->layer;
+}
+//**************************get_mdi_zorder****************
+static gint my_atk_component_get_mdi_zorder(AtkComponent *component)
+{
+    g_return_val_if_fail(MY_IS_ATK_COMPONENT(component), G_MININT);
+    
+    MyAtkComponent *self = MY_ATK_COMPONENT(component);
+    return self->zorder;
+}
+//***********************contains**********************
+static gboolean my_atk_component_contains(AtkComponent *component,
+    gint x, gint y, AtkCoordType coord_type)
+{
+    g_return_val_if_fail(MY_IS_ATK_COMPONENT(component), FALSE);
+    //for extract extent
+    gint x_tmp, y_tmp, width_tmp, height_tmp;
+    my_atk_component_get_extents(component, &x_tmp, &y_tmp, &width_tmp, &height_tmp, coord_type);
+    
+    if( (x >= x_tmp) &&(y >= y_tmp) &&(x < x_tmp + width_tmp) && (y < y_tmp + height_tmp) )
+    {
+        return TRUE;
+    }
+    else
+    {
+        return FALSE;
+    }
+}
+//**********************ref_accessible_at_point***********************
+/*
+ * Retuns accessible child that implements AtkCOmponent and contains the given point.
+ */
+static AtkObject* my_atk_component_ref_accessible_at_point(AtkComponent* component,
+    gint x, gint y, AtkCoordType coord_type)
+{
+    g_return_val_if_fail(MY_IS_ATK_COMPONENT(component), NULL);
+    gint i;
+    
+    gint n_children = atk_object_get_n_accessible_children((AtkObject*)component);
+    for(i = 0; i < n_children; i++)
+    {
+        AtkObject *child = atk_object_ref_accessible_child((AtkObject*)component, i);
+        if(ATK_IS_COMPONENT(child)
+            && atk_component_contains((AtkComponent*)child, x, y, coord_type))
+        {
+            return child;
+        }
+        g_object_unref(child);
+    }
+    return NULL;
+}
+//*************************************grab_focus*********************************
+static gboolean my_atk_component_grab_focus(AtkComponent* component)
+{
+    if(component == focus_object)
+    { 
+        //Already has focus
+        return TRUE;
+    }
+    if(is_modal)
+    {
+        //cannot grab focus
+        return FALSE;
+    }
+    AtkComponent *focus_object_old = focus_object;
+    focus_object = component;
+    
+    atk_focus_tracker_notify((AtkObject*)component);
+    
+    if(focus_object_old != NULL)
+    {
+        //signals for object which lost focus
+        change_focus(focus_object_old, FALSE);
+    }
+    if(component != NULL)
+    {
+        //signals for object which grab focus
+        change_focus(component, TRUE);
+    }
+    return TRUE;
+}
+//***********************my_atk_component_add_focus_handler*********************
+static guint my_atk_component_add_focus_handler(AtkComponent *component, AtkFocusHandler handler)
+{
+    g_return_val_if_fail(MY_IS_ATK_COMPONENT(component),0);
+    //verify whether handler already connect to object
+    gulong found_handler_id = g_signal_handler_find(component,
+        G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_FUNC,
+        focus_signal_id,
+        0,
+        NULL,
+        (gpointer)handler,
+        NULL);
+    if(found_handler_id == 0)
+    {
+        //handler hasn't been connected yet
+        return g_signal_connect_closure_by_id(component,
+            focus_signal_id,
+            0,
+            g_cclosure_new( (GCallback)handler,
+                NULL,
+                NULL),
+            FALSE);
+    }
+    else/* found_handler_id != 0*/
+    {
+        //handler has already been connected
+        return 0;
+    }
+
+}
+//***********************my_atk_component_remove_focus_handler*********************
+static void my_atk_component_remove_focus_handler(AtkComponent *component, guint handler_id)
+{
+    g_signal_handler_disconnect(component, handler_id);
+}
+//***********************my_atk_component_set_modal(my function)***************
+void my_atk_component_set_modal(gboolean value)
+{
+    is_modal = value;
+}
+//******************my_atk_component_set_manage_descendants(my_function)*******
+void my_atk_component_set_manage_descendants(MyAtkComponent* component, gboolean value)
+{
+    if(component->is_manage_descendants == value)return;
+    component->is_manage_descendants = value;
+    g_signal_emit_by_name(component, "state-change::manages-descendants",
+        "manages-descendants", value);
+}
+//Others funtions
+static void my_atk_component_instance_init(GTypeInstance *obj, gpointer g_class)
+{
+    MyAtkComponent *self = (MyAtkComponent*)obj;
+    //set defaults values
+    self->extent.x = 0;
+    self->extent.y = 0;
+    self->extent.width = 10;
+    self->extent.height = 10;
+    self->is_extent_may_changed = TRUE;
+    self->layer = ATK_LAYER_INVALID;
+    self->zorder = -2147;
+}
+static void my_atk_component_instance_finalize(GObject* obj)
+{
+    MyAtkComponent* component = (MyAtkComponent*)obj;
+
+    if(((AtkObject*)component) == atk_get_focus_object())
+    {
+        atk_focus_tracker_notify(NULL);
+    }
+}
+
+static void my_atk_component_class_init(gpointer g_class, gpointer class_data)
+{
+    GObjectClass* g_object_class = (GObjectClass*)g_class;
+    AtkObjectClass* atkObject_class = (AtkObjectClass*)g_class;
+    //GObject virtual table
+    g_object_class->finalize = my_atk_component_instance_finalize;
+    //AtkObject virtual table
+    atkObject_class->ref_state_set = my_atk_component_ref_state_set;
+    //parent_class
+    component_parent_class = g_type_class_peek_parent(g_class);
+    //make focus_tracker's table not empty.
+    focus_tracker_id = atk_add_focus_tracker(my_event_listener);
+    //store "focus-event"-signal id
+    focus_signal_id = g_signal_lookup("focus-event",MY_TYPE_ATK_COMPONENT);
+}
+/*
+ * Though, according to the documentation, this function will never called for 
+ * static-registred types.
+ * Base_init function doesn't suite for this work,
+ * because it will called in every derived classes.
+ */
+/*static void my_atk_component_class_finalize(gpointer g_class, gpointer class_data)
+{
+    
+    if(focus_tracker_id != 0)
+    {
+        atk_remove_focus_tracker(focus_tracker_id);
+        focus_tracker_id = 0;
+    }
+}*/
+static void my_atk_component_interface_init(gpointer g_iface, gpointer iface_data)
+{
+    AtkComponentIface *klass = (AtkComponentIface*)g_iface;
+    
+    klass->get_extents = my_atk_component_get_extents;
+    klass->get_position = my_atk_component_get_position;
+    klass->get_size = my_atk_component_get_size;
+    
+    klass->set_extents = my_atk_component_set_extents;
+    klass->set_position = my_atk_component_set_position;
+    klass->set_size = my_atk_component_set_size;
+    
+    klass->contains = my_atk_component_contains;
+    klass->ref_accessible_at_point = my_atk_component_ref_accessible_at_point;
+    
+    klass->get_layer = my_atk_component_get_layer;
+    klass->get_mdi_zorder = my_atk_component_get_mdi_zorder;
+    
+    klass->grab_focus = my_atk_component_grab_focus;
+    klass->add_focus_handler = my_atk_component_add_focus_handler;
+    klass->remove_focus_handler = my_atk_component_remove_focus_handler;
+}
+
+GType my_atk_component_get_type()
+{
+    static GType type = 0;
+    if(type == 0)
+    {
+        static const GTypeInfo typeInfo = 
+        {
+            sizeof(MyAtkComponentClass),
+            NULL, //base_init
+            NULL, //base_finalize
+            my_atk_component_class_init, //class_init
+            NULL, //class_finalize
+            NULL, //class_data
+            sizeof(MyAtkComponent),
+            0, //n_preallocs
+            my_atk_component_instance_init //instance_init
+        };
+
+        static const GInterfaceInfo iface_info = 
+        {
+            my_atk_component_interface_init,    /* interface_init*/
+            NULL,                               /* interface_finalize*/
+            NULL                                /* interface_data */
+        };
+        type = g_type_register_static(MY_TYPE_ATK_OBJECT, "MyAtkComponent", &typeInfo, 0);
+        g_type_add_interface_static(type,
+            ATK_TYPE_COMPONENT,
+            &iface_info);
+    }
+    return type;    
+}
diff --git a/tests/dummyatk/my-atk-component.h b/tests/dummyatk/my-atk-component.h
new file mode 100644 (file)
index 0000000..3fa5850
--- /dev/null
@@ -0,0 +1,41 @@
+#ifndef MY_ATK_COMPONENT_H
+#define MY_ATK_COMPONENT_H
+/*
+ * MyAtkComponent: derives AtkObject(with parent-child accessibilities)
+ * and implements AtkComponent.
+ */
+#include <atk/atk.h>
+
+#include "my-atk-object.h"
+
+#define MY_TYPE_ATK_COMPONENT             (my_atk_component_get_type ())
+#define MY_ATK_COMPONENT(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), MY_TYPE_ATK_COMPONENT, MyAtkComponent))
+#define MY_ATK_COMPONENT_CLASS(vtable)    (G_TYPE_CHECK_CLASS_CAST ((vtable), MY_TYPE_ATK_COMPONENT, MyAtkComponentClass))
+#define MY_IS_ATK_COMPONENT(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MY_TYPE_ATK_COMPONENT))
+#define MY_IS_ATK_COMPONENT_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), MY_TYPE_ATK_COMPONENT))
+#define MY_ATK_COMPONENT_GET_CLASS(inst)  (G_TYPE_INSTANCE_GET_CLASS ((inst), MY_TYPE_ATK_COMPONENT, MyAtkComponentClass))
+
+typedef struct _MyAtkComponent MyAtkComponent;
+typedef struct _MyAtkComponentClass MyAtkComponentClass;
+
+struct _MyAtkComponent
+{
+    MyAtkObject parent;
+    //relative coordinates, which coincides with absolute ones
+    AtkRectangle extent;
+    //whether component may be relocated
+    gboolean is_extent_may_changed;
+    //for emit "active-descendant-changed" signal
+    gboolean is_manage_descendants;
+    //
+    AtkLayer layer;
+    gint zorder;
+};
+
+struct _MyAtkComponentClass
+{
+    MyAtkObjectClass parent;
+};
+
+GType my_atk_component_get_type();
+#endif /*MY_ATK_COMPONENT_H*/
diff --git a/tests/dummyatk/my-atk-hyperlink.c b/tests/dummyatk/my-atk-hyperlink.c
new file mode 100644 (file)
index 0000000..cb6b435
--- /dev/null
@@ -0,0 +1,137 @@
+#include <atk/atk.h>
+
+#include "my-atk-text.h"
+#include "my-atk-hyperlink.h"
+#include "resources_storage.h"
+
+//***************************implementation****************************************
+static MyAtkTextClass *parent_class_atk_hyperlink = NULL;
+
+// Implementation of virtual functions
+//***************************my_atk_hyperlink_get_uri**************
+gchar* my_atk_hyperlink_get_uri(AtkHyperlink* link_, gint index)
+{
+    MyAtkHyperlink* self = (MyAtkHyperlink*)link_;
+    
+    if(index < 0 || index >= self->number_of_anchors) return NULL;
+    return g_strdup(self->uri);
+}
+//**************************my_atk_hyperlink_is_valid**************************
+gboolean my_atk_hyperlink_is_valid(AtkHyperlink* link_)
+{
+    MyAtkHyperlink* self = (MyAtkHyperlink*)link_;
+    
+    return (resource_storage_get(self->uri) != NULL);
+}
+//*************************my_atk_hyperlink_get_object************************
+AtkObject* my_atk_hyperlink_get_object(AtkHyperlink* link_, gint index)
+{
+    MyAtkHyperlink* self = (MyAtkHyperlink*)link_;
+    
+    if(index < 0 || index >= self->number_of_anchors) return NULL;
+    return resource_storage_get(self->uri);
+}
+//***************************my_atk_hyperlink_get_start_index**************
+gint my_atk_hyperlink_get_start_index(AtkHyperlink* link_)
+{
+    MyAtkHyperlink* self = (MyAtkHyperlink*)link_;
+    
+    return self->start_index;
+}
+//***************************my_atk_hyperlink_get_end_index**************
+gint my_atk_hyperlink_get_end_index(AtkHyperlink* link_)
+{
+    MyAtkHyperlink* self = (MyAtkHyperlink*)link_;
+    
+    return self->end_index;
+}
+//***************************my_atk_hyperlink_link_state*******************
+guint my_atk_hyperlink_link_state(AtkHyperlink* link_)
+{
+    return 0;
+}
+//***************************my_atk_hyperlink_get_n_anchors*******************
+gboolean my_atk_hyperlink_get_n_anchors(AtkHyperlink* link_)
+{
+    return ((MyAtkHyperlink*)link_)->number_of_anchors;
+}
+//***************************my_atk_hypertlink_is_selected_link***********
+gboolean my_atk_hyperlink_is_selected_link(AtkHyperlink* link_)
+{
+    MyAtkHyperlink* self = (MyAtkHyperlink*)link_;
+    
+    return self->is_selected;
+}
+//others functions
+MyAtkHyperlink* my_atk_hyperlink_new(gint start_index, gint end_index,const gchar* uri)
+{
+    MyAtkHyperlink* result = g_object_new(MY_TYPE_ATK_HYPERLINK, NULL);
+    if(result == NULL) return NULL;
+    result->start_index = start_index;
+    result->end_index = end_index;
+    result->uri = g_strdup(uri);
+    result->number_of_anchors = 1;
+    return result;
+}
+void my_atk_hyperlink_activate(MyAtkHyperlink* hyperlink)
+{
+    g_signal_emit_by_name(hyperlink, "link-activated");
+}
+//initialize/finalize functions
+static void my_atk_hyperlink_instance_init(GTypeInstance *obj, gpointer g_class)
+{
+    MyAtkHyperlink *self = (MyAtkHyperlink*)obj;
+    
+    self->start_index = self->end_index = 0;
+    self->uri = NULL;
+    self->is_selected = FALSE;
+    self->number_of_anchors = 0;
+}
+static void my_atk_hyperlink_instance_finalize(GObject* obj)
+{
+    MyAtkHyperlink *self = (MyAtkHyperlink*)obj;
+    
+    g_free(self->uri);
+}
+
+static void my_atk_hyperlink_class_init(gpointer g_class, gpointer class_data)
+{
+    GObjectClass* g_object_class = (GObjectClass*)g_class;
+    //GObject virtual table
+    g_object_class->finalize = my_atk_hyperlink_instance_finalize;
+    //parent_class
+    parent_class_atk_hyperlink = g_type_class_peek_parent(g_class);
+    //
+    AtkHyperlinkClass* atkHyperlinkClass = (AtkHyperlinkClass*)g_class;
+    
+    atkHyperlinkClass->get_uri = my_atk_hyperlink_get_uri;
+    atkHyperlinkClass->get_object = my_atk_hyperlink_get_object;
+    atkHyperlinkClass->get_start_index = my_atk_hyperlink_get_start_index;
+    atkHyperlinkClass->get_end_index = my_atk_hyperlink_get_end_index;
+    atkHyperlinkClass->is_valid = my_atk_hyperlink_is_valid;
+    atkHyperlinkClass->link_state = my_atk_hyperlink_link_state;
+    atkHyperlinkClass->get_n_anchors = my_atk_hyperlink_get_n_anchors;
+    atkHyperlinkClass->is_selected_link = my_atk_hyperlink_is_selected_link; 
+}
+
+GType my_atk_hyperlink_get_type()
+{
+    static GType type = 0;
+    if(type == 0)
+    {
+        static const GTypeInfo typeInfo = 
+        {
+            sizeof(MyAtkHyperlinkClass),
+            NULL,                       //base_init
+            NULL,                       //base_finalize
+            my_atk_hyperlink_class_init,     //class_init
+            NULL,                       //class_finalize
+            NULL,                       //class_data
+            sizeof(MyAtkHyperlink),
+            0,                          //n_preallocs
+            my_atk_hyperlink_instance_init   //instance_init
+        };
+        type = g_type_register_static(ATK_TYPE_HYPERLINK, "MyAtkHyperlink", &typeInfo, 0);
+    }
+    return type;    
+}
diff --git a/tests/dummyatk/my-atk-hyperlink.h b/tests/dummyatk/my-atk-hyperlink.h
new file mode 100644 (file)
index 0000000..026ba54
--- /dev/null
@@ -0,0 +1,35 @@
+#ifndef MY_ATK_HYPERLINK_H
+#define MY_ATK_HYPERLINK_H
+/*
+ * MyAtkHyperlink: implements AtkHyperlink
+ */
+#include <atk/atk.h>
+
+#define MY_TYPE_ATK_HYPERLINK             (my_atk_hyperlink_get_type ())
+#define MY_ATK_HYPERLINK(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), MY_TYPE_ATK_HYPERLINK, MyAtkHyperlink))
+#define MY_ATK_HYPERLINK_CLASS(vtable)    (G_TYPE_CHECK_CLASS_CAST ((vtable), MY_TYPE_ATK_HYPERLINK, MyAtkHyperlinkClass))
+#define MY_IS_ATK_HYPERLINK(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MY_TYPE_ATK_HYPERLINK))
+#define MY_IS_ATK_HYPERLINK_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), MY_TYPE_ATK_HYPERLINK))
+#define MY_ATK_HYPERLINK_GET_CLASS(inst)  (G_TYPE_INSTANCE_GET_CLASS ((inst), MY_TYPE_ATK_HYPERLINK, MyAtkHyperlinkClass))
+
+typedef struct _MyAtkHyperlink MyAtkHyperlink;
+typedef struct _MyAtkHyperlinkClass MyAtkHyperlinkClass;
+
+struct _MyAtkHyperlink
+{
+    AtkHyperlink parent;
+    
+    gint start_index, end_index;
+    
+    gchar* uri;
+    gint number_of_anchors;//0 on "clear" hyperlink and 1 after set it
+    gboolean is_selected;
+};
+
+struct _MyAtkHyperlinkClass
+{
+    AtkHyperlinkClass parent;
+};
+
+GType my_atk_hyperlink_get_type();
+#endif /*MY_ATK_HYPERLINK_H*/
diff --git a/tests/dummyatk/my-atk-hypertext.c b/tests/dummyatk/my-atk-hypertext.c
new file mode 100644 (file)
index 0000000..1bba620
--- /dev/null
@@ -0,0 +1,172 @@
+#include <atk/atk.h>
+
+#include "my-atk-text.h"
+#include "my-atk-hyperlink.h"
+#include "my-atk-hypertext.h"
+
+//***************************implementation****************************************
+static MyAtkTextClass *parent_class_atk_text = NULL;
+
+typedef struct
+{
+    gint start_offset, end_offset;
+    gint index;
+}HyperlinkRange;
+// Implementation of virtual functions
+
+//***************************my_atk_hypertext_get_n_links*************************
+gint my_atk_hypertext_get_n_links(AtkHypertext* hypertext)
+{
+    MyAtkHypertext* self = (MyAtkHypertext*)hypertext;
+    
+    return self->hyperlinks->len; 
+}
+//***************************my_atk_hypertext_get_link***********************
+AtkHyperlink* my_atk_hypertext_get_link(AtkHypertext* hypertext, gint link_index)
+{
+    MyAtkHypertext* self = (MyAtkHypertext*)hypertext;
+    
+    if(link_index < 0 || link_index >= self->hyperlinks->len)
+        return NULL;
+    return g_ptr_array_index(self->hyperlinks, link_index);
+}
+//*************************my_atk_hypertext_get_link_index*******************
+gint my_atk_hypertext_get_link_index(AtkHypertext* hypertext, gint char_index)
+{
+    gint i;
+    MyAtkHypertext* self = (MyAtkHypertext*)hypertext;
+    GArray* ranges = self->hyperlink_ranges; 
+    for(i = ranges->len - 1; i >= 0; i--)
+    {
+        HyperlinkRange *range = &g_array_index(ranges, HyperlinkRange, i);
+        if(range->start_offset <= char_index)
+        {
+            if(range->end_offset > char_index)return range->index;
+            break;
+        }
+    }
+    return -1;
+}
+//others functions
+gboolean my_atk_hypertext_add_hyperlink(MyAtkHypertext* hypertext,
+    gint start_index, gint end_index, const gchar* uri)
+{
+    MyAtkHyperlink* new_hyperlink;
+    GArray* ranges = hypertext->hyperlink_ranges;
+    gint i;
+    for(i = 0; i < ranges->len; i++)
+    {
+        HyperlinkRange *range = &g_array_index(ranges, HyperlinkRange, i);
+        if(range->end_offset <= start_index) continue;
+        if(range->start_offset < end_index) return FALSE;
+        break;
+    }
+    new_hyperlink = my_atk_hyperlink_new(start_index, end_index, uri);
+    g_ptr_array_add(hypertext->hyperlinks, new_hyperlink);
+    HyperlinkRange new_range;
+    new_range.start_offset = start_index;
+    new_range.end_offset = end_index;
+    new_range.index = hypertext->hyperlinks->len - 1;
+    g_array_insert_val(ranges, i, new_range);
+    return TRUE;
+}
+//
+void my_atk_hypertext_select_link(MyAtkHypertext* hypertext, gint index)
+{
+    if(index < 0 || index >= my_atk_hypertext_get_n_links((AtkHypertext*)hypertext))
+        return;
+    
+    if(hypertext->current_selected_link != -1)
+    {
+        MyAtkHyperlink *selected_link_old = 
+            (MyAtkHyperlink*)my_atk_hypertext_get_link(
+            (AtkHypertext*)hypertext, hypertext->current_selected_link);
+        selected_link_old->is_selected = FALSE;
+    }
+    
+    hypertext->current_selected_link = index;
+    MyAtkHyperlink *selected_link_new = (MyAtkHyperlink*)my_atk_hypertext_get_link(
+        (AtkHypertext*)hypertext, hypertext->current_selected_link);
+    selected_link_new->is_selected = TRUE;
+    
+    g_signal_emit_by_name(hypertext,
+        "link-selected", hypertext->current_selected_link);    
+}
+//initialize/finalize functions
+static void my_atk_hypertext_instance_init(GTypeInstance *obj, gpointer g_class)
+{
+    MyAtkHypertext *self = (MyAtkHypertext*)obj;
+    
+    self->hyperlink_ranges = g_array_new(FALSE, FALSE, sizeof(HyperlinkRange));
+    self->hyperlinks = g_ptr_array_new();
+    
+    self->current_selected_link = -1;
+}
+static void my_atk_hypertext_instance_finalize(GObject* obj)
+{
+    MyAtkHypertext *self = (MyAtkHypertext*)obj;
+    
+    g_array_free(self->hyperlink_ranges, FALSE);
+    
+    g_ptr_array_foreach(self->hyperlinks,(GFunc)g_object_unref, NULL);
+    g_ptr_array_free(self->hyperlinks, FALSE);
+}
+
+static void my_atk_hypertext_class_init(gpointer g_class, gpointer class_data)
+{
+    GObjectClass* g_object_class = (GObjectClass*)g_class;
+    //GObject virtual table
+    g_object_class->finalize = my_atk_hypertext_instance_finalize;
+    //parent_class
+    parent_class_atk_text = g_type_class_peek_parent(g_class);
+}
+static void my_atk_hypertext_interface_init(gpointer g_iface, gpointer iface_data)
+{
+    AtkHypertextIface *klass = (AtkHypertextIface*)g_iface;
+    
+    klass->get_link = my_atk_hypertext_get_link;
+    klass->get_n_links = my_atk_hypertext_get_n_links;
+    klass->get_link_index = my_atk_hypertext_get_link_index;
+}
+
+GType my_atk_hypertext_get_type()
+{
+    static GType type = 0;
+    if(type == 0)
+    {
+        static const GTypeInfo typeInfo = 
+        {
+            sizeof(MyAtkHypertextClass),
+            NULL,                       //base_init
+            NULL,                       //base_finalize
+            my_atk_hypertext_class_init,     //class_init
+            NULL,                       //class_finalize
+            NULL,                       //class_data
+            sizeof(MyAtkHypertext),
+            0,                          //n_preallocs
+            my_atk_hypertext_instance_init   //instance_init
+        };
+
+        static const GInterfaceInfo AtkTextIface_info = 
+        {
+            my_atk_text_interface_init,         /* interface_init*/
+            NULL,                               /* interface_finalize*/
+            NULL                                /* interface_data */
+        };
+        static const GInterfaceInfo AtkHypertextIface_info = 
+        {
+            my_atk_hypertext_interface_init,/* interface_init*/
+            NULL,                               /* interface_finalize*/
+            NULL                                /* interface_data */
+        };
+        type = g_type_register_static(MY_TYPE_ATK_TEXT, "MyAtkHypertext", &typeInfo, 0);
+        g_type_add_interface_static(type,
+            ATK_TYPE_TEXT,
+            &AtkTextIface_info);
+        
+        g_type_add_interface_static(type,
+            ATK_TYPE_HYPERTEXT,
+            &AtkHypertextIface_info);
+    }
+    return type;    
+}
diff --git a/tests/dummyatk/my-atk-hypertext.h b/tests/dummyatk/my-atk-hypertext.h
new file mode 100644 (file)
index 0000000..50d02cf
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef MY_ATK_HYPERTEXT_H
+#define MY_ATK_HYPERTEXT_H
+/*
+ * MyAtkHypertext: implements AtkHypertext
+ */
+#include <atk/atk.h>
+#include <my-atk-text.h>
+
+#define MY_TYPE_ATK_HYPERTEXT             (my_atk_hypertext_get_type ())
+#define MY_ATK_HYPERTEXT(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), MY_TYPE_ATK_HYPERTEXT, MyAtkHypertext))
+#define MY_ATK_HYPERTEXT_CLASS(vtable)    (G_TYPE_CHECK_CLASS_CAST ((vtable), MY_TYPE_ATK_HYPERTEXT, MyAtkHypertextClass))
+#define MY_IS_ATK_HYPERTEXT(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MY_TYPE_ATK_HYPERTEXT))
+#define MY_IS_ATK_HYPERTEXT_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), MY_TYPE_ATK_HYPERTEXT))
+#define MY_ATK_HYPERTEXT_GET_CLASS(inst)  (G_TYPE_INSTANCE_GET_CLASS ((inst), MY_TYPE_ATK_HYPERTEXT, MyAtkHypertextClass))
+
+typedef struct _MyAtkHypertext MyAtkHypertext;
+typedef struct _MyAtkHypertextClass MyAtkHypertextClass;
+
+struct _MyAtkHypertext
+{
+    MyAtkText parent;
+    
+    GArray* hyperlink_ranges;
+    GPtrArray* hyperlinks;
+    
+    gint current_selected_link;
+};
+
+struct _MyAtkHypertextClass
+{
+    MyAtkTextClass parent;
+};
+#endif /*MY_ATK_HYPERTEXT_H*/
diff --git a/tests/dummyatk/my-atk-object.c b/tests/dummyatk/my-atk-object.c
new file mode 100644 (file)
index 0000000..d45a2ef
--- /dev/null
@@ -0,0 +1,156 @@
+
+#include <atk/atk.h>
+
+#include "my-atk-object.h"
+
+static AtkObjectClass *atk_object_parent_class = NULL;
+
+
+//add/remove child to/from array of parent(for internal use)
+static void my_atk_object_add_child(MyAtkObject* parent, MyAtkObject* child)
+{
+    g_ptr_array_add(parent->children, child);
+    g_object_ref(child);
+    
+    g_signal_emit_by_name(parent, "children-changed::add",
+        parent->children->len - 1, child);
+}
+
+static void my_atk_object_remove_child(MyAtkObject* parent, MyAtkObject* child)
+{
+    gint i;
+    for(i = parent->children->len - 1; i >= 0; i--)
+    {
+        if(g_ptr_array_index(parent->children, i) == child) break;
+    }
+    if(i < 0)return;
+
+    g_ptr_array_remove_index(parent->children, i);
+    g_object_unref(child);
+    g_signal_emit_by_name(parent, "children-changed::remove",
+        i, child);
+}
+
+static void my_atk_object_set_parent(AtkObject *accessible, AtkObject *parent)
+{
+    //applicable only with corresponding type of parent
+    g_return_if_fail(parent == NULL || MY_IS_ATK_OBJECT(parent));
+
+    MyAtkObject *self = MY_ATK_OBJECT(accessible);
+    AtkObject *parent_old = (atk_object_get_parent(accessible));
+    
+    if(parent_old == parent)
+    {
+        //nothing to do because parent does not change
+        return;
+    }
+    
+    //set field 'parent' in child using 'base-method'
+    atk_object_parent_class->set_parent(accessible, parent);
+    
+    if(parent_old != NULL)
+    {
+        my_atk_object_remove_child((MyAtkObject*)parent_old, self);
+    }
+    if(parent != NULL)
+    {
+        my_atk_object_add_child((MyAtkObject*)parent, self);
+    }
+}
+
+static gint my_atk_object_get_n_children(AtkObject *accessible)
+{
+    return MY_ATK_OBJECT(accessible)->children->len;
+}
+
+static AtkObject* my_atk_object_ref_child(AtkObject *accessible, gint i)
+{
+    MyAtkObject *self = MY_ATK_OBJECT(accessible); 
+    if(i < 0 || i >= self->children->len)
+    {
+        printf("ref_child: Incorrect index of child.\n");
+        return NULL;
+    }
+
+    AtkObject* child = (AtkObject*)
+        g_ptr_array_index(self->children, i);
+
+    return (child == NULL) ? NULL : g_object_ref(child);
+}
+
+static gint my_atk_object_get_index_in_parent(AtkObject *accessible)
+{
+    AtkObject *parent = atk_object_get_parent(accessible);
+    if(parent == NULL) return -1;//no parent
+    
+    MyAtkObject *parent_my = MY_ATK_OBJECT(parent);
+
+    int i = parent_my->children->len;
+    for(; i>=0; i--)
+    {
+        if(g_ptr_array_index(parent_my->children,i) == accessible)
+            break;
+    }
+    if(i < 0)printf("Something wrong in parent-child strucutre.\n");
+    return i;//if error, i will be equal to -1
+}
+
+//function, needed in instance_finalize()
+static void my_unref1(gpointer data, gpointer user_data)
+{
+    g_object_unref(data);
+}
+
+static void my_atk_object_instance_finalize(GObject *obj)
+{
+    MyAtkObject *self = (MyAtkObject*) obj;
+    //unrefs all children
+    g_ptr_array_foreach(self->children, my_unref1, NULL);
+    //then free array (without frees pointers)
+    g_ptr_array_free(self->children, FALSE);
+    //chain to parent class
+    G_OBJECT_CLASS(atk_object_parent_class)->finalize(obj);
+}
+
+void my_atk_object_class_init(gpointer g_class, gpointer g_class_data)
+{
+    AtkObjectClass *atkObjectClass = (AtkObjectClass*)g_class;
+    
+    ((GObjectClass*)g_class)->finalize = my_atk_object_instance_finalize;
+    //set pointers to new functions in table of virtuals functions
+    atkObjectClass->set_parent = my_atk_object_set_parent;
+    atkObjectClass->get_n_children = my_atk_object_get_n_children;
+    atkObjectClass->ref_child = my_atk_object_ref_child;
+    atkObjectClass->get_index_in_parent = my_atk_object_get_index_in_parent;
+
+    atk_object_parent_class = g_type_class_peek_parent(g_class);
+}
+
+static void my_atk_object_instance_init(GTypeInstance *obj, gpointer g_class)
+{
+    MyAtkObject *self = (MyAtkObject*)obj;
+
+    self->children = g_ptr_array_sized_new(10);
+}
+
+GType my_atk_object_get_type()
+{
+    static GType type = 0;
+    if(type == 0)
+    {
+        static const GTypeInfo typeInfo = 
+        {
+            sizeof(MyAtkObjectClass),
+            NULL, //base_init
+            NULL, //base_finalize
+            my_atk_object_class_init, //class_init
+            NULL, //class_finalize
+            NULL, //class_data
+            sizeof(MyAtkObject),
+            0, //n_preallocs
+            my_atk_object_instance_init //instance_init
+        };
+        type = g_type_register_static(ATK_TYPE_OBJECT,"MyAtkObject",&typeInfo,0);
+    }
+    return type;
+}
diff --git a/tests/dummyatk/my-atk-object.h b/tests/dummyatk/my-atk-object.h
new file mode 100644 (file)
index 0000000..3e1eed6
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef MY_ATK_OBJECT_H
+#define MY_ATK_OBJECT_H
+
+#include <atk/atk.h>
+
+#define MY_TYPE_ATK_OBJECT             (my_atk_object_get_type ())
+#define MY_ATK_OBJECT(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), MY_TYPE_ATK_OBJECT, MyAtkObject))
+#define MY_ATK_OBJECT_CLASS(vtable)    (G_TYPE_CHECK_CLASS_CAST ((vtable), MY_TYPE_ATK_OBJECT, MyAtkObjectClass))
+#define MY_IS_ATK_OBJECT(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MY_TYPE_ATK_OBJECT))
+#define MY_IS_ATK_OBJECT_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), MY_TYPE_ATK_OBJECT))
+#define MY_ATK_OBJECT_GET_CLASS(inst)  (G_TYPE_INSTANCE_GET_CLASS ((inst), MY_TYPE_ATK_OBJECT, MyAtkObjectClass))
+
+typedef struct _MyAtkObject MyAtkObject;
+typedef struct _MyAtkObjectClass MyAtkObjectClass;
+
+struct _MyAtkObject
+{
+    AtkObject parent;
+    //array of children
+    GPtrArray* children;
+};
+
+struct _MyAtkObjectClass
+{
+    AtkObjectClass parent;
+};
+
+GType my_atk_object_get_type();
+
+#endif /*MY_ATK_OBJECT_H*/
diff --git a/tests/dummyatk/my-atk-streamable-content.c b/tests/dummyatk/my-atk-streamable-content.c
new file mode 100644 (file)
index 0000000..4141344
--- /dev/null
@@ -0,0 +1,95 @@
+#include <atk/atk.h>
+
+#include "my-atk-streamable-content.h"
+
+//*************************implementation***********************
+//implementation of virtual functions
+//*****************get_n_mime_types************
+static gint my_atk_streamable_content_get_n_mime_types(
+    AtkStreamableContent *streamable)
+{
+    g_return_val_if_fail(MY_IS_ATK_STREAMABLE_CONTENT(streamable), 0);
+    
+    return sizeof(mime_types) / sizeof(mime_types[0]);
+}
+//*****************get_mime_type****************
+static const gchar* my_atk_streamable_content_get_mime_type(
+    AtkStreamableContent *streamable,
+    gint i)
+{
+    g_return_val_if_fail(MY_IS_ATK_STREAMABLE_CONTENT(streamable), NULL);
+    
+    if((i < 0) || (i >= sizeof(mime_types) / sizeof(mime_types[0])))
+    {
+        return NULL;
+    }
+    return mime_types[i];
+}
+//**********************get_stream*******************
+static GIOChannel* my_atk_streamable_content_get_stream(
+    AtkStreamableContent *streamable,
+    const gchar* mime_type)
+{
+    gint i;
+    g_return_val_if_fail(MY_IS_ATK_STREAMABLE_CONTENT(streamable), NULL);
+    
+    for(i = 0; i < sizeof(mime_types) / sizeof(mime_types[0]); i++)
+    {
+        if(strcmp(mime_type, mime_types[i]) == 0)
+        {
+            GError *error = NULL;
+            gchar* full_filename = T2C_GET_DATA_PATH(file_names[i]);
+            GIOChannel* channel = g_io_channel_new_file(full_filename, "r", &error);
+            if(error != NULL)
+            {
+                TRACE("Cannot open file '%s' for read: %s", full_filename,
+                    error->message);
+                g_error_free(error);
+            }
+            g_free(full_filename);
+            return channel;
+        }
+    }
+    return NULL;
+}
+//others functions
+static void my_atk_streamable_content_interface_init(gpointer g_iface, gpointer iface_data)
+{
+    AtkStreamableContentIface *klass = (AtkStreamableContentIface*)g_iface;
+    
+    klass->get_n_mime_types = my_atk_streamable_content_get_n_mime_types;
+    klass->get_mime_type = my_atk_streamable_content_get_mime_type;
+    klass->get_stream = my_atk_streamable_content_get_stream;
+}
+
+GType my_atk_streamable_content_get_type()
+{
+    static GType type = 0;
+    if(type == 0)
+    {
+        static const GTypeInfo typeInfo = 
+        {
+            sizeof(MyAtkStreamableContentClass),
+            NULL, //base_init
+            NULL, //base_finalize
+            NULL, //class_init
+            NULL, //class_finalize
+            NULL, //class_data
+            sizeof(MyAtkStreamableContent),
+            0, //n_preallocs
+            NULL //instance_init
+        };
+
+        static const GInterfaceInfo iface_info = 
+        {
+            my_atk_streamable_content_interface_init,    /* interface_init*/
+            NULL,                               /* interface_finalize*/
+            NULL                                /* interface_data */
+        };
+        type = g_type_register_static(G_TYPE_OBJECT, "MyAtkStreamableContent", &typeInfo, 0);
+        g_type_add_interface_static(type,
+            ATK_TYPE_STREAMABLE_CONTENT,
+            &iface_info);
+    }
+    return type;    
+}
diff --git a/tests/dummyatk/my-atk-streamable-content.h b/tests/dummyatk/my-atk-streamable-content.h
new file mode 100644 (file)
index 0000000..d8e0a4e
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef MY_ATK_STREAMABLE_CONTENT_H
+#define MY_ATK_STREAMABLE_CONTENT_H
+
+/*
+ * MyAtkStreamableContent: derives GObject and implements AtkStreamableContent
+ */
+
+#include <atk/atk.h>
+
+#define MY_TYPE_ATK_STREAMABLE_CONTENT             (my_atk_streamable_content_get_type ())
+#define MY_ATK_STREAMABLE_CONTENT(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), MY_TYPE_ATK_STREAMABLE_CONTENT, MyAtkStreamableContent))
+#define MY_ATK_STREAMABLE_CONTENT_CLASS(vtable)    (G_TYPE_CHECK_CLASS_CAST ((vtable), MY_TYPE_ATK_STREAMABLE_CONTENT, MyAtkStreamableContentClass))
+#define MY_IS_ATK_STREAMABLE_CONTENT(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MY_TYPE_ATK_STREAMABLE_CONTENT))
+#define MY_IS_ATK_STREAMABLE_CONTENT_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), MY_TYPE_ATK_STREAMABLE_CONTENT))
+#define MY_ATK_STREAMABLE_CONTENT_GET_CLASS(inst)  (G_TYPE_INSTANCE_GET_CLASS ((inst), MY_TYPE_ATK_STREAMABLE_CONTENT, MyAtkStreamableContentClass))
+
+typedef struct _MyAtkStreamableContent MyAtkStreamableContent;
+typedef struct _MyAtkStreamableContentClass MyAtkStreamableContentClass;
+
+static const gchar* mime_types[]={"text/plain", "text/richtext"};
+static const gchar* file_names[]={"file1", "file2"};
+struct _MyAtkStreamableContent
+{
+    GObject parent;
+};
+
+struct _MyAtkStreamableContentClass
+{
+    GObjectClass parent;
+};
+
+GType my_atk_streamable_content_get_type();
+#endif /*MY_ATK_STREAMABLE_CONTENT_H*/
diff --git a/tests/dummyatk/my-atk-text.c b/tests/dummyatk/my-atk-text.c
new file mode 100644 (file)
index 0000000..caee42e
--- /dev/null
@@ -0,0 +1,1290 @@
+
+#include <atk/atk.h>
+#include <string.h>
+#include <limits.h>
+#include <useful_functions.h>
+
+#include "my-atk-text.h"
+//*************************implementation***********************
+
+//Attributes
+/*
+ * Auxiliary functions create/copy/print/free structures
+ * Use the same naming principe, as atk, but without 'atk' prefix
+ */
+ //AtkAttribute
+AtkAttribute* attribute_new(const gchar* name, const gchar* value)
+{
+    AtkAttribute* attr = g_malloc(sizeof(AtkAttribute));
+    if(attr == NULL) return NULL;
+    attr->name = g_strdup(name);
+    attr->value = g_strdup(value);
+    return attr;
+}
+AtkAttribute* attribute_copy(AtkAttribute* attr)
+{
+    return attribute_new(attr->name, attr->value);
+}
+void attribute_print(AtkAttribute* attr)
+{
+    TRACE("name=%s, value=%s", attr->name, attr->value);
+}
+
+//AtkAttributeSet
+AtkAttributeSet* attribute_set_copy(AtkAttributeSet* attr)
+{
+    GSList *tmp;
+    AtkAttributeSet* result = g_slist_copy(attr);
+    for(tmp = result; tmp != NULL; tmp = tmp->next)
+        tmp->data = attribute_copy((AtkAttribute*)tmp->data);
+    return result;
+}
+void attribute_set_print(AtkAttributeSet *set)
+{
+    if(set == NULL) 
+       TRACE0("(empty)");
+    else 
+       g_slist_foreach(set, (GFunc)attribute_print, NULL);
+}
+
+// STATIC FUNCTIONS
+//
+//auxiliary functions for search tokens
+//Number of different characters
+#define TABLE_SIZE 256  
+//modificator static isn't used because this tables will be use in tests
+/*static*/ gboolean  table_word_symbols[TABLE_SIZE],
+                     table_sentence_symbols[TABLE_SIZE],
+                     table_line_symbols[TABLE_SIZE];
+static gboolean *tables[7]={NULL,
+    table_word_symbols,
+    table_word_symbols,
+    table_sentence_symbols,
+    table_sentence_symbols,
+    table_line_symbols,
+    table_line_symbols
+    };
+
+static gboolean current_token(const gchar* str, gint offset, gint *token_start, gint *token_end,
+    const gboolean table_token_symbols[TABLE_SIZE])
+{
+    const gchar *current = str + offset;
+    if(!table_token_symbols[(guchar)*current])
+    {
+        return FALSE;
+    }
+    for( --current; (current >= str) && table_token_symbols[(guchar)*current]; --current);
+    *token_start = current - str + 1;
+    for(current = str + offset + 1;
+        (*current != 0) && table_token_symbols[(guchar)*current]; ++current);
+    *token_end = current - str;
+    return TRUE;
+}
+static gboolean next_token(const gchar* str, gint offset, gint *token_start, gint *token_end,
+    const gboolean table_token_symbols[TABLE_SIZE])
+{
+    const gchar *current = str + offset;
+    for( ; (*current != 0) && table_token_symbols[(guchar)*current]; ++current);
+    if(*current == 0)
+        return FALSE;
+    for(++current ; (*current != 0) && !table_token_symbols[(guchar)*current]; ++current);
+    if(!table_token_symbols[(guchar)*current])
+        return FALSE;
+    return current_token(str, current - str, token_start, token_end, table_token_symbols);
+}
+static gboolean previous_token(const gchar* str, gint offset, gint *token_start, gint *token_end,
+    const gboolean table_token_symbols[TABLE_SIZE])
+{
+    const gchar *current = str + offset;
+    for( ; (current > str) && table_token_symbols[(guchar)*current]; --current);
+    if(current == str)
+        return FALSE;
+    for( ; (current > str) && !table_token_symbols[(guchar)*current]; --current);
+    if(!table_token_symbols[(guchar)*current])
+        return FALSE;
+    return current_token(str, current - str, token_start, token_end, table_token_symbols);
+}
+
+
+//Range: type of data, containing in list of attributes
+typedef struct
+{
+    gint start,end;//range, containing this attributes
+    AtkAttributeSet* attributeSet;
+} Range;
+//auxiliary functions for ranges
+Range* range_new(gint start, gint end)
+{
+    Range *range = g_malloc(sizeof(Range));
+    range->start = start;
+    range->end = end;
+    range->attributeSet = NULL;
+    return range;
+}
+
+void range_free(Range* range)
+{
+    atk_attribute_set_free(range->attributeSet);
+    g_free(range);
+}
+void range_print(const Range*range)
+{
+    TRACE("[%d,%d):", range->start, range->end);
+    attribute_set_print(range->attributeSet);
+}
+//only for correct list of ranges - ranges shouldn't intersect
+gint range_compare(const Range* range1, const Range* range2)
+{
+    return range1->start - range2->start;//never equal
+}
+//Bounds of text
+void text_bounds_init(TextBounds *bounds)
+{
+    bounds->base_x = 0;
+    bounds->base_y = 0;
+    bounds->pixels_above_line = 2;
+    bounds->pixels_below_line = 3;
+    bounds->size = 8;
+    bounds->pixels_between_characters = 1;
+    bounds->width = 3;
+}
+
+//auxiliary function - create new range according to start_offset and end_offset
+AtkTextRange* text_range_new(AtkText* text,
+    gint start_offset, gint end_offset, AtkCoordType coord_type)
+{
+    AtkTextRange* range = g_malloc(sizeof(AtkTextRange));
+    if(range == NULL) return NULL;
+    range->start_offset = start_offset;
+    range->end_offset = end_offset;
+    range->content = atk_text_get_text(text, start_offset, end_offset);
+    atk_text_get_range_extents(text, start_offset, end_offset, coord_type, &range->bounds);
+    return range;
+}
+// Returns number of line, which contain given character.
+// Also return relative offset - offset of this character from start of the line
+gint get_character_line(MyAtkText *text, gint offset, gint *relative_offset)
+{
+    gint result = 0;
+    //simple realization - counts lines from start of the text, until reaches offset
+    const guchar *text_str = (guchar*)text->str; 
+    gboolean state_FSM = table_line_symbols[text_str[0]];
+    gint i, last_line_start = 0;
+    for(i = 1; i <= offset; state_FSM = table_line_symbols[text_str[i++]])
+    {
+        if(state_FSM)continue;
+        result++;
+        last_line_start = i;
+    }
+    if(relative_offset != NULL) *relative_offset = offset - last_line_start;
+    return result;
+}
+// Compute extent of character,
+// as it was at line 'line' and at offset 'relative_offset' in that line
+//(geometry) 
+void get_extents(MyAtkText *text, gint line, gint relative_offset, AtkTextRectangle *rect)
+{
+    rect->x = text->bounds.base_x + relative_offset * 
+        (text->bounds.width + text->bounds.pixels_between_characters);
+    rect->y = text->bounds.base_y + text->bounds.pixels_above_line + line *
+        (text->bounds.size + text->bounds.pixels_below_line + text->bounds.pixels_above_line);
+    rect->width = text->bounds.width;
+    rect->height = text->bounds.size; 
+}
+//return line, corresponding to given y-coordinate
+gint get_point_line(MyAtkText *text, gint y)
+{
+    //slightly differ from invers operation
+    if(y - text->bounds.base_y < 0)return -1;
+    return  (y - text->bounds.base_y)
+        /(text->bounds.size + text->bounds.pixels_below_line + text->bounds.pixels_above_line);
+}
+// Returns offset from left boundary of text, correspondind to x-coordinate
+gint get_point_relative_offset(MyAtkText *text, gint x)
+{
+    //slightly differ from invers operation
+    if(x - text->bounds.base_x < 0)return -1;
+    return (x - text->bounds.base_x) 
+        /(text->bounds.width + text->bounds.pixels_between_characters);
+}
+// Returns offset where given line start(even if this line is empty)
+// If line number too small(<0)return -1, if too big - return length of the text 
+gint get_offset_at_line_start(MyAtkText *text, gint line)
+{
+    gint i;
+    if(line < 0)return -1;
+    if(line == 0)return 0;
+    gint len = my_strlen(text->str);
+    guchar *str = (guchar*)text->str;
+    gint current_line = 0;
+    gboolean state_FSM = table_line_symbols[str[0]];
+    for(i = 1; i < len; state_FSM = table_line_symbols[str[i++]])
+    {
+        if(state_FSM || ++current_line != line)continue;
+        return i;
+    }
+    return len;
+    
+}
+// Return offset of character at the given line and at the given offset at this line
+// If such character doesn't exist, return -1 
+gint get_offset_at_line(MyAtkText *text, gint line, gint relative_offset)
+{
+    gint j;
+    if(line < 0 || relative_offset < 0)return -1; 
+    const guchar* str = (guchar*)text->str;
+    gint len = my_strlen(text->str);
+    gint offset_at_line_start = get_offset_at_line_start(text, line);
+    if(offset_at_line_start + relative_offset >= len)return -1;
+    for(j = 0; j <= relative_offset; j++) 
+        if(!table_line_symbols[str[offset_at_line_start + j]])
+            return -1;
+    return offset_at_line_start + relative_offset;
+}
+/*
+ * Count ranges of text, which clipping by rel_start_offset and relative_end_offset.
+ * 'offset' stands start of search(start of first line),
+ * number_of_lines - maximum number of lines for search.
+ * If 'ranges' not NULL, writes ranges to it. 'coord_type' used only in this case.
+ */
+gint count_ranges(MyAtkText *text, gint offset, gint rel_start_offset, gint rel_end_offset, 
+                 gint number_of_lines, AtkTextRange** ranges, AtkCoordType coord_type)
+{
+    guchar *str = (guchar*)text->str;
+    gint len = my_strlen(text->str);
+    
+    gint number_of_ranges = 0;
+    gint current_line = 0;
+    gint current_line_start = offset;
+    for(;(current_line < number_of_lines) && (current_line_start < len); current_line ++)
+    {
+        if(!table_line_symbols[str[current_line_start]])
+        {
+            current_line_start++;
+            continue;
+        }
+        gint start_offset,end_offset;
+        gchar *tmp_str = atk_text_get_text_at_offset((AtkText*)text, current_line_start,
+            ATK_TEXT_BOUNDARY_LINE_END, &start_offset, &end_offset);
+        g_free(tmp_str);
+        if(end_offset - current_line_start > rel_start_offset)
+        {
+            if(ranges != NULL)
+            {
+                gint range_start_offset = current_line_start + rel_start_offset;
+                gint range_end_offset =  current_line_start + rel_end_offset + 1;
+                if(range_end_offset > end_offset)
+                    range_end_offset = end_offset;
+                //add element    
+                ranges[number_of_ranges] = text_range_new((AtkText*)text, 
+                    range_start_offset, range_end_offset, coord_type);
+            }
+            number_of_ranges++;
+        }
+        current_line_start = end_offset + 1;
+    }
+    if(ranges != NULL) ranges[number_of_ranges] = NULL;
+    return number_of_ranges;
+}
+
+//"free"-functions(for internal using, because them don't emit signals)
+void my_atk_text_free_run_attributes(MyAtkText *text)
+{
+    g_list_foreach(text->attributes, (GFunc)range_free, NULL);
+    g_list_free(text->attributes);
+    text->attributes = NULL;
+}
+void my_atk_text_free_default_attributes(MyAtkText *text)
+{
+    atk_attribute_set_free(text->default_attributes);
+    text->default_attributes = NULL;
+}
+void my_atk_text_clear_selections(MyAtkText *text)
+{
+    if(text->selections->len != 0)
+        g_array_remove_range(text->selections, 0, text->selections->len);
+}
+void table_symbols_init()
+{
+    //word
+    gint i;
+    for(i = TABLE_SIZE - 1;i > 0 ; --i)
+        table_word_symbols[i] = g_ascii_isalnum(i);
+    table_word_symbols['\0'] = FALSE;
+    //sentence
+    for(i = TABLE_SIZE - 1;i >= 0x20; --i)
+        table_sentence_symbols[i] = TRUE;
+    table_sentence_symbols['.'] = FALSE; 
+    table_sentence_symbols['!'] = FALSE;
+    table_sentence_symbols['?'] = FALSE;
+    for(i = 0x1f;i > 0; --i)
+        table_sentence_symbols[i] = FALSE;
+    table_sentence_symbols['\0'] = FALSE;
+    //line
+    for(i = TABLE_SIZE - 1;i > 0 ; --i)
+        table_line_symbols[i] = TRUE;
+    table_line_symbols['\n'] = FALSE;
+    table_line_symbols['\0'] = FALSE;
+}
+void correct_selections_after_insert(MyAtkText *text, gint position, gint length)
+{
+    gint i;
+    GArray* selections = text->selections;
+    for(i = selections->len - 1; i >=0; i--)
+    {
+        TextSelection* sel  = &g_array_index(selections, TextSelection, i);
+        if(sel->end_offset >= position) sel->end_offset+= length;
+        if(sel->start_offset >= position) sel->start_offset+= length;
+        else break;
+    }
+}
+void correct_selections_after_delete(MyAtkText *text, gint position, gint length)
+{
+    gint i;
+    GArray* selections = text->selections;
+    for(i = selections->len - 1; i >=0; i--)
+    {
+        TextSelection* sel  = &g_array_index(selections, TextSelection, i);
+        if(sel->start_offset >= position)
+        {
+             if(sel->start_offset >= position + length)
+             {
+                sel->start_offset-= length;
+                sel->end_offset-= length;
+             }
+             //position <= sel->start_offset < position + length
+             else if(sel->end_offset > position + length)
+             {
+                sel->start_offset = position;
+                sel->end_offset -= length;
+             }
+             else
+             {
+                g_array_remove_index(selections, i);
+             }
+             continue;  
+        }
+        /*sel->start_offset < position*/
+        if(sel->end_offset > position + length) sel->end_offset-= length;
+        else if(sel->end_offset > position) sel->end_offset = position;
+        break;
+    }
+}
+void correct_attributes_after_insert(MyAtkText* text, gint position, gint length)
+{
+    GList *attributes = text->attributes;
+    GList *tmp;
+    //before inserted position
+    for(tmp = attributes; tmp != NULL; tmp = tmp -> next)
+    {
+        Range* range = (Range*)tmp->data;
+        if(range->end <= position)continue;
+        //range->end > position
+        if(range->start < position)
+            range->start -= length;//will be restore in the next loop
+        break;
+    }
+    //after inserted position
+    for(; tmp != NULL; tmp = tmp -> next)
+    {
+        Range* range = (Range*)tmp->data;
+        range->end += length;
+        range->start += length;
+    }
+}
+void correct_attributes_after_delete(MyAtkText* text, gint position, gint length)
+{
+    GList *attributes = text->attributes;
+    GList *tmp = attributes;
+    //before deleting range
+    for(tmp = attributes; tmp != NULL; tmp = tmp->next)
+    {
+        Range* range  = (Range*)tmp->data;
+        if(range->end <= position) continue;
+        if(range->start < position)
+        {
+             if(range->end > position + length) range->end -= length;
+             else range->end = position;
+             tmp = tmp->next;
+        }
+        break;
+    }
+    //at deleting range
+    while(tmp != NULL)
+    {
+        Range* range = (Range*)tmp->data;
+        if(range->start >= position + length) break;
+        if(range->end <= position + length)
+        {
+            GList *tmp1 = tmp->next;
+            range_free(range);
+            attributes = g_list_remove_link(attributes, tmp);
+            tmp = tmp1;
+            continue;
+        }
+        //range->end > position + length
+        //range->start < position + length
+        range->start = position + length;//will be restored in next loop
+        break;
+    }
+    //after deleting range
+    for(;tmp != NULL; tmp = tmp->next)
+    {
+        Range* range = (Range*)tmp->data;
+        range->end -= length;
+        range->start -= length;
+    }
+    text->attributes = attributes;
+}
+void correct_caret_after_insert(MyAtkText* text, gint position, gint length)
+{
+    if(text->caret_offset > position)text->caret_offset += length;
+}
+void correct_caret_after_delete(MyAtkText* text, gint position, gint length)
+{
+    if(text->caret_offset >= position + length)text->caret_offset -= length;
+    else if(text->caret_offset > position) text->caret_offset = position;
+}
+
+// Implementation of virtual functions
+//******************************my_atk_text_get_character_count*************************
+static gint my_atk_text_get_character_count(AtkText *text)
+{
+    MyAtkText *self = (MyAtkText*)text;
+    return my_strlen(self->str);
+}
+//**************************************my_atk_text_get_text*****************************
+static gchar* my_atk_text_get_text(AtkText *text, gint start_offset, gint end_offset)
+{
+    gchar *str = ((MyAtkText*)text)->str;
+    if((start_offset < 0) || (end_offset > my_strlen(str)) || (end_offset <= start_offset))
+    {
+        //incorrect bounds
+        return NULL;
+    }
+    return g_strndup(str + start_offset, end_offset - start_offset);
+    
+}
+//*******************************my_atk_text_get_character_at_offset************************
+static gunichar my_atk_text_get_character_at_offset(AtkText *text, gint offset)
+{
+    gchar *str = ((MyAtkText*)text)->str;
+    if(offset < 0 || offset >= my_strlen(str))
+    {
+        return 0;
+    }
+    return (gunichar)str[offset];
+}
+// In the next 3 functions some code is commented for verify tests themselves on 'mutants'
+// in realization.
+//******************************my_atk_text_get_text_after_offset***************************
+static gchar* my_atk_text_get_text_after_offset(AtkText *text, gint offset,
+    AtkTextBoundary boundary_type, gint *start_offset, gint *end_offset)
+{
+    gchar *str = ((MyAtkText*)text)->str;
+    gint len = my_strlen(str);
+    if((offset < 0) || (offset >= len))
+    {
+        return NULL;//incorrect offset
+    }
+    
+    // This variable is set in switch statement. If after that statement variable is TRUE,
+    // then return text from 'strat_offset' to 'end_offset'. Otherwise NULL will be returned.
+    gboolean is_successed = TRUE;
+    
+    gint start_tmp;
+    gint end_tmp;
+    
+    switch(boundary_type)
+    {
+    case ATK_TEXT_BOUNDARY_CHAR:
+        if(offset + 1 == len)
+        {
+            is_successed = FALSE;
+            break;
+        }
+        *start_offset = offset + 1;
+        *end_offset = offset + 2;
+        is_successed = TRUE;
+        break;
+    case ATK_TEXT_BOUNDARY_WORD_START:
+    case ATK_TEXT_BOUNDARY_SENTENCE_START:
+    case ATK_TEXT_BOUNDARY_LINE_START:
+        if(!next_token(str, offset, start_offset, &end_tmp, tables[boundary_type]))
+        {
+            //debug
+//            if(current_token(str, offset, start_offset, end_offset, tables[boundary_type]))
+//            {
+//                is_successed = TRUE;
+//                break;
+//            }
+            is_successed = FALSE;
+            break;
+        }
+        if(!next_token(str, end_tmp, end_offset, &end_tmp, tables[boundary_type]))
+        {
+            *end_offset = len;
+        }
+        is_successed = TRUE;
+        //debug
+//        (*start_offset)++;
+//        if(*start_offset > 10) ++(*start_offset);
+        break;
+    case ATK_TEXT_BOUNDARY_WORD_END:
+    case ATK_TEXT_BOUNDARY_SENTENCE_END:
+    case ATK_TEXT_BOUNDARY_LINE_END:
+        if(!current_token(str, offset, &start_tmp, start_offset, tables[boundary_type]))
+        {
+            if(!next_token(str, offset, &start_tmp, start_offset, tables[boundary_type]))
+            {
+                is_successed = FALSE;
+                break;
+            }
+        }
+        //debug
+//        else if(*start_offset > strlen(str) - 7)
+//        {
+//           *end_offset = *start_offset + 3;
+//            is_successed = TRUE;
+//           break;
+//        }
+        if(!next_token(str, *start_offset, &start_tmp, end_offset, tables[boundary_type]))
+        {
+            is_successed = FALSE;
+            break;
+        }
+        //debug
+//        --(*start_offset);
+        is_successed = TRUE;
+        
+        break;
+    default:
+        is_successed = FALSE;
+    }
+
+    if(is_successed)
+    {
+        return my_atk_text_get_text(text, *start_offset, *end_offset);
+    }
+    else
+    {
+        return NULL;
+    }
+}
+//*******************************my_atk_text_get_text_at_offset*******************************
+static gchar* my_atk_text_get_text_at_offset(AtkText *text, gint offset,
+    AtkTextBoundary boundary_type, gint *start_offset, gint *end_offset)
+{
+    gchar *str = ((MyAtkText*)text)->str;
+    gint len = my_strlen(str);
+    if((offset < 0) || (offset >= len))
+    {
+        return NULL;
+    }
+    
+    // This variable is set in switch statement. If after that statement variable is TRUE,
+    // then return text from 'strat_offset' to 'end_offset'. Otherwise NULL will be returned.
+    gboolean is_successed = TRUE;
+    
+    gint start_tmp;
+    gint end_tmp;
+    
+    switch(boundary_type)
+    {
+    case ATK_TEXT_BOUNDARY_CHAR:
+        *start_offset = offset;
+        *end_offset = offset + 1;
+        is_successed = TRUE;
+        break;
+    case ATK_TEXT_BOUNDARY_WORD_START:
+    case ATK_TEXT_BOUNDARY_SENTENCE_START:
+    case ATK_TEXT_BOUNDARY_LINE_START:
+        if(!current_token(str, offset, start_offset, &end_tmp, tables[boundary_type]))
+        {
+            if(!previous_token(str, offset, start_offset, &end_tmp, tables[boundary_type]))
+            {
+                is_successed = FALSE;
+                break;
+            }
+        }
+        if(!next_token(str, offset, end_offset, &end_tmp, tables[boundary_type]))
+        {
+            *end_offset = len;
+        }
+        is_successed = TRUE;
+        break;
+    case ATK_TEXT_BOUNDARY_WORD_END:
+    case ATK_TEXT_BOUNDARY_SENTENCE_END:
+    case ATK_TEXT_BOUNDARY_LINE_END:
+        if(!current_token(str, offset, &start_tmp, end_offset, tables[boundary_type]))
+        {
+            if(!next_token(str, offset, &start_tmp, end_offset, tables[boundary_type]))
+            {
+                is_successed = FALSE;
+                break;
+            }
+        }
+        if(!previous_token(str, start_tmp, &start_tmp, start_offset, tables[boundary_type]))
+        {
+            *start_offset = 0;
+        }
+        is_successed = TRUE;
+        //debug
+//        ++(*start_offset);
+        break;
+    default:
+        is_successed = FALSE;
+    }
+
+    if(is_successed)
+    {
+        //debug
+//        if(boundary_type == ATK_TEXT_BOUNDARY_LINE_START)
+//            return my_atk_text_get_text(text, ++(*start_offset), *end_offset);
+        return my_atk_text_get_text(text, *start_offset, *end_offset);
+    }
+    else
+    {
+        return NULL;
+    }
+}
+//***********************************my_atk_text_get_text_before_offset******************
+static gchar* my_atk_text_get_text_before_offset(AtkText *text, gint offset,
+    AtkTextBoundary boundary_type, gint *start_offset, gint *end_offset)
+{
+    gchar *str = ((MyAtkText*)text)->str;
+    gint len = my_strlen(str);
+    if((offset < 0) || (offset >= len))
+    {
+        return NULL;
+    }
+    
+    // This variable is set in switch statement. If after that statement variable is TRUE,
+    // then return text from 'strat_offset' to 'end_offset'. Otherwise NULL will be returned.
+    gboolean is_successed = TRUE;
+    
+    gint start_tmp;
+    gint end_tmp;
+    
+    switch(boundary_type)
+    {
+    case ATK_TEXT_BOUNDARY_CHAR:
+        if(offset == 0)
+        {
+            is_successed = FALSE;
+            break;
+        }
+        *start_offset = offset - 1;
+        *end_offset = offset;
+        is_successed = TRUE;
+        break;
+    case ATK_TEXT_BOUNDARY_WORD_START:
+    case ATK_TEXT_BOUNDARY_SENTENCE_START:
+    case ATK_TEXT_BOUNDARY_LINE_START:
+        if(!current_token(str, offset, end_offset, &end_tmp, tables[boundary_type]))
+        {
+            if(!previous_token(str, offset, end_offset, &end_tmp, tables[boundary_type]))
+            {
+                is_successed = FALSE;
+                break;
+            }
+        }
+        if(!previous_token(str, *end_offset, start_offset, &end_tmp, tables[boundary_type]))
+        {
+            is_successed = FALSE;
+            break;    
+        }
+        is_successed = TRUE;
+        //debug
+//        ++(*start_offset);
+        break;
+    case ATK_TEXT_BOUNDARY_WORD_END:
+    case ATK_TEXT_BOUNDARY_SENTENCE_END:
+    case ATK_TEXT_BOUNDARY_LINE_END:
+        if(!previous_token(str, offset, &start_tmp, end_offset, tables[boundary_type]))
+        {
+            is_successed = FALSE;
+            break;
+        }
+        if(!previous_token(str, start_tmp, &start_tmp, start_offset, tables[boundary_type]))
+        {
+            *start_offset = 0;
+        }
+        is_successed = TRUE;
+        break;
+    default:
+        is_successed = FALSE;
+    }
+
+    if(is_successed)
+    {
+        return my_atk_text_get_text(text, *start_offset, *end_offset);
+    }
+    else
+    {
+        return NULL;
+    }
+}
+//*********************************my_atk_text_get_run_attributes*****************
+AtkAttributeSet* my_atk_text_get_run_attributes(AtkText* text, gint offset,
+    gint *start_offset, gint *end_offset)
+{
+    GList *tmp;
+    GList *attributes = ((MyAtkText*)text)->attributes;
+    if(offset < 0 || offset >= my_atk_text_get_character_count(text))
+    {
+        TRACE0("Incorrect value of offset.");
+        return NULL;
+    }
+    gint start = -1, end = -1;
+    for(tmp = attributes; tmp != NULL; tmp = tmp->next)
+    {
+        Range* range = (Range*)(tmp->data); 
+        if(range->end <= offset)
+        {
+             start = range->end;
+             continue;
+        }
+        if(range->start > offset)
+        {
+             end = range->start;
+             break;
+        }
+        
+        *start_offset = range->start;
+        *end_offset = range->end;
+        return attribute_set_copy(range->attributeSet);
+    }    
+    *start_offset = (start == -1) ? 0 : start;
+    *end_offset = (end == -1) ? my_atk_text_get_character_count(text) : end;
+    return NULL;
+}
+//*********************************my_atk_text_get_default_attributes*****************
+AtkAttributeSet* my_atk_text_get_default_attributes(AtkText* text)
+{
+    return attribute_set_copy(((MyAtkText*)text)->default_attributes);
+}
+//*********************************my_atk_text_get_character_extents*****************
+void my_atk_text_get_character_extents(AtkText* text, gint offset, gint *x, gint *y,
+    gint *width, gint *height, AtkCoordType coord_type)
+{
+    AtkTextRectangle result;
+    gint relative_offset, line;
+    line = get_character_line((MyAtkText*)text, offset, &relative_offset);
+    get_extents((MyAtkText*)text, line, relative_offset, &result);
+    *x = result.x;
+    *y = result.y;
+    *width = result.width;
+    *height = result.height;
+}
+//*******************************my_atk_text_get_range_extents************************
+void my_atk_text_get_range_extents(AtkText *text, gint start_offset, gint end_offset,
+    AtkCoordType coord_type, AtkTextRectangle *rect)
+{
+    //simple - union of extents of the characters, contained in this range
+    AtkTextRectangle result, bounds_tmp;
+    gint i;
+
+    atk_text_get_character_extents (text, start_offset,
+                                  &result.x, &result.y,
+                                  &result.width, &result.height,
+                                  coord_type);
+
+    for (i = start_offset + 1; i < end_offset; i++)
+    {
+        my_atk_text_get_character_extents (text, i,&bounds_tmp.x, &bounds_tmp.y, 
+            &bounds_tmp.width, &bounds_tmp.height, coord_type);
+        
+        if(bounds_tmp.x < result.x)
+        {
+            //corrects left boundary
+            result.width += result.x - bounds_tmp.x;
+            result.x = bounds_tmp.x; 
+        }
+        if(bounds_tmp.x + bounds_tmp.width > result.x + result.width)
+        {
+            //corrects right boundary
+            result.width = bounds_tmp.x + bounds_tmp.width - result.x;
+        }
+        if(bounds_tmp.y < result.y)
+        {
+            //corrects top boundary
+            result.height += result.y - bounds_tmp.y;
+            result.y = bounds_tmp.y; 
+        }
+        if(bounds_tmp.y + bounds_tmp.height > result.y + result.height)
+        {
+            //corrects buttom boundary
+            result.height = bounds_tmp.y + bounds_tmp.height - result.y;
+        }
+    }
+    *rect = result;
+}
+//**********************************my_atk_text_get_offset_at_point*********************
+gint my_atk_text_get_offset_at_point(AtkText* text, gint x, gint y, AtkCoordType coord_type)
+{
+    gint line, relative_offset;
+
+    line = get_point_line((MyAtkText*)text, y);
+    relative_offset = get_point_relative_offset((MyAtkText*)text, x);
+
+    return get_offset_at_line((MyAtkText*)text, line, relative_offset);
+}
+//*****************************my_atk_text_get_bounded_ranges******************************
+AtkTextRange** my_atk_text_get_bounded_ranges(AtkText *text, AtkTextRectangle *rect,
+    AtkCoordType coord_type, AtkTextClipType x_clip_type, AtkTextClipType y_clip_type)
+{
+    MyAtkText *self = (MyAtkText*)text;
+    
+    gint start_line, end_line, start_rel_offset, end_rel_offset;
+    AtkTextRange** result;    
+    gint len = my_strlen(self->str);
+//macro for simplify return empty ranges when fail to do smth
+#define RETURN_EMTPY_RANGES {result = g_malloc(sizeof(AtkTextRange*));result[0] = NULL;return result;}
+    //start line
+    start_line = (y_clip_type == ATK_TEXT_CLIP_NONE) || (y_clip_type == ATK_TEXT_CLIP_MAX)
+        ? 0 : get_point_line(self, rect->y);
+    if(start_line < 0) start_line = 0;
+    //end line
+    end_line = (y_clip_type == ATK_TEXT_CLIP_NONE) || (y_clip_type == ATK_TEXT_CLIP_MIN)
+        ? G_MAXINT/2 : get_point_line(self, rect->y  + rect->height);
+    if(end_line < 0) RETURN_EMTPY_RANGES;
+    //start relative offset
+    start_rel_offset = (x_clip_type == ATK_TEXT_CLIP_NONE) || (x_clip_type == ATK_TEXT_CLIP_MAX)
+        ? 0 : get_point_relative_offset(self, rect->x);
+    if(start_rel_offset < 0) start_rel_offset = 0;
+    //end relative offset
+    end_rel_offset = (x_clip_type == ATK_TEXT_CLIP_NONE) || (x_clip_type == ATK_TEXT_CLIP_MIN)
+        ? G_MAXINT/2 : get_point_relative_offset(self, rect->x + rect->width);
+    if(end_rel_offset < 0) RETURN_EMTPY_RANGES;
+    //start offset(at the start of 'start_line')
+    gint start_offset = get_offset_at_line_start(self, start_line);
+    if(start_offset + start_rel_offset >= len) RETURN_EMTPY_RANGES;
+    //count ranges
+    gint number_of_ranges = count_ranges(self, start_offset,
+        start_rel_offset, end_rel_offset, end_line - start_line + 1, NULL, coord_type);
+    //create array(with just getting size)
+    result = g_malloc(sizeof(AtkTextRange*) * (number_of_ranges + 1));
+    //write ranges
+    count_ranges(self, start_offset,
+        start_rel_offset, end_rel_offset, end_line - start_line + 1, result, coord_type);
+#undef RETURN_EMPTY_RANGES
+    return result;
+}
+
+//********************************my_atk_text_get_n_selections*******************************
+gint my_atk_text_get_n_selections(AtkText *text)
+{
+    MyAtkText *self = (MyAtkText*)text;
+    return self->selections->len;
+}
+
+//********************************my_atk_text_get_selection*******************************
+gchar* my_atk_text_get_selection(AtkText *text,
+    gint selection_num, gint *start_offset, gint *end_offset)
+{
+    MyAtkText *self = (MyAtkText*)text;
+    GArray *selections = self->selections;
+    if(selection_num < 0 || selection_num >= selections->len) return NULL;
+    *start_offset = g_array_index(selections, TextSelection, selection_num).start_offset;
+    *end_offset = g_array_index(selections, TextSelection, selection_num).end_offset;
+    return my_atk_text_get_text(text, *start_offset, *end_offset);
+}
+//********************************my_atk_text_remove_selection*******************************
+gboolean my_atk_text_remove_selection(AtkText *text, gint selection_num)
+{
+    MyAtkText *self = (MyAtkText*)text;
+    GArray *selections = self->selections;
+    if(selection_num < 0 || selection_num >= selections->len) return FALSE;
+    g_array_remove_index(selections, selection_num);
+    
+    g_signal_emit_by_name(text, "text-selection-changed");
+    return TRUE;
+}
+//********************************my_atk_text_add_selection*******************************
+gboolean my_atk_text_add_selection(AtkText *text, gint start_offset, gint end_offset)
+{
+    if(start_offset < 0 || end_offset > my_atk_text_get_character_count(text) 
+        || start_offset >= end_offset) return FALSE;
+
+    MyAtkText *self = (MyAtkText*)text;
+    GArray *selections = self->selections;
+    gint i;
+    for(i = 0; i < selections->len; i++)
+    {
+        if(g_array_index(selections, TextSelection, i).start_offset >= start_offset)
+        {
+            if(g_array_index(selections, TextSelection, i).start_offset < end_offset)
+                return FALSE;
+            break;
+        }
+    }    
+    TextSelection new_selection;
+    new_selection.start_offset = start_offset;
+    new_selection.end_offset = end_offset;
+    g_array_insert_val(selections, i, new_selection);
+    
+    g_signal_emit_by_name(text, "text-selection-changed");
+    return TRUE;
+}
+//********************************my_atk_text_set_selection*******************************
+gboolean my_atk_text_set_selection(AtkText *text,
+    gint selection_num, gint start_offset, gint end_offset)
+{
+    MyAtkText *self = (MyAtkText*)text;
+    GArray *selections = self->selections;
+    if(selection_num < 0 || selection_num >= selections->len) return NULL;
+    
+    if((selection_num == 0 
+        || g_array_index(selections, TextSelection, selection_num - 1).end_offset <= start_offset)
+        && (selection_num == selections->len - 1
+        || g_array_index(selections, TextSelection, selection_num + 1).start_offset >= end_offset)
+        )
+    {
+        //Arrange of selections won't change
+        g_array_index(selections, TextSelection, selection_num).start_offset = 
+            start_offset;
+        g_array_index(selections, TextSelection, selection_num).end_offset =
+            end_offset;
+        g_signal_emit_by_name(text, "text-selection-changed");
+        return TRUE;
+    }
+    gint start_offset_old = 
+        g_array_index(selections, TextSelection, selection_num).start_offset;
+    gint end_offset_old = 
+        g_array_index(selections, TextSelection, selection_num).end_offset;
+    
+    my_atk_text_remove_selection(text, selection_num);
+    if(!my_atk_text_add_selection(text, start_offset, end_offset))
+    {
+        //fail when adding selection. Restore initial state.
+        my_atk_text_add_selection(text, start_offset_old, end_offset_old);
+        return FALSE;
+    }
+    g_signal_emit_by_name(text, "text-selection-changed");
+    return TRUE;
+
+}
+
+//************************************my_atk_text_get_caret_offset******************
+gint my_atk_text_get_caret_offset(AtkText *text)
+{
+    MyAtkText *self = (MyAtkText*)text;
+    return self->caret_offset;
+}
+//************************************my_atk_text_set_caret_offset******************
+gboolean my_atk_text_set_caret_offset(AtkText *text, gint offset)
+{
+    MyAtkText *self = (MyAtkText*)text;
+    //caret may be set just after the last character
+    if(offset < 0 || offset > my_atk_text_get_character_count(text))return FALSE;
+    self->caret_offset = offset;
+    g_signal_emit_by_name(self, "text-caret-moved", offset);
+    return TRUE;
+}
+
+//***********************my_atk_text_insert_text*******************************
+void my_atk_text_insert_text(AtkEditableText* text, const gchar* string,
+    gint length, gint *position)
+{
+    gint i;
+    MyAtkText* myAtkText = (MyAtkText*)text;
+    gchar *str = myAtkText->str;
+    gint strlen_old = my_strlen(str);
+    
+    if(string == NULL) return;
+    //correct length
+    for(i = 0; i < length; i ++)
+    {
+        if(string[i] == '\0') {length = i; break;}
+    }
+    
+    if(*position < 0 || *position > strlen_old || length <= 0 )return;
+    
+    
+    gchar *new_str = g_malloc(strlen_old + length + 1);
+    if(new_str == NULL)return;
+    
+    if(*position != 0) 
+        memcpy(new_str, str, (size_t)*position);
+    memcpy(new_str + *position, string, (size_t)length);
+    if(strlen_old != *position) 
+        memcpy(new_str + *position + length, str + *position,
+            (size_t)(strlen_old - *position));
+    new_str[strlen_old + length] = '\0';
+    
+    g_free(str);
+    myAtkText->str = new_str;
+    correct_selections_after_insert(myAtkText, *position, length);
+    correct_attributes_after_insert(myAtkText, *position, length);
+    correct_caret_after_insert(myAtkText, *position, length);
+    g_signal_emit_by_name(text, "text-changed::insert", *position, length);
+    g_signal_emit_by_name(text, "text-selection-changed");
+    g_signal_emit_by_name(text, "text-attributes-changed");
+    g_signal_emit_by_name(text, "text-caret-moved", myAtkText->caret_offset);
+    
+    (*position) += length;
+}
+//*************************my_atk_text_delete_text*******************
+void my_atk_text_delete_text(AtkEditableText* text, gint start_pos, gint end_pos)
+{
+    MyAtkText* myAtkText = (MyAtkText*)text;
+    gchar *str = myAtkText->str;
+    gint strlen_old = my_strlen(str);
+    
+    if(start_pos < 0 || end_pos > strlen_old || start_pos >= end_pos )return;
+    if(strlen_old != end_pos)
+        memmove(str + start_pos, str + end_pos, strlen_old - end_pos);
+    str[start_pos - end_pos + strlen_old] = '\0';
+    
+    correct_selections_after_delete(myAtkText, start_pos, end_pos - start_pos);
+    correct_attributes_after_delete(myAtkText, start_pos, end_pos - start_pos);
+    correct_caret_after_delete(myAtkText, start_pos, end_pos - start_pos);
+    g_signal_emit_by_name(text, "text-changed::delete", start_pos, end_pos - start_pos);
+    g_signal_emit_by_name(text, "text-selection-changed");
+    g_signal_emit_by_name(text, "text-attributes-changed");
+    g_signal_emit_by_name(text, "text-caret-moved", myAtkText->caret_offset);
+}
+//***********************my_atk_text_set_text_contents*************************
+void my_atk_text_set_text_contents(AtkEditableText* text, const gchar* string)
+{
+    my_atk_text_delete_text(text, 0, my_atk_text_get_character_count((AtkText*)text));
+    gint position = 0;
+    my_atk_text_insert_text(text, string, my_strlen(string), &position);
+}
+//**********************my_atk_text_copy_text***************************
+void my_atk_text_copy_text(AtkEditableText* text, gint start_pos, gint end_pos)
+{
+    MyAtkText* myAtkText = (MyAtkText*)text;
+    const gchar *str = myAtkText->str;
+    gint strlen_old = my_strlen(str);
+    if(start_pos < 0 || end_pos > strlen_old || start_pos >= end_pos )return;
+    
+    MyAtkTextClass *parent = MY_ATK_TEXT_GET_CLASS(text); 
+    g_free(parent->clipboard);
+    /*parent->clipboard = g_malloc(end_pos - start_pos + 1);
+    
+    strncpy(parent->clipboard, str + start_pos, end_pos - start_pos);
+    parent->clipboard[end_pos - start_pos] = '\0';*/
+    parent->clipboard = g_strndup(str + start_pos, end_pos - start_pos); 
+}
+//**********************my_atk_text_paste_text***************************
+void my_atk_text_paste_text(AtkEditableText *text, gint position)
+{
+    //NULL-clipboard process corretly
+    MyAtkTextClass* parent = MY_ATK_TEXT_GET_CLASS(text);
+    my_atk_text_insert_text(text, parent->clipboard, my_strlen(parent->clipboard), &position);
+}
+//**********************my_atk_text_cut_text***************************
+void my_atk_text_cut_text(AtkEditableText* text, gint start_pos, gint end_pos)
+{
+    my_atk_text_copy_text(text, start_pos, end_pos);
+    my_atk_text_delete_text(text, start_pos, end_pos);
+}
+//*********************my_atk_text_set_run_attributes************************
+gboolean my_atk_text_set_run_attributes(AtkEditableText* text, AtkAttributeSet* attrib_set,
+    gint start_offset, gint end_offset)
+{
+    MyAtkText* self = (MyAtkText*)text;
+    gint len = atk_text_get_character_count((AtkText*)text);
+    if(start_offset < 0 || start_offset >= end_offset || end_offset > len)
+        return FALSE;
+    GList *attributes = self->attributes;
+    GList *tmp = attributes;
+    
+    while(tmp != NULL)
+    {
+        Range *range = (Range*)tmp->data;
+        if(range->start < start_offset)
+        {
+            if(range->end <= end_offset)
+            {
+                if(range->end > start_offset) range->end = start_offset; 
+                tmp = tmp->next;
+                continue;
+            }
+            /*range->end > end_offset*/
+            Range* additional_range = range_new(end_offset, range->end);
+            additional_range->attributeSet = attribute_set_copy(range->attributeSet);
+            range->end = start_offset;
+            attributes = g_list_insert_before(attributes, tmp->next, additional_range);
+            tmp = tmp->next;
+            break;
+        }
+        else/*range->start >= start_offset*/
+        {
+            if(range->end <= end_offset)
+            {
+                GList *tmp1 = tmp->next;
+                attributes = g_list_remove_link(attributes, tmp);
+                tmp = tmp1;
+                continue;
+            }
+            /*range->end > end_offset*/
+            if(range->start < end_offset) range->start = end_offset;
+            break;
+        }
+    }
+    Range *new_range = range_new(start_offset, end_offset);
+    new_range->attributeSet = attribute_set_copy(attrib_set);
+    if(tmp == NULL)attributes = g_list_append(attributes, new_range);
+    else attributes = g_list_insert_before(attributes, tmp, new_range);
+    
+    self->attributes = attributes;
+    g_signal_emit_by_name(self, "text_attributes_changed");
+    return TRUE;
+}
+
+//others functions
+//sets default attributes
+void my_atk_text_set_default_attributes(MyAtkText* text, AtkAttributeSet *set)
+{
+    atk_attribute_set_free(text->default_attributes);
+    text->default_attributes = attribute_set_copy(set);
+    g_signal_emit_by_name(text, "text-attributes-changed");
+}
+
+void my_atk_text_print_run_attributes(MyAtkText *text)
+{
+    g_list_foreach(text->attributes, (GFunc)range_print, NULL);
+}
+void my_atk_text_print_default_attributes(MyAtkText *text)
+{
+    attribute_set_print(text->default_attributes);
+}
+//need for separate testing interfaces
+void auxiliary_atk_text_set_text_contents(MyAtkText* text, const gchar* string)
+{
+    my_atk_text_set_text_contents((AtkEditableText*)text, string);
+}
+void auxiliary_atk_text_set_run_attributes(MyAtkText* text, AtkAttributeSet* attrib_set,
+    gint start_offset, gint end_offset)
+{
+    my_atk_text_set_run_attributes((AtkEditableText*)text, attrib_set, start_offset, end_offset);
+}
+
+//initialize/finalize functions
+static void my_atk_text_instance_init(GTypeInstance *obj, gpointer g_class)
+{
+    MyAtkText *self = (MyAtkText*)obj;
+    
+    self->str = NULL;
+    self->attributes = NULL;
+    self->default_attributes = NULL;
+    text_bounds_init(&self->bounds);
+    
+    self->selections = g_array_new(FALSE, FALSE, sizeof(TextSelection));
+    
+    self->caret_offset = 0;
+}
+static void my_atk_text_instance_finalize(GObject* obj)
+{
+    MyAtkText *self = (MyAtkText*)obj;
+    g_free(self->str);
+    my_atk_text_free_run_attributes(self);
+    my_atk_text_free_default_attributes(self);
+    if(self->selections != NULL)g_array_free(self->selections, FALSE);
+}
+
+static void my_atk_text_class_init(gpointer g_class, gpointer class_data)
+{
+    GObjectClass* g_object_class = (GObjectClass*)g_class;
+    //GObject virtual table
+    g_object_class->finalize = my_atk_text_instance_finalize;
+    //Fills tables of symbols
+    table_symbols_init();
+    //initialize clipboard
+    ((MyAtkTextClass*)g_class)->clipboard = NULL;
+}
+//Because of static registration of type, finalization function will never been called
+//And glib prints warning if use it in registration.
+/*static void my_atk_text_class_finalize(gpointer g_class, gpointer class_data)
+{
+    MyAtkTextClass *self = (MyAtkTextClass*)g_class;
+    
+    g_free(self->clipboard);
+    self->clipboard = NULL; 
+}*/
+void my_atk_text_interface_init(gpointer g_iface, gpointer iface_data)
+{
+    AtkTextIface *klass = (AtkTextIface*)g_iface;
+    //"get_text"
+    klass->get_character_count = my_atk_text_get_character_count;
+    klass->get_text = my_atk_text_get_text;
+    klass->get_character_at_offset = my_atk_text_get_character_at_offset;
+    klass->get_text_after_offset = my_atk_text_get_text_after_offset;
+    klass->get_text_at_offset = my_atk_text_get_text_at_offset;
+    klass->get_text_before_offset = my_atk_text_get_text_before_offset;
+    //"attributes"
+    klass->get_run_attributes = my_atk_text_get_run_attributes;
+    klass->get_default_attributes = my_atk_text_get_default_attributes;
+    //"bounds"
+    klass->get_character_extents = my_atk_text_get_character_extents;
+    klass->get_range_extents = my_atk_text_get_range_extents;
+    klass->get_offset_at_point = my_atk_text_get_offset_at_point;
+    klass->get_bounded_ranges = my_atk_text_get_bounded_ranges; 
+    //"selection"
+    klass->get_n_selections = my_atk_text_get_n_selections;
+    klass->get_selection = my_atk_text_get_selection;
+    klass->remove_selection = my_atk_text_remove_selection;
+    klass->add_selection = my_atk_text_add_selection;
+    klass->set_selection = my_atk_text_set_selection;
+    //"caret"
+    klass->get_caret_offset = my_atk_text_get_caret_offset;
+    klass->set_caret_offset = my_atk_text_set_caret_offset;
+}
+
+static void my_atk_editable_text_interface_init(gpointer g_iface, gpointer iface_data)
+{
+    AtkEditableTextIface *klass = (AtkEditableTextIface*)g_iface;
+    
+    klass->set_text_contents = my_atk_text_set_text_contents;
+    klass->set_run_attributes = my_atk_text_set_run_attributes;
+    klass->copy_text =  my_atk_text_copy_text;
+    klass->insert_text =  my_atk_text_insert_text;
+    klass->paste_text = my_atk_text_paste_text;
+    klass->cut_text = my_atk_text_cut_text;
+    klass->delete_text = my_atk_text_delete_text;
+}
+GType my_atk_text_get_type()
+{
+    static GType type = 0;
+    if(type == 0)
+    {
+        static const GTypeInfo typeInfo = 
+        {
+            sizeof(MyAtkTextClass),
+            NULL,                       //base_init
+            NULL,                       //base_finalize
+            my_atk_text_class_init,     //class_init
+            NULL,                       //class_finalize
+            NULL,                       //class_data
+            sizeof(MyAtkText),
+            0,                          //n_preallocs
+            my_atk_text_instance_init   //instance_init
+        };
+
+        static const GInterfaceInfo AtkTextIface_info = 
+        {
+            my_atk_text_interface_init,         /* interface_init*/
+            NULL,                               /* interface_finalize*/
+            NULL                                /* interface_data */
+        };
+        static const GInterfaceInfo AtkEditableTextIface_info = 
+        {
+            my_atk_editable_text_interface_init,/* interface_init*/
+            NULL,                               /* interface_finalize*/
+            NULL                                /* interface_data */
+        };
+        type = g_type_register_static(ATK_TYPE_OBJECT, "MyAtkText", &typeInfo, 0);
+        g_type_add_interface_static(type,
+            ATK_TYPE_TEXT,
+            &AtkTextIface_info);
+        
+        g_type_add_interface_static(type,
+            ATK_TYPE_EDITABLE_TEXT,
+            &AtkEditableTextIface_info);
+    }
+    return type;    
+}
diff --git a/tests/dummyatk/my-atk-text.h b/tests/dummyatk/my-atk-text.h
new file mode 100644 (file)
index 0000000..158e5db
--- /dev/null
@@ -0,0 +1,58 @@
+#ifndef MY_ATK_TEXT_H
+#define MY_ATK_TEXT_H
+/*
+ * MyAtkText: implements AtkText and AtkEditableText
+ */
+#include <atk/atk.h>
+       
+#define MY_TYPE_ATK_TEXT             (my_atk_text_get_type ())
+#define MY_ATK_TEXT(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), MY_TYPE_ATK_TEXT, MyAtkText))
+#define MY_ATK_TEXT_CLASS(vtable)    (G_TYPE_CHECK_CLASS_CAST ((vtable), MY_TYPE_ATK_TEXT, MyAtkTextClass))
+#define MY_IS_ATK_TEXT(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MY_TYPE_ATK_TEXT))
+#define MY_IS_ATK_TEXT_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), MY_TYPE_ATK_TEXT))
+#define MY_ATK_TEXT_GET_CLASS(inst)  (G_TYPE_INSTANCE_GET_CLASS ((inst), MY_TYPE_ATK_TEXT, MyAtkTextClass))
+
+typedef struct _MyAtkText MyAtkText;
+typedef struct _MyAtkTextClass MyAtkTextClass;
+
+//Struct, describing bounds of one selection.
+typedef struct
+{
+    gint start_offset, end_offset;
+}TextSelection;
+//Struct, describing values, needed for determine extent of characters 
+typedef struct
+{
+    gint base_x, base_y;//coordinates of the top-left corner of text
+    gint pixels_above_line;
+    gint pixels_below_line;
+    gint size;//size of the character(height in pixels)
+    gint pixels_between_characters;//monoscaped font
+    gint width;//width of character
+}TextBounds; 
+
+struct _MyAtkText
+{
+    AtkObject parent;
+    
+    gchar* str;//string, containing text
+    GList* attributes;//running atributes
+    AtkAttributeSet *default_attributes;//default attributes
+    
+    TextBounds bounds;
+    
+    GArray* selections;
+    
+    gint caret_offset;
+};
+
+struct _MyAtkTextClass
+{
+    AtkObjectClass parent;
+    gchar* clipboard;
+};
+
+GType my_atk_text_get_type();
+
+void my_atk_text_interface_init(gpointer g_iface, gpointer iface_data);
+#endif /*MY_ATK_TEXT_H*/
diff --git a/tests/dummyatk/my-atk-value.c b/tests/dummyatk/my-atk-value.c
new file mode 100644 (file)
index 0000000..d94bd71
--- /dev/null
@@ -0,0 +1,112 @@
+#include <atk/atk.h>
+#include <limits.h>
+
+#include "my-atk-value.h"
+
+//*************************implementation***********************
+//implementation of virtual functions
+//******************get_current_value*******************
+static void my_atk_value_get_current_value(AtkValue *obj, GValue *value)
+{
+    g_return_if_fail(MY_IS_ATK_VALUE(obj));
+    MyAtkValue* self = (MyAtkValue*)obj;
+    
+    g_value_init(value, G_TYPE_INT);
+    g_value_set_int(value, self->current);
+}
+//******************get_maximum_value*******************
+static void my_atk_value_get_maximum_value(AtkValue *obj, GValue *value)
+{
+    g_return_if_fail(MY_IS_ATK_VALUE(obj));
+    MyAtkValue* self = (MyAtkValue*)obj;
+    
+    g_value_init(value, G_TYPE_INT);
+    g_value_set_int(value, self->maximum);
+}
+//******************get_minimum_value*******************
+static void my_atk_value_get_minimum_value(AtkValue *obj, GValue *value)
+{
+    g_return_if_fail(MY_IS_ATK_VALUE(obj));
+    MyAtkValue* self = (MyAtkValue*)obj;
+    
+    g_value_init(value, G_TYPE_INT);
+    g_value_set_int(value, self->minimum);
+}
+//******************set_current_value*******************
+static gboolean my_atk_value_set_current_value(AtkValue *obj, const GValue *value)
+{
+    g_return_val_if_fail(MY_IS_ATK_VALUE(obj), FALSE);
+    MyAtkValue* self = (MyAtkValue*)obj;
+    
+    if(self->readonly) return FALSE;
+    
+    gint new_value = g_value_get_int(value);
+    if(new_value < self->minimum || new_value > self->maximum) return FALSE;
+    
+    self->current = new_value;
+    return TRUE;
+}
+
+//others
+MyAtkValue* my_atk_value_new(gint minimum, gint maximum, gint current)
+{
+    MyAtkValue* result = g_object_new(MY_TYPE_ATK_VALUE, NULL);
+    if(result == NULL) return NULL;
+    result->minimum = minimum;
+    result->maximum = maximum;
+    result->current = current;
+    
+    return result;
+}
+static void my_atk_value_instance_init(GTypeInstance *obj, gpointer g_class)
+{
+    MyAtkValue *self = (MyAtkValue*)obj;
+    
+    self->minimum = 0;
+    self->maximum = 0;
+    self->current = 0;
+    
+    self->readonly = FALSE;
+}
+static void my_atk_value_interface_init(gpointer g_iface, gpointer iface_data)
+{
+    AtkValueIface *klass = (AtkValueIface*)g_iface;
+    
+    klass->get_current_value = my_atk_value_get_current_value;
+    klass->get_minimum_value = my_atk_value_get_minimum_value;
+    klass->get_maximum_value = my_atk_value_get_maximum_value;
+    
+    klass->set_current_value = my_atk_value_set_current_value;
+}
+
+GType my_atk_value_get_type()
+{
+    static GType type = 0;
+    if(type == 0)
+    {
+        static const GTypeInfo typeInfo = 
+        {
+            sizeof(MyAtkValueClass),
+            NULL, //base_init
+            NULL, //base_finalize
+            NULL, //class_init
+            NULL, //class_finalize
+            NULL, //class_data
+            sizeof(MyAtkValue),
+            0, //n_preallocs
+            my_atk_value_instance_init //instance_init
+        };
+
+        static const GInterfaceInfo iface_info = 
+        {
+            my_atk_value_interface_init,        /* interface_init*/
+            NULL,                               /* interface_finalize*/
+            NULL                                /* interface_data */
+        };
+        type = g_type_register_static(ATK_TYPE_OBJECT, "MyAtkValue", &typeInfo, 0);
+        g_type_add_interface_static(type,
+            ATK_TYPE_VALUE,
+            &iface_info);
+    }
+    return type;    
+}
diff --git a/tests/dummyatk/my-atk-value.h b/tests/dummyatk/my-atk-value.h
new file mode 100644 (file)
index 0000000..9e989fb
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef MY_ATK_VALUE_H
+#define MY_ATK_VALUE_H
+/*
+ * MyAtkValue: derives AtkObject
+ * and implements AtkValue
+ */
+#include <atk/atk.h>
+
+#define MY_TYPE_ATK_VALUE             (my_atk_value_get_type ())
+#define MY_ATK_VALUE(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), MY_TYPE_ATK_VALUE, MyAtkValue))
+#define MY_ATK_VALUE_CLASS(vtable)    (G_TYPE_CHECK_CLASS_CAST ((vtable), MY_TYPE_ATK_VALUE, MyAtkValueClass))
+#define MY_IS_ATK_VALUE(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MY_TYPE_ATK_VALUE))
+#define MY_IS_ATK_VALUE_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), MY_TYPE_ATK_VALUE))
+#define MY_ATK_VALUE_GET_CLASS(inst)  (G_TYPE_INSTANCE_GET_CLASS ((inst), MY_TYPE_ATK_VALUE, MyAtkValueClass))
+
+typedef struct _MyAtkValue MyAtkValue;
+typedef struct _MyAtkValueClass MyAtkValueClass;
+
+struct _MyAtkValue
+{
+    AtkObject parent;
+    
+    gint minimum, maximum, current;
+    gboolean readonly;
+};
+
+struct _MyAtkValueClass
+{
+    AtkObjectClass parent;
+};
+
+MyAtkValue* my_atk_value_new(gint minimum, gint maximium, gint current);
+GType my_atk_value_get_type();
+#endif /*MY_ATK_VALUE_H*/
diff --git a/tests/dummyatk/my-atk.h b/tests/dummyatk/my-atk.h
new file mode 100644 (file)
index 0000000..935d3d8
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef MY_ATK_H
+#define MY_ATK_H
+
+#include <my-atk-action.h>
+#include <my-atk-component.h>
+#include <my-atk.h>
+#include <my-atk-hyperlink.h>
+#include <my-atk-hypertext.h>
+#include <my-atk-object.h>
+#include <my-atk-streamable-content.h>
+#include <my-atk-text.h>
+#include <my-atk-value.h>
+
+#endif /*MY_ATK_H*/
diff --git a/tests/dummyatk/resources_storage.c b/tests/dummyatk/resources_storage.c
new file mode 100644 (file)
index 0000000..e86051d
--- /dev/null
@@ -0,0 +1,34 @@
+#include <atk/atk.h>
+
+GHashTable* resources = NULL;
+
+void resource_storage_init()
+{
+    if(resources == NULL)
+    resources = g_hash_table_new_full(g_str_hash, g_str_equal,
+        (GDestroyNotify)g_free, (GDestroyNotify)g_object_unref);
+}
+
+void resource_storage_free()
+{
+    if(resources == NULL) return;
+    g_hash_table_destroy(resources);
+    resources = NULL;
+}
+
+void resource_storage_add(const gchar* name, AtkObject* resource)
+{
+    if(resources == NULL) return;
+    g_hash_table_insert(resources, g_strdup(name), g_object_ref(resource));
+}
+
+AtkObject* resource_storage_get(const gchar* name)
+{
+    if(resources == NULL) return NULL;
+    return g_hash_table_lookup(resources, name);
+}
+void resources_storage_remove(const gchar* name)
+{
+    if(resources == NULL) return;
+    g_hash_table_remove(resources, name);
+}
diff --git a/tests/dummyatk/resources_storage.h b/tests/dummyatk/resources_storage.h
new file mode 100644 (file)
index 0000000..3decd86
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef RESOURCES_STORAGE_H
+#define RESOURCES_STORAGE_H
+
+#include <glib.h>
+
+void resource_storage_init();
+
+void resource_storage_free();
+
+void resource_storage_add(const gchar* name, AtkObject* resource);
+
+AtkObject* resource_storage_get(const gchar* name);
+
+void resources_storage_remove(const gchar* name);
+
+#endif /*RESOURCES_STORAGE_H*/
diff --git a/tests/dummyatk/useful_functions.c b/tests/dummyatk/useful_functions.c
new file mode 100644 (file)
index 0000000..49b6a82
--- /dev/null
@@ -0,0 +1,29 @@
+#include <string.h>
+#include <glib.h>
+/*
+ * Functions and macros widely used in the tests.
+ */
+//same as strcmp() == 0 but works properly for NULL pointers
+gboolean my_strcmp(const gchar* str1, const gchar* str2)
+{
+    if(str1 == str2) return TRUE;
+    if(str1 == NULL || str2 == NULL) return FALSE;
+    
+    return strcmp(str1,str2) == 0;
+}
+//same as strlen but works properly for NULL pointer and returns gint instead of guint
+gint my_strlen(const gchar* str)
+{
+    if(str == NULL)return 0;
+    return (gint)strlen(str);
+}
+//same as strncmp() == 0 but works properly for NULL pointers
+gboolean my_strncmp(const gchar* str1, const gchar* str2, gint n)
+{
+    if(n <= 0)return TRUE;
+    if(str1 == str2)return TRUE;
+    if(str1 == NULL || str2 == NULL)return FALSE;
+
+    return strncmp(str1, str2, n) == 0;
+}
diff --git a/tests/dummyatk/useful_functions.h b/tests/dummyatk/useful_functions.h
new file mode 100644 (file)
index 0000000..4fbce6c
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef USEFUL_FUNCTIONS_H
+#define USEFUL_FUNCTIONS_H
+/*
+ * Functions and macros widely used in the tests.
+ */
+//macro for creating objects in startup section
+#define OBJECT_NEW(obj, type, type_str) obj = g_object_new(type,NULL);\
+    if(obj == NULL)\
+    {\
+        INIT_FAILED("Cannot create instance of type" type_str ".\n");\
+    }
+//macro for destroying object 
+#define OBJECT_UNREF(obj) if(obj != NULL)\
+    {\
+        g_object_unref((gpointer)obj);\
+    }
+//for testing signals
+#define HANDLER_DISCONNECT(obj, h) if((h) != 0)\
+       {\
+               g_signal_handler_disconnect(obj, h);\
+       }
+
+gboolean my_strcmp(const gchar* str1, const gchar* str2);
+
+gint my_strlen(const gchar* str);
+
+gboolean my_strncmp(const gchar* str1, const gchar* str2, gint n);
+
+#endif /*USEFUL_FUNCTIONS_H*/
diff --git a/tests/dummyatk/user_marshal.c b/tests/dummyatk/user_marshal.c
new file mode 100644 (file)
index 0000000..1f8ecf0
--- /dev/null
@@ -0,0 +1,41 @@
+#include       <glib-object.h>
+
+/* INT:BOXED (marshal.list:1) */
+void
+g_cclosure_user_marshal_INT__BOXED (GClosure     *closure,
+                                    GValue       *return_value,
+                                    guint         n_param_values,
+                                    const GValue *param_values,
+                                    gpointer      invocation_hint,
+                                    gpointer      marshal_data)
+{
+  typedef gint (*GMarshalFunc_INT__BOXED) (gpointer     data1,
+                                           gpointer     arg_1,
+                                           gpointer     data2);
+  register GMarshalFunc_INT__BOXED callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+  gint v_return;
+
+  g_return_if_fail (return_value != NULL);
+  g_return_if_fail (n_param_values == 2);
+
+  if (G_CCLOSURE_SWAP_DATA (closure))
+    {
+      data1 = closure->data;
+      data2 = g_value_peek_pointer (param_values + 0);
+    }
+  else
+    {
+      data1 = g_value_peek_pointer (param_values + 0);
+      data2 = closure->data;
+    }
+  callback = (GMarshalFunc_INT__BOXED) (marshal_data ? marshal_data : cc->callback);
+
+  v_return = callback (data1,
+                       g_marshal_value_peek_boxed (param_values + 1),
+                       data2);
+
+  g_value_set_int (return_value, v_return);
+}
+
diff --git a/tests/dummyatk/user_marshal.h b/tests/dummyatk/user_marshal.h
new file mode 100644 (file)
index 0000000..a845925
--- /dev/null
@@ -0,0 +1,60 @@
+#ifndef USER_MARSHAL_H
+#define USER_MARSHAL_H
+
+#include       <glib-object.h>
+
+
+#ifdef G_ENABLE_DEBUG
+#define g_marshal_value_peek_boolean(v)  g_value_get_boolean (v)
+#define g_marshal_value_peek_char(v)     g_value_get_char (v)
+#define g_marshal_value_peek_uchar(v)    g_value_get_uchar (v)
+#define g_marshal_value_peek_int(v)      g_value_get_int (v)
+#define g_marshal_value_peek_uint(v)     g_value_get_uint (v)
+#define g_marshal_value_peek_long(v)     g_value_get_long (v)
+#define g_marshal_value_peek_ulong(v)    g_value_get_ulong (v)
+#define g_marshal_value_peek_int64(v)    g_value_get_int64 (v)
+#define g_marshal_value_peek_uint64(v)   g_value_get_uint64 (v)
+#define g_marshal_value_peek_enum(v)     g_value_get_enum (v)
+#define g_marshal_value_peek_flags(v)    g_value_get_flags (v)
+#define g_marshal_value_peek_float(v)    g_value_get_float (v)
+#define g_marshal_value_peek_double(v)   g_value_get_double (v)
+#define g_marshal_value_peek_string(v)   (char*) g_value_get_string (v)
+#define g_marshal_value_peek_param(v)    g_value_get_param (v)
+#define g_marshal_value_peek_boxed(v)    g_value_get_boxed (v)
+#define g_marshal_value_peek_pointer(v)  g_value_get_pointer (v)
+#define g_marshal_value_peek_object(v)   g_value_get_object (v)
+#else /* !G_ENABLE_DEBUG */
+/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
+ *          Do not access GValues directly in your code. Instead, use the
+ *          g_value_get_*() functions
+ */
+#define g_marshal_value_peek_boolean(v)  (v)->data[0].v_int
+#define g_marshal_value_peek_char(v)     (v)->data[0].v_int
+#define g_marshal_value_peek_uchar(v)    (v)->data[0].v_uint
+#define g_marshal_value_peek_int(v)      (v)->data[0].v_int
+#define g_marshal_value_peek_uint(v)     (v)->data[0].v_uint
+#define g_marshal_value_peek_long(v)     (v)->data[0].v_long
+#define g_marshal_value_peek_ulong(v)    (v)->data[0].v_ulong
+#define g_marshal_value_peek_int64(v)    (v)->data[0].v_int64
+#define g_marshal_value_peek_uint64(v)   (v)->data[0].v_uint64
+#define g_marshal_value_peek_enum(v)     (v)->data[0].v_long
+#define g_marshal_value_peek_flags(v)    (v)->data[0].v_ulong
+#define g_marshal_value_peek_float(v)    (v)->data[0].v_float
+#define g_marshal_value_peek_double(v)   (v)->data[0].v_double
+#define g_marshal_value_peek_string(v)   (v)->data[0].v_pointer
+#define g_marshal_value_peek_param(v)    (v)->data[0].v_pointer
+#define g_marshal_value_peek_boxed(v)    (v)->data[0].v_pointer
+#define g_marshal_value_peek_pointer(v)  (v)->data[0].v_pointer
+#define g_marshal_value_peek_object(v)   (v)->data[0].v_pointer
+#endif /* !G_ENABLE_DEBUG */
+
+
+void
+g_cclosure_user_marshal_INT__BOXED (GClosure     *closure,
+                                    GValue       *return_value,
+                                    guint         n_param_values,
+                                    const GValue *param_values,
+                                    gpointer      invocation_hint,
+                                    gpointer      marshal_data);
+
+#endif /*USER_MARSHAL_H*/
diff --git a/tests/testapps/Makefile.am b/tests/testapps/Makefile.am
new file mode 100644 (file)
index 0000000..a37699c
--- /dev/null
@@ -0,0 +1,28 @@
+check_PROGRAMS = test-application
+check_LTLIBRARIES = libnoopapp.la libobjectapp.la
+
+test_application_CFLAGS = $(DBUS_GLIB_CFLAGS)  \
+                         $(ATK_CFLAGS)         \
+                         $(GMODULE_CFLAGS)     \
+                         -I$(top_srcdir)
+
+test_application_LDADD = $(DBUS_GLIB_LIBS)     \
+                        $(GMODULE_LIBS)        \
+                        $(ATK_LIBS)
+
+test_application_SOURCES = test-application.c
+
+
+TEST_APP_CFLAGS = $(ATK_CFLAGS) $(GMODULE_CFLAGS) -I$(top_srcdir) -I$(top_srcdir)/tests/dummyatk/
+TEST_APP_LDFLAGS = -no-undefined -module -avoid-version -rpath /a/fake/path
+TEST_APP_LIBADD = $(ATK_LIBS) $(GMODULE_LIBS) $(top_builddir)/tests/dummyatk/libdummyatk.la
+
+libnoopapp_la_CFLAGS = $(TEST_APP_CFLAGS)
+libnoopapp_la_LDFLAGS = $(TEST_APP_LDFLAGS)
+libnoopapp_la_LIBADD = $(TEST_APP_LIBADD)
+libnoopapp_la_SOURCES = noop-app.c
+
+libobjectapp_la_CFLAGS = $(TEST_APP_CFLAGS)
+libobjectapp_la_LDFLAGS = $(TEST_APP_LDFLAGS)
+libobjectapp_la_LIBADD = $(TEST_APP_LIBADD)
+libobjectapp_la_SOURCES = object-app.c
diff --git a/tests/testapps/noop-app.c b/tests/testapps/noop-app.c
new file mode 100644 (file)
index 0000000..7b997cc
--- /dev/null
@@ -0,0 +1,14 @@
+#include <gmodule.h>
+#include <atk/atk.h>
+
+G_MODULE_EXPORT void
+test_init (void)
+{
+  ;
+}
+
+G_MODULE_EXPORT AtkObject *
+test_get_root (void)
+{
+  return NULL;
+}
diff --git a/tests/testapps/object-app.c b/tests/testapps/object-app.c
new file mode 100644 (file)
index 0000000..85d4f2c
--- /dev/null
@@ -0,0 +1,21 @@
+#include <gmodule.h>
+#include <atk/atk.h>
+#include <my-atk.h>
+
+static AtkObject *root_accessible;
+
+G_MODULE_EXPORT void
+test_init (void)
+{
+  root_accessible = (AtkObject *) g_object_new(MY_TYPE_ATK_OBJECT, NULL);
+
+  atk_object_set_name(root_accessible, "Root Accessible");
+  atk_object_set_role(root_accessible, ATK_ROLE_ACCEL_LABEL);
+  atk_object_set_description(root_accessible, "A root object for the test tree"); 
+}
+
+G_MODULE_EXPORT AtkObject *
+test_get_root (void)
+{
+  return root_accessible;
+}
diff --git a/tests/testapps/test-application.c b/tests/testapps/test-application.c
new file mode 100644 (file)
index 0000000..a499584
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * AT-SPI - Assistive Technology Service Provider Interface
+ * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
+ *
+ * Copyright 2008 Codethink Ltd.
+ *
+ * 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; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Testing AT-SPI requires both a test application and AT client. 
+ * Test applications are built using the Dummy ATK implementation: MyAtk.
+ * This file contains the entry point for all test applications.
+ * Each test is built as a GModule, and this program loads the 
+ * test module, as well as the AT-SPI module. The test module will
+ * provide its own implementation of atk_get_root, and as such provide
+ * all the application state for the test.
+ */
+
+#include <glib.h>
+#include <gmodule.h>
+#include <atk/atk.h>
+#include <dbus/dbus.h>
+
+/* The test module, GModule containing interface for an atk-test */
+static GModule *test_module;
+static gpointer test_module_get_root;
+
+/* Test module interface */
+/*************************/
+
+typedef AtkObject *(*TestModuleGetRoot) (void);
+
+/* Calls into the test module to get the root atk object */
+static AtkObject *
+get_root(void)
+{
+  return ((TestModuleGetRoot) test_module_get_root)();
+}
+
+/*************************/
+
+/* The AtkUtil class is called to find the root accessible and to deal
+ * with events. Its an incomplete class, its v-table needs to be filled in.
+ */
+static void
+setup_atk_util(void)
+{
+  AtkUtilClass *klass;
+
+  klass = g_type_class_ref(ATK_TYPE_UTIL);
+  klass->get_root = get_root;
+  g_type_class_unref(klass);
+}
+
+typedef void (*GtkModuleInit) (int argc, char *argv[]);
+
+/* AT-SPI is a gtk module that must be loaded and initialized */
+static void
+load_atspi_module(const char *path, int argc, char *argv[])
+{
+  GModule *bridge;
+  gpointer init;
+
+  bridge = g_module_open(path, G_MODULE_BIND_LOCAL|G_MODULE_BIND_LAZY);
+  if (!bridge)
+    g_error("Couldn't load atk-bridge module : %s\n", path);
+
+  if (!g_module_symbol(bridge, "gtk_module_init", &init))
+    g_error("Couldn't load symbol \"gtk_module_init\"\n");
+
+  ((GtkModuleInit) init)(argc, argv);
+}
+
+static void
+load_test_module(const char *path, int argc, char *argv[])
+{
+  gpointer init;
+
+  test_module = g_module_open(path, G_MODULE_BIND_LOCAL|G_MODULE_BIND_LAZY);
+  if (!test_module)
+    g_error("Couldn't load test module : %s\n", path);
+
+  if (!g_module_symbol(test_module, "test_init", &init))
+    g_error("Couldn't load symbol \"test_init\"\n");
+
+  if (!g_module_symbol(test_module, "test_get_root", &test_module_get_root))
+    g_error("Couldn't load symbol \"test_get_root\"\n");
+
+  ((GtkModuleInit) init)(argc, argv);
+}
+
+/*Command line data*/
+static gchar *tmodule_path;
+static gchar *amodule_path;
+
+static GOptionEntry optentries[] = 
+{
+  {"test-module", 't', 0, G_OPTION_ARG_STRING, &tmodule_path, "Module containing test scenario", NULL},
+  {"atspi-module", 'a', 0, G_OPTION_ARG_STRING, &amodule_path, "Gtk module with atk-atspi adaptor", NULL},
+  {NULL}
+};
+
+/* main
+ * 
+ * Entry point for all test applications.
+ */
+main(int argc, char *argv[])
+{
+  GMainLoop *mainloop;
+  GOptionContext *opt;
+  GError *err = NULL;
+
+  /*Parse command options*/
+  opt = g_option_context_new(NULL);
+  g_option_context_add_main_entries(opt, optentries, NULL);
+  if (!g_option_context_parse(opt, &argc, &argv, &err))
+    {
+      g_print("Option parsing failed: %s\n", err->message);
+      exit(1);
+    }
+
+  g_type_init();
+
+  setup_atk_util();
+  load_test_module(tmodule_path, argc, argv);
+  load_atspi_module(amodule_path, argc, argv);
+
+  mainloop = g_main_loop_new (NULL, FALSE);
+  g_main_loop_run (mainloop);
+
+  return 0;
+}