From c0f753d108b7a2ad23dbf5a5b596f80175442927 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Pi=C3=B1eiro?= Date: Mon, 14 Jun 2010 13:38:25 +0200 Subject: [PATCH] Add Cally The Clutter Accessibility Library is an implementation of the ATK, the Accessibility Toolkit, which exposes Clutter actors to accessibility tools. This allows not only writing accessible user interfaces, but also allows testing and verification frameworks based on A11Y technologies to inspect and test a Clutter scene graph. http://bugzilla.clutter-project.org/show_bug.cgi?id=2097 Signed-off-by: Emmanuele Bassi --- clutter/Makefile.am | 10 +- clutter/cally/Makefile.am | 79 ++ clutter/cally/cally-actor-private.h | 35 + clutter/cally/cally-actor.c | 1491 +++++++++++++++++++++++++++ clutter/cally/cally-actor.h | 108 ++ clutter/cally/cally-clone.c | 105 ++ clutter/cally/cally-clone.h | 63 ++ clutter/cally/cally-factory.h | 91 ++ clutter/cally/cally-group.c | 138 +++ clutter/cally/cally-group.h | 65 ++ clutter/cally/cally-rectangle.c | 102 ++ clutter/cally/cally-rectangle.h | 63 ++ clutter/cally/cally-root.c | 273 +++++ clutter/cally/cally-root.h | 64 ++ clutter/cally/cally-stage.c | 258 +++++ clutter/cally/cally-stage.h | 62 ++ clutter/cally/cally-text.c | 1255 ++++++++++++++++++++++ clutter/cally/cally-text.h | 63 ++ clutter/cally/cally-texture.c | 103 ++ clutter/cally/cally-texture.h | 63 ++ clutter/cally/cally-util.c | 557 ++++++++++ clutter/cally/cally-util.h | 61 ++ clutter/cally/cally.pc.in | 18 + configure.ac | 2 + 24 files changed, 5126 insertions(+), 3 deletions(-) create mode 100644 clutter/cally/Makefile.am create mode 100644 clutter/cally/cally-actor-private.h create mode 100644 clutter/cally/cally-actor.c create mode 100644 clutter/cally/cally-actor.h create mode 100644 clutter/cally/cally-clone.c create mode 100644 clutter/cally/cally-clone.h create mode 100644 clutter/cally/cally-factory.h create mode 100644 clutter/cally/cally-group.c create mode 100644 clutter/cally/cally-group.h create mode 100644 clutter/cally/cally-rectangle.c create mode 100644 clutter/cally/cally-rectangle.h create mode 100644 clutter/cally/cally-root.c create mode 100644 clutter/cally/cally-root.h create mode 100644 clutter/cally/cally-stage.c create mode 100644 clutter/cally/cally-stage.h create mode 100644 clutter/cally/cally-text.c create mode 100644 clutter/cally/cally-text.h create mode 100644 clutter/cally/cally-texture.c create mode 100644 clutter/cally/cally-texture.h create mode 100644 clutter/cally/cally-util.c create mode 100644 clutter/cally/cally-util.h create mode 100644 clutter/cally/cally.pc.in 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