/*
* Copyright (C) 2011 Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Authored by: Rodrigo Moya
*/
#include
#include "panel-indicator-accessible.h"
#include "panel-indicator-entry-accessible.h"
#include "panel-service.h"
/* AtkObject methods */
static void pia_component_interface_init (AtkComponentIface *iface);
static void panel_indicator_accessible_initialize (AtkObject *accessible, gpointer data);
static gint panel_indicator_accessible_get_n_children (AtkObject *accessible);
static AtkObject *panel_indicator_accessible_ref_child (AtkObject *accessible, gint i);
static AtkStateSet *panel_indicator_accessible_ref_state_set (AtkObject *accessible);
struct _PanelIndicatorAccessiblePrivate
{
IndicatorObject *indicator;
PanelService *service;
GSList *a11y_children;
gint x;
gint y;
gint width;
gint height;
};
G_DEFINE_TYPE_WITH_CODE(PanelIndicatorAccessible,
panel_indicator_accessible,
ATK_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (ATK_TYPE_COMPONENT, pia_component_interface_init))
#define GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), PANEL_TYPE_INDICATOR_ACCESSIBLE, PanelIndicatorAccessiblePrivate))
/* Indicator callbacks */
static void
on_indicator_entry_added (IndicatorObject *io, IndicatorObjectEntry *entry, gpointer user_data)
{
AtkObject *accessible;
PanelIndicatorAccessible *pia = PANEL_INDICATOR_ACCESSIBLE (user_data);
accessible = panel_indicator_entry_accessible_new (entry);
if (accessible != NULL)
{
atk_object_set_parent (accessible, ATK_OBJECT (pia));
pia->priv->a11y_children = g_slist_append (pia->priv->a11y_children, accessible);
g_signal_emit_by_name (ATK_OBJECT (pia), "children-changed::add",
g_slist_length (pia->priv->a11y_children) - 1,
accessible);
}
}
static void
on_indicator_entry_removed (IndicatorObject *io, IndicatorObjectEntry *entry, gpointer user_data)
{
GSList *l;
guint count = 0;
PanelIndicatorAccessible *pia = PANEL_INDICATOR_ACCESSIBLE (user_data);
gboolean found = FALSE;
AtkObject *accessible = NULL;
for (l = pia->priv->a11y_children; l != NULL; l = g_slist_next (l))
{
accessible = ATK_OBJECT (l->data);
if (entry == panel_indicator_entry_accessible_get_entry (PANEL_INDICATOR_ENTRY_ACCESSIBLE (accessible)))
{
found = TRUE;
break;
}
else
count++;
}
if (found)
{
pia->priv->a11y_children = g_slist_remove (pia->priv->a11y_children, accessible);
g_signal_emit_by_name (ATK_OBJECT (pia), "children-changed::remove",
count, accessible);
g_object_unref (accessible);
}
}
static void
on_accessible_desc_updated (IndicatorObject *io, IndicatorObjectEntry *entry, gpointer user_data)
{
GSList *l;
PanelIndicatorAccessible *pia = PANEL_INDICATOR_ACCESSIBLE (user_data);
gboolean found = FALSE;
AtkObject *entry_accessible = NULL;
AtkObject *widget_accessible = NULL;
for (l = pia->priv->a11y_children; l != NULL; l = l->next)
{
entry_accessible = ATK_OBJECT (l->data);
if (entry == panel_indicator_entry_accessible_get_entry (PANEL_INDICATOR_ENTRY_ACCESSIBLE (entry_accessible)))
{
found = TRUE;
break;
}
}
if (!found)
return;
if (GTK_IS_LABEL (entry->label))
{
widget_accessible = gtk_widget_get_accessible (GTK_WIDGET (entry->label));
}
else if (GTK_IS_IMAGE (entry->image))
{
widget_accessible = gtk_widget_get_accessible (GTK_WIDGET (entry->image));
}
else
{
g_warning ("a11y: Current entry is not a label or a image.");
}
if (ATK_IS_OBJECT (widget_accessible))
{
gchar *name = (gchar*) atk_object_get_name (widget_accessible);
gchar *description = (gchar*) entry->accessible_desc;
if (name == NULL)
name = "";
if (description == NULL)
description = "";
atk_object_set_name (entry_accessible, name);
atk_object_set_description (entry_accessible, description);
}
}
static void
on_geometries_changed_cb (PanelService *service,
IndicatorObject *object,
IndicatorObjectEntry *entry,
gint x,
gint y,
gint width,
gint height,
gpointer user_data)
{
PanelIndicatorAccessible *pia;
AtkRectangle rect;
GSList *l;
gboolean minimum_set = FALSE;
pia = PANEL_INDICATOR_ACCESSIBLE (user_data);
g_return_if_fail (PANEL_IS_INDICATOR_ACCESSIBLE (pia));
if (object != pia->priv->indicator)
return;
/* Iterate over all children to get width and height */
pia->priv->width = pia->priv->height = 0;
for (l = pia->priv->a11y_children; l != NULL; l = l->next)
{
gint e_x, e_y, e_width, e_height;
AtkObject *accessible = ATK_OBJECT (l->data);
atk_component_get_extents (ATK_COMPONENT (accessible), &e_x, &e_y, &e_width, &e_height, ATK_XY_SCREEN);
if (minimum_set)
{
if (e_x < pia->priv->x)
pia->priv->x = e_x;
if (e_y < pia->priv->y)
pia->priv->y = e_y;
}
else
{
pia->priv->x = e_x;
pia->priv->y = e_y;
minimum_set = TRUE;
}
pia->priv->width += e_width;
if (e_height > pia->priv->height)
pia->priv->height = e_height;
}
/* Notify ATK objects of change of coordinates */
rect.x = pia->priv->x;
rect.y = pia->priv->y;
rect.width = pia->priv->width;
rect.height = pia->priv->height;
g_signal_emit_by_name (ATK_COMPONENT (pia), "bounds-changed", &rect);
}
static void
panel_indicator_accessible_finalize (GObject *object)
{
PanelIndicatorAccessible *pia = PANEL_INDICATOR_ACCESSIBLE (object);
if (pia->priv != NULL)
{
if (pia->priv->indicator != NULL)
{
g_signal_handlers_disconnect_by_func (pia->priv->indicator, on_indicator_entry_added, pia);
g_signal_handlers_disconnect_by_func (pia->priv->indicator, on_indicator_entry_removed, pia);
g_signal_handlers_disconnect_by_func (pia->priv->indicator, on_accessible_desc_updated, pia);
g_object_unref (G_OBJECT (pia->priv->indicator));
}
if (pia->priv->a11y_children != NULL)
{
g_slist_free_full(pia->priv->a11y_children, g_object_unref);
pia->priv->a11y_children = NULL;
}
g_signal_handlers_disconnect_by_func (pia->priv->service, on_geometries_changed_cb, pia);
}
G_OBJECT_CLASS (panel_indicator_accessible_parent_class)->finalize (object);
}
static void
panel_indicator_accessible_class_init (PanelIndicatorAccessibleClass *klass)
{
GObjectClass *object_class;
AtkObjectClass *atk_class;
/* GObject */
object_class = G_OBJECT_CLASS (klass);
object_class->finalize = panel_indicator_accessible_finalize;
/* AtkObject */
atk_class = ATK_OBJECT_CLASS (klass);
atk_class->initialize = panel_indicator_accessible_initialize;
atk_class->get_n_children = panel_indicator_accessible_get_n_children;
atk_class->ref_child = panel_indicator_accessible_ref_child;
atk_class->ref_state_set = panel_indicator_accessible_ref_state_set;
g_type_class_add_private (object_class, sizeof (PanelIndicatorAccessiblePrivate));
}
static void
panel_indicator_accessible_init (PanelIndicatorAccessible *pia)
{
pia->priv = GET_PRIVATE (pia);
pia->priv->a11y_children = NULL;
pia->priv->x = pia->priv->y = pia->priv->width = pia->priv->height = 0;
/* Set up signals for listening to service changes */
pia->priv->service = panel_service_get_default ();
g_signal_connect (pia->priv->service, "geometries-changed",
G_CALLBACK (on_geometries_changed_cb), pia);
}
AtkObject *
panel_indicator_accessible_new (IndicatorObject *indicator)
{
PanelIndicatorAccessible *pia;
pia = g_object_new (PANEL_TYPE_INDICATOR_ACCESSIBLE, NULL);
atk_object_initialize (ATK_OBJECT (pia), indicator);
return ATK_OBJECT (pia);
}
/* Implementation of AtkObject methods */
static void
panel_indicator_accessible_get_extents (AtkComponent *component,
gint *x,
gint *y,
gint *width,
gint *height,
AtkCoordType coord_type)
{
PanelIndicatorAccessible *pia;
g_return_if_fail (PANEL_IS_INDICATOR_ACCESSIBLE (component));
pia = PANEL_INDICATOR_ACCESSIBLE (component);
/* We ignore AtkCoordType for now, as the panel is always at the top left
corner and so relative and absolute coordinates are the same */
*x = pia->priv->x;
*y = pia->priv->y;
*width = pia->priv->width;
*height = pia->priv->height;
}
static void
pia_component_interface_init (AtkComponentIface *iface)
{
g_return_if_fail (iface != NULL);
iface->get_extents = panel_indicator_accessible_get_extents;
}
static void
panel_indicator_accessible_initialize (AtkObject *accessible, gpointer data)
{
PanelIndicatorAccessible *pia;
GList *entries, *l;
g_return_if_fail (PANEL_IS_INDICATOR_ACCESSIBLE (accessible));
ATK_OBJECT_CLASS (panel_indicator_accessible_parent_class)->initialize (accessible, data);
pia = PANEL_INDICATOR_ACCESSIBLE (accessible);
atk_object_set_role (accessible, ATK_ROLE_PANEL);
/* Setup the indicator object */
pia->priv->indicator = g_object_ref (data);
g_signal_connect (G_OBJECT (pia->priv->indicator), "entry-added",
G_CALLBACK (on_indicator_entry_added), pia);
g_signal_connect (G_OBJECT (pia->priv->indicator), "entry-removed",
G_CALLBACK (on_indicator_entry_removed), pia);
g_signal_connect (G_OBJECT (pia->priv->indicator), "accessible_desc_update",
G_CALLBACK (on_accessible_desc_updated), pia);
/* Retrieve all entries and create their accessible objects */
entries = indicator_object_get_entries (pia->priv->indicator);
for (l = entries; l != NULL; l = l->next)
{
AtkObject *child;
IndicatorObjectEntry *entry = (IndicatorObjectEntry *) l->data;
child = panel_indicator_entry_accessible_new (entry);
atk_object_set_parent (child, accessible);
pia->priv->a11y_children = g_slist_append (pia->priv->a11y_children, child);
}
g_list_free (entries);
}
static gint
panel_indicator_accessible_get_n_children (AtkObject *accessible)
{
PanelIndicatorAccessible *pia = PANEL_INDICATOR_ACCESSIBLE (accessible);
g_return_val_if_fail (PANEL_IS_INDICATOR_ACCESSIBLE (pia), 0);
return g_slist_length (pia->priv->a11y_children);
}
static AtkObject *
panel_indicator_accessible_ref_child (AtkObject *accessible, gint i)
{
PanelIndicatorAccessible *pia = PANEL_INDICATOR_ACCESSIBLE (accessible);
g_return_val_if_fail (PANEL_IS_INDICATOR_ACCESSIBLE (pia), NULL);
return g_object_ref (g_slist_nth_data (pia->priv->a11y_children, i));
}
static AtkStateSet *
panel_indicator_accessible_ref_state_set (AtkObject *accessible)
{
AtkStateSet *state_set;
g_return_val_if_fail (PANEL_IS_INDICATOR_ACCESSIBLE (accessible), NULL);
/* Retrieve state_set from parent_class */
state_set = ATK_OBJECT_CLASS (panel_indicator_accessible_parent_class)->ref_state_set (accessible);
atk_state_set_add_state (state_set, ATK_STATE_VISIBLE);
return state_set;
}