From: Mark Doffman Date: Wed, 28 May 2008 20:37:20 +0000 (+0100) Subject: 2008-05-28 Mark Doffman X-Git-Tag: AT_SPI2_ATK_2_12_0~674^2~9 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=f346af9d38b2a69945543e38e1109ddb8131c886;p=platform%2Fcore%2Fuifw%2Fat-spi2-atk.git 2008-05-28 Mark Doffman * 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. --- diff --git a/Makefile.am b/Makefile.am index d92ac75..edfea24 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 diff --git a/configure.ac b/configure.ac index e4226d0..8b51084 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/tests/Makefile.am b/tests/Makefile.am index bc906a3..b012e65 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -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 index 0000000..397460b --- /dev/null +++ b/tests/dummyatk/Makefile.am @@ -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 index 0000000..048d0b2 --- /dev/null +++ b/tests/dummyatk/my-atk-action.c @@ -0,0 +1,221 @@ +#include +#include +#include + +#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) && (in)) + { + 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 index 0000000..dc4da8b --- /dev/null +++ b/tests/dummyatk/my-atk-action.h @@ -0,0 +1,56 @@ +#ifndef MY_ATK_ACTION_H +#define MY_ATK_ACTION_H +//Object, which implement interface AtkAction(all functions) +#include +#include +#include + +//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 index 0000000..e304c7d --- /dev/null +++ b/tests/dummyatk/my-atk-component.c @@ -0,0 +1,403 @@ +#include +#include +#include + +#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 index 0000000..3fa5850 --- /dev/null +++ b/tests/dummyatk/my-atk-component.h @@ -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 + +#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 index 0000000..cb6b435 --- /dev/null +++ b/tests/dummyatk/my-atk-hyperlink.c @@ -0,0 +1,137 @@ +#include + +#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 index 0000000..026ba54 --- /dev/null +++ b/tests/dummyatk/my-atk-hyperlink.h @@ -0,0 +1,35 @@ +#ifndef MY_ATK_HYPERLINK_H +#define MY_ATK_HYPERLINK_H +/* + * MyAtkHyperlink: implements AtkHyperlink + */ +#include + +#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 index 0000000..1bba620 --- /dev/null +++ b/tests/dummyatk/my-atk-hypertext.c @@ -0,0 +1,172 @@ +#include + +#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 index 0000000..50d02cf --- /dev/null +++ b/tests/dummyatk/my-atk-hypertext.h @@ -0,0 +1,33 @@ +#ifndef MY_ATK_HYPERTEXT_H +#define MY_ATK_HYPERTEXT_H +/* + * MyAtkHypertext: implements AtkHypertext + */ +#include +#include + +#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 index 0000000..d45a2ef --- /dev/null +++ b/tests/dummyatk/my-atk-object.c @@ -0,0 +1,156 @@ + +#include + +#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 index 0000000..3e1eed6 --- /dev/null +++ b/tests/dummyatk/my-atk-object.h @@ -0,0 +1,30 @@ +#ifndef MY_ATK_OBJECT_H +#define MY_ATK_OBJECT_H + +#include + +#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 index 0000000..4141344 --- /dev/null +++ b/tests/dummyatk/my-atk-streamable-content.c @@ -0,0 +1,95 @@ +#include + +#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 index 0000000..d8e0a4e --- /dev/null +++ b/tests/dummyatk/my-atk-streamable-content.h @@ -0,0 +1,33 @@ +#ifndef MY_ATK_STREAMABLE_CONTENT_H +#define MY_ATK_STREAMABLE_CONTENT_H + +/* + * MyAtkStreamableContent: derives GObject and implements AtkStreamableContent + */ + +#include + +#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 index 0000000..caee42e --- /dev/null +++ b/tests/dummyatk/my-atk-text.c @@ -0,0 +1,1290 @@ + +#include +#include +#include +#include + +#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 index 0000000..158e5db --- /dev/null +++ b/tests/dummyatk/my-atk-text.h @@ -0,0 +1,58 @@ +#ifndef MY_ATK_TEXT_H +#define MY_ATK_TEXT_H +/* + * MyAtkText: implements AtkText and AtkEditableText + */ +#include + +#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 index 0000000..d94bd71 --- /dev/null +++ b/tests/dummyatk/my-atk-value.c @@ -0,0 +1,112 @@ +#include +#include + +#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 index 0000000..9e989fb --- /dev/null +++ b/tests/dummyatk/my-atk-value.h @@ -0,0 +1,34 @@ +#ifndef MY_ATK_VALUE_H +#define MY_ATK_VALUE_H +/* + * MyAtkValue: derives AtkObject + * and implements AtkValue + */ +#include + +#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 index 0000000..935d3d8 --- /dev/null +++ b/tests/dummyatk/my-atk.h @@ -0,0 +1,14 @@ +#ifndef MY_ATK_H +#define MY_ATK_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif /*MY_ATK_H*/ diff --git a/tests/dummyatk/resources_storage.c b/tests/dummyatk/resources_storage.c new file mode 100644 index 0000000..e86051d --- /dev/null +++ b/tests/dummyatk/resources_storage.c @@ -0,0 +1,34 @@ +#include + +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 index 0000000..3decd86 --- /dev/null +++ b/tests/dummyatk/resources_storage.h @@ -0,0 +1,16 @@ +#ifndef RESOURCES_STORAGE_H +#define RESOURCES_STORAGE_H + +#include + +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 index 0000000..49b6a82 --- /dev/null +++ b/tests/dummyatk/useful_functions.c @@ -0,0 +1,29 @@ +#include +#include +/* + * 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 index 0000000..4fbce6c --- /dev/null +++ b/tests/dummyatk/useful_functions.h @@ -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 index 0000000..1f8ecf0 --- /dev/null +++ b/tests/dummyatk/user_marshal.c @@ -0,0 +1,41 @@ +#include + +/* 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 index 0000000..a845925 --- /dev/null +++ b/tests/dummyatk/user_marshal.h @@ -0,0 +1,60 @@ +#ifndef USER_MARSHAL_H +#define USER_MARSHAL_H + +#include + + +#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 index 0000000..a37699c --- /dev/null +++ b/tests/testapps/Makefile.am @@ -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 index 0000000..7b997cc --- /dev/null +++ b/tests/testapps/noop-app.c @@ -0,0 +1,14 @@ +#include +#include + +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 index 0000000..85d4f2c --- /dev/null +++ b/tests/testapps/object-app.c @@ -0,0 +1,21 @@ +#include +#include +#include + +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 index 0000000..a499584 --- /dev/null +++ b/tests/testapps/test-application.c @@ -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 +#include +#include +#include + +/* 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; +}