diff --git a/clutter/Makefile.am b/clutter/Makefile.am index 75017804d..cc204b2ac 100644 --- a/clutter/Makefile.am +++ b/clutter/Makefile.am @@ -2,7 +2,7 @@ include $(top_srcdir)/build/autotools/Makefile.am.silent NULL = -SUBDIRS = cogl $(CLUTTER_WINSYS_BASE) $(CLUTTER_WINSYS) +SUBDIRS = cogl $(CLUTTER_WINSYS_BASE) $(CLUTTER_WINSYS) cally if LOCAL_JSON_GLIB SUBDIRS += json @@ -12,7 +12,7 @@ clutter_json_libadd = $(top_builddir)/clutter/json/libclutter-json.la clutter_json_gir = ClutterJson-@CLUTTER_API_VERSION@.gir endif -DIST_SUBDIRS = glx egl cogl json osx x11 win32 fruity +DIST_SUBDIRS = glx egl cogl json osx x11 win32 fruity cally # common definitions CLEANFILES = @@ -39,6 +39,7 @@ endif # SUPPORT_WIN32 INCLUDES = \ -I$(top_srcdir) \ + -I$(top_srcdir)/clutter/cally \ -I$(top_srcdir)/clutter/cogl \ -I$(top_srcdir)/clutter/cogl/pango \ -I$(top_srcdir)/clutter \ @@ -260,13 +261,16 @@ source_h_priv = \ libclutter_@CLUTTER_SONAME_INFIX@_@CLUTTER_API_VERSION@_la_LIBADD = \ $(CLUTTER_LIBS) \ + $(top_builddir)/clutter/cally/libcally.la \ $(top_builddir)/clutter/cogl/cogl/libclutter-cogl.la \ $(top_builddir)/clutter/cogl/pango/libcoglpango.la \ $(top_builddir)/clutter/$(CLUTTER_WINSYS)/libclutter-$(CLUTTER_WINSYS).la \ $(clutter_json_libadd) \ $(CLUTTER_WINSYS_BASE_LIB) + libclutter_@CLUTTER_SONAME_INFIX@_@CLUTTER_API_VERSION@_la_DEPENDENCIES = \ + $(top_builddir)/clutter/cally/libcally.la \ $(top_builddir)/clutter/cogl/cogl/libclutter-cogl.la \ $(top_builddir)/clutter/cogl/pango/libcoglpango.la \ $(top_builddir)/clutter/$(CLUTTER_WINSYS)/libclutter-$(CLUTTER_WINSYS).la \ @@ -289,7 +293,7 @@ libclutter_@CLUTTER_SONAME_INFIX@_@CLUTTER_API_VERSION@_la_LDFLAGS = \ $(CLUTTER_LT_LDFLAGS) \ $(GCOV_LDFLAGS) \ -export-dynamic \ - -export-symbols-regex "^(clutter|cogl|json).*" \ + -export-symbols-regex "^(clutter|cogl|cally|json).*" \ -rpath $(libdir) \ $(win32_resources_ldflag) \ $(NULL) diff --git a/clutter/cally/Makefile.am b/clutter/cally/Makefile.am new file mode 100644 index 000000000..75cec6a2b --- /dev/null +++ b/clutter/cally/Makefile.am @@ -0,0 +1,79 @@ +include $(top_srcdir)/build/autotools/Makefile.am.silent + +EXTRA_DIST = +CLEANFILES = +DISTCLEANFILES = + +# pkg-config ================================================================== +pc_files = \ + cally-$(CLUTTER_API_VERSION).pc + +cally-$(CLUTTER_API_VERSION).pc: cally.pc + $(QUIET_GEN)cp -f $< $(@F) + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = $(pc_files) + +EXTRA_DIST += cally.pc.in +CLEANFILES += $(pc_files) + +noinst_LTLIBRARIES = libcally.la + +cally_h_sources = cally.h \ + cally-actor.h \ + cally-factory.h \ + cally-group.h \ + cally-rectangle.h \ + cally-root.h \ + cally-stage.h \ + cally-text.h \ + cally-texture.h \ + cally-clone.h \ + cally-util.h + +cally_private_h_sources = cally-actor-private.h + +cally_c_sources = cally.c \ + cally-actor.c \ + cally-group.c \ + cally-rectangle.c \ + cally-root.c \ + cally-stage.c \ + cally-text.c \ + cally-texture.c \ + cally-clone.c \ + cally-util.c + +libcally_la_SOURCES = \ + $(cally_private_h_sources) \ + $(cally_h_sources) \ + $(cally_c_sources) + +INCLUDES = \ + -I$(top_srcdir) \ + -I$(top_srcdir)/clutter \ + -I$(top_srcdir)/clutter/cally \ + -I$(top_srcdir)/clutter/cogl + +AM_CPPFLAGS = \ + -DG_LOG_DOMAIN=\"Cally\" \ + -DCLUTTER_COMPILATION \ + -DVERSION=\"$(VERSION)\" \ + $(CLUTTER_DEBUG_CFLAGS) + +AM_CFLAGS = \ + $(CLUTTER_CFLAGS) \ + $(MAINTAINER_CFLAGS) + + +libcallydir=$(includedir)/clutter-@CLUTTER_API_VERSION@/cally + +# In opposit to GAIL, CALLY exports all the headers. It will very +# unlikely in any real final clutter-based application to use only raw +# CALLY. In fact, after HAIL experience, probably export GAIL +# interfaces would be a good idea +libcally_HEADERS = \ + $(cally_h_sources) + +libcally_la_LIBADD = \ + $(CLUTTER_LIBS) \ No newline at end of file diff --git a/clutter/cally/cally-actor-private.h b/clutter/cally/cally-actor-private.h new file mode 100644 index 000000000..b9c0d0b46 --- /dev/null +++ b/clutter/cally/cally-actor-private.h @@ -0,0 +1,35 @@ +/* CALLY - The Clutter Accessibility Implementation Library + * + * Copyright (C) 2009 Igalia, S.L. + * + * Author: Alejandro Piñeiro Iglesias + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef __CALLY_ACTOR_PRIVATE_H__ +#define __CALLY_ACTOR_PRIVATE_H__ + +#include "cally-actor.h" + +/* + * Auxiliar define, in order to get the clutter actor from the AtkObject using + * AtkGObject methods + * + */ +#define CALLY_GET_CLUTTER_ACTOR(cally_object) \ + (CLUTTER_ACTOR (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (cally_object)))) + +#endif /* __CALLY_ACTOR_PRIVATE_H__ */ diff --git a/clutter/cally/cally-actor.c b/clutter/cally/cally-actor.c new file mode 100644 index 000000000..b8a86de2f --- /dev/null +++ b/clutter/cally/cally-actor.c @@ -0,0 +1,1491 @@ +/* CALLY - The Clutter Accessibility Implementation Library + * + * Copyright (C) 2008 Igalia, S.L. + * + * Author: Alejandro Piñeiro Iglesias + * + * Some parts are based on GailWidget, GailContaineer, GailCell from GAIL + * GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001, 2002, 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** + * SECTION:cally-actor + * @short_description: Implementation of the ATK interfaces for #ClutterActor + * @see_also: #ClutterActor + * + * #CallyActor implements the required ATK interfaces of #ClutterActor + * exposing the common elements on each ClutterActor (position, extents, etc). + */ + +/** + * + * IMPLEMENTATION NOTES: + * + * #### + * + * Focus: clutter hasn't got the focus concept in the same way that GTK, but it + * has a key focus managed by the stage. Basically any actor can be focused using + * clutter_stage_set_key_focus. So, we will use this approach: all actors are + * focusable, and we get the currently focused using clutter_stage_get_key_focus + * This affects focus related stateset and some atk_componenet focus methods (like + * grab focus) + * + * #### + * + * #ClutterContainer : cally_actor implements some of his methods based on + * #ClutterContainer interface, although there are the posibility to not + * implement it. Could be strange to do that but: + * * Some methods (like get_n_children and ref_child) are easily implemented using + * this interface so a general implementation can be done + * * #ClutterContainer is a popular interface, so several classes will implement + * that. + * * So we can implement a a11y class similar to GailContainer for each clutter + * object implementing that, and their clutter subclasses will have a proper + * a11y class, but not if they are parallel classes (ie: #ClutterGroup, + * #TinyFrame, #TinyScrollView) + * * So, on all this objects, will be required to reimplement again some methods + * on the objects. + * * A auxiliar object (a kind of a11y specific #ClutterContainer implementation) + * could be used to implement this methods in only a place, anyway, this will + * require some code on each concrete class to manage it. + * * So this implementation is based in that is better to manage a interface + * on the top abstract object, instead that C&P some code, with the minor + * problem that we need to check if we are implementing or not the interface. + * + * This methods can be reimplemented, in concrete cases that we can get ways more + * efficient to implement that. Take a look to #CallyGroup as a example of this. + * + * Anyway, there are several examples of behaviour changes depending of the current + * type of the object you are granting access. + * + * TODO,FIXME: check if an option would be to use a dynamic type, as + * it has been done on the webkit a11y implementation: + * See: https://bugs.webkit.org/show_bug.cgi?id=21546 + * + * ### + * + * #AtkAction implementation. As ClutterActor has signals for "press" + * and "release", and most of the general Clutter objects are being + * used as buttons, it has sense to implement #AtkAction on + * #CallyActor, so this actions were added in this level. + * + * So we should search a way to extend #AtkAction on subclasses, to + * add actions. The direct solution would be just extend it normally, + * but we also should have the option to remove actions if required. + * + * So it was used the solution implemented in GailCell: maintain a + * list of actions, and add a _add_action and _remove_action public + * methods. + * + * This is another reason to not isolate CALLY as GAIL (although the + * current idea is to not do that). + * + */ + +#include "config.h" + +#include +#include + +#ifdef HAVE_CLUTTER_X11 +#include +#endif + +#include + +#include "cally-actor.h" +#include "cally-actor-private.h" + +typedef struct _CallyActorActionInfo CallyActorActionInfo; + +/** + * CallyActorActionInfo: + * @name: name of the action + * @description: description of the action + * @keybinding: keybinding related to the action + * @do_action_func: callback + * + * Utility structure to maintain the different actions added to the + * #CallyActor + */ +struct _CallyActorActionInfo +{ + gchar *name; + gchar *description; + gchar *keybinding; + CallyActionFunc do_action_func; +}; + +static void cally_actor_class_init (CallyActorClass *klass); +static void cally_actor_init (CallyActor *cally_actor); +static void cally_actor_initialize (AtkObject *obj, + gpointer data); +static void cally_actor_finalize (GObject *obj); + +/* AtkObject.h */ +static AtkObject* cally_actor_get_parent (AtkObject *obj); +static gint cally_actor_get_index_in_parent (AtkObject *obj); +static AtkStateSet* cally_actor_ref_state_set (AtkObject *obj); +static G_CONST_RETURN gchar* cally_actor_get_name (AtkObject *obj); +static gint cally_actor_get_n_children (AtkObject *obj); +static AtkObject* cally_actor_ref_child (AtkObject *obj, + gint i); +static gboolean _cally_actor_all_parents_visible (ClutterActor *actor); + +/* ClutterContainer */ +static gint cally_actor_add_actor (ClutterActor *container, + ClutterActor *actor, + gpointer data); +static gint cally_actor_remove_actor (ClutterActor *container, + ClutterActor *actor, + gpointer data); +static gint cally_actor_real_add_actor (ClutterActor *container, + ClutterActor *actor, + gpointer data); +static gint cally_actor_real_remove_actor (ClutterActor *container, + ClutterActor *actor, + gpointer data); + +/* AtkComponent.h */ +static void cally_actor_component_interface_init (AtkComponentIface *iface); +static void cally_actor_get_extents (AtkComponent *component, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coord_type); +static gint cally_actor_get_mdi_zorder (AtkComponent *component); +static gboolean cally_actor_grab_focus (AtkComponent *component); +static guint cally_actor_add_focus_handler (AtkComponent *component, + AtkFocusHandler handler); +static void cally_actor_remove_focus_handler (AtkComponent *component, + guint handler_id); +static void cally_actor_focus_event (AtkObject *obj, + gboolean focus_in); +static gboolean _is_actor_on_screen (ClutterActor *actor); +static void _get_top_level_origin (ClutterActor *actor, + gint *x, + gint *y); + +/* AtkAction.h */ +static void cally_actor_action_interface_init (AtkActionIface *iface); +static gboolean cally_actor_action_do_action (AtkAction *action, + gint i); +static gboolean idle_do_action (gpointer data); +static gint cally_actor_action_get_n_actions (AtkAction *action); +static G_CONST_RETURN gchar* cally_actor_action_get_description (AtkAction *action, + gint i); +static G_CONST_RETURN gchar* cally_actor_action_get_keybinding (AtkAction *action, + gint i); +static G_CONST_RETURN gchar* cally_actor_action_get_name (AtkAction *action, + gint i); +static gboolean cally_actor_action_set_description (AtkAction *action, + gint i, + const gchar *desc); +static void _cally_actor_press_action (CallyActor *cally_actor); +static void _cally_actor_release_action (CallyActor *cally_actor); +static void _cally_actor_click_action (CallyActor *cally_actor); +static void _cally_actor_destroy_action_info (gpointer action_info, + gpointer user_data); +static void _cally_actor_clean_action_list (CallyActor *cally_actor); + +static CallyActorActionInfo* _cally_actor_get_action_info (CallyActor *cally_actor, + gint index); +/* Misc functions */ +static void cally_actor_notify_clutter (GObject *obj, + GParamSpec *pspec); +static void cally_actor_real_notify_clutter (GObject *obj, + GParamSpec *pspec); +static gboolean cally_actor_focus_clutter (ClutterActor *actor, + gpointer data); +static gboolean cally_actor_real_focus_clutter (ClutterActor *actor, + gpointer data); + +G_DEFINE_TYPE_WITH_CODE (CallyActor, + cally_actor, + ATK_TYPE_GOBJECT_ACCESSIBLE, + G_IMPLEMENT_INTERFACE (ATK_TYPE_COMPONENT, + cally_actor_component_interface_init) + G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION, + cally_actor_action_interface_init)); + +#define CALLY_ACTOR_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CALLY_TYPE_ACTOR, CallyActorPrivate)) + + +struct _CallyActorPrivate +{ + GQueue *action_queue; + guint action_idle_handler; + GList *action_list; + + GList *children; +}; + + +AtkObject* +cally_actor_new (ClutterActor *actor) +{ + gpointer object; + AtkObject *atk_object; + + g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), NULL); + + object = g_object_new (CALLY_TYPE_ACTOR, NULL); + + atk_object = ATK_OBJECT (object); + atk_object_initialize (atk_object, actor); + + return atk_object; +} + +static void +cally_actor_initialize (AtkObject *obj, + gpointer data) +{ + CallyActor *self = NULL; + CallyActorPrivate *priv = NULL; + ClutterActor *actor = NULL; + guint handler_id; + + ATK_OBJECT_CLASS (cally_actor_parent_class)->initialize (obj, data); + + self = CALLY_ACTOR(obj); + priv = self->priv; + actor = CLUTTER_ACTOR (data); + + g_signal_connect_after (actor, + "key-focus-in", + G_CALLBACK (cally_actor_focus_clutter), + GINT_TO_POINTER (TRUE)); + g_signal_connect_after (actor, + "key-focus-out", + G_CALLBACK (cally_actor_focus_clutter), + GINT_TO_POINTER (FALSE)); + g_signal_connect (actor, + "notify", + G_CALLBACK (cally_actor_notify_clutter), + NULL); + atk_component_add_focus_handler (ATK_COMPONENT (self), + cally_actor_focus_event); + + g_object_set_data (G_OBJECT (obj), "atk-component-layer", + GINT_TO_POINTER (ATK_LAYER_MDI)); + + /* add basic actions */ + cally_actor_add_action (self, "press", NULL, NULL, + _cally_actor_press_action); + + cally_actor_add_action (self, "release", NULL, NULL, + _cally_actor_release_action); + + cally_actor_add_action (self, "click", NULL, NULL, + _cally_actor_click_action); + + /* Depends if the object implement ClutterContainer */ + if (CLUTTER_IS_CONTAINER(actor)) + { + priv->children = clutter_container_get_children (CLUTTER_CONTAINER (actor)); + + /* + * We store the handler ids for these signals in case some objects + * need to remove these handlers. + */ + handler_id = g_signal_connect (actor, + "actor-added", + G_CALLBACK (cally_actor_add_actor), + obj); + g_object_set_data (G_OBJECT (obj), "cally-add-handler-id", + GUINT_TO_POINTER (handler_id)); + handler_id = g_signal_connect (actor, + "actor-removed", + G_CALLBACK (cally_actor_remove_actor), + obj); + g_object_set_data (G_OBJECT (obj), "cally-remove-handler-id", + GUINT_TO_POINTER (handler_id)); + + obj->role = ATK_ROLE_PANEL; /* typically objects implementing ClutterContainer + interface would be a panel */ + } + else + { + priv->children = NULL; + obj->role = ATK_ROLE_UNKNOWN; + } +} + +static void +cally_actor_class_init (CallyActorClass *klass) +{ + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + klass->focus_clutter = cally_actor_real_focus_clutter; + klass->notify_clutter = cally_actor_real_notify_clutter; + klass->add_actor = cally_actor_real_add_actor; + klass->remove_actor = cally_actor_real_remove_actor; + + /* GObject */ + gobject_class->finalize = cally_actor_finalize; + + /* AtkObject */ + class->get_name = cally_actor_get_name; + class->get_parent = cally_actor_get_parent; + class->get_index_in_parent = cally_actor_get_index_in_parent; + class->ref_state_set = cally_actor_ref_state_set; + class->initialize = cally_actor_initialize; + class->get_n_children = cally_actor_get_n_children; + class->ref_child = cally_actor_ref_child; + + g_type_class_add_private (gobject_class, sizeof (CallyActorPrivate)); +} + +static void +cally_actor_init (CallyActor *cally_actor) +{ + CallyActorPrivate *priv = CALLY_ACTOR_GET_PRIVATE (cally_actor); + + cally_actor->priv = priv; + + priv->action_queue = NULL; + priv->action_idle_handler = 0; + + priv->action_list = NULL; + + priv->children = NULL; +} + + +static void +cally_actor_finalize (GObject *obj) +{ + CallyActor *cally_actor = NULL; + CallyActorPrivate *priv = NULL; + + cally_actor = CALLY_ACTOR (obj); + priv = cally_actor->priv; + + _cally_actor_clean_action_list (cally_actor); + + if (priv->action_idle_handler) + { + g_source_remove (priv->action_idle_handler); + priv->action_idle_handler = 0; + } + + if (priv->action_queue) + { + g_queue_free (priv->action_queue); + } + + if (priv->children) + { + g_list_free (priv->children); + priv->children = NULL; + } + + G_OBJECT_CLASS (cally_actor_parent_class)->finalize (obj); +} + +/* AtkObject */ + +static G_CONST_RETURN gchar* +cally_actor_get_name (AtkObject *obj) +{ + G_CONST_RETURN gchar* name = NULL; + + g_return_val_if_fail (CALLY_IS_ACTOR (obj), NULL); + + name = ATK_OBJECT_CLASS (cally_actor_parent_class)->get_name (obj); + if (name == NULL) + { + CallyActor *cally_actor = NULL; + ClutterActor *actor = NULL; + + cally_actor = CALLY_ACTOR (obj); + actor = CALLY_GET_CLUTTER_ACTOR (cally_actor); + if (actor == NULL) /* State is defunct */ + name = NULL; + else + name = clutter_actor_get_name (actor); + } + return name; +} + +static AtkObject * +cally_actor_get_parent (AtkObject *obj) +{ + ClutterActor *parent_actor = NULL; + AtkObject *parent = NULL; + ClutterActor *actor = NULL; + CallyActor *cally_actor = NULL; + + g_return_val_if_fail (CALLY_IS_ACTOR (obj), NULL); + + /* Check if we have and assigned parent */ + if (obj->accessible_parent) + return obj->accessible_parent; + + /* Try to get it from the clutter parent */ + cally_actor = CALLY_ACTOR (obj); + actor = CALLY_GET_CLUTTER_ACTOR (cally_actor); + if (actor == NULL) /* Object is defunct */ + return NULL; + + parent_actor = clutter_actor_get_parent (actor); + if (parent_actor == NULL) + return NULL; + + parent = clutter_actor_get_accessible (parent_actor); + + /* FIXME: I need to review the clutter-embed, to check if in this case I + * should get the widget accessible + */ + + return parent; +} + +static gint +cally_actor_get_index_in_parent (AtkObject *obj) +{ + CallyActor *cally_actor = NULL; + ClutterActor *actor = NULL; + ClutterActor *parent_actor = NULL; + GList *children = NULL; + gint index = -1; + + g_return_val_if_fail (CALLY_IS_ACTOR (obj), -1); + + if (obj->accessible_parent) + { + gint n_children, i; + gboolean found = FALSE; + + n_children = atk_object_get_n_accessible_children (obj->accessible_parent); + for (i = 0; i < n_children; i++) + { + AtkObject *child; + + child = atk_object_ref_accessible_child (obj->accessible_parent, i); + if (child == obj) + found = TRUE; + + g_object_unref (child); + if (found) + return i; + } + return -1; + } + + cally_actor = CALLY_ACTOR (obj); + actor = CALLY_GET_CLUTTER_ACTOR (cally_actor); + if (actor == NULL) /* Object is defunct */ + return -1; + + parent_actor = clutter_actor_get_parent(actor); + if ((parent_actor == NULL)||(!CLUTTER_IS_CONTAINER(parent_actor))) + return -1; + + children = clutter_container_get_children(CLUTTER_CONTAINER(parent_actor)); + + index = g_list_index (children, actor); + g_list_free (children); + + return index; +} + +static AtkStateSet* +cally_actor_ref_state_set (AtkObject *obj) +{ + ClutterActor *actor = NULL; + AtkStateSet *state_set = NULL; + ClutterStage *stage = NULL; + ClutterActor *focus_actor = NULL; + CallyActor *cally_actor = NULL; + + g_return_val_if_fail (CALLY_IS_ACTOR (obj), NULL); + cally_actor = CALLY_ACTOR (obj); + + state_set = ATK_OBJECT_CLASS (cally_actor_parent_class)->ref_state_set (obj); + + actor = CALLY_GET_CLUTTER_ACTOR (cally_actor); + + if (actor == NULL) /* Object is defunct */ + { + atk_state_set_add_state (state_set, ATK_STATE_DEFUNCT); + } + else + { + if (CLUTTER_ACTOR_IS_REACTIVE (actor)) + { + atk_state_set_add_state (state_set, ATK_STATE_SENSITIVE); + atk_state_set_add_state (state_set, ATK_STATE_ENABLED); + } + + if (CLUTTER_ACTOR_IS_VISIBLE (actor)) + { + atk_state_set_add_state (state_set, ATK_STATE_VISIBLE); + + if (_is_actor_on_screen (actor) && + _cally_actor_all_parents_visible (actor)) + atk_state_set_add_state (state_set, ATK_STATE_SHOWING); + } + + /* See focus section on implementation notes */ + atk_state_set_add_state (state_set, ATK_STATE_FOCUSABLE); + + stage = CLUTTER_STAGE (clutter_actor_get_stage (actor)); + /* If for any reason this actor doesn't have a stage + associated, we try the default one as fallback */ + if (stage == NULL) + stage = CLUTTER_STAGE (clutter_stage_get_default ()); + + focus_actor = clutter_stage_get_key_focus (stage); + if (focus_actor == actor) + atk_state_set_add_state (state_set, ATK_STATE_FOCUSED); + } + + return state_set; +} + +static gint +cally_actor_get_n_children (AtkObject *obj) +{ + ClutterActor *actor = NULL; + CallyActorPrivate *priv = NULL; + GList *children = NULL; + gint num = 0; + + g_return_val_if_fail (CALLY_IS_ACTOR (obj), 0); + + priv = CALLY_ACTOR (obj)->priv; + actor = CALLY_GET_CLUTTER_ACTOR (obj); + + if (actor == NULL) /* State is defunct */ + return 0; + + g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), 0); + + if (CLUTTER_IS_CONTAINER (actor)) + { + children = clutter_container_get_children (CLUTTER_CONTAINER (actor)); + num = g_list_length (children); + + g_list_free (children); + } + else + { + num = 0; + } + + return num; +} + +static AtkObject* +cally_actor_ref_child (AtkObject *obj, + gint i) +{ + ClutterActor *actor = NULL; + ClutterActor *child = NULL; + CallyActorPrivate *priv = NULL; + GList *children = NULL; + AtkObject *result = NULL; + + g_return_val_if_fail (CALLY_IS_ACTOR (obj), NULL); + + priv = CALLY_ACTOR (obj)->priv; + actor = CALLY_GET_CLUTTER_ACTOR (obj); + + if (actor == NULL) /* State is defunct */ + { + return NULL; + } + + g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), NULL); + + if (CLUTTER_IS_CONTAINER (actor)) + { + children = clutter_container_get_children (CLUTTER_CONTAINER (actor)); + child = g_list_nth_data (children, i); + + result = clutter_actor_get_accessible (child); + + g_object_ref (result); + g_list_free (children); + } + else + { + result = NULL; + } + + return result; +} + +/* ClutterContainer */ +static gint +cally_actor_add_actor (ClutterActor *container, + ClutterActor *actor, + gpointer data) +{ + CallyActor *cally_actor = CALLY_ACTOR (data); + CallyActorClass *klass = NULL; + + klass = CALLY_ACTOR_GET_CLASS (cally_actor); + + if (klass->add_actor) + return klass->add_actor (container, actor, data); + else + return 1; +} + +static gint +cally_actor_remove_actor (ClutterActor *container, + ClutterActor *actor, + gpointer data) +{ + CallyActor *cally_actor = CALLY_ACTOR (data); + CallyActorClass *klass = NULL; + + klass = CALLY_ACTOR_GET_CLASS (cally_actor); + + if (klass->remove_actor) + return klass->remove_actor (container, actor, data); + else + return 1; +} + + +static gint +cally_actor_real_add_actor (ClutterActor *container, + ClutterActor *actor, + gpointer data) +{ + AtkObject *atk_parent = ATK_OBJECT (data); + AtkObject *atk_child = clutter_actor_get_accessible (actor); + CallyActor *cally_actor = CALLY_ACTOR (atk_parent); + CallyActorPrivate *priv = cally_actor->priv; + gint index; + + g_return_val_if_fail (CLUTTER_IS_CONTAINER (container), 0); + g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), 0); + + g_object_notify (G_OBJECT (atk_child), "accessible_parent"); + + g_list_free (priv->children); + + priv->children = + clutter_container_get_children (CLUTTER_CONTAINER(container)); + + index = g_list_index (priv->children, actor); + g_signal_emit_by_name (atk_parent, "children_changed::add", + index, atk_child, NULL); + + return 1; +} + +static gint +cally_actor_real_remove_actor (ClutterActor *container, + ClutterActor *actor, + gpointer data) +{ + AtkPropertyValues values = { NULL }; + AtkObject* atk_parent = NULL; + AtkObject *atk_child = NULL; + CallyActorPrivate *priv = NULL; + gint index; + + g_return_val_if_fail (CLUTTER_IS_CONTAINER (container), 0); + g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), 0); + + atk_parent = ATK_OBJECT (data); + atk_child = clutter_actor_get_accessible (actor); + + if (atk_child) + { + g_value_init (&values.old_value, G_TYPE_POINTER); + g_value_set_pointer (&values.old_value, atk_parent); + + values.property_name = "accessible-parent"; + + g_object_ref (atk_child); + g_signal_emit_by_name (atk_child, + "property_change::accessible-parent", &values, NULL); + g_object_unref (atk_child); + } + + priv = CALLY_ACTOR (atk_parent)->priv; + index = g_list_index (priv->children, actor); + g_list_free (priv->children); + priv->children = clutter_container_get_children (CLUTTER_CONTAINER(container)); + + if (index >= 0 && index <= g_list_length (priv->children)) + g_signal_emit_by_name (atk_parent, "children_changed::remove", + index, atk_child, NULL); + + return 1; +} + +/* AtkComponent implementation */ +static void +cally_actor_component_interface_init (AtkComponentIface *iface) +{ + g_return_if_fail (iface != NULL); + + iface->get_extents = cally_actor_get_extents; + iface->get_mdi_zorder = cally_actor_get_mdi_zorder; + + /* focus management */ + iface->grab_focus = cally_actor_grab_focus; + iface->add_focus_handler = cally_actor_add_focus_handler; + iface->remove_focus_handler = cally_actor_remove_focus_handler; +} + +static void +cally_actor_get_extents (AtkComponent *component, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coord_type) +{ + CallyActor *cally_actor = NULL; + ClutterActor *actor = NULL; + gint top_level_x, top_level_y; + gfloat f_width, f_height; + ClutterVertex verts[4]; + + g_return_if_fail (CALLY_IS_ACTOR (component)); + + cally_actor = CALLY_ACTOR (component); + actor = CALLY_GET_CLUTTER_ACTOR (cally_actor); + + if (actor == NULL) /* actor is defunct */ + return; + + clutter_actor_get_abs_allocation_vertices (actor, verts); + clutter_actor_get_transformed_size (actor, &f_width, &f_height); + + *x = verts[0].x; + *y = verts[0].y; + *width = ceilf (f_width); + *height = ceilf (f_height); + + /* In the ATK_XY_WINDOW case, we consider the stage as the + * "top-level-window" + * + * http://library.gnome.org/devel/atk/stable/AtkUtil.html#AtkCoordType + */ + + if (coord_type == ATK_XY_SCREEN) + { + _get_top_level_origin (actor, &top_level_x, &top_level_y); + + *x += top_level_x; + *y += top_level_y; + } + + return; +} + +static gint +cally_actor_get_mdi_zorder (AtkComponent *component) +{ + CallyActor *cally_actor = NULL; + ClutterActor *actor = NULL; + + g_return_val_if_fail (CALLY_IS_ACTOR (component), G_MININT); + + cally_actor = CALLY_ACTOR(component); + actor = CALLY_GET_CLUTTER_ACTOR (cally_actor); + + return clutter_actor_get_depth (actor); +} + +static gboolean +cally_actor_grab_focus (AtkComponent *component) +{ + ClutterActor *actor = NULL; + ClutterActor *stage = NULL; + CallyActor *cally_actor = NULL; + + g_return_val_if_fail (CALLY_IS_ACTOR (component), FALSE); + + /* See focus section on implementation notes */ + cally_actor = CALLY_ACTOR(component); + actor = CALLY_GET_CLUTTER_ACTOR (cally_actor); + stage = clutter_actor_get_stage (actor); + + clutter_stage_set_key_focus (CLUTTER_STAGE (stage), + actor); + + return TRUE; +} + +/* + * These methods are basically taken from gail, as I don't see any + * reason to modify it. It makes me wonder why it is really required + * to be implemented in the toolkit + */ +static guint +cally_actor_add_focus_handler (AtkComponent *component, + AtkFocusHandler handler) +{ + GSignalMatchType match_type; + gulong ret; + guint signal_id; + + match_type = G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_FUNC; + signal_id = g_signal_lookup ("focus-event", ATK_TYPE_OBJECT); + + ret = g_signal_handler_find (component, match_type, signal_id, 0, NULL, + (gpointer) handler, NULL); + if (!ret) + { + return g_signal_connect_closure_by_id (component, + signal_id, 0, + g_cclosure_new (G_CALLBACK (handler), NULL, + (GClosureNotify) NULL), + FALSE); + } + else + { + return 0; + } +} + +static void +cally_actor_remove_focus_handler (AtkComponent *component, + guint handler_id) +{ + g_signal_handler_disconnect (component, handler_id); +} + +/* This method should check if the actor is currently on screen */ +static gboolean +_is_actor_on_screen (ClutterActor *actor) +{ + /* FIXME: FILL ME!! + * You could get some ideas from clutter_actor_is_on_stage, a private clutter + * function (note: it doesn't exists in the last versions of clutter) + * A occlusion check could be a good idea too + */ + + return TRUE; +} + +/** + * + * This gets the top level origin, it is, the position of the stage in + * the global screen. You can see it as the absolute display position + * of the stage. + * + * FIXME: only the case with x11 is implemented, other backends are + * required + * + **/ +static void +_get_top_level_origin (ClutterActor *actor, + gint *x, + gint *y) +{ + /* default values */ + *x = 0; + *y = 0; + +#ifdef HAVE_CLUTTER_X11 + ClutterActor *stage = NULL; + Display *display = NULL; + Window root_window; + Window stage_window; + Window child; + gint return_val = 0; + + stage = clutter_actor_get_stage (actor); + + display = clutter_x11_get_default_display (); /* FIXME: what happens if you use another + display with clutter_backend_x11_set_display ?*/ + root_window = clutter_x11_get_root_window (); + stage_window = clutter_x11_get_stage_window (CLUTTER_STAGE (stage)); + + return_val = XTranslateCoordinates (display, stage_window, root_window, + 0, 0, x, y, + &child); + + if (!return_val) + g_warning ("[x11] We were not able to get proper absolute position of the stage"); +#else + static gboolean yet_warned = FALSE; + + if (!yet_warned) + { + yet_warned = TRUE; + + g_warning ("Using a clutter backend not supported. " + "atk_component_get_extents using ATK_XY_SCREEN could return a wrong screen position"); + } + +#endif +} + +/* AtkAction implementation */ +static void +cally_actor_action_interface_init (AtkActionIface *iface) +{ + g_return_if_fail (iface != NULL); + + iface->do_action = cally_actor_action_do_action; + iface->get_n_actions = cally_actor_action_get_n_actions; + iface->get_description = cally_actor_action_get_description; + iface->get_keybinding = cally_actor_action_get_keybinding; + iface->get_name = cally_actor_action_get_name; + iface->set_description = cally_actor_action_set_description; +} + +static gboolean +cally_actor_action_do_action (AtkAction *action, + gint index) +{ + ClutterActor *actor = NULL; + CallyActor *cally_actor = NULL; + AtkStateSet *set = NULL; + CallyActorPrivate *priv = NULL; + CallyActorActionInfo *info = NULL; + + cally_actor = CALLY_ACTOR (action); + priv = cally_actor->priv; + actor = CALLY_GET_CLUTTER_ACTOR (cally_actor); + + set = atk_object_ref_state_set (ATK_OBJECT (cally_actor)); + + if (atk_state_set_contains_state (set, ATK_STATE_DEFUNCT)) + return FALSE; + + if (!atk_state_set_contains_state (set, ATK_STATE_SENSITIVE) || + !atk_state_set_contains_state (set, ATK_STATE_SHOWING)) + return FALSE; + + g_object_unref (set); + + info = _cally_actor_get_action_info (cally_actor, index); + + if (info == NULL) + return FALSE; + + if (info->do_action_func == NULL) + return FALSE; + + if (!priv->action_queue) + priv->action_queue = g_queue_new (); + + g_queue_push_head (priv->action_queue, info); + + if (!priv->action_idle_handler) + priv->action_idle_handler = g_idle_add (idle_do_action, cally_actor); + + return TRUE; +} + +static gboolean +idle_do_action (gpointer data) +{ + CallyActor *cally_actor = NULL; + CallyActorPrivate *priv = NULL; + ClutterActor *actor = NULL; + + cally_actor = CALLY_ACTOR (data); + priv = cally_actor->priv; + actor = CALLY_GET_CLUTTER_ACTOR (cally_actor); + priv->action_idle_handler = 0; + + if (actor == NULL) /* state is defunct*/ + return FALSE; + + while (!g_queue_is_empty (priv->action_queue)) + { + CallyActorActionInfo *info = NULL; + + info = (CallyActorActionInfo *) g_queue_pop_head (priv->action_queue); + + info->do_action_func (cally_actor); + } + + return FALSE; +} + +static gint +cally_actor_action_get_n_actions (AtkAction *action) +{ + CallyActor *cally_actor = NULL; + CallyActorPrivate *priv = NULL; + + g_return_val_if_fail (CALLY_IS_ACTOR (action), 0); + + cally_actor = CALLY_ACTOR (action); + priv = cally_actor->priv; + + return g_list_length (priv->action_list); +} + +static G_CONST_RETURN gchar* +cally_actor_action_get_name (AtkAction *action, + gint i) +{ + CallyActor *cally_actor = NULL; + CallyActorActionInfo *info = NULL; + + g_return_val_if_fail (CALLY_IS_ACTOR (action), NULL); + cally_actor = CALLY_ACTOR (action); + info = _cally_actor_get_action_info (cally_actor, i); + + if (info == NULL) + return NULL; + + return info->name; +} + +static G_CONST_RETURN gchar* +cally_actor_action_get_description (AtkAction *action, + gint i) +{ + CallyActor *cally_actor = NULL; + CallyActorActionInfo *info = NULL; + + g_return_val_if_fail (CALLY_IS_ACTOR (action), NULL); + cally_actor = CALLY_ACTOR (action); + info = _cally_actor_get_action_info (cally_actor, i); + + if (info == NULL) + return NULL; + + return info->description; +} + +static gboolean +cally_actor_action_set_description (AtkAction *action, + gint i, + const gchar *desc) +{ + CallyActor *cally_actor = NULL; + CallyActorActionInfo *info = NULL; + + g_return_val_if_fail (CALLY_IS_ACTOR (action), FALSE); + cally_actor = CALLY_ACTOR (action); + info = _cally_actor_get_action_info (cally_actor, i); + + if (info == NULL) + return FALSE; + + g_free (info->description); + info->description = g_strdup (desc); + + return TRUE; +} + +static G_CONST_RETURN gchar* +cally_actor_action_get_keybinding (AtkAction *action, + gint i) +{ + CallyActor *cally_actor = NULL; + CallyActorActionInfo *info = NULL; + + g_return_val_if_fail (CALLY_IS_ACTOR (action), NULL); + cally_actor = CALLY_ACTOR (action); + info = _cally_actor_get_action_info (cally_actor, i); + + if (info == NULL) + return NULL; + + return info->keybinding; +} + +/* Misc functions */ + +/** + * Checks if the parent actor, and his parent, etc is all visible + * Used to check the showing property + * + * FIXME: the same functionality is implemented on clutter since version 0.8.4 + * by clutter_actor_get_paint_visibility, so we should change this function + * if a clutter version update is made + **/ +static gboolean +_cally_actor_all_parents_visible (ClutterActor *actor) +{ + ClutterActor *iter_parent = NULL; + gboolean result = TRUE; + ClutterActor *stage = NULL; + + stage = clutter_actor_get_stage (actor); + + for (iter_parent = clutter_actor_get_parent(actor); iter_parent; + iter_parent = clutter_actor_get_parent(iter_parent)) + { + if (!CLUTTER_ACTOR_IS_VISIBLE (iter_parent)) + { + /* stage parent */ + if (iter_parent != stage) + result = FALSE; + else + result = TRUE; + + break; + } + } + + return result; +} + +/* + * This function is a signal handler for key_focus_in and + * key_focus_out signal which gets emitted on a ClutterActor + */ +static gboolean +cally_actor_focus_clutter (ClutterActor *actor, + gpointer data) +{ + CallyActor *cally_actor = NULL; + CallyActorClass *klass = NULL; + + cally_actor = CALLY_ACTOR (clutter_actor_get_accessible (actor)); + klass = CALLY_ACTOR_GET_CLASS (cally_actor); + if (klass->focus_clutter) + return klass->focus_clutter (actor, data); + else + return FALSE; +} + +static gboolean +cally_actor_real_focus_clutter (ClutterActor *actor, + gpointer data) +{ + CallyActor *cally_actor = NULL; + gboolean return_val = FALSE; + gboolean in = FALSE; + + in = GPOINTER_TO_INT (data); + cally_actor = CALLY_ACTOR (clutter_actor_get_accessible (actor)); + + g_signal_emit_by_name (cally_actor, "focus_event", in, &return_val); + atk_focus_tracker_notify (ATK_OBJECT (cally_actor)); + + return FALSE; +} + +/* + * This function is a signal handler for notify signal which gets emitted + * when a property changes value on the ClutterActor associated with the object. + * + * It calls a function for the CallyActor type + */ +static void +cally_actor_notify_clutter (GObject *obj, + GParamSpec *pspec) +{ + CallyActor *cally_actor = NULL; + CallyActorClass *klass = NULL; + + cally_actor = CALLY_ACTOR (clutter_actor_get_accessible (CLUTTER_ACTOR (obj))); + klass = CALLY_ACTOR_GET_CLASS (cally_actor); + + if (klass->notify_clutter) + klass->notify_clutter (obj, pspec); +} + +/* + * This function is a signal handler for notify signal which gets emitted + * when a property changes value on the ClutterActor associated with a CallyActor + * + * It constructs an AtkPropertyValues structure and emits a "property_changed" + * signal which causes the user specified AtkPropertyChangeHandler + * to be called. + */ +static void +cally_actor_real_notify_clutter (GObject *obj, + GParamSpec *pspec) +{ + ClutterActor* actor = CLUTTER_ACTOR (obj); + AtkObject* atk_obj = clutter_actor_get_accessible (CLUTTER_ACTOR(obj)); + AtkState state; + gboolean value; + + if (strcmp (pspec->name, "visible") == 0) + { + state = ATK_STATE_VISIBLE; + value = CLUTTER_ACTOR_IS_VISIBLE (actor); + } + else if (strcmp (pspec->name, "reactive") == 0) + { + state = ATK_STATE_SENSITIVE; + value = CLUTTER_ACTOR_IS_REACTIVE (actor); + } + else + return; + + atk_object_notify_state_change (atk_obj, state, value); +} + +static void +cally_actor_focus_event (AtkObject *obj, + gboolean focus_in) +{ + atk_object_notify_state_change (obj, ATK_STATE_FOCUSED, focus_in); +} + + +static void +_cally_actor_clean_action_list (CallyActor *cally_actor) +{ + CallyActorPrivate *priv = NULL; + + priv = cally_actor->priv; + + if (priv->action_list) + { + g_list_foreach (priv->action_list, + (GFunc) _cally_actor_destroy_action_info, + NULL); + g_list_free (priv->action_list); + priv->action_list = NULL; + } +} + +static CallyActorActionInfo * +_cally_actor_get_action_info (CallyActor *cally_actor, + gint index) +{ + CallyActorPrivate *priv = NULL; + GList *node = NULL; + + g_return_val_if_fail (CALLY_IS_ACTOR (cally_actor), NULL); + + priv = cally_actor->priv; + + if (priv->action_list == NULL) + return NULL; + + node = g_list_nth (priv->action_list, index); + + if (node == NULL) + return NULL; + + return (CallyActorActionInfo *)(node->data); +} + +static void +_cally_actor_click_action (CallyActor *cally_actor) +{ + ClutterEvent tmp_event; + ClutterActor *stage = NULL; + ClutterActor *actor = NULL; + + actor = CALLY_GET_CLUTTER_ACTOR (cally_actor); + stage = clutter_actor_get_stage (actor); + + /* press */ + tmp_event.button.type = CLUTTER_BUTTON_PRESS; + tmp_event.button.time = CLUTTER_CURRENT_TIME; + tmp_event.button.stage = CLUTTER_STAGE (stage); + tmp_event.button.source = actor; + tmp_event.button.button = 1; + + clutter_actor_event (actor, &tmp_event, FALSE); + + /* release */ + tmp_event.button.type = CLUTTER_BUTTON_RELEASE; + + clutter_actor_event (actor, &tmp_event, FALSE); +} + +static void +_cally_actor_press_action (CallyActor *cally_actor) +{ + ClutterEvent tmp_event; + ClutterActor *stage = NULL; + ClutterActor *actor = NULL; + + actor = CALLY_GET_CLUTTER_ACTOR (cally_actor); + stage = clutter_actor_get_stage (actor); + + tmp_event.button.type = CLUTTER_BUTTON_PRESS; + tmp_event.button.time = CLUTTER_CURRENT_TIME; + tmp_event.button.stage = CLUTTER_STAGE (stage); + tmp_event.button.source = actor; + tmp_event.button.button = 1; + + clutter_actor_event (actor, &tmp_event, FALSE); +} + +static void +_cally_actor_release_action (CallyActor *cally_actor) +{ + ClutterEvent tmp_event; + ClutterActor *stage = NULL; + ClutterActor *actor = NULL; + + actor = CALLY_GET_CLUTTER_ACTOR (cally_actor); + stage = clutter_actor_get_stage (actor); + + tmp_event.button.type = CLUTTER_BUTTON_RELEASE; + tmp_event.button.time = CLUTTER_CURRENT_TIME; + tmp_event.button.stage = CLUTTER_STAGE (stage); + tmp_event.button.source = actor; + tmp_event.button.button = 1; + + clutter_actor_event (actor, &tmp_event, FALSE); +} + +/** + * cally_actor_add_action: + * @action_name: the action name + * @action_description: the action description + * @action_keybinding: the action keybinding + * @action_func: the callback of the action, to be executed with do_action + * + * Adds a new action to be accessed with the AtkAction interface. + * + * Return value: added action id, or 0 if failure + * + * Since: 1.2 + */ +guint +cally_actor_add_action (CallyActor *cally_actor, + const gchar *action_name, + const gchar *action_description, + const gchar *action_keybinding, + CallyActionFunc action_func) +{ + CallyActorActionInfo *info = NULL; + CallyActorPrivate *priv = NULL; + + g_return_val_if_fail (CALLY_IS_ACTOR (cally_actor), -1); + g_return_val_if_fail (action_func != NULL, -1); + + priv = cally_actor->priv; + + info = g_new (CallyActorActionInfo, 1); + + if (action_name != NULL) + info->name = g_strdup (action_name); + else + info->name = NULL; + + if (action_description != NULL) + info->description = g_strdup (action_description); + else + info->description = NULL; + + if (action_keybinding != NULL) + info->keybinding = g_strdup (action_keybinding); + else + info->keybinding = NULL; + + info->do_action_func = action_func; + + priv->action_list = g_list_append (priv->action_list, (gpointer) info); + + return g_list_length (priv->action_list); +} + +/** + * cally_actor_remove_action: + * @action_id: the action id + * + * Removes a action, using the @action_id returned by cally_actor_add_action + * + * Return value: TRUE if the operation was succesful, FALSE otherwise + * + * Since: 1.2 + */ +gboolean +cally_actor_remove_action (CallyActor *cally_actor, + gint action_id) +{ + GList *list_node = NULL; + CallyActorPrivate *priv = NULL; + + g_return_val_if_fail (CALLY_IS_ACTOR (cally_actor), FALSE); + priv = cally_actor->priv; + + list_node = g_list_nth (priv->action_list, action_id - 1); + + if (!list_node) + return FALSE; + + _cally_actor_destroy_action_info (list_node->data, NULL); + + priv->action_list = g_list_remove_link (priv->action_list, list_node); + + return TRUE; +} + +/** + * cally_actor_remove_action: + * @action_name: the name of the action + * + * Removes a action, using the @action_name used when the action was added + * with cally_actor_add_action + * + * Return value: TRUE if the operation was succesful, FALSE otherwise + * + * Since: 1.2 + */ +gboolean +cally_actor_remove_action_by_name (CallyActor *cally_actor, + const gchar *action_name) +{ + GList *node = NULL; + gboolean action_found = FALSE; + CallyActorPrivate *priv = NULL; + + g_return_val_if_fail (CALLY_IS_ACTOR (cally_actor), FALSE); + priv = CALLY_ACTOR (cally_actor)->priv; + + for (node = priv->action_list; node && !action_found; + node = node->next) + { + if (!g_strcasecmp (((CallyActorActionInfo *)(node->data))->name, action_name)) + { + action_found = TRUE; + break; + } + } + if (!action_found) + return FALSE; + + _cally_actor_destroy_action_info (node->data, NULL); + priv->action_list = g_list_remove_link (priv->action_list, node); + + return TRUE; +} + + +static void +_cally_actor_destroy_action_info (gpointer action_info, + gpointer user_data) +{ + CallyActorActionInfo *info = (CallyActorActionInfo *)action_info; + + g_assert (info != NULL); + + g_free (info->name); + g_free (info->description); + g_free (info->keybinding); + + g_free (info); +} diff --git a/clutter/cally/cally-actor.h b/clutter/cally/cally-actor.h new file mode 100644 index 000000000..707845903 --- /dev/null +++ b/clutter/cally/cally-actor.h @@ -0,0 +1,108 @@ +/* CALLY - The Clutter Accessibility Implementation Library + * + * Copyright (C) 2008 Igalia, S.L. + * + * Author: Alejandro Piñeiro Iglesias + * + * Some parts are based on GailWidget from GAIL + * GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001, 2002, 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __CALLY_ACTOR_H__ +#define __CALLY_ACTOR_H__ + +#include +#include + +G_BEGIN_DECLS + +#define CALLY_TYPE_ACTOR (cally_actor_get_type ()) +#define CALLY_ACTOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CALLY_TYPE_ACTOR, CallyActor)) +#define CALLY_ACTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CALLY_TYPE_ACTOR, CallyActorClass)) +#define CALLY_IS_ACTOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CALLY_TYPE_ACTOR)) +#define CALLY_IS_ACTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CALLY_TYPE_ACTOR)) +#define CALLY_ACTOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CALLY_TYPE_ACTOR, CallyActorClass)) + +typedef struct _CallyActor CallyActor; +typedef struct _CallyActorClass CallyActorClass; +typedef struct _CallyActorPrivate CallyActorPrivate; + +/** + * CallyActionFunc: + * @cally_actor: a #CallyActor + * + * Action func, to be used on AtkAction implementation as a individual + * action + */ +typedef void (*CallyActionFunc) (CallyActor *cally_actor); + +struct _CallyActor +{ + AtkGObjectAccessible parent; + + /* < private > */ + CallyActorPrivate *priv; +}; + +struct _CallyActorClass +{ + AtkGObjectAccessibleClass parent_class; + + /* Signal handler for notify signal on Clutter Actor */ + void (*notify_clutter) (GObject *object, + GParamSpec *pspec); + + /* + * Signal handler for key_focus_in and key_focus_out on Clutter Actor + */ + gboolean (*focus_clutter) (ClutterActor *actor, + gpointer data); + + gint (*add_actor) (ClutterActor *container, + ClutterActor *actor, + gpointer data); + + gint (*remove_actor) (ClutterActor *container, + ClutterActor *actor, + gpointer data); + + /* padding for future expansion */ + gpointer _padding_dummy[30]; +}; + + +GType cally_actor_get_type (void); + +AtkObject* cally_actor_new (ClutterActor *actor); +guint cally_actor_add_action (CallyActor *cally_actor, + const gchar *action_name, + const gchar *action_description, + const gchar *action_keybinding, + CallyActionFunc action_func); + +gboolean cally_actor_remove_action (CallyActor *cally_actor, + gint action_id); + +gboolean cally_actor_remove_action_by_name (CallyActor *cally_actor, + const gchar *action_name); + + +G_END_DECLS + +#endif /* __CALLY_ACTOR_H__ */ diff --git a/clutter/cally/cally-clone.c b/clutter/cally/cally-clone.c new file mode 100644 index 000000000..0bde12053 --- /dev/null +++ b/clutter/cally/cally-clone.c @@ -0,0 +1,105 @@ +/* CALLY - The Clutter Accessibility Implementation Library + * + * Copyright (C) 2010 Igalia, S.L. + * + * Author: Alejandro Piñeiro Iglesias + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** + * SECTION:callyclutterclone + * @short_description: Implementation of the ATK interfaces for a #ClutterClone + * @see_also: #ClutterClone + * + * #CallyClutterClone implements the required ATK interfaces of #ClutterClone + * + * In particular it sets a proper role for the clone, as just a image, + * as it is the sanest and simplest approach. + * + * Check http://lists.o-hand.com/clutter/3797.html for more information + */ + +#include "cally-clone.h" +#include "cally-actor-private.h" + +#define CALLY_CLONE_DEFAULT_DESCRIPTION "ClutterClone accessibility object" + +static void cally_clone_class_init (CallyCloneClass *klass); +static void cally_clone_init (CallyClone *clone); + +/* AtkObject */ +static void cally_clone_real_initialize (AtkObject *obj, + gpointer data); +static G_CONST_RETURN gchar *cally_clone_get_description (AtkObject *obj); + + +G_DEFINE_TYPE (CallyClone, cally_clone, CALLY_TYPE_ACTOR) + +static void +cally_clone_class_init (CallyCloneClass *klass) +{ +/* GObjectClass *gobject_class = G_OBJECT_CLASS (klass); */ + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + + class->initialize = cally_clone_real_initialize; + class->get_description = cally_clone_get_description; +} + +static void +cally_clone_init (CallyClone *clone) +{ + /* nothing to do yet */ +} + +AtkObject* +cally_clone_new (ClutterActor *actor) +{ + GObject *object = NULL; + AtkObject *accessible = NULL; + + g_return_val_if_fail (CLUTTER_IS_CLONE (actor), NULL); + + object = g_object_new (CALLY_TYPE_CLONE, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, actor); + + return accessible; +} + +static void +cally_clone_real_initialize (AtkObject *obj, + gpointer data) +{ + ATK_OBJECT_CLASS (cally_clone_parent_class)->initialize (obj, data); + + obj->role = ATK_ROLE_IMAGE; +} + +static G_CONST_RETURN gchar * +cally_clone_get_description (AtkObject *obj) +{ + G_CONST_RETURN gchar *description = NULL; + + g_return_val_if_fail (CALLY_IS_CLONE (obj), NULL); + + description = ATK_OBJECT_CLASS (cally_clone_parent_class)->get_description (obj); + if (description == NULL) + description = CALLY_CLONE_DEFAULT_DESCRIPTION; + + return description; +} diff --git a/clutter/cally/cally-clone.h b/clutter/cally/cally-clone.h new file mode 100644 index 000000000..7b63dbeae --- /dev/null +++ b/clutter/cally/cally-clone.h @@ -0,0 +1,63 @@ +/* CALLY - The Clutter Accessibility Implementation Library + * + * Copyright (C) 2010 Igalia, S.L. + * + * Author: Alejandro Piñeiro Iglesias + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __CALLY_CLONE_H__ +#define __CALLY_CLONE_H__ + +#include "cally-actor.h" + +G_BEGIN_DECLS + +#define CALLY_TYPE_CLONE (cally_clone_get_type ()) +#define CALLY_CLONE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CALLY_TYPE_CLONE, CallyClone)) +#define CALLY_CLONE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CALLY_TYPE_CLONE, CallyCloneClass)) +#define CALLY_IS_CLONE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CALLY_TYPE_CLONE)) +#define CALLY_IS_CLONE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CALLY_TYPE_CLONE)) +#define CALLY_CLONE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CALLY_TYPE_CLONE, CallyCloneClass)) + + +typedef struct _CallyClone CallyClone; +typedef struct _CallyCloneClass CallyCloneClass; +typedef struct _CallyClonePrivate CallyClonePrivate; + +struct _CallyClone +{ + CallyActor parent; + + /* < private > */ + CallyClonePrivate *priv; +}; + +struct _CallyCloneClass +{ + CallyActorClass parent_class; + + /* padding for future expansion */ + gpointer _padding_dummy[30]; +}; + +GType cally_clone_get_type (void); +AtkObject *cally_clone_new (ClutterActor *actor); + +G_END_DECLS + +#endif /* __CALLY_CLONE_H__ */ diff --git a/clutter/cally/cally-factory.h b/clutter/cally/cally-factory.h new file mode 100644 index 000000000..37e0219c2 --- /dev/null +++ b/clutter/cally/cally-factory.h @@ -0,0 +1,91 @@ +/* CALLY - The Clutter Accessibility Implementation Library + * + * Copyright (C) 2008 Igalia, S.L. + * + * Author: Alejandro Piñeiro Iglesias + * + * Based on gailfactory.h from GAIL + * Copyright 2001, 2002, 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef _CALLY_FACTORY_H__ +#define _CALLY_FACTORY_H__ + +#include +#include + +#define CALLY_ACCESSIBLE_FACTORY(type, type_as_function, opt_create_accessible) \ + \ +static GType \ +type_as_function ## _factory_get_accessible_type (void) \ +{ \ + return type; \ +} \ + \ +static AtkObject* \ +type_as_function ## _factory_create_accessible (GObject *obj) \ +{ \ + ClutterActor *actor; \ + AtkObject *accessible; \ + \ + g_return_val_if_fail (CLUTTER_ACTOR (obj), NULL); \ + \ + actor = CLUTTER_ACTOR (obj); \ + \ + accessible = opt_create_accessible (actor); \ + \ + return accessible; \ +} \ + \ +static void \ +type_as_function ## _factory_class_init (AtkObjectFactoryClass *klass) \ +{ \ + klass->create_accessible = type_as_function ## _factory_create_accessible; \ + klass->get_accessible_type = type_as_function ## _factory_get_accessible_type;\ +} \ + \ +static GType \ +type_as_function ## _factory_get_type (void) \ +{ \ + static GType t = 0; \ + \ + if (!t) \ + { \ + char *name; \ + static const GTypeInfo tinfo = \ + { \ + sizeof (AtkObjectFactoryClass), \ + NULL, NULL, (GClassInitFunc) type_as_function ## _factory_class_init, \ + NULL, NULL, sizeof (AtkObjectFactory), 0, NULL, NULL \ + }; \ + \ + name = g_strconcat (g_type_name (type), "Factory", NULL); \ + t = g_type_register_static ( \ + ATK_TYPE_OBJECT_FACTORY, name, &tinfo, 0); \ + g_free (name); \ + } \ + \ + return t; \ +} + +#define CALLY_ACTOR_SET_FACTORY(widget_type, type_as_function) \ + atk_registry_set_factory_type (atk_get_default_registry (), \ + widget_type, \ + type_as_function ## _factory_get_type ()) + +#endif /* _CALLY_FACTORY_H__ */ diff --git a/clutter/cally/cally-group.c b/clutter/cally/cally-group.c new file mode 100644 index 000000000..ddcd9f81d --- /dev/null +++ b/clutter/cally/cally-group.c @@ -0,0 +1,138 @@ +/* CALLY - The Clutter Accessibility Implementation Library + * + * Copyright (C) 2008 Igalia, S.L. + * + * Author: Alejandro Piñeiro Iglesias + * + * Based on GailContainer from GAIL + * Copyright 2001, 2002, 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** + * SECTION:callycluttergroup + * @short_description: Implementation of the ATK interfaces for a #ClutterGroup + * @see_also: #ClutterGroup + * + * #CallyClutterGroup implements the required ATK interfaces of #ClutterGroup + * In particular it exposes: + * + * Each of the Clutter actors contained in the Clutter Group. + * + */ + +#include "cally-group.h" +#include "cally-actor-private.h" + +static void cally_group_class_init (CallyGroupClass *klass); +static void cally_group_init (CallyGroup *group); +static gint cally_group_get_n_children (AtkObject *obj); +static AtkObject* cally_group_ref_child (AtkObject *obj, + gint i); +static void cally_group_real_initialize (AtkObject *obj, + gpointer data); + +G_DEFINE_TYPE (CallyGroup, cally_group, CALLY_TYPE_ACTOR) + +static void +cally_group_class_init (CallyGroupClass *klass) +{ +/* GObjectClass *gobject_class = G_OBJECT_CLASS (klass); */ + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + + class->get_n_children = cally_group_get_n_children; + class->ref_child = cally_group_ref_child; + class->initialize = cally_group_real_initialize; +} + +static void +cally_group_init (CallyGroup *group) +{ + /* nothing to do yet */ +} + +AtkObject* +cally_group_new (ClutterActor *actor) +{ + GObject *object = NULL; + AtkObject *accessible = NULL; + + g_return_val_if_fail (CLUTTER_IS_GROUP (actor), NULL); + + object = g_object_new (CALLY_TYPE_GROUP, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, actor); + + return accessible; +} + +static gint +cally_group_get_n_children (AtkObject *obj) +{ + ClutterActor *actor = NULL; + gint count = 0; + + g_return_val_if_fail (CALLY_IS_GROUP (obj), count); + + actor = CALLY_GET_CLUTTER_ACTOR (obj); + + if (actor == NULL) /* defunct */ + return 0; + + g_return_val_if_fail (CLUTTER_IS_GROUP(actor), count); + + count = clutter_group_get_n_children (CLUTTER_GROUP (actor)); + + return count; +} + +static AtkObject* +cally_group_ref_child (AtkObject *obj, + gint i) +{ + AtkObject *accessible = NULL; + ClutterActor *actor = NULL; + ClutterActor *child = NULL; + + g_return_val_if_fail (CALLY_IS_GROUP (obj), NULL); + g_return_val_if_fail ((i >= 0), NULL); + + actor = CALLY_GET_CLUTTER_ACTOR (obj); + + g_return_val_if_fail (CLUTTER_IS_GROUP(actor), NULL); + child = clutter_group_get_nth_child (CLUTTER_GROUP(actor), i); + + if (!child) + return NULL; + + accessible = clutter_actor_get_accessible (child); + + if (accessible != NULL) + g_object_ref (accessible); + + return accessible; +} + +static void +cally_group_real_initialize (AtkObject *obj, + gpointer data) +{ + ATK_OBJECT_CLASS (cally_group_parent_class)->initialize (obj, data); + + obj->role = ATK_ROLE_PANEL; +} diff --git a/clutter/cally/cally-group.h b/clutter/cally/cally-group.h new file mode 100644 index 000000000..86920030d --- /dev/null +++ b/clutter/cally/cally-group.h @@ -0,0 +1,65 @@ +/* CALLY - The Clutter Accessibility Implementation Library + * + * Copyright (C) 2008 Igalia, S.L. + * + * Author: Alejandro Piñeiro Iglesias + * + * Based on GailContainer from GAIL + * Copyright 2001, 2002, 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __CALLY_GROUP_H__ +#define __CALLY_GROUP_H__ + +#include "cally-actor.h" + +G_BEGIN_DECLS + +#define CALLY_TYPE_GROUP (cally_group_get_type ()) +#define CALLY_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CALLY_TYPE_GROUP, CallyGroup)) +#define CALLY_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CALLY_TYPE_GROUP, CallyGroupClass)) +#define CALLY_IS_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CALLY_TYPE_GROUP)) +#define CALLY_IS_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CALLY_TYPE_GROUP)) +#define CALLY_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CALLY_TYPE_GROUP, CallyGroupClass)) + +typedef struct _CallyGroup CallyGroup; +typedef struct _CallyGroupClass CallyGroupClass; +typedef struct _CallyGroupPrivate CallyGroupPrivate; + +struct _CallyGroup +{ + CallyActor parent; + + /* < private > */ + CallyGroupPrivate *priv; +}; + +struct _CallyGroupClass +{ + CallyActorClass parent_class; + + /* padding for future expansion */ + gpointer _padding_dummy[30]; +}; + +GType cally_group_get_type (void); +AtkObject* cally_group_new (ClutterActor *actor); + +G_END_DECLS + +#endif /* __CALLY_GROUP_H__ */ diff --git a/clutter/cally/cally-rectangle.c b/clutter/cally/cally-rectangle.c new file mode 100644 index 000000000..934beb996 --- /dev/null +++ b/clutter/cally/cally-rectangle.c @@ -0,0 +1,102 @@ +/* CALLY - The Clutter Accessibility Implementation Library + * + * Copyright (C) 2009 Igalia, S.L. + * + * Author: Alejandro Piñeiro Iglesias + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** + * SECTION:callyclutterrectangle + * @short_description: Implementation of the ATK interfaces for a #ClutterRectangle + * @see_also: #ClutterRectangle + * + * #CallyClutterRectangle implements the required ATK interfaces of #ClutterRectangle + * + * In particular it sets a proper role for the rectangle. + */ + +#include "cally-rectangle.h" +#include "cally-actor-private.h" + +#define CALLY_RECTANGLE_DEFAULT_DESCRIPTION "A rectangle" + +static void cally_rectangle_class_init (CallyRectangleClass *klass); +static void cally_rectangle_init (CallyRectangle *rectangle); + +/* AtkObject */ +static void cally_rectangle_real_initialize (AtkObject *obj, + gpointer data); +static G_CONST_RETURN gchar *cally_rectangle_get_description (AtkObject *obj); + + +G_DEFINE_TYPE (CallyRectangle, cally_rectangle, CALLY_TYPE_ACTOR) + +static void +cally_rectangle_class_init (CallyRectangleClass *klass) +{ +/* GObjectClass *gobject_class = G_OBJECT_CLASS (klass); */ + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + + class->initialize = cally_rectangle_real_initialize; + class->get_description = cally_rectangle_get_description; +} + +static void +cally_rectangle_init (CallyRectangle *rectangle) +{ + /* nothing to do yet */ +} + +AtkObject* +cally_rectangle_new (ClutterActor *actor) +{ + GObject *object = NULL; + AtkObject *accessible = NULL; + + g_return_val_if_fail (CLUTTER_IS_RECTANGLE (actor), NULL); + + object = g_object_new (CALLY_TYPE_RECTANGLE, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, actor); + + return accessible; +} + +static void +cally_rectangle_real_initialize (AtkObject *obj, + gpointer data) +{ + ATK_OBJECT_CLASS (cally_rectangle_parent_class)->initialize (obj, data); + + obj->role = ATK_ROLE_IMAGE; +} + +static G_CONST_RETURN gchar * +cally_rectangle_get_description (AtkObject *obj) +{ + G_CONST_RETURN gchar *description = NULL; + + g_return_val_if_fail (CALLY_IS_RECTANGLE (obj), NULL); + + description = ATK_OBJECT_CLASS (cally_rectangle_parent_class)->get_description (obj); + if (description == NULL) + description = CALLY_RECTANGLE_DEFAULT_DESCRIPTION; + + return description; +} diff --git a/clutter/cally/cally-rectangle.h b/clutter/cally/cally-rectangle.h new file mode 100644 index 000000000..b2230c311 --- /dev/null +++ b/clutter/cally/cally-rectangle.h @@ -0,0 +1,63 @@ +/* CALLY - The Clutter Accessibility Implementation Library + * + * Copyright (C) 2009 Igalia, S.L. + * + * Author: Alejandro Piñeiro Iglesias + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __CALLY_RECTANGLE_H__ +#define __CALLY_RECTANGLE_H__ + +#include "cally-actor.h" + +G_BEGIN_DECLS + +#define CALLY_TYPE_RECTANGLE (cally_rectangle_get_type ()) +#define CALLY_RECTANGLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CALLY_TYPE_RECTANGLE, CallyRectangle)) +#define CALLY_RECTANGLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CALLY_TYPE_RECTANGLE, CallyRectangleClass)) +#define CALLY_IS_RECTANGLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CALLY_TYPE_RECTANGLE)) +#define CALLY_IS_RECTANGLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CALLY_TYPE_RECTANGLE)) +#define CALLY_RECTANGLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CALLY_TYPE_RECTANGLE, CallyRectangleClass)) + + +typedef struct _CallyRectangle CallyRectangle; +typedef struct _CallyRectangleClass CallyRectangleClass; +typedef struct _CallyRectanglePrivate CallyRectanglePrivate; + +struct _CallyRectangle +{ + CallyActor parent; + + /* < private > */ + CallyRectanglePrivate *priv; +}; + +struct _CallyRectangleClass +{ + CallyActorClass parent_class; + + /* padding for future expansion */ + gpointer _padding_dummy[30]; +}; + +GType cally_rectangle_get_type (void); +AtkObject* cally_rectangle_new (ClutterActor *actor); + +G_END_DECLS + +#endif /* __CALLY_RECTANGLE_H__ */ diff --git a/clutter/cally/cally-root.c b/clutter/cally/cally-root.c new file mode 100644 index 000000000..b7897534a --- /dev/null +++ b/clutter/cally/cally-root.c @@ -0,0 +1,273 @@ +/* CALLY - The Clutter Accessibility Implementation Library + * + * Copyright (C) 2008 Igalia, S.L. + * + * Author: Alejandro Piñeiro Iglesias + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** + * SECTION:callyroot + * @short_description: Root object for the CALLY toolkit + * @see_also: #ClutterStage + * + * #CallyRoot is the root object of the accessibility tree-like + * hierarchy, exposing the application level. + * + * Somewhat equivalent to GailTopLevel. We consider that this class + * expose the a11y information of the ClutterStageManager, as the + * children of this object are the different ClutterStage managed (so + * the GObject used in the atk_object_initialize is the + * ClutterStageManager). + * + */ + +#include +#include "cally-root.h" + +/* GObject */ +static void cally_root_class_init (CallyRootClass *klass); +static void cally_root_init (CallyRoot *root); +static void cally_root_finalize (GObject *object); + +/* AtkObject.h */ +static void cally_root_initialize (AtkObject *accessible, + gpointer data); +static gint cally_root_get_n_children (AtkObject *obj); +static AtkObject *cally_root_ref_child (AtkObject *obj, + gint i); +static AtkObject *cally_root_get_parent (AtkObject *obj); + +/* Private */ +static void cally_util_stage_added_cb (ClutterStageManager *stage_manager, + ClutterStage *stage, + gpointer data); +static void cally_util_stage_removed_cb (ClutterStageManager *stage_manager, + ClutterStage *stage, + gpointer data); + +#define CALLY_ROOT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CALLY_TYPE_ROOT, CallyRootPrivate)) + +G_DEFINE_TYPE (CallyRoot, cally_root, ATK_TYPE_GOBJECT_ACCESSIBLE) + +struct _CallyRootPrivate +{ +/* We save the CallyStage objects. Other option could save the stage + * list, and then just get the a11y object on the ref_child, etc. But + * the ref_child is more common that the init and the stage-add, + * stage-remove, so we avoid getting the accessible object + * constantly + */ + GSList *stage_list; + + /* signals id */ + guint stage_added_id; + guint stage_removed_id; +}; + +static void +cally_root_class_init (CallyRootClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + + gobject_class->finalize = cally_root_finalize; + + /* AtkObject */ + class->get_n_children = cally_root_get_n_children; + class->ref_child = cally_root_ref_child; + class->get_parent = cally_root_get_parent; + class->initialize = cally_root_initialize; + + g_type_class_add_private (gobject_class, sizeof (CallyRootPrivate)); +} + +static void +cally_root_init (CallyRoot *root) +{ + root->priv = CALLY_ROOT_GET_PRIVATE (root); + + root->priv->stage_list = NULL; + root->priv->stage_added_id = 0; + root->priv->stage_removed_id = 0; +} + +AtkObject* +cally_root_new (void) +{ + GObject *object = NULL; + AtkObject *accessible = NULL; + ClutterStageManager *stage_manager = NULL; + + object = g_object_new (CALLY_TYPE_ROOT, NULL); + + accessible = ATK_OBJECT (object); + stage_manager = clutter_stage_manager_get_default (); + + atk_object_initialize (accessible, stage_manager); + + return accessible; +} + +static void +cally_root_finalize (GObject *object) +{ + CallyRoot *root = CALLY_ROOT (object); + GObject *stage_manager = NULL; + + g_return_if_fail (CALLY_IS_ROOT (object)); + + if (root->priv->stage_list) + { + g_slist_free (root->priv->stage_list); + root->priv->stage_list = NULL; + } + + stage_manager = atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (root)); + + g_signal_handler_disconnect (stage_manager, + root->priv->stage_added_id); + + g_signal_handler_disconnect (stage_manager, + root->priv->stage_added_id); + + G_OBJECT_CLASS (cally_root_parent_class)->finalize (object); +} + +/* AtkObject.h */ +static void +cally_root_initialize (AtkObject *accessible, + gpointer data) +{ + ClutterStageManager *stage_manager = NULL; + const GSList *iter = NULL; + const GSList *stage_list = NULL; + ClutterStage *clutter_stage = NULL; + AtkObject *cally_stage = NULL; + CallyRoot *root = NULL; + + accessible->role = ATK_ROLE_APPLICATION; + accessible->name = g_get_prgname(); + accessible->accessible_parent = NULL; + + /* children initialization */ + root = CALLY_ROOT (accessible); + stage_manager = CLUTTER_STAGE_MANAGER (data); + stage_list = clutter_stage_manager_peek_stages (stage_manager); + + for (iter = stage_list; iter != NULL; iter = g_slist_next (iter)) + { + clutter_stage = CLUTTER_STAGE (iter->data); + cally_stage = clutter_actor_get_accessible (CLUTTER_ACTOR (clutter_stage)); + + root->priv->stage_list = g_slist_append (root->priv->stage_list, + cally_stage); + } + + root->priv->stage_added_id = + g_signal_connect (G_OBJECT (stage_manager), "stage-added", + G_CALLBACK (cally_util_stage_added_cb), root); + + root->priv->stage_removed_id = + g_signal_connect (G_OBJECT (stage_manager), "stage-removed", + G_CALLBACK (cally_util_stage_removed_cb), root); + + ATK_OBJECT_CLASS (cally_root_parent_class)->initialize (accessible, data); +} + + +static gint +cally_root_get_n_children (AtkObject *obj) +{ + CallyRoot *root = CALLY_ROOT (obj); + + return g_slist_length (root->priv->stage_list); +} + +static AtkObject* +cally_root_ref_child (AtkObject *obj, + gint i) +{ + CallyRoot *cally_root = NULL; + GSList *stage_list = NULL; + gint num = 0; + AtkObject *item = NULL; + + cally_root = CALLY_ROOT (obj); + stage_list = cally_root->priv->stage_list; + num = g_slist_length (stage_list); + + g_return_val_if_fail ((i < num)&&(i >= 0), NULL); + + item = g_slist_nth_data (stage_list, i); + if (!item) + { + return NULL; + } + + g_object_ref (item); + + return item; +} + +static AtkObject* +cally_root_get_parent (AtkObject *obj) +{ + return NULL; +} + +/* -------------------------------- PRIVATE --------------------------------- */ + +static void +cally_util_stage_added_cb (ClutterStageManager *stage_manager, + ClutterStage *stage, + gpointer data) +{ + CallyRoot *root = CALLY_ROOT (data); + AtkObject *cally_stage = NULL; + gint index = -1; + + cally_stage = clutter_actor_get_accessible (CLUTTER_ACTOR (stage)); + + root->priv->stage_list = g_slist_append (root->priv->stage_list, + cally_stage); + + index = g_slist_index (root->priv->stage_list, cally_stage); + g_signal_emit_by_name (root, "children_changed::add", + index, cally_stage, NULL); +} + +static void +cally_util_stage_removed_cb (ClutterStageManager *stage_manager, + ClutterStage *stage, + gpointer data) +{ + CallyRoot *root = CALLY_ROOT (data); + AtkObject *cally_stage = NULL; + gint index = -1; + + cally_stage = clutter_actor_get_accessible (CLUTTER_ACTOR (stage)); + + index = g_slist_index (root->priv->stage_list, cally_stage); + + root->priv->stage_list = g_slist_remove (root->priv->stage_list, + cally_stage); + + index = g_slist_index (root->priv->stage_list, cally_stage); + g_signal_emit_by_name (root, "children_changed::remove", + index, cally_stage, NULL); +} diff --git a/clutter/cally/cally-root.h b/clutter/cally/cally-root.h new file mode 100644 index 000000000..540a74282 --- /dev/null +++ b/clutter/cally/cally-root.h @@ -0,0 +1,64 @@ +/* CALLY - The Clutter Accessibility Implementation Library + * + * Copyright (C) 2009 Igalia, S.L. + * + * Author: Alejandro Piñeiro Iglesias + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __CALLY_ROOT_H__ +#define __CALLY_ROOT_H__ + +#include + +G_BEGIN_DECLS + +#define CALLY_TYPE_ROOT (cally_root_get_type ()) +#define CALLY_ROOT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CALLY_TYPE_ROOT, CallyRoot)) +#define CALLY_ROOT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CALLY_TYPE_ROOT, CallyRootClass)) +#define CALLY_IS_ROOT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CALLY_TYPE_ROOT)) +#define CALLY_IS_ROOT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CALLY_TYPE_ROOT)) +#define CALLY_ROOT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CALLY_TYPE_ROOT, CallyRootClass)) + +typedef struct _CallyRoot CallyRoot; +typedef struct _CallyRootClass CallyRootClass; +typedef struct _CallyRootPrivate CallyRootPrivate; + +struct _CallyRoot +{ + AtkGObjectAccessible parent; + + /* < private > */ + CallyRootPrivate *priv; +}; + +struct _CallyRootClass +{ + AtkGObjectAccessibleClass parent_class; + + /* padding for future expansion */ + gpointer _padding_dummy[30]; +}; + + +GType cally_root_get_type (void); +AtkObject *cally_root_new (void); + + +G_END_DECLS + +#endif /* __CALLY_ROOT_H__ */ diff --git a/clutter/cally/cally-stage.c b/clutter/cally/cally-stage.c new file mode 100644 index 000000000..57f037be4 --- /dev/null +++ b/clutter/cally/cally-stage.c @@ -0,0 +1,258 @@ +/* CALLY - The Clutter Accessibility Implementation Library + * + * Copyright (C) 2008 Igalia, S.L. + * + * Author: Alejandro Piñeiro Iglesias + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** + * SECTION:callystage + * @short_description: Implementation of the ATK interfaces for a #ClutterStage + * @see_also: #ClutterStage + * + * #CallyStage implements the required ATK interfaces of #ClutterStage + * + */ + +#include "cally-stage.h" +#include "cally-actor-private.h" + +enum { + ACTIVATE, + CREATE, + DEACTIVATE, + DESTROY, + LAST_SIGNAL +}; + +static guint cally_stage_signals [LAST_SIGNAL] = { 0, }; + +static void cally_stage_class_init (CallyStageClass *klass); +static void cally_stage_init (CallyStage *stage); + +/* AtkObject.h */ +static G_CONST_RETURN gchar *cally_stage_get_name (AtkObject *obj); +static G_CONST_RETURN gchar *cally_stage_get_description (AtkObject *obj); +static void cally_stage_real_initialize (AtkObject *obj, + gpointer data); +static AtkStateSet* cally_stage_ref_state_set (AtkObject *obj); + +/* Auxiliar */ +static void cally_stage_activate_cb (ClutterStage *stage, + gpointer data); +static void cally_stage_deactivate_cb (ClutterStage *stage, + gpointer data); + + +#define CALLY_STAGE_DEFAULT_NAME "Stage" +#define CALLY_STAGE_DEFAULT_DESCRIPTION "Top level 'window' on which child actors are placed and manipulated" + +G_DEFINE_TYPE (CallyStage, cally_stage, CALLY_TYPE_GROUP); + +#define CALLY_STAGE_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CALLY_TYPE_STAGE, CallyStagePrivate)) + +struct _CallyStagePrivate +{ + gboolean active; +}; + +static void +cally_stage_class_init (CallyStageClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); +/* CallyActorClass *cally_class = CALLY_ACTOR_CLASS (klass); */ + + /* AtkObject */ + class->get_name = cally_stage_get_name; + class->get_description = cally_stage_get_description; + class->initialize = cally_stage_real_initialize; + class->ref_state_set = cally_stage_ref_state_set; + + g_type_class_add_private (gobject_class, sizeof (CallyStagePrivate)); + + cally_stage_signals [ACTIVATE] = + g_signal_new ("activate", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, /* default signal handler */ + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + cally_stage_signals [CREATE] = + g_signal_new ("create", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, /* default signal handler */ + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + cally_stage_signals [DEACTIVATE] = + g_signal_new ("deactivate", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, /* default signal handler */ + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + cally_stage_signals [DESTROY] = + g_signal_new ("destroy", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, /* default signal handler */ + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + +static void +cally_stage_init (CallyStage *cally_stage) +{ + CallyStagePrivate *priv = CALLY_STAGE_GET_PRIVATE (cally_stage); + + cally_stage->priv = priv; + + priv->active = FALSE; +} + +AtkObject* +cally_stage_new (ClutterActor *actor) +{ + GObject *object = NULL; + AtkObject *accessible = NULL; + + g_return_val_if_fail (CLUTTER_IS_STAGE (actor), NULL); + + object = g_object_new (CALLY_TYPE_STAGE, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, actor); + + return accessible; +} + +/* AtkObject.h */ +static G_CONST_RETURN gchar * +cally_stage_get_name (AtkObject *obj) +{ + G_CONST_RETURN gchar *name = NULL; + + g_return_val_if_fail (CALLY_IS_STAGE (obj), NULL); + + /* parent name */ + name = ATK_OBJECT_CLASS (cally_stage_parent_class)->get_name (obj); + + if (name == NULL) + name = CALLY_STAGE_DEFAULT_NAME; + + return name; +} + +static G_CONST_RETURN gchar * +cally_stage_get_description (AtkObject *obj) +{ + G_CONST_RETURN gchar *description = NULL; + + g_return_val_if_fail (CALLY_IS_STAGE (obj), NULL); + + /* parent description */ + description = ATK_OBJECT_CLASS (cally_stage_parent_class)->get_description (obj); + + if (description == NULL) + description = CALLY_STAGE_DEFAULT_DESCRIPTION; + + return description; +} + +static void +cally_stage_real_initialize (AtkObject *obj, + gpointer data) +{ + ClutterStage *stage = NULL; + + g_return_if_fail (CALLY_IS_STAGE (obj)); + + ATK_OBJECT_CLASS (cally_stage_parent_class)->initialize (obj, data); + + stage = CLUTTER_STAGE (CALLY_GET_CLUTTER_ACTOR (obj)); + + g_signal_connect (stage, "activate", G_CALLBACK (cally_stage_activate_cb), obj); + g_signal_connect (stage, "deactivate", G_CALLBACK (cally_stage_deactivate_cb), obj); + + obj->role = ATK_ROLE_CANVAS; +} + +static AtkStateSet* +cally_stage_ref_state_set (AtkObject *obj) +{ + CallyStage *cally_stage = NULL; + AtkStateSet *state_set = NULL; + ClutterStage *stage = NULL; + + g_return_val_if_fail (CALLY_IS_STAGE (obj), NULL); + cally_stage = CALLY_STAGE (obj); + + state_set = ATK_OBJECT_CLASS (cally_stage_parent_class)->ref_state_set (obj); + stage = CLUTTER_STAGE (CALLY_GET_CLUTTER_ACTOR (cally_stage)); + + if (stage == NULL) + return state_set; + + if (cally_stage->priv->active) + atk_state_set_add_state (state_set, ATK_STATE_ACTIVE); + + return state_set; +} + +/* Auxiliar */ +static void +cally_stage_activate_cb (ClutterStage *stage, + gpointer data) +{ + CallyStage *cally_stage = NULL; + + g_return_if_fail (CALLY_IS_STAGE (data)); + + cally_stage = CALLY_STAGE (data); + + cally_stage->priv->active = TRUE; + + atk_object_notify_state_change (ATK_OBJECT (cally_stage), + ATK_STATE_ACTIVE, TRUE); + + g_signal_emit (cally_stage, cally_stage_signals [ACTIVATE], 0); +} + +static void +cally_stage_deactivate_cb (ClutterStage *stage, + gpointer data) +{ + CallyStage *cally_stage = NULL; + + g_return_if_fail (CALLY_IS_STAGE (data)); + + cally_stage = CALLY_STAGE (data); + + cally_stage->priv->active = FALSE; + + atk_object_notify_state_change (ATK_OBJECT (cally_stage), + ATK_STATE_ACTIVE, FALSE); + + g_signal_emit (cally_stage, cally_stage_signals [DEACTIVATE], 0); +} diff --git a/clutter/cally/cally-stage.h b/clutter/cally/cally-stage.h new file mode 100644 index 000000000..e95c08eed --- /dev/null +++ b/clutter/cally/cally-stage.h @@ -0,0 +1,62 @@ +/* CALLY - The Clutter Accessibility Implementation Library + * + * Copyright (C) 2008 Igalia, S.L. + * + * Author: Alejandro Piñeiro Iglesias + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __CALLY_STAGE_H__ +#define __CALLY_STAGE_H__ + +#include "cally-group.h" + +G_BEGIN_DECLS + +#define CALLY_TYPE_STAGE (cally_stage_get_type ()) +#define CALLY_STAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CALLY_TYPE_STAGE, CallyStage)) +#define CALLY_STAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CALLY_TYPE_STAGE, CallyStageClass)) +#define CALLY_IS_STAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CALLY_TYPE_STAGE)) +#define CALLY_IS_STAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CALLY_TYPE_STAGE)) +#define CALLY_STAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CALLY_TYPE_STAGE, CallyStageClass)) + +typedef struct _CallyStage CallyStage; +typedef struct _CallyStageClass CallyStageClass; +typedef struct _CallyStagePrivate CallyStagePrivate; + +struct _CallyStage +{ + CallyGroup parent; + + /* < private > */ + CallyStagePrivate *priv; +}; + +struct _CallyStageClass +{ + CallyGroupClass parent_class; + + /* padding for future expansion */ + gpointer _padding_dummy[30]; +}; + +GType cally_stage_get_type (void); +AtkObject *cally_stage_new (ClutterActor *actor); + +G_END_DECLS + +#endif /* __CALLY_STAGE_H__ */ diff --git a/clutter/cally/cally-text.c b/clutter/cally/cally-text.c new file mode 100644 index 000000000..bd55d299c --- /dev/null +++ b/clutter/cally/cally-text.c @@ -0,0 +1,1255 @@ +/* CALLY - The Clutter Accessibility Implementation Library + * + * Copyright (C) 2009 Igalia, S.L. + * + * Author: Alejandro Piñeiro Iglesias + * + * Some parts are based on GailLabel, GailEntry, GailTextView from GAIL + * GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001, 2002, 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** + * SECTION:cally-text + * @short_description: Implementation of the ATK interfaces for a #ClutterText + * @see_also: #ClutterText + * + * #CallyClutterText implements the required ATK interfaces of + * #ClutterText, #AtkText and #AtkEditableText + * + * + */ + +/** + * IMPLEMENTATION NOTES: + * + * * AtkText: There are still some methods not implemented yet: + * atk_text_get_default_attributes + * atk_text_get_character_extents + * atk_text_get_offset_at_point + * + * See details on bug CB#1733 + * + * * AtkEditableText: some methods will not be implemented + * + * * atk_editable_text_set_run_attributes: ClutterText has some + * properties equivalent to the AtkAttributte, but it doesn't + * allow you to define it by + * + * * atk_editable_text_copy: Clutter has no Clipboard support + * + * * atk_editable_text_paste: Clutter has no Clipboard support + * + * * atk_editable_text_cut: Clutter has no Clipboard support. In + * this case, as cut is basically a copy&delete combination, + * we could have implemented it using the delete, but IMHO, + * it would be weird to cut a text, get the text removed and + * then not be able to paste the text + * + * See details on bug CB#1734 + */ + +#include "cally-text.h" +#include "cally-actor-private.h" + +static void cally_text_class_init (CallyTextClass *klass); +static void cally_text_init (CallyText *cally_text); +static void cally_text_finalize (GObject *obj); + +/* AtkObject */ +static void cally_text_real_initialize (AtkObject *obj, + gpointer data); +static G_CONST_RETURN gchar * cally_text_get_name (AtkObject *obj); +static AtkStateSet* cally_text_ref_state_set (AtkObject *obj); + +/* atkaction */ + +static void _cally_text_activate_action (CallyActor *cally_actor); +static void _check_activate_action (CallyText *cally_text, + ClutterText *clutter_text); + +/* AtkText */ +static void cally_text_text_interface_init (AtkTextIface *iface); +static gchar* cally_text_get_text (AtkText *text, + gint start_offset, + gint end_offset); +static gunichar cally_text_get_character_at_offset (AtkText *text, + gint offset); +static gchar* cally_text_get_text_before_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset); +static gchar* cally_text_get_text_at_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset); +static gchar* cally_text_get_text_after_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset); +static gint cally_text_get_caret_offset (AtkText *text); +static gboolean cally_text_set_caret_offset (AtkText *text, + gint offset); +static gint cally_text_get_character_count (AtkText *text); +static gint cally_text_get_n_selections (AtkText *text); +static gchar* cally_text_get_selection (AtkText *text, + gint selection_num, + gint *start_offset, + gint *end_offset); +static gboolean cally_text_add_selection (AtkText *text, + gint start_offset, + gint end_offset); +static gboolean cally_text_remove_selection (AtkText *text, + gint selection_num); +static gboolean cally_text_set_selection (AtkText *text, + gint selection_num, + gint start_offset, + gint end_offset); +static AtkAttributeSet* cally_text_get_run_attributes (AtkText *text, + gint offset, + gint *start_offset, + gint *end_offset); +static void _cally_text_get_selection_bounds (ClutterText *clutter_text, + gint *start_offset, + gint *end_offset); +static void _cally_text_text_changed_cb (ClutterText *clutter_text, + gpointer data); +static void _cally_text_insert_text_cb (ClutterText *clutter_text, + gchar *new_text, + gint new_text_length, + gint *position, + gpointer data); +static void _cally_text_delete_text_cb (ClutterText *clutter_text, + gint start_pos, + gint end_pos, + gpointer data); +static gboolean _idle_notify_insert (gpointer data); +static void _notify_insert (CallyText *cally_text); +static void _notify_delete (CallyText *cally_text); + +/* AtkEditableText */ +static void cally_text_editable_text_interface_init (AtkEditableTextIface *iface); +static void cally_text_set_text_contents (AtkEditableText *text, + const gchar *string); +static void cally_text_insert_text (AtkEditableText *text, + const gchar *string, + gint length, + gint *position); +static void cally_text_delete_text (AtkEditableText *text, + gint start_pos, + gint end_pos); + +/* CallyActor */ +static void cally_text_notify_clutter (GObject *obj, + GParamSpec *pspec); + +static gboolean _check_for_selection_change (CallyText *cally_text, + ClutterText *clutter_text); + +/* Misc functions */ +static AtkAttributeSet* _cally_misc_add_attribute (AtkAttributeSet *attrib_set, + AtkTextAttribute attr, + gchar *value); + +static AtkAttributeSet* _cally_misc_layout_get_run_attributes (AtkAttributeSet *attrib_set, + PangoLayout *layout, + gchar *text, + gint offset, + gint *start_offset, + gint *end_offset); + +G_DEFINE_TYPE_WITH_CODE (CallyText, + cally_text, + CALLY_TYPE_ACTOR, + G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT, + cally_text_text_interface_init) + G_IMPLEMENT_INTERFACE (ATK_TYPE_EDITABLE_TEXT, + cally_text_editable_text_interface_init)); + +#define CALLY_TEXT_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CALLY_TYPE_TEXT, CallyTextPrivate)) + +struct _CallyTextPrivate +{ + /* Cached ClutterText values*/ + gint cursor_position; + gint selection_bound; + + /* text_changed::insert stuff */ + gchar *signal_name_insert; + gint position_insert; + gint length_insert; + guint insert_idle_handler; + + /* text_changed::delete stuff */ + gchar *signal_name_delete; + gint position_delete; + gint length_delete; + + /* action */ + guint activate_action_id; +}; + +static void +cally_text_class_init (CallyTextClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + CallyActorClass *cally_class = CALLY_ACTOR_CLASS (klass); + + gobject_class->finalize = cally_text_finalize; + + class->initialize = cally_text_real_initialize; + class->get_name = cally_text_get_name; + class->ref_state_set = cally_text_ref_state_set; + + cally_class->notify_clutter = cally_text_notify_clutter; + + g_type_class_add_private (gobject_class, sizeof (CallyTextPrivate)); +} + +static void +cally_text_init (CallyText *cally_text) +{ + CallyTextPrivate *priv = CALLY_TEXT_GET_PRIVATE (cally_text); + + cally_text->priv = priv; + + priv->cursor_position = 0; + priv->selection_bound = 0; + + priv->signal_name_insert = NULL; + priv->position_insert = -1; + priv->length_insert = -1; + priv->insert_idle_handler = 0; + + priv->signal_name_delete = NULL; + priv->position_delete = -1; + priv->length_delete = -1; + + priv->activate_action_id = 0; +} + +static void +cally_text_finalize (GObject *obj) +{ + CallyText *cally_text = CALLY_TEXT (obj); + +/* g_object_unref (cally_text->priv->textutil); */ +/* cally_text->priv->textutil = NULL; */ + + if (cally_text->priv->insert_idle_handler) + { + g_source_remove (cally_text->priv->insert_idle_handler); + cally_text->priv->insert_idle_handler = 0; + } + + G_OBJECT_CLASS (cally_text_parent_class)->finalize (obj); +} + +AtkObject* +cally_text_new (ClutterActor *actor) +{ + GObject *object = NULL; + AtkObject *accessible = NULL; + + g_return_val_if_fail (CLUTTER_IS_TEXT (actor), NULL); + + object = g_object_new (CALLY_TYPE_TEXT, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, actor); + + return accessible; +} + +/* atkobject.h */ + +static void +cally_text_real_initialize(AtkObject *obj, + gpointer data) +{ + ClutterText *clutter_text = NULL; + CallyText *cally_text = NULL; + + ATK_OBJECT_CLASS (cally_text_parent_class)->initialize (obj, data); + + g_return_if_fail (CLUTTER_TEXT (data)); + + cally_text = CALLY_TEXT (obj); + clutter_text = CLUTTER_TEXT (data); + + cally_text->priv->cursor_position = clutter_text_get_cursor_position (clutter_text); + cally_text->priv->selection_bound = clutter_text_get_selection_bound (clutter_text); + + g_signal_connect (clutter_text, "text-changed", + G_CALLBACK (_cally_text_text_changed_cb), + cally_text); + g_signal_connect (clutter_text, "insert-text", + G_CALLBACK (_cally_text_insert_text_cb), + cally_text); + g_signal_connect (clutter_text, "delete-text", + G_CALLBACK (_cally_text_delete_text_cb), + cally_text); + + _check_activate_action (cally_text, clutter_text); + + obj->role = ATK_ROLE_TEXT; +} + +static G_CONST_RETURN gchar * +cally_text_get_name (AtkObject *obj) +{ + G_CONST_RETURN gchar *name; + + g_return_val_if_fail (CALLY_IS_ACTOR (obj), NULL); + + name = ATK_OBJECT_CLASS (cally_text_parent_class)->get_name (obj); + if (name == NULL) + { + ClutterActor *actor = NULL; + + actor = CALLY_GET_CLUTTER_ACTOR (obj); + + if (actor == NULL) /* State is defunct */ + name = NULL; + else + name = clutter_text_get_text (CLUTTER_TEXT (actor)); + } + + return name; +} + +static AtkStateSet* +cally_text_ref_state_set (AtkObject *obj) +{ + AtkStateSet *result = NULL; + ClutterActor *actor = NULL; + + result = ATK_OBJECT_CLASS (cally_text_parent_class)->ref_state_set (obj); + + actor = CALLY_GET_CLUTTER_ACTOR (obj); + + if (actor == NULL) + return result; + + if (clutter_text_get_editable (CLUTTER_TEXT (actor))) + atk_state_set_add_state (result, ATK_STATE_EDITABLE); + + if (clutter_text_get_selectable (CLUTTER_TEXT (actor))) + atk_state_set_add_state (result, ATK_STATE_SELECTABLE_TEXT); + + return result; +} + + +/***** atktext.h ******/ + +static void +cally_text_text_interface_init (AtkTextIface *iface) +{ + g_return_if_fail (iface != NULL); + + iface->get_text = cally_text_get_text; + iface->get_character_at_offset = cally_text_get_character_at_offset; + iface->get_text_before_offset = cally_text_get_text_before_offset; + iface->get_text_at_offset = cally_text_get_text_at_offset; + iface->get_text_after_offset = cally_text_get_text_after_offset; + iface->get_character_count = cally_text_get_character_count; + iface->get_caret_offset = cally_text_get_caret_offset; + iface->set_caret_offset = cally_text_set_caret_offset; + iface->get_n_selections = cally_text_get_n_selections; + iface->get_selection = cally_text_get_selection; + iface->add_selection = cally_text_add_selection; + iface->remove_selection = cally_text_remove_selection; + iface->set_selection = cally_text_set_selection; + iface->get_run_attributes = cally_text_get_run_attributes; +/* iface->get_default_attributes = cally_text_get_default_attributes; */ +/* iface->get_character_extents = */ +/* iface->get_offset_at_point = */ + +} + +static gchar* +cally_text_get_text (AtkText *text, + gint start_offset, + gint end_offset) +{ + ClutterActor *actor = NULL; + + g_return_val_if_fail (CALLY_IS_TEXT (text), NULL); + + actor = CALLY_GET_CLUTTER_ACTOR (text); + if (actor == NULL) /* Object is defunct */ + return NULL; + + return clutter_text_get_chars (CLUTTER_TEXT (actor), + start_offset, end_offset); +} + +static gunichar +cally_text_get_character_at_offset (AtkText *text, + gint offset) +{ + ClutterActor *actor = NULL; + CallyText *cally_text = NULL; + gchar *string = NULL; + gchar *index = NULL; + gunichar unichar; + + actor = CALLY_GET_CLUTTER_ACTOR (text); + if (actor == NULL) /* State is defunct */ + return '\0'; + + cally_text = CALLY_TEXT (text); + string = clutter_text_get_chars (CLUTTER_TEXT (actor), 0, -1); + if (offset >= g_utf8_strlen (string, -1)) + { + unichar = '\0'; + } + else + { + index = g_utf8_offset_to_pointer (string, offset); + + unichar = g_utf8_get_char(index); + } + + g_free(string); + + return unichar; +} + +static gchar* +cally_text_get_text_before_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + ClutterActor *actor = NULL; + ClutterText *clutter_text = NULL; + CallyText *cally_text = NULL; + + actor = CALLY_GET_CLUTTER_ACTOR (text); + if (actor == NULL) /* State is defunct */ + return NULL; + + clutter_text = CLUTTER_TEXT (actor); + cally_text = CALLY_TEXT (text); + +/* return gail_text_util_get_text (cally_text->priv->textutil, */ +/* clutter_text_get_layout (clutter_text), */ +/* GAIL_BEFORE_OFFSET, boundary_type, */ +/* offset, start_offset, end_offset); */ + return NULL; +} + +static gchar* +cally_text_get_text_at_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + ClutterActor *actor = NULL; + ClutterText *clutter_text = NULL; + CallyText *cally_text = NULL; + + actor = CALLY_GET_CLUTTER_ACTOR (text); + if (actor == NULL) /* State is defunct */ + return NULL; + + clutter_text = CLUTTER_TEXT (actor); + cally_text = CALLY_TEXT (text); + +/* return gail_text_util_get_text (cally_text->priv->textutil, */ +/* clutter_text_get_layout (clutter_text), GAIL_AT_OFFSET, */ +/* boundary_type, offset, start_offset, end_offset); */ + return NULL; +} + +static gchar* +cally_text_get_text_after_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + ClutterActor *actor = NULL; + ClutterText *clutter_text = NULL; + CallyText *cally_text = NULL; + + actor = CALLY_GET_CLUTTER_ACTOR (text); + if (actor == NULL) /* State is defunct */ + return NULL; + + clutter_text = CLUTTER_TEXT (actor); + cally_text = CALLY_TEXT (text); + +/* return gail_text_util_get_text (cally_text->priv->textutil, */ +/* clutter_text_get_layout (clutter_text), GAIL_AFTER_OFFSET, */ +/* boundary_type, offset, start_offset, end_offset); */ + return NULL; +} + +static gint +cally_text_get_caret_offset (AtkText *text) +{ + ClutterActor *actor = NULL; + + actor = CALLY_GET_CLUTTER_ACTOR (text); + if (actor == NULL) /* State is defunct */ + return -1; + + return clutter_text_get_cursor_position (CLUTTER_TEXT (actor)); +} + +static gboolean +cally_text_set_caret_offset (AtkText *text, + gint offset) +{ + ClutterActor *actor = NULL; + + actor = CALLY_GET_CLUTTER_ACTOR (text); + if (actor == NULL) /* State is defunct */ + return FALSE; + + clutter_text_set_cursor_position (CLUTTER_TEXT (actor), offset); + + /* like in gailentry, we suppose that this always works, as clutter text + doesn't return anything */ + return TRUE; +} + +static gint +cally_text_get_character_count (AtkText *text) +{ + ClutterActor *actor = NULL; + ClutterText *clutter_text = NULL; + + actor = CALLY_GET_CLUTTER_ACTOR (text); + if (actor == NULL) /* State is defunct */ + return 0; + + clutter_text = CLUTTER_TEXT (actor); + return g_utf8_strlen (clutter_text_get_text (clutter_text), -1); +} + +static gint +cally_text_get_n_selections (AtkText *text) +{ + ClutterActor *actor = NULL; + gint selection_bound = -1; + + actor = CALLY_GET_CLUTTER_ACTOR (text); + if (actor == NULL) /* State is defunct */ + return 0; + + if (!clutter_text_get_selectable (CLUTTER_TEXT (actor))) + return 0; + + selection_bound = clutter_text_get_selection_bound (CLUTTER_TEXT (actor)); + + if (selection_bound > 0) + return 1; + else + return 0; +} + +static gchar* +cally_text_get_selection (AtkText *text, + gint selection_num, + gint *start_offset, + gint *end_offset) +{ + ClutterActor *actor = NULL; + + actor = CALLY_GET_CLUTTER_ACTOR (text); + if (actor == NULL) /* State is defunct */ + return NULL; + + /* As in gailentry, only let the user get the selection if one is set, and if + * the selection_num is 0. + */ + if (selection_num != 0) + return NULL; + + _cally_text_get_selection_bounds (CLUTTER_TEXT (actor), start_offset, end_offset); + + if (*start_offset != *end_offset) + return clutter_text_get_selection (CLUTTER_TEXT (actor)); + else + return NULL; +} + +/* ClutterText only allows one selection. So this method will set the selection + if no selection exists, but as in gailentry, it will not change the current + selection */ +static gboolean +cally_text_add_selection (AtkText *text, + gint start_offset, + gint end_offset) +{ + ClutterActor *actor; + gint select_start, select_end; + + actor = CALLY_GET_CLUTTER_ACTOR (text); + if (actor == NULL) /* State is defunct */ + return FALSE; + + _cally_text_get_selection_bounds (CLUTTER_TEXT (actor), + &select_start, &select_end); + + /* Like in gailentry, if there is already a selection, then don't allow another + * to be added, since ClutterText only supports one selected region. + */ + if (select_start == select_end) + { + clutter_text_set_selection (CLUTTER_TEXT (actor), + start_offset, end_offset); + + return TRUE; + } + else + return FALSE; +} + + +static gboolean +cally_text_remove_selection (AtkText *text, + gint selection_num) +{ + ClutterActor *actor = NULL; + gint caret_pos = -1; + gint select_start = -1; + gint select_end = -1; + + actor = CALLY_GET_CLUTTER_ACTOR (text); + if (actor == NULL) /* State is defunct */ + return FALSE; + + /* only one selection is allowed */ + if (selection_num != 0) + return FALSE; + + _cally_text_get_selection_bounds (CLUTTER_TEXT (actor), + &select_start, &select_end); + + if (select_start != select_end) + { + /* Setting the start & end of the selected region to the caret position + * turns off the selection. + */ + caret_pos = clutter_text_get_cursor_position (CLUTTER_TEXT (actor)); + clutter_text_set_selection (CLUTTER_TEXT (actor), + caret_pos, caret_pos); + return TRUE; + } + else + return FALSE; +} + +static gboolean +cally_text_set_selection (AtkText *text, + gint selection_num, + gint start_offset, + gint end_offset) +{ + ClutterActor *actor = NULL; + gint select_start = -1; + gint select_end = -1; + + actor = CALLY_GET_CLUTTER_ACTOR (text); + if (actor == NULL) /* State is defunct */ + return FALSE; + + /* Like in gailentry, only let the user move the selection if one is set, + * and if the selection_num is 0 + */ + if (selection_num != 0) + return FALSE; + + _cally_text_get_selection_bounds (CLUTTER_TEXT (actor), + &select_start, &select_end); + + if (select_start != select_end) + { + clutter_text_set_selection (CLUTTER_TEXT (actor), + start_offset, end_offset); + return TRUE; + } + else + return FALSE; +} + +static AtkAttributeSet* +cally_text_get_run_attributes (AtkText *text, + gint offset, + gint *start_offset, + gint *end_offset) +{ + ClutterActor *actor = NULL; + ClutterText *clutter_text = NULL; + AtkAttributeSet *at_set = NULL; + + actor = CALLY_GET_CLUTTER_ACTOR (text); + if (actor == NULL) /* State is defunct */ + return NULL; + + /* Clutter don't have any reference to the direction*/ + + clutter_text = CLUTTER_TEXT (actor); + + at_set = _cally_misc_layout_get_run_attributes (at_set, + clutter_text_get_layout (clutter_text), + (gchar*)clutter_text_get_text (clutter_text), + offset, + start_offset, + end_offset); + + return at_set; +} + +/******** Auxiliar private methods ******/ + +/* ClutterText only maintains the current cursor position and a extra selection + bound, but this could be before or after the cursor. This method returns + the start and end positions in a proper order (so start<=end). This is + similar to the function gtk_editable_get_selection_bounds */ +static void +_cally_text_get_selection_bounds (ClutterText *clutter_text, + gint *start_offset, + gint *end_offset) +{ + gint pos = -1; + gint selection_bound = -1; + + pos = clutter_text_get_cursor_position (clutter_text); + selection_bound = clutter_text_get_selection_bound (clutter_text); + + if (pos < selection_bound) + { + *start_offset = pos; + *end_offset = selection_bound; + } + else + { + *start_offset = selection_bound; + *end_offset = pos; + } +} + +static void +_cally_text_text_changed_cb (ClutterText *clutter_text, + gpointer data) +{ + CallyText *cally_text = NULL; + + g_return_if_fail (CALLY_IS_TEXT (data)); + + cally_text = CALLY_TEXT (data); +} + +static void +_cally_text_delete_text_cb (ClutterText *clutter_text, + gint start_pos, + gint end_pos, + gpointer data) +{ + CallyText *cally_text = NULL; + + g_return_if_fail (CALLY_IS_TEXT (data)); + + /* Ignore zero lengh deletions */ + if (end_pos - start_pos == 0) + return; + + cally_text = CALLY_TEXT (data); + + if (!cally_text->priv->signal_name_delete) + { + cally_text->priv->signal_name_delete = "text_changed::delete"; + cally_text->priv->position_delete = start_pos; + cally_text->priv->length_delete = end_pos - start_pos; + } + + _notify_delete (cally_text); +} + +static void +_cally_text_insert_text_cb (ClutterText *clutter_text, + gchar *new_text, + gint new_text_length, + gint *position, + gpointer data) +{ + CallyText *cally_text = NULL; + + g_return_if_fail (CALLY_IS_TEXT (data)); + + cally_text = CALLY_TEXT (data); + + if (!cally_text->priv->signal_name_insert) + { + cally_text->priv->signal_name_insert = "text_changed::insert"; + cally_text->priv->position_insert = *position; + cally_text->priv->length_insert = g_utf8_strlen (new_text, new_text_length); + } + + /* + * The signal will be emitted when the cursor position is updated, + * or in an idle handler if it not updated. + */ + if (cally_text->priv->insert_idle_handler == 0) + cally_text->priv->insert_idle_handler = clutter_threads_add_idle (_idle_notify_insert, + cally_text); +} + +/***** atkeditabletext.h ******/ + +static void +cally_text_editable_text_interface_init (AtkEditableTextIface *iface) +{ + g_return_if_fail (iface != NULL); + + iface->set_text_contents = cally_text_set_text_contents; + iface->insert_text = cally_text_insert_text; + iface->delete_text = cally_text_delete_text; + + /* Not implemented, see IMPLEMENTATION NOTES*/ + iface->set_run_attributes = NULL; + iface->copy_text = NULL; + iface->cut_text = NULL; + iface->paste_text = NULL; +} + +static void +cally_text_set_text_contents (AtkEditableText *text, + const gchar *string) +{ + ClutterActor *actor = NULL; + + actor = CALLY_GET_CLUTTER_ACTOR (text); + if (actor == NULL) + return; + + if (!clutter_text_get_editable (CLUTTER_TEXT (actor))) + return; + + clutter_text_set_text (CLUTTER_TEXT (actor), + string); +} + + +static void +cally_text_insert_text (AtkEditableText *text, + const gchar *string, + gint length, + gint *position) +{ + ClutterActor *actor = NULL; + + actor = CALLY_GET_CLUTTER_ACTOR (text); + if (actor == NULL) + return; + + if (!clutter_text_get_editable (CLUTTER_TEXT (actor))) + return; + + if (length < 0) + length = g_utf8_strlen (string, -1); + + clutter_text_insert_text (CLUTTER_TEXT (actor), + string, *position); + + /* we suppose that the text insertion will be succesful, + clutter-text doesn't warn about it. A option would be search for + the text, but it seems not really required */ + *position += length; +} + +static void cally_text_delete_text (AtkEditableText *text, + gint start_pos, + gint end_pos) +{ + ClutterActor *actor = NULL; + + actor = CALLY_GET_CLUTTER_ACTOR (text); + if (actor == NULL) + return; + + if (!clutter_text_get_editable (CLUTTER_TEXT (actor))) + return; + + clutter_text_delete_text (CLUTTER_TEXT (actor), + start_pos, end_pos); +} + +/* CallyActor */ +static void +cally_text_notify_clutter (GObject *obj, + GParamSpec *pspec) +{ + ClutterText *clutter_text = NULL; + CallyText *cally_text = NULL; + AtkObject *atk_obj = NULL; + + clutter_text = CLUTTER_TEXT (obj); + atk_obj = clutter_actor_get_accessible (CLUTTER_ACTOR (obj)); + cally_text = CALLY_TEXT (atk_obj); + + if (g_strcmp0 (pspec->name, "position") == 0) + { + /* the selection can change also for the cursor position */ + if (_check_for_selection_change (cally_text, clutter_text)) + g_signal_emit_by_name (atk_obj, "text_selection_changed"); + + g_signal_emit_by_name (atk_obj, "text_caret_moved", + clutter_text_get_cursor_position (clutter_text)); + } + else if (g_strcmp0 (pspec->name, "selection-bound") == 0) + { + if (_check_for_selection_change (cally_text, clutter_text)) + g_signal_emit_by_name (atk_obj, "text_selection_changed"); + } + else if (g_strcmp0 (pspec->name, "editable") == 0) + { + atk_object_notify_state_change (atk_obj, ATK_STATE_EDITABLE, + clutter_text_get_editable (clutter_text)); + } + else if (g_strcmp0 (pspec->name, "activatable") == 0) + { + _check_activate_action (cally_text, clutter_text); + } + else + { + CALLY_ACTOR_CLASS (cally_text_parent_class)->notify_clutter (obj, pspec); + } +} + +static gboolean +_check_for_selection_change (CallyText *cally_text, + ClutterText *clutter_text) +{ + gboolean ret_val = FALSE; + gint clutter_pos = -1; + gint clutter_bound = -1; + + clutter_pos = clutter_text_get_cursor_position (clutter_text); + clutter_bound = clutter_text_get_selection_bound (clutter_text); + + if (clutter_pos != clutter_bound) + { + if (clutter_pos != cally_text->priv->cursor_position || + clutter_bound != cally_text->priv->selection_bound) + /* + * This check is here as this function can be called for + * notification of selection_bound and current_pos. The + * values of current_pos and selection_bound may be the same + * for both notifications and we only want to generate one + * text_selection_changed signal. + */ + ret_val = TRUE; + } + else + { + /* We had a selection */ + ret_val = (cally_text->priv->cursor_position != cally_text->priv->selection_bound); + } + + cally_text->priv->cursor_position = clutter_pos; + cally_text->priv->selection_bound = clutter_bound; + + return ret_val; +} + +static gboolean +_idle_notify_insert (gpointer data) +{ + CallyText *cally_text = NULL; + + cally_text = CALLY_TEXT (data); + cally_text->priv->insert_idle_handler = 0; + + _notify_insert (cally_text); + + return FALSE; +} + +static void +_notify_insert (CallyText *cally_text) +{ + if (cally_text->priv->signal_name_insert) + { + g_signal_emit_by_name (cally_text, + cally_text->priv->signal_name_insert, + cally_text->priv->position_insert, + cally_text->priv->length_insert); + cally_text->priv->signal_name_insert = NULL; + } +} + +static void +_notify_delete (CallyText *cally_text) +{ + if (cally_text->priv->signal_name_delete) + { + g_signal_emit_by_name (cally_text, + cally_text->priv->signal_name_delete, + cally_text->priv->position_delete, + cally_text->priv->length_delete); + cally_text->priv->signal_name_delete = NULL; + } +} +/* atkaction */ + +static void +_cally_text_activate_action (CallyActor *cally_actor) +{ + ClutterActor *actor = NULL; + + actor = CALLY_GET_CLUTTER_ACTOR (cally_actor); + + clutter_text_activate (CLUTTER_TEXT (actor)); +} + +static void +_check_activate_action (CallyText *cally_text, + ClutterText *clutter_text) +{ + + if (clutter_text_get_activatable (clutter_text)) + { + if (cally_text->priv->activate_action_id != 0) + return; + + cally_text->priv->activate_action_id = cally_actor_add_action (CALLY_ACTOR (cally_text), + "activate", NULL, NULL, + _cally_text_activate_action); + } + else + { + if (cally_text->priv->activate_action_id == 0) + return; + + if (cally_actor_remove_action (CALLY_ACTOR (cally_text), + cally_text->priv->activate_action_id)) + { + cally_text->priv->activate_action_id = 0; + } + } +} + +/* GailTextUtil/GailMisc reimplementation methods */ + +/** + * _cally_misc_add_attribute: + * + * Reimplementation of gail_misc_layout_get_run_attributes (check this + * function for more documentation). + * + * Returns: A pointer to the new #AtkAttributeSet. + **/ +static AtkAttributeSet* +_cally_misc_add_attribute (AtkAttributeSet *attrib_set, + AtkTextAttribute attr, + gchar *value) +{ + AtkAttributeSet *return_set; + AtkAttribute *at = g_malloc (sizeof (AtkAttribute)); + at->name = g_strdup (atk_text_attribute_get_name (attr)); + at->value = value; + return_set = g_slist_prepend(attrib_set, at); + return return_set; +} + +/** + * _cally_misc_layout_get_run_attributes: + * + * Reimplementation of gail_misc_layout_get_run_attributes (check this + * function for more documentation). + * + * Returns: A pointer to the #AtkAttributeSet. + **/ +static AtkAttributeSet* +_cally_misc_layout_get_run_attributes (AtkAttributeSet *attrib_set, + PangoLayout *layout, + gchar *text, + gint offset, + gint *start_offset, + gint *end_offset) +{ + PangoAttrIterator *iter; + PangoAttrList *attr; + PangoAttrString *pango_string; + PangoAttrInt *pango_int; + PangoAttrColor *pango_color; + PangoAttrLanguage *pango_lang; + PangoAttrFloat *pango_float; + gint index, start_index, end_index; + gboolean is_next = TRUE; + gchar *value = NULL; + glong len; + + len = g_utf8_strlen (text, -1); + /* Grab the attributes of the PangoLayout, if any */ + if ((attr = pango_layout_get_attributes (layout)) == NULL) + { + *start_offset = 0; + *end_offset = len; + return attrib_set; + } + iter = pango_attr_list_get_iterator (attr); + /* Get invariant range offsets */ + /* If offset out of range, set offset in range */ + if (offset > len) + offset = len; + else if (offset < 0) + offset = 0; + + index = g_utf8_offset_to_pointer (text, offset) - text; + pango_attr_iterator_range (iter, &start_index, &end_index); + while (is_next) + { + if (index >= start_index && index < end_index) + { + *start_offset = g_utf8_pointer_to_offset (text, + text + start_index); + if (end_index == G_MAXINT) + /* Last iterator */ + end_index = len; + + *end_offset = g_utf8_pointer_to_offset (text, + text + end_index); + break; + } + is_next = pango_attr_iterator_next (iter); + pango_attr_iterator_range (iter, &start_index, &end_index); + } + /* Get attributes */ + if ((pango_string = (PangoAttrString*) pango_attr_iterator_get (iter, + PANGO_ATTR_FAMILY)) != NULL) + { + value = g_strdup_printf("%s", pango_string->value); + attrib_set = _cally_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_FAMILY_NAME, + value); + } + if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, + PANGO_ATTR_STYLE)) != NULL) + { + attrib_set = _cally_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_STYLE, + g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_STYLE, pango_int->value))); + } + if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, + PANGO_ATTR_WEIGHT)) != NULL) + { + value = g_strdup_printf("%i", pango_int->value); + attrib_set = _cally_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_WEIGHT, + value); + } + if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, + PANGO_ATTR_VARIANT)) != NULL) + { + attrib_set = _cally_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_VARIANT, + g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_VARIANT, pango_int->value))); + } + if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, + PANGO_ATTR_STRETCH)) != NULL) + { + attrib_set = _cally_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_STRETCH, + g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_STRETCH, pango_int->value))); + } + if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, + PANGO_ATTR_SIZE)) != NULL) + { + value = g_strdup_printf("%i", pango_int->value / PANGO_SCALE); + attrib_set = _cally_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_SIZE, + value); + } + if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, + PANGO_ATTR_UNDERLINE)) != NULL) + { + attrib_set = _cally_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_UNDERLINE, + g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_UNDERLINE, pango_int->value))); + } + if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, + PANGO_ATTR_STRIKETHROUGH)) != NULL) + { + attrib_set = _cally_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_STRIKETHROUGH, + g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_STRIKETHROUGH, pango_int->value))); + } + if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, + PANGO_ATTR_RISE)) != NULL) + { + value = g_strdup_printf("%i", pango_int->value); + attrib_set = _cally_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_RISE, + value); + } + if ((pango_lang = (PangoAttrLanguage*) pango_attr_iterator_get (iter, + PANGO_ATTR_LANGUAGE)) != NULL) + { + value = g_strdup( pango_language_to_string( pango_lang->value)); + attrib_set = _cally_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_LANGUAGE, + value); + } + if ((pango_float = (PangoAttrFloat*) pango_attr_iterator_get (iter, + PANGO_ATTR_SCALE)) != NULL) + { + value = g_strdup_printf("%g", pango_float->value); + attrib_set = _cally_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_SCALE, + value); + } + if ((pango_color = (PangoAttrColor*) pango_attr_iterator_get (iter, + PANGO_ATTR_FOREGROUND)) != NULL) + { + value = g_strdup_printf ("%u,%u,%u", + pango_color->color.red, + pango_color->color.green, + pango_color->color.blue); + attrib_set = _cally_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_FG_COLOR, + value); + } + if ((pango_color = (PangoAttrColor*) pango_attr_iterator_get (iter, + PANGO_ATTR_BACKGROUND)) != NULL) + { + value = g_strdup_printf ("%u,%u,%u", + pango_color->color.red, + pango_color->color.green, + pango_color->color.blue); + attrib_set = _cally_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_BG_COLOR, + value); + } + pango_attr_iterator_destroy (iter); + return attrib_set; +} diff --git a/clutter/cally/cally-text.h b/clutter/cally/cally-text.h new file mode 100644 index 000000000..54e15d155 --- /dev/null +++ b/clutter/cally/cally-text.h @@ -0,0 +1,63 @@ +/* CALLY - The Clutter Accessibility Implementation Library + * + * Copyright (C) 2009 Igalia, S.L. + * + * Author: Alejandro Piñeiro Iglesias + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __CALLY_TEXT_H__ +#define __CALLY_TEXT_H__ + +#include "cally-actor.h" + +G_BEGIN_DECLS + +#define CALLY_TYPE_TEXT (cally_text_get_type ()) +#define CALLY_TEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CALLY_TYPE_TEXT, CallyText)) +#define CALLY_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CALLY_TYPE_TEXT, CallyTextClass)) +#define CALLY_IS_TEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CALLY_TYPE_TEXT)) +#define CALLY_IS_TEXT_CLASS(klass)(G_TYPE_CHECK_CLASS_TYPE ((klass), CALLY_TYPE_TEXT)) +#define CALLY_TEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CALLY_TYPE_TEXT, CallyTextClass)) + + +typedef struct _CallyText CallyText; +typedef struct _CallyTextClass CallyTextClass; +typedef struct _CallyTextPrivate CallyTextPrivate; + +struct _CallyText +{ + CallyActor parent; + + /* < private > */ + CallyTextPrivate *priv; +}; + +struct _CallyTextClass +{ + CallyActorClass parent_class; + + /* padding for future expansion */ + gpointer _padding_dummy[30]; +}; + +GType cally_text_get_type (void); +AtkObject* cally_text_new (ClutterActor *actor); + +G_END_DECLS + +#endif /* __CALLY_TEXT_H__ */ diff --git a/clutter/cally/cally-texture.c b/clutter/cally/cally-texture.c new file mode 100644 index 000000000..13c3a6095 --- /dev/null +++ b/clutter/cally/cally-texture.c @@ -0,0 +1,103 @@ +/* CALLY - The Clutter Accessibility Implementation Library + * + * Copyright (C) 2009 Igalia, S.L. + * + * Author: Alejandro Piñeiro Iglesias + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** + * SECTION:callycluttertexture + * @short_description: Implementation of the ATK interfaces for a #ClutterTexture + * @see_also: #ClutterTexture + * + * #CallyClutterTexture implements the required ATK interfaces of #ClutterTexture + * + * In particular it sets a proper role for the texture. + */ + +#include "cally-texture.h" +#include "cally-actor-private.h" + +#define CALLY_TEXTURE_DEFAULT_DESCRIPTION "A texture" + +static void cally_texture_class_init (CallyTextureClass *klass); +static void cally_texture_init (CallyTexture *texture); + +/* AtkObject */ +static void cally_texture_real_initialize (AtkObject *obj, + gpointer data); +static G_CONST_RETURN gchar *cally_texture_get_description (AtkObject *obj); + + +G_DEFINE_TYPE (CallyTexture, cally_texture, CALLY_TYPE_ACTOR) + +static void +cally_texture_class_init (CallyTextureClass *klass) +{ +/* GObjectClass *gobject_class = G_OBJECT_CLASS (klass); */ + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + + class->initialize = cally_texture_real_initialize; + class->get_description = cally_texture_get_description; +} + +static void +cally_texture_init (CallyTexture *texture) +{ + /* nothing to do yet */ +} + +AtkObject* +cally_texture_new (ClutterActor *actor) +{ + GObject *object = NULL; + AtkObject *accessible = NULL; + + g_return_val_if_fail (CLUTTER_IS_TEXTURE (actor), NULL); + + object = g_object_new (CALLY_TYPE_TEXTURE, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, actor); + + return accessible; +} + +static void +cally_texture_real_initialize (AtkObject *obj, + gpointer data) +{ + ATK_OBJECT_CLASS (cally_texture_parent_class)->initialize (obj, data); + + /* default role */ + obj->role = ATK_ROLE_IMAGE; +} + +static G_CONST_RETURN gchar * +cally_texture_get_description (AtkObject *obj) +{ + G_CONST_RETURN gchar *description = NULL; + + g_return_val_if_fail (CALLY_IS_TEXTURE (obj), NULL); + + description = ATK_OBJECT_CLASS (cally_texture_parent_class)->get_description (obj); + if (description == NULL) + description = CALLY_TEXTURE_DEFAULT_DESCRIPTION; + + return description; +} diff --git a/clutter/cally/cally-texture.h b/clutter/cally/cally-texture.h new file mode 100644 index 000000000..6603ee2d1 --- /dev/null +++ b/clutter/cally/cally-texture.h @@ -0,0 +1,63 @@ +/* CALLY - The Clutter Accessibility Implementation Library + * + * Copyright (C) 2009 Igalia, S.L. + * + * Author: Alejandro Piñeiro Iglesias + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __CALLY_TEXTURE_H__ +#define __CALLY_TEXTURE_H__ + +#include "cally-actor.h" + +G_BEGIN_DECLS + +#define CALLY_TYPE_TEXTURE (cally_texture_get_type ()) +#define CALLY_TEXTURE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CALLY_TYPE_TEXTURE, CallyTexture)) +#define CALLY_TEXTURE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CALLY_TYPE_TEXTURE, CallyTextureClass)) +#define CALLY_IS_TEXTURE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CALLY_TYPE_TEXTURE)) +#define CALLY_IS_TEXTURE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CALLY_TYPE_TEXTURE)) +#define CALLY_TEXTURE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CALLY_TYPE_TEXTURE, CallyTextureClass)) + + +typedef struct _CallyTexture CallyTexture; +typedef struct _CallyTextureClass CallyTextureClass; +typedef struct _CallyTexturePrivate CallyTexturePrivate; + +struct _CallyTexture +{ + CallyActor parent; + + /* < private > */ + CallyTexturePrivate *priv; +}; + +struct _CallyTextureClass +{ + CallyActorClass parent_class; + + /* padding for future expansion */ + gpointer _padding_dummy[30]; +}; + +GType cally_texture_get_type (void); +AtkObject *cally_texture_new (ClutterActor *actor); + +G_END_DECLS + +#endif /* __CALLY_TEXTURE_H__ */ diff --git a/clutter/cally/cally-util.c b/clutter/cally/cally-util.c new file mode 100644 index 000000000..14a4a8e7d --- /dev/null +++ b/clutter/cally/cally-util.c @@ -0,0 +1,557 @@ +/* CALLY - The Clutter Accessibility Implementation Library + * + * Copyright (C) 2008 Igalia, S.L. + * + * Author: Alejandro Piñeiro Iglesias + * + * Based on GailUtil from GAIL + * Copyright 2001, 2002, 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include +#include +#include + +#include "cally-util.h" +#include "cally-root.h" +#include "cally-stage.h" + +#ifdef HAVE_CLUTTER_X11 +#include +#endif + +static void cally_util_class_init (CallyUtilClass *klass); +static void cally_util_init (CallyUtil *cally_util); + +/* atkutil.h */ + +static guint cally_util_add_global_event_listener (GSignalEmissionHook listener, + const gchar* event_type); +static void cally_util_remove_global_event_listener (guint remove_listener); +static guint cally_util_add_key_event_listener (AtkKeySnoopFunc listener, + gpointer data); +static void cally_util_remove_key_event_listener (guint remove_listener); +static AtkObject* cally_util_get_root (void); +static G_CONST_RETURN gchar *cally_util_get_toolkit_name (void); +static G_CONST_RETURN gchar *cally_util_get_toolkit_version (void); + +/* private */ +static void _listener_info_destroy (gpointer data); +static guint add_listener (GSignalEmissionHook listener, + const gchar *object_type, + const gchar *signal, + const gchar *hook_data); +static void cally_util_simulate_snooper_install (void); +static void cally_util_simulate_snooper_remove (void); +static gboolean cally_key_snooper (ClutterActor *actor, + ClutterEvent *event, + gpointer user_data); +static void cally_util_stage_added_cb (ClutterStageManager *stage_manager, + ClutterStage *stage, + gpointer data); +static void cally_util_stage_removed_cb (ClutterStageManager *stage_manager, + ClutterStage *stage, + gpointer data); +static gboolean notify_hf (gpointer key, + gpointer value, + gpointer data); +static void insert_hf (gpointer key, + gpointer value, + gpointer data); +static AtkKeyEventStruct * atk_key_event_from_clutter_event_key (ClutterKeyEvent *event); +static void do_window_event_initialization (void); + + +/* This is just a copy of the Gail one, a shared library or place to + define it could be a good idea. */ +typedef struct _CallyUtilListenerInfo CallyUtilListenerInfo; +typedef struct _CallyKeyEventInfo CallyKeyEventInfo; + +struct _CallyUtilListenerInfo +{ + gint key; + guint signal_id; + gulong hook_id; +}; + +struct _CallyKeyEventInfo +{ + AtkKeySnoopFunc listener; + gpointer func_data; +}; + +static AtkObject* root = NULL; +static GHashTable *listener_list = NULL; +static GHashTable *key_listener_list = NULL; +static gint listener_idx = 1; + + +G_DEFINE_TYPE (CallyUtil, cally_util, ATK_TYPE_UTIL); + +static void +cally_util_class_init (CallyUtilClass *klass) +{ + AtkUtilClass *atk_class; + gpointer data; + + data = g_type_class_peek (ATK_TYPE_UTIL); + atk_class = ATK_UTIL_CLASS (data); + + atk_class->add_global_event_listener = cally_util_add_global_event_listener; + atk_class->remove_global_event_listener = cally_util_remove_global_event_listener; + atk_class->add_key_event_listener = cally_util_add_key_event_listener; + atk_class->remove_key_event_listener = cally_util_remove_key_event_listener; + atk_class->get_root = cally_util_get_root; + atk_class->get_toolkit_name = cally_util_get_toolkit_name; + atk_class->get_toolkit_version = cally_util_get_toolkit_version; + + /* FIXME: Instead of create this on the class, I think that would + worth to implement CallyUtil as a singleton instance, so the + class methods will access this instance. This will be a good + future enhancement, meanwhile, just using the same *working* + implementation used on GailUtil */ + + listener_list = g_hash_table_new_full (g_int_hash, g_int_equal, NULL, + _listener_info_destroy); +} + +static void +cally_util_init (CallyUtil *cally_util) +{ + /* instance init: usually not required */ +} + +/* ------------------------------ ATK UTIL METHODS -------------------------- */ + +static AtkObject* +cally_util_get_root (void) +{ + if (!root) + root = cally_root_new (); + + return root; +} + +static G_CONST_RETURN gchar * +cally_util_get_toolkit_name (void) +{ + return "CALLY"; +} + +static G_CONST_RETURN gchar * +cally_util_get_toolkit_version (void) +{ + /* + * FIXME: + * Version is passed in as a -D flag when this file is + * compiled. + */ + return "0.1"; +} + + +static guint +cally_util_add_global_event_listener (GSignalEmissionHook listener, + const gchar *event_type) +{ + guint rc = 0; + gchar **split_string; + + split_string = g_strsplit (event_type, ":", 3); + + if (split_string) + { + if (!strcmp ("window", split_string[0])) + { + /* Using ClutterStage as the window equivalent, although + several methods (move, etc) are missing. This would be + probably defined for other window-related classes (MxWindow) + + FIXME: for this reason, this process should be extendable + on the future.*/ + static gboolean initialized = FALSE; + + if (initialized == FALSE) + { + do_window_event_initialization (); + initialized = TRUE; + } + + rc = add_listener (listener, "CallyStage", split_string[1], event_type); + } + else + { + rc = add_listener (listener, split_string[1], split_string[2], event_type); + } + + g_strfreev (split_string); + } + + return rc; +} + +static void +cally_util_remove_global_event_listener (guint remove_listener) +{ + if (remove_listener > 0) + { + CallyUtilListenerInfo *listener_info; + gint tmp_idx = remove_listener; + + listener_info = (CallyUtilListenerInfo *) + g_hash_table_lookup(listener_list, &tmp_idx); + + if (listener_info != NULL) + { + /* Hook id of 0 and signal id of 0 are invalid */ + if (listener_info->hook_id != 0 && listener_info->signal_id != 0) + { + /* Remove the emission hook */ + g_signal_remove_emission_hook(listener_info->signal_id, + listener_info->hook_id); + + /* Remove the element from the hash */ + g_hash_table_remove(listener_list, &tmp_idx); + } + else + { + g_warning("Invalid listener hook_id %ld or signal_id %d\n", + listener_info->hook_id, listener_info->signal_id); + } + } + else + { + g_warning("No listener with the specified listener id %d", + remove_listener); + } + } + else + { + g_warning("Invalid listener_id %d", remove_listener); + } +} + +static guint +cally_util_add_key_event_listener (AtkKeySnoopFunc listener, + gpointer data) +{ + static guint key=0; + CallyKeyEventInfo *event_info = NULL; + + if (!key_listener_list) + { + key_listener_list = g_hash_table_new_full (NULL, NULL, NULL, g_free); + + cally_util_simulate_snooper_install (); + } + + event_info = g_new (CallyKeyEventInfo, 1); + event_info->listener = listener; + event_info->func_data = data; + + g_hash_table_insert (key_listener_list, GUINT_TO_POINTER (key++), event_info); + /* XXX: we don't check to see if n_listeners > MAXUINT */ + return key - 1; +} + +static void +cally_util_remove_key_event_listener (guint remove_listener) +{ + if (!g_hash_table_remove (key_listener_list, GUINT_TO_POINTER (remove_listener))) { + g_warning ("Not able to remove listener with id %i", remove_listener); + } + + if (g_hash_table_size (key_listener_list) == 0) + { + cally_util_simulate_snooper_remove (); + } +} + +/* ------------------------------ PRIVATE FUNCTIONS ------------------------- */ + +static void +_listener_info_destroy (gpointer data) +{ + g_free(data); +} + +static guint +add_listener (GSignalEmissionHook listener, + const gchar *object_type, + const gchar *signal, + const gchar *hook_data) +{ + GType type; + guint signal_id; + gint rc = 0; + + type = g_type_from_name (object_type); + if (type) + { + signal_id = g_signal_lookup (signal, type); + if (signal_id > 0) + { + CallyUtilListenerInfo *listener_info; + + rc = listener_idx; + + listener_info = g_new (CallyUtilListenerInfo, 1); + listener_info->key = listener_idx; + listener_info->hook_id = + g_signal_add_emission_hook (signal_id, 0, listener, + g_strdup (hook_data), + (GDestroyNotify) g_free); + listener_info->signal_id = signal_id; + + g_hash_table_insert(listener_list, &(listener_info->key), listener_info); + listener_idx++; + } + else + { + /* This is mainly because some "window:xxx" methods not + implemented on CallyStage */ + g_debug ("Signal type %s not supported\n", signal); + } + } + else + { + g_warning("Invalid object type %s\n", object_type); + } + return rc; +} + +/* Trying to emulate gtk_key_snooper install (a kind of wrapper). This + could be implemented without it, but I will maintain it in this + way, so if in the future clutter implements it natively it would be + easier the transition */ +static void +cally_util_simulate_snooper_install (void) +{ + ClutterStageManager *stage_manager = NULL; + ClutterStage *stage = NULL; + GSList *stage_list = NULL; + GSList *iter = NULL; + + stage_manager = clutter_stage_manager_get_default (); + stage_list = clutter_stage_manager_list_stages (stage_manager); + + for (iter = stage_list; iter != NULL; iter = g_slist_next (iter)) + { + stage = CLUTTER_STAGE (iter->data); + + g_signal_connect (G_OBJECT (stage), "captured-event", + G_CALLBACK (cally_key_snooper), NULL); + } + + g_signal_connect (G_OBJECT (stage_manager), "stage-added", + G_CALLBACK (cally_util_stage_added_cb), cally_key_snooper); + g_signal_connect (G_OBJECT (stage_manager), "stage-removed", + G_CALLBACK (cally_util_stage_removed_cb), cally_key_snooper); +} + +static void +cally_util_simulate_snooper_remove (void) +{ + ClutterStageManager *stage_manager = NULL; + ClutterStage *stage = NULL; + GSList *stage_list = NULL; + GSList *iter = NULL; + gint num = 0; + + stage_manager = clutter_stage_manager_get_default (); + stage_list = clutter_stage_manager_list_stages (stage_manager); + + for (iter = stage_list; iter != NULL; iter = g_slist_next (iter)) + { + stage = CLUTTER_STAGE (iter->data); + + num += g_signal_handlers_disconnect_by_func (stage, cally_key_snooper, NULL); + } + + g_signal_handlers_disconnect_by_func (G_OBJECT (stage_manager), + G_CALLBACK (cally_util_stage_added_cb), + cally_key_snooper); + + g_signal_handlers_disconnect_by_func (G_OBJECT (stage_manager), + G_CALLBACK (cally_util_stage_removed_cb), + cally_key_snooper); + +#ifdef CALLY_DEBUG + g_print ("Number of snooper callbacks disconnected: %i\n", num); +#endif +} + +static AtkKeyEventStruct * +atk_key_event_from_clutter_event_key (ClutterKeyEvent *clutter_event) +{ + AtkKeyEventStruct *atk_event = g_new0 (AtkKeyEventStruct, 1); + gunichar key_unichar; + + switch (clutter_event->type) + { + case CLUTTER_KEY_PRESS: + atk_event->type = ATK_KEY_EVENT_PRESS; + break; + case CLUTTER_KEY_RELEASE: + atk_event->type = ATK_KEY_EVENT_RELEASE; + break; + default: + g_assert_not_reached (); + return NULL; + } + + atk_event->state = clutter_event->modifier_state; + + /* We emit the clutter keyval. This is not exactly the one expected + by AtkKeyEventStruct, as it expects a Gdk-like event, with the + modifiers applied. But to avoid a dependency to gdk, we delegate + that on the AT application. + More information: Bug 1952 and bug 2072 + */ + atk_event->keyval = clutter_event->keyval; + + /* It is expected to store a key defining string here (ie "Space" in + case you press a space). Anyway, there are no function on clutter + to obtain that, and we want to avoid a gdk dependency here, so we + delegate on the AT application to obtain that string using the + rest of the data on the ATK event struct. + + More information: Bug 1952 and 2072 + */ + + key_unichar = clutter_event_get_key_unicode ((ClutterEvent *) clutter_event); + + if (g_unichar_validate (key_unichar) && !g_unichar_iscntrl (key_unichar)) + { + GString *new = NULL; + + new = g_string_new (""); + new = g_string_insert_unichar (new, 0, key_unichar); + atk_event->string = new->str; + g_string_free (new, FALSE); + } + else + atk_event->string = NULL; + + atk_event->length = 0; + + atk_event->keycode = clutter_event->hardware_keycode; + atk_event->timestamp = clutter_event->time; + +#ifdef CALLY_DEBUG + + g_debug ("CallyKeyEvent:\tsym 0x%x\n\t\tmods %x\n\t\tcode %u\n\t\ttime %lx \n\t\tstring %s\n", + (unsigned int) atk_event->keyval, + (unsigned int) atk_event->state, + (unsigned int) atk_event->keycode, + (unsigned long int) atk_event->timestamp, + atk_event->string); +#endif + + return atk_event; +} + + +static gboolean +notify_hf (gpointer key, gpointer value, gpointer data) +{ + CallyKeyEventInfo *info = (CallyKeyEventInfo *) value; + AtkKeyEventStruct *key_event = (AtkKeyEventStruct *)data; + + return (*(AtkKeySnoopFunc) info->listener) (key_event, info->func_data) ? TRUE : FALSE; +} + +static void +insert_hf (gpointer key, gpointer value, gpointer data) +{ + GHashTable *new_table = (GHashTable *) data; + g_hash_table_insert (new_table, key, value); +} + +static gboolean +cally_key_snooper (ClutterActor *actor, + ClutterEvent *event, + gpointer user_data) +{ + AtkKeyEventStruct *key_event = NULL; + gint consumed = 0; + + /* filter key events */ + if ((event->type != CLUTTER_KEY_PRESS) && (event->type != CLUTTER_KEY_RELEASE)) + { + return FALSE; + } + + if (key_listener_list) + { + GHashTable *new_hash = g_hash_table_new (NULL, NULL); + + g_hash_table_foreach (key_listener_list, insert_hf, new_hash); + key_event = atk_key_event_from_clutter_event_key ((ClutterKeyEvent *)event); + /* func data is inside the hash table */ + consumed = g_hash_table_foreach_steal (new_hash, notify_hf, key_event); + g_hash_table_destroy (new_hash); + } + + g_free (key_event->string); + g_free (key_event); + + return (consumed ? 1 : 0); +} + +static void +cally_util_stage_added_cb (ClutterStageManager *stage_manager, + ClutterStage *stage, + gpointer data) +{ + GCallback cally_key_snooper = G_CALLBACK (data); + AtkObject *cally_stage = NULL; + + g_signal_connect (G_OBJECT (stage), "captured-event", cally_key_snooper, NULL); + + cally_stage = clutter_actor_get_accessible (CLUTTER_ACTOR (stage)); + if (cally_stage != NULL) + g_signal_emit_by_name (G_OBJECT(cally_stage), "create", 0); +} + +static void +cally_util_stage_removed_cb (ClutterStageManager *stage_manager, + ClutterStage *stage, + gpointer data) +{ + GCallback cally_key_snooper = G_CALLBACK (data); + gint num = 0; + AtkObject *cally_stage = NULL; + + num = g_signal_handlers_disconnect_by_func (stage, cally_key_snooper, NULL); + + cally_stage = clutter_actor_get_accessible (CLUTTER_ACTOR (stage)); + if (cally_stage != NULL) + g_signal_emit_by_name (G_OBJECT(cally_stage), "destroy", 0); +} + +static void +do_window_event_initialization (void) +{ + /* + * Ensure that CallyStageClass exists. + */ + g_type_class_unref (g_type_class_ref (CALLY_TYPE_STAGE)); +} diff --git a/clutter/cally/cally-util.h b/clutter/cally/cally-util.h new file mode 100644 index 000000000..dfb68390b --- /dev/null +++ b/clutter/cally/cally-util.h @@ -0,0 +1,61 @@ +/* CALLY - The Clutter Accessibility Implementation Library + * + * Copyright (C) 2008 Igalia, S.L. + * + * Author: Alejandro Piñeiro Iglesias + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __CALLY_UTIL_H__ +#define __CALLY_UTIL_H__ + +#include + +G_BEGIN_DECLS + +#define CALLY_TYPE_UTIL (cally_util_get_type ()) +#define CALLY_UTIL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CALLY_TYPE_UTIL, CallyUtil)) +#define CALLY_UTIL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CALLY_TYPE_UTIL, CallyUtilClass)) +#define CALLY_IS_UTIL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CALLY_TYPE_UTIL)) +#define CALLY_IS_UTIL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CALLY_TYPE_UTIL)) +#define CALLY_UTIL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CALLY_TYPE_UTIL, CallyUtilClass)) + +typedef struct _CallyUtil CallyUtil; +typedef struct _CallyUtilClass CallyUtilClass; +typedef struct _CallyUtilPrivate CallyUtilPrivate; + +struct _CallyUtil +{ + AtkUtil parent; + + /* < private > */ + CallyUtilPrivate *priv; +}; + +struct _CallyUtilClass +{ + AtkUtilClass parent_class; + + /* padding for future expansion */ + gpointer _padding_dummy[30]; +}; + +GType cally_util_get_type (void); + +G_END_DECLS + +#endif /* __CALLY_UTIL_H__ */ diff --git a/clutter/cally/cally.pc.in b/clutter/cally/cally.pc.in new file mode 100644 index 000000000..baf0dc859 --- /dev/null +++ b/clutter/cally/cally.pc.in @@ -0,0 +1,18 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ +apiversion=@CLUTTER_API_VERSION@ +requires=@CLUTTER_REQUIRES@ +backend=@COGL_WINSYS@ #only kept for backward compatability +winsys=@COGL_WINSYS@ +cogl=@COGL_DRIVER@ #only kept for backward compatability +driver=@COGL_DRIVER@ + +Name: Cally +Description: Clutter Accessibility Implementation Library +Version: @VERSION@ +Requires: atk clutter-1.0 +Libs: -L${libdir} -lclutter-${winsys}-${apiversion} +Cflags: -I${includedir}/clutter-${apiversion} +Requires: ${requires} diff --git a/configure.ac b/configure.ac index dc35b08c5..9a35c8244 100644 --- a/configure.ac +++ b/configure.ac @@ -1017,6 +1017,8 @@ AC_CONFIG_FILES([ clutter/osx/Makefile clutter/win32/Makefile clutter/win32/clutter-win32.pc + clutter/cally/Makefile + clutter/cally/cally.pc clutter/cogl/Makefile clutter/cogl/cogl/Makefile clutter/cogl/cogl/cogl-defines.h