/* $Id: papi_atkobject.c,v 1.19.2.1 2006/11/04 12:29:40 marcusva Exp $
 *
 * Copyright (c) 2006, Marcus von Appen
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "papi.h"
#include "papi_private.h"

/* Forward declarations for AtkObject bindings. */
static PyObject* _atkobject_new (PyTypeObject *type, PyObject *args,
                                 PyObject *kwds);
static int _atkobject_init (PyAtkObject *self, PyObject *args, PyObject *kwds);
static void _atkobject_dealloc (PyAtkObject *self);
static PyObject* _atkobject_get_dict (PyAtkObject *self, void *closure);
static PyObject* _atkobject_ref_accessible_child (PyAtkObject *self,
                                                  PyObject *args);
static PyObject* _atkobject_ref_relation_set (PyAtkObject *self);
static PyObject* _atkobject_ref_state_set (PyAtkObject *self);
static PyObject* _atkobject_connect_property_change_handler (PyAtkObject *self,
                                                             PyObject *args);
static PyObject* _atkobject_remove_property_change_handler (PyAtkObject *self,
                                                            PyObject *args);
static PyObject* _atkobject_notify_state_change (PyAtkObject *self,
                                                 PyObject *args);
static PyObject* _atkobject_add_relationship (PyAtkObject *self,
                                              PyObject *args);
static PyObject* _atkobject_remove_relationship (PyAtkObject *self,
                                                 PyObject *args);

static PyObject* _atkobject_get_name (PyAtkObject *self, void *closure);
static int _atkobject_set_name (PyAtkObject *self, PyObject *value,
                                void *closure);
static PyObject* _atkobject_get_description (PyAtkObject *self, void *closure);
static int _atkobject_set_description (PyAtkObject *self, PyObject *value,
                                       void *closure);
static PyObject* _atkobject_get_parent (PyAtkObject *self, void *closure);
static int _atkobject_set_parent (PyAtkObject *self, PyObject *value,
                                  void *closure);
static PyObject* _atkobject_get_n_accessible_children (PyAtkObject *self,
                                                       void *closure);
static PyObject* _atkobject_get_layer (PyAtkObject *self, void *closure);
static PyObject* _atkobject_get_mdi_zorder (PyAtkObject *self, void *closure);
static PyObject* _atkobject_get_role (PyAtkObject *self, void *closure);
static int _atkobject_set_role (PyAtkObject *self, PyObject *value,
                                void *closure);
static PyObject* _atkobject_get_index_in_parent (PyAtkObject *self,
                                                 void *closure);
static PyObject* _atkobject_get_attributes (PyAtkObject *self, void *closure);

/* Nonstandard enhancements. */
static PyObject* _atkobject_emit (PyAtkObject *self, PyObject *args);

static int _class_get_n_children (AtkObject *obj);
static AtkObject* _class_ref_child (AtkObject *obj, int i);
static int _class_get_index_in_parent (AtkObject *obj);

/* Signal handler for the property changes. */
static void _atkobject_handle_property_change (AtkObject *obj,
                                               AtkPropertyValues *value);

/**
 * Methods, which are bound to the PyAtkObject type.
 */
static PyMethodDef _atkobject_methods[] =
{
    { "ref_accessible_child", (PyCFunction) _atkobject_ref_accessible_child,
      METH_VARARGS,
      "O.ref_accessible_child (index) -> AtkObject\n\n"
      "Gets a reference to the specified accessible child of the object.\n\n"
      "The accessible children are 0-based so the first accessible child\n"
      "is at index 0, the second at index 1 and so on." },
    { "ref_relation_set", (PyCFunction) _atkobject_ref_relation_set,
      METH_NOARGS,
      "O.ref_relation_set () -> AtkRelationSet\n\n"
      "Gets the AtkRelationSet associated with the object." },
    { "ref_state_set", (PyCFunction) _atkobject_ref_state_set, METH_NOARGS,
      "O.ref_state_set () -> AtkStateSet\n\n"
      "Gets a reference to the state set of the accessible." },
    { "connect_property_change_handler",
      (PyCFunction) _atkobject_connect_property_change_handler, METH_VARARGS,
      "O.connect_property_change_handler (...) -> None\n\n"
      "Specifies a function to be called when a property changes value.\n\n"
      "Currently not implemented."},
    { "remove_property_change_handler",
      (PyCFunction) _atkobject_remove_property_change_handler, METH_VARARGS,
      "O.remove_property_change_handler (...) -> None\n\n"
      "Removes a property change handler.\n\n"
      "Currently not implemented."},
    { "notify_state_change", (PyCFunction) _atkobject_notify_state_change,
      METH_VARARGS,
      "O.notify_state_change (state, value) -> None\n\n"
      "Emits a state-change signal for the specified state." },
    { "add_relationship", (PyCFunction) _atkobject_add_relationship,
      METH_VARARGS,
      "O.add_relationship (type, obj) -> None\n\n"
      "Adds a relationship of a specific type with the specified target." },
    { "remove_relationship", (PyCFunction) _atkobject_remove_relationship,
      METH_VARARGS,
      "O.remove_relationship (type, obj) -> None\n\n"
      "Removes a relationship of a specific type with the specified target." },

    /* Nonstandard enhancement. */
    { "emit", (PyCFunction) _atkobject_emit, METH_VARARGS,
      "O.emit (type) -> None\n\n"
      "Emits a specific signal on the underlying AT-SPI implementation." },
    { NULL, NULL, 0, NULL }
};

/**
 * Getters and setters for the PyAtkObject.
 */
static PyGetSetDef _atkobject_getsets[] = {
    { "__dict__", (getter) _atkobject_get_dict, NULL, NULL, NULL },
    { "name", (getter) _atkobject_get_name, (setter) _atkobject_set_name,
      "The name of the accessible.", NULL },
    { "description", (getter) _atkobject_get_description,
      (setter) _atkobject_set_description, 
      "The description of the accessible.", NULL },
    { "parent", (getter) _atkobject_get_parent, (setter) _atkobject_set_parent,
      "The parent of the accessible.", NULL },
    { "n_accessible_children", (getter) _atkobject_get_n_accessible_children,
      NULL, "The number of accessible children of the accessible.", NULL },
    { "layer", (getter) _atkobject_get_layer, NULL,
      "The layer of the accessible.", NULL },
    { "mdi_zorder", (getter) _atkobject_get_mdi_zorder, NULL,
      "The z-order of the accessible. The value None will be returned if\n"
      "the layer of the accessible is not ATK_LAYER_MDI.", NULL },
    { "role", (getter) _atkobject_get_role, (setter) _atkobject_set_role,
      "The role of the accessible.", NULL },
    { "index_in_parent", (getter) _atkobject_get_index_in_parent, NULL,
      "The 0-based index of this accessible in its parent or -1\n"
      "if the accessible does not have an accessible parent.", NULL },
    { "attributes", (getter) _atkobject_get_attributes, NULL,
      "A list of AtkAttributes applied to this AtkObject as a whole.", NULL },
    { NULL, NULL, NULL, NULL, NULL }
};

/**
 * PyAtkObject wrapper type, that encapsulates the AtkObject.
 */
PyTypeObject PyAtkObject_Type =
{
    PyObject_HEAD_INIT(NULL)
    0,
    "papi.AtkObject",           /* tp_name */
    sizeof (PyAtkObject),       /* tp_basicsize */
    0,                          /* tp_itemsize */
    (destructor) _atkobject_dealloc, /* tp_dealloc */
    0,                          /* tp_print */
    0,                          /* tp_getattr */
    0,                          /* tp_setattr */
    0,                          /* tp_compare */
    0,                          /* tp_repr */
    0,                          /* tp_as_number */
    0,                          /* tp_as_sequence */
    0,                          /* tp_as_mapping */
    0,                          /* tp_hash */
    0,                          /* tp_call */
    0,                          /* tp_str */
    0,                          /* tp_getattro */
    0,                          /* tp_setattro */
    0,                          /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
    "AtkObject (interfaces) -> AtkObject\n\n"
    "ATK accessibility object.\n\n",
    0,                          /* tp_traverse */
    0,                          /* tp_clear */
    0,                          /* tp_richcompare */
    offsetof (PyAtkObject, weakrefs), /* tp_weaklistoffset */
    0,                          /* tp_iter */
    0,                          /* tp_iternext */
    _atkobject_methods,         /* tp_methods */
    0,                          /* tp_members */
    _atkobject_getsets,         /* tp_getset */
    0,                          /* tp_base */
    0,                          /* tp_dict */
    0,                          /* tp_descr_get */
    0,                          /* tp_descr_set */
    offsetof (PyAtkObject, dict), /* tp_dictoffset */
    (initproc) _atkobject_init, /* tp_init */
    0,                          /* tp_alloc */
    _atkobject_new,             /* tp_new */
    0,                          /* tp_free */
    0,                          /* tp_is_gc */
    0,                          /* tp_bases */
    0,                          /* tp_mro */
    0,                          /* tp_cache */
    0,                          /* tp_subclasses */
    0,                          /* tp_weaklist */
    0                           /* tp_del */
};

/**
 * Creates a new PyAtkObject.
 */
static PyObject*
_atkobject_new (PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    PyAtkObject *self;

    debug ("_atkobject_new\n");

    self = (PyAtkObject *) type->tp_alloc (type, 0);
    self->dict = NULL;
    self->prop_handlers = NULL;
    self->weakrefs = NULL;
    self->obj = NULL;

    return (PyObject *) self;
}

/**
 * Initializes the PyAtkObject members, such as the AtkObject.
 */
static int
_atkobject_init (PyAtkObject *self, PyObject *args, PyObject *kwds)
{
    debug ("_atkobject_init\n");
    AtkInterfaceType ifaces = 0;

    if (!PyArg_ParseTuple (args, "|l:init", &ifaces))
        return -1;

    /* No interfaces provided, create default AtkObject. */
    if (!self->obj)
    {
        if (ifaces == IFACE_INVALID)
            self->obj = g_object_new (ATK_TYPE_OBJECT, NULL);
        else
            self->obj = g_object_new (atktype_get_type (ifaces), NULL);

        /* Prepare some data slots for the references. */
        g_object_set_data (G_OBJECT (self->obj), PAPI_CHILDREN, NULL);
        g_object_set_data (G_OBJECT (self->obj), PAPI_PYOBJECT, self);
    }
    
    /* Update the interface bindings. */
    atktype_add_interface_methods ((PyObject *) self, ifaces);
    
    return 0;
}

/**
 * Deallocates the PyAtkObject and its members.
 */
static void
_atkobject_dealloc (PyAtkObject *self)
{
    debug ("_atkobject_dealloc\n");

    if (self->obj)
        g_object_unref (self->obj);
    if (self->dict)
    {
	Py_DECREF (self->dict);
    }
    self->dict = NULL;

    if (self->prop_handlers)
    {
        Py_DECREF (self->prop_handlers);
    }
    self->prop_handlers = NULL;

    if (self->weakrefs)
        PyObject_ClearWeakRefs ((PyObject *) self);

    self->ob_type->tp_free ((PyObject *) self);
}

/**
 * Getter for AtkObject.__dict__.
 */
static PyObject*
_atkobject_get_dict (PyAtkObject *self, void *closure)
{
    if (!self->dict)
    {
	self->dict = PyDict_New ();
        if (!self->dict)
            return NULL;
    }

    Py_INCREF (self->dict);
    return self->dict;
}

/**
 * Wrapper for atk_object_get_name().
 */
static PyObject*
_atkobject_get_name (PyAtkObject *self, void *closure)
{
    const gchar *val;

    debug ("_atkobject_get_name\n");

    val = atk_object_get_name (ATKOBJECT (self));
    if (val)
        return PyString_FromString (val);
    Py_RETURN_NONE;
}

/**
 * Wrapper for atk_object_set_name().
 */
static int
_atkobject_set_name (PyAtkObject *self, PyObject *value, void *closure)
{
    char *val;

    debug ("_atkobject_set_name\n");

    if (!value)
    {
        PyErr_SetString (PyExc_TypeError, "Cannot delete the name attribute");
        return -1;
    }

    val = PyString_AsString (value);
    if (!val)
        return -1;
    atk_object_set_name (ATKOBJECT (self), val);
    return 0;
}

/**
 * Wrapper for atk_object_get_description().
 */
static PyObject*
_atkobject_get_description (PyAtkObject *self, void *closure)
{
    const gchar *val;

    debug ("_atkobject_get_description\n");

    val = atk_object_get_description (ATKOBJECT (self));
    if (val)
        return PyString_FromString (val);
    Py_RETURN_NONE;
}

/**
 * Wrapper for atk_object_set_description().
 */
static int
_atkobject_set_description (PyAtkObject *self, PyObject *value, void *closure)
{
    char *val;

    debug ("_atkobject_set_description\n");

    if (!value)
    {
        PyErr_SetString (PyExc_TypeError,
                         "Cannot delete the description attribute");
        return -1;
    }

    val = PyString_AsString (value);
    if (!val)
        return -1;
    atk_object_set_description (ATKOBJECT (self), val);
    return 0;
}

/**
 * Wrapper for atk_object_get_parent().
 */
static PyObject*
_atkobject_get_parent (PyAtkObject *self, void *closure)
{
    AtkObject *val;

    debug ("_atkobject_get_parent\n");

    val = atk_object_get_parent (ATKOBJECT (self));
    if (val)
    {
        PyAtkObject *obj = PyObject_New (PyAtkObject, &PyAtkObject_Type);
        obj->obj = val;
        return (PyObject *) obj;
    }
    Py_RETURN_NONE;
}

/**
 * Wrapper for atk_object_set_parent().
 */
static int
_atkobject_set_parent (PyAtkObject *self, PyObject *value, void *closure)
{
    gpointer data;

    debug ("_atkobject_set_parent\n");

    if (!value)
    {
        PyErr_SetString (PyExc_TypeError,
                         "Cannot delete the parent attribute");
        return -1;
    }

    /* Nullify parent. */
    if (value == Py_None)
    {
        AtkObject *parent = ATKOBJECT (self)->accessible_parent;
        if (parent)
        {
            data = g_object_get_data (G_OBJECT (parent), PAPI_CHILDREN);
            data = g_list_remove (data, self->obj);
            ATKOBJECT (self)->accessible_parent = NULL;
        }
        return 0;
    }
    else if (!PyObject_TypeCheck (value, &PyAtkObject_Type))
    {
        PyErr_SetString (PyExc_TypeError, "parameter must be an AtkObject");
        return -1;
    }
    else
        atk_object_set_parent (ATKOBJECT (self), ATKOBJECT (value));

    /* Add the object to the list of children of the parent. */
    data = g_object_get_data (G_OBJECT (ATKOBJECT (value)), PAPI_CHILDREN);
    data = g_list_append (data, self->obj);
    g_object_set_data (G_OBJECT (ATKOBJECT (value)), PAPI_CHILDREN, data);

   return 0;
}

/**
 * Wrapper for atk_object_get_n_accessible_children().
 */
static PyObject*
_atkobject_get_n_accessible_children (PyAtkObject *self, void *closure)
{
    gint val;

    debug ("_atkobject_get_n_accessible_children\n");

    val = atk_object_get_n_accessible_children (ATKOBJECT (self));
    return PyInt_FromLong ((long) val);
}

/**
 * Wrapper for atk_object_ref_accessible_child().
 */
static PyObject*
_atkobject_ref_accessible_child (PyAtkObject *self, PyObject *args)
{
    PyAtkObject *obj;
    AtkObject *val;
    int i;

    debug ("_atkobject_ref_accessible_child\n");

    if (!PyArg_ParseTuple (args, "i:ref_accessible_child", &i))
        return NULL;

    val = atk_object_ref_accessible_child (ATKOBJECT (self), i);
    obj = PyObject_New (PyAtkObject, &PyAtkObject_Type);
    obj->obj = val;
    return (PyObject *) obj;
}

/**
 * Wrapper for atk_object_ref_relation_set().
 */
static PyObject*
_atkobject_ref_relation_set (PyAtkObject *self)
{
    PyAtkRelationSet *set;
    AtkRelationSet *aset;

    debug ("_atkobject_ref_relationset\n");

    aset = atk_object_ref_relation_set (ATKOBJECT (self));
    if (!aset)
        Py_RETURN_NONE;
    set = PyObject_New (PyAtkRelationSet, &PyAtkRelationSet_Type);
    set->obj = aset;
    return (PyObject *) set;
}

/**
 * Wrapper for atk_object_get_layer().
 */
static PyObject*
_atkobject_get_layer (PyAtkObject *self, void *closure)
{
    AtkLayer layer;

    debug ("_atkobject_get_layer\n");

    layer = atk_object_get_layer (ATKOBJECT (self));
    return PyInt_FromLong ((long) layer);
}

/**
 * Wrapper for atk_object_get_mdi_zorder().
 */
static PyObject*
_atkobject_get_mdi_zorder (PyAtkObject *self, void *closure)
{
    int z;

    debug ("_atkobject_get_mdi_zorder\n");

    z = atk_object_get_mdi_zorder (ATKOBJECT (self));
    return PyInt_FromLong ((long) z);
}

/**
 * Wrapper for atk_object_get_role().
 */
static PyObject*
_atkobject_get_role (PyAtkObject *self, void *closure)
{
    AtkRole role;

    debug ("_atkobject_get_role\n");

    role = atk_object_get_role (ATKOBJECT (self));
    return PyInt_FromLong ((long) role);
}

/**
 * Wrapper for atk_object_set_role().
 */
static int
_atkobject_set_role (PyAtkObject *self, PyObject *value, void *closure)
{
    debug ("_atkobject_set_role\n");
    
    if (!value)
    {
        PyErr_SetString (PyExc_TypeError, "Cannot delete the role attribute");
        return -1;
    }

    atk_object_set_role (ATKOBJECT (self), PyInt_AsLong (value));
    return 0;
}

/**
 * Wrapper for atk_object_ref_state_set().
 */
static PyObject*
_atkobject_ref_state_set (PyAtkObject *self)
{
    PyAtkStateSet *retval;
    AtkStateSet *set;

    debug ("_atkobject_ref_state_set\n");

    set = atk_object_ref_state_set (ATKOBJECT (self));
    retval = PyObject_New (PyAtkStateSet, &PyAtkStateSet_Type);
    retval->obj = set;
    return (PyObject *) retval;
}

/**
 * Wrapper for atk_object_get_index_in_parent().
 */
static PyObject*
_atkobject_get_index_in_parent (PyAtkObject *self, void *closure)
{
    int val;

    debug ("_atkobject_index_in_parent\n");

    val = atk_object_get_index_in_parent (ATKOBJECT (self));
    return PyInt_FromLong ((long) val);
}

/**
 * Wrapper for atk_object_get_attributes().
 */
static PyObject*
_atkobject_get_attributes (PyAtkObject *self, void *closure)
{
    PyObject *list;
    PyAtkAttribute *attr;
    AtkAttributeSet *set;
    guint amount = 0;
    int i = 0;

    debug ("_atkobject_get_attributes\n");
    
    set = atk_object_get_attributes (ATKOBJECT (self));
    amount = g_slist_length (set);

    if (amount == 0)
    {
        Py_RETURN_NONE;
    }

    list = PyList_New ((int) g_slist_length (set));
    if (!list)
        return NULL;

    do
    {
        attr = PyObject_New (PyAtkAttribute, &PyAtkAttribute_Type);
        attr->obj = (AtkAttribute *) set->data;
        PyList_SetItem (list, i++, (PyObject *) attr);
    }
    while ((set = g_slist_next (set)));

    return list;
}

/**
 * Wrapper for atk_object_connect_property_change_handler().
 */
static PyObject*
_atkobject_connect_property_change_handler (PyAtkObject *self, PyObject *args)
{
    PyObject *callback;
    guint id = 0;

    debug ("_atkobject_connect_property_change_handler\n");

    if (!PyArg_ParseTuple (args, "O:connect_property_change_handler",
                           &callback))
        return NULL;

    if (!PyCallable_Check (callback))
    {
        PyErr_SetString (PyExc_TypeError, "parameter must be callable");
        return NULL;
    }

    /* No property change handler registered for now. Set up the list. */
    if (!self->prop_handlers)
    {
        self->prop_handlers = PyDict_New ();
        if (!self->prop_handlers)
            return NULL;

        /* Just bind our wrapper only once as it will cycle through
         * the dict and invoke each connected hander function. */
        id = atk_object_connect_property_change_handler
            (ATKOBJECT (self),
             (AtkPropertyChangeHandler *) _atkobject_handle_property_change);
    }
    
    /* Could not append to list. */
    if (PyDict_SetItem (self->prop_handlers,
                        PyInt_FromLong (PyDict_Size (self->prop_handlers)),
                        callback) == -1)
    {
        atk_object_remove_property_change_handler (ATKOBJECT (self), id);
        return NULL;
    }

    return PyInt_FromLong (PyDict_Size (self->prop_handlers));
}

/**
 * Wrapper for atk_object_remove_property_change_handler().
 */
static PyObject*
_atkobject_remove_property_change_handler (PyAtkObject *self, PyObject *args)
{
    int id = 0;

    debug ("_atkobject_remove_property_change_handler\n");

    if (!PyArg_ParseTuple (args, "i:remove_property_change_handler", &id))
        return NULL;
    if (PyDict_DelItem (self->prop_handlers, PyInt_FromLong (id)) == -1)
        return NULL;
    Py_RETURN_NONE;
}

/**
 * Wrapper for atk_object_notify_state_change().
 */
static PyObject*
_atkobject_notify_state_change (PyAtkObject *self, PyObject *args)
{
    AtkState state;
    int val;

    debug ("_atkobject_notify_state_change\n");

    if (!PyArg_ParseTuple (args, "ii:notify_state_changed", &state, &val))
        return NULL;

    atk_object_notify_state_change (ATKOBJECT (self), state, val);
    Py_RETURN_NONE;
}

/**
 * Wrapper for atk_object_add_relationship().
 */
static PyObject*
_atkobject_add_relationship (PyAtkObject *self, PyObject *args)
{
    PyObject *target;
    AtkRelationType type;

    debug ("_atkobject_add_relationship\n");

    if (!PyArg_ParseTuple (args, "iO:add_relationship", &type, &target))
        return NULL;

    if (!PyObject_TypeCheck (target, &PyAtkObject_Type))
    {
        PyErr_SetString (PyExc_TypeError, "argument 2 must be an AtkObject");
        return NULL;
    }

    if (atk_object_add_relationship (ATKOBJECT (self), type,
                                     ATKOBJECT (target)))
        Py_RETURN_TRUE;
    Py_RETURN_FALSE;
}

/**
 * Wrapper for atk_object_remove_relationship().
 */
static PyObject*
_atkobject_remove_relationship (PyAtkObject *self, PyObject *args)
{
    PyObject *target;
    AtkRelationType type;

    debug ("_atkobject_remove_relationship\n");

    if (!PyArg_ParseTuple (args, "iO:remove_relationship", &type, &target))
        return NULL;

    if (!PyObject_TypeCheck (target, &PyAtkObject_Type))
    {
        PyErr_SetString (PyExc_TypeError, "argument 2 must be an AtkObject");
        return NULL;
    }

    if (atk_object_remove_relationship (ATKOBJECT (self), type,
                                        ATKOBJECT (target)))
        Py_RETURN_TRUE;
    Py_RETURN_FALSE;
}

/**
 * Emits signals on the registered listener hooks.
 */
static PyObject*
_atkobject_emit (PyAtkObject *self, PyObject *args)
{
    PyObject *dict = NULL;
    PyObject *obj = NULL;
    PyObject *list = NULL;
    int size = 0;
    guint id = 0;
    char *type = NULL;
    GValue *value = NULL;
    GSignalInvocationHint hint;
    
    debug ("_atkobject_emit\n");

    if (!PyArg_ParseTuple (args, "s:emit", &type))
        return NULL;

#ifdef DEBUG
    printf ("DEBUG: Received signal: %s\n", type);
#endif

    id = atkutil_lookup_signal (type);
    if (!id) /* id == 0 */
    {
        PyErr_Format (PyExc_KeyError, "Signal '%s' does not exist.", type);
        return NULL;
    }

    dict = PyDict_GetItemString (atkutil_get_listeners (), type);
    if (!dict)
    {
        PyErr_Format (PyExc_KeyError, "Key '%s' does not exist.", type);
        return NULL;
    }

    list = PyDict_Values (dict);
    size = PyList_Size (list);

    /* Iterate over the attached emission hooks and invoke them. */
    for (int i = 0; i < size; i++)
    {
        obj = PyList_GetItem (list, i);

        /* Create the args for the GSignalEmissionHook. */
        hint.signal_id = id;
        hint.detail = g_quark_from_string (type);
        hint.run_type = G_SIGNAL_RUN_FIRST;

        /* Create the arguments for the hook. */
        value = g_new0 (GValue, 1);
        g_value_init (value, G_TYPE_FROM_INSTANCE (ATKOBJECT (self)));
        g_value_set_instance (value, ATKOBJECT (self));

        /* Invoke it. */
        ((GSignalEmissionHook) PyCObject_AsVoidPtr (obj)) (&hint, 1, value,
                                                           NULL);
        g_free (value);
    }
    Py_DECREF (list);
    Py_RETURN_NONE;
}

/**
 * Interface for AtkObjectClass->get_n_children.
 */
static int
_class_get_n_children (AtkObject *obj)
{
    debug ("_class_get_n_children\n");

    GList *list = g_object_get_data (G_OBJECT (obj), PAPI_CHILDREN);
    if (list)
        return g_list_length (list);
    return 0;
}

/**
 * Interface for AtkObjectClass->ref_child.
 */
static AtkObject*
_class_ref_child (AtkObject *obj, int i)
{
    AtkObject *val = NULL;
    GList *list = NULL;

    debug ("_class_ref_child\n");

    list = g_object_get_data (G_OBJECT (obj), PAPI_CHILDREN);
    if (list)
    {
        val = g_list_nth_data (list, (guint) i);
        if (!val)
            return NULL;
    }
    if (val)
        g_object_ref (val);
    return val;
}

/**
 * Interface for AtkObjectClass->get_index_in_parent.
 */
static int
_class_get_index_in_parent (AtkObject *obj)
{
    GList *list;
    AtkObject *parent;

    debug ("_class_get_index_in_parent\n");

    parent = atk_object_get_parent (obj);
    if (!parent)
        return -1;

    list = g_object_get_data (G_OBJECT (parent), PAPI_CHILDREN);
    if (list)
        return g_list_index (list, obj);
    return -1;
}

/**
 * Generic callback handler for property changes, which invokes the
 * user-defined python handlers.
 */
static void
_atkobject_handle_property_change (AtkObject *obj, AtkPropertyValues *value)
{
    PyObject *callback = NULL;
    PyObject *retval = NULL;
    PyObject *name = NULL;
    PyObject *oldval = NULL;
    PyObject *newval = NULL;
    int pos = 0;
    PyAtkObject *atkobj = (PyAtkObject *) g_object_get_data (G_OBJECT (obj),
                                                             PAPI_PYOBJECT);

    while (PyDict_Next (atkobj->prop_handlers, &pos, NULL, &callback))
    {
        name = PyString_FromString (value->property_name);
        oldval = pyobject_from_gvalue (&(value->old_value));
        newval = pyobject_from_gvalue (&(value->new_value));

        retval = PyObject_CallFunctionObjArgs (callback, name, oldval, newval,
                                               NULL);
        Py_DECREF (name);
        Py_DECREF (oldval);
        Py_DECREF (newval);
        Py_DECREF (retval);
    }
}

/**
 * Bind the different AtkObjectClass interfaces to their needed
 * implementations.
 */
void
atkobjectclass_init (AtkObjectClass *class)
{
    debug ("Hooking AtkObjectClass interfaces.\n");

    class->get_n_children = _class_get_n_children;
    class->ref_child = _class_ref_child;
    class->get_index_in_parent = _class_get_index_in_parent;
}
