mutter/clutter/cally/cally-actor.c
Emmanuele Bassi 2b81d90dd7 Eliminate G_CONST_RETURN
The G_CONST_RETURN define in GLib is, and has always been, a bit fuzzy.

We always used it to conform to the platform, at least for public-facing
API.

At first I assumed it has something to do with brain-damaged compilers
or with weird platforms where const was not really supported; sadly,
it's something much, much worse: it's a define that can be toggled at
compile-time to remove const from the signature of public API. This is a
truly terrifying feature that I assume was added in the past century,
and whose inception clearly had something to do with massive doses of
absynthe and opium — because any other explanation would make the
existence of such a feature even worse than assuming drugs had anything
to do with it.

Anyway, and pleasing the gods, this dubious feature is being
removed/deprecated in GLib; see bug:

  https://bugzilla.gnome.org/show_bug.cgi?id=644611

Before deprecation, though, we should just remove its usage from the
whole API. We should especially remove its usage from Cally's internals,
since there it never made sense in the first place.
2011-06-07 16:06:24 +01:00

1462 lines
43 KiB
C

/* CALLY - The Clutter Accessibility Implementation Library
*
* Copyright (C) 2008 Igalia, S.L.
*
* Author: Alejandro Piñeiro Iglesias <apinheiro@igalia.com>
*
* 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, see <http://www.gnu.org/licenses/>.
*/
/**
* SECTION:cally-actor
* @Title: CallyActor
* @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 actor (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: on previous releases ClutterActor added
* the actions "press", "release" and "click", as at that time some
* general-purpose actors like textures were directly used as buttons.
*
* But now, new toolkits appeared, providing high-level widgets, like
* buttons. So in this environment, it doesn't make sense to keep
* adding them as default.
*
* Anyway, current implementation of AtkAction is done at CallyActor
* providing methods to add and remove actions. This is based on the
* one used at gailcell, and proposed as a change on #AtkAction
* interface:
*
* https://bugzilla.gnome.org/show_bug.cgi?id=649804
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <glib.h>
#include <clutter/clutter.h>
#ifdef HAVE_CLUTTER_GLX
#include <clutter/x11/clutter-x11.h>
#endif
#include <math.h>
#include "cally-actor.h"
#include "cally-actor-private.h"
typedef struct _CallyActorActionInfo CallyActorActionInfo;
/*< private >
* CallyActorActionInfo:
* @name: name of the action
* @description: description of the action
* @keybinding: keybinding related to the action
* @do_action_func: callback
* @user_data: data to be passed to @do_action_func
* @notify: function to be called when removing the action
*
* Utility structure to maintain the different actions added to the
* #CallyActor
*/
struct _CallyActorActionInfo
{
gchar *name;
gchar *description;
gchar *keybinding;
CallyActionCallback do_action_func;
gpointer user_data;
GDestroyNotify notify;
};
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 const 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 const gchar* cally_actor_action_get_description (AtkAction *action,
gint i);
static const gchar* cally_actor_action_get_keybinding (AtkAction *action,
gint i);
static const 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_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;
};
/**
* cally_actor_new:
* @actor: a #ClutterActor
*
* Creates a new #CallyActor for the given @actor
*
* Return value: the newly created #AtkObject
*
* Since: 1.4
*/
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));
/* 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 const gchar*
cally_actor_get_name (AtkObject *obj)
{
const 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;
GList *children = NULL;
gint num = 0;
g_return_val_if_fail (CALLY_IS_ACTOR (obj), 0);
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;
GList *children = NULL;
AtkObject *result = NULL;
g_return_val_if_fail (CALLY_IS_ACTOR (obj), NULL);
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_GLX
{
ClutterActor *stage = NULL;
Display *display = NULL;
Window root_window;
Window stage_window;
Window child;
gint return_val = 0;
stage = clutter_actor_get_stage (actor);
/* FIXME: what happens if you use another display with
clutter_backend_x11_set_display ?*/
display = clutter_x11_get_default_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)
{
CallyActor *cally_actor = NULL;
AtkStateSet *set = NULL;
CallyActorPrivate *priv = NULL;
CallyActorActionInfo *info = NULL;
cally_actor = CALLY_ACTOR (action);
priv = cally_actor->priv;
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, info->user_data);
}
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 const 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 const 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 const 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 (g_strcmp0 (pspec->name, "visible") == 0)
{
state = ATK_STATE_VISIBLE;
value = CLUTTER_ACTOR_IS_VISIBLE (actor);
}
else if (g_strcmp0 (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);
}
/**
* cally_actor_add_action: (skip)
* @cally_actor: a #CallyActor
* @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 -1 if failure
*
* Since: 1.4
*/
guint
cally_actor_add_action (CallyActor *cally_actor,
const gchar *action_name,
const gchar *action_description,
const gchar *action_keybinding,
CallyActionFunc action_func)
{
return cally_actor_add_action_full (cally_actor,
action_name,
action_description,
action_keybinding,
(CallyActionCallback) action_func,
NULL, NULL);
}
/**
* cally_actor_add_action_full:
* @cally_actor: a #CallyActor
* @action_name: the action name
* @action_description: the action description
* @action_keybinding: the action keybinding
* @callback: (scope notified): the callback of the action
* @user_data: (closure): data to be passed to @callback
* @notify: function to be called when removing the action
*
* Adds a new action to be accessed with the #AtkAction interface.
*
* Return value: added action id, or -1 if failure
*
* Rename to: cally_actor_add_action
*
* Since: 1.6
*/
guint
cally_actor_add_action_full (CallyActor *cally_actor,
const gchar *action_name,
const gchar *action_description,
const gchar *action_keybinding,
CallyActionCallback callback,
gpointer user_data,
GDestroyNotify notify)
{
CallyActorActionInfo *info = NULL;
CallyActorPrivate *priv = NULL;
g_return_val_if_fail (CALLY_IS_ACTOR (cally_actor), -1);
g_return_val_if_fail (callback != NULL, -1);
priv = cally_actor->priv;
info = g_slice_new (CallyActorActionInfo);
info->name = g_strdup (action_name);
info->description = g_strdup (action_description);
info->keybinding = g_strdup (action_keybinding);
info->do_action_func = callback;
info->user_data = user_data;
info->notify = notify;
priv->action_list = g_list_append (priv->action_list, info);
return g_list_length (priv->action_list);
}
/**
* cally_actor_remove_action:
* @cally_actor: a #CallyActor
* @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.4
*/
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_by_name:
* @cally_actor: a #CallyActor
* @action_name: the name of the action to remove
*
* Removes an 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.4
*/
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)
{
CallyActorActionInfo *ainfo = node->data;
if (!g_ascii_strcasecmp (ainfo->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 = action_info;
g_assert (info != NULL);
g_free (info->name);
g_free (info->description);
g_free (info->keybinding);
if (info->notify)
info->notify (info->user_data);
g_slice_free (CallyActorActionInfo, info);
}