/* GAIL - The GNOME Accessibility Implementation Library
* Copyright 2001 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 <math.h>
#include <gtk/gtk.h>
#include <libgnomecanvas/gnome-canvas.h>
#include <libgnomecanvas/gnome-canvas-util.h>
#include "gailcanvasitem.h"
#include <libgail-util/gailmisc.h>
static void gail_canvas_item_initialize (AtkObject *obj,
gpointer data);
static AtkObject* gail_canvas_item_get_parent (AtkObject *obj);
static gint gail_canvas_item_get_index_in_parent (AtkObject *obj);
static AtkStateSet* gail_canvas_item_ref_state_set (AtkObject *obj);
static void gail_canvas_item_component_interface_init (AtkComponentIface *iface);
static guint gail_canvas_item_add_focus_handler (AtkComponent *component,
AtkFocusHandler handler);
static void gail_canvas_item_get_extents (AtkComponent *component,
gint *x,
gint *y,
gint *width,
gint *height,
AtkCoordType coord_type);
static gint gail_canvas_item_get_mdi_zorder (AtkComponent *component);
static gboolean gail_canvas_item_grab_focus (AtkComponent *component);
static void gail_canvas_item_remove_focus_handler (AtkComponent *component,
guint handler_id);
static gboolean is_item_on_screen (GnomeCanvasItem *item);
static void get_item_extents (GnomeCanvasItem *item,
GdkRectangle *extents);
static gboolean is_item_in_window (GnomeCanvasItem *item,
const GdkRectangle *extents);
static AtkGObjectAccessibleClass *parent_class = NULL;
G_DEFINE_TYPE_WITH_CODE (GailCanvasItem,
gail_canvas_item,
ATK_TYPE_GOBJECT_ACCESSIBLE,
G_IMPLEMENT_INTERFACE (ATK_TYPE_COMPONENT,
gail_canvas_item_component_interface_init));
static void
gail_canvas_item_init (GailCanvasItem *foo)
{
;
}
AtkObject*
gail_canvas_item_new (GObject *obj)
{
gpointer object;
AtkObject *atk_object;
g_return_val_if_fail (GNOME_IS_CANVAS_ITEM (obj), NULL);
object = g_object_new (GAIL_TYPE_CANVAS_ITEM, NULL);
atk_object = ATK_OBJECT (object);
atk_object_initialize (atk_object, obj);
atk_object->role = ATK_ROLE_UNKNOWN;
return atk_object;
}
static void
gail_canvas_item_initialize (AtkObject *obj,
gpointer data)
{
ATK_OBJECT_CLASS (parent_class)->initialize (obj, data);
g_object_set_data (G_OBJECT (obj), "atk-component-layer",
GINT_TO_POINTER (ATK_LAYER_MDI));
}
static void
gail_canvas_item_class_init (GailCanvasItemClass *klass)
{
AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
parent_class = g_type_class_peek_parent (klass);
class->get_parent = gail_canvas_item_get_parent;
class->get_index_in_parent = gail_canvas_item_get_index_in_parent;
class->ref_state_set = gail_canvas_item_ref_state_set;
class->initialize = gail_canvas_item_initialize;
}
static AtkObject *
gail_canvas_item_get_parent (AtkObject *obj)
{
AtkGObjectAccessible *atk_gobj;
GObject *g_obj;
GnomeCanvasItem *item;
g_return_val_if_fail (GAIL_IS_CANVAS_ITEM (obj), NULL);
if (obj->accessible_parent)
return obj->accessible_parent;
atk_gobj = ATK_GOBJECT_ACCESSIBLE (obj);
g_obj = atk_gobject_accessible_get_object (atk_gobj);
if (g_obj == NULL)
/* Object is defunct */
return NULL;
item = GNOME_CANVAS_ITEM (g_obj);
if (item->parent)
return atk_gobject_accessible_for_object (G_OBJECT (item->parent));
else
return gtk_widget_get_accessible (GTK_WIDGET (item->canvas));
}
static gint
gail_canvas_item_get_index_in_parent (AtkObject *obj)
{
AtkGObjectAccessible *atk_gobj;
GObject *g_obj;
GnomeCanvasItem *item;
g_return_val_if_fail (GAIL_IS_CANVAS_ITEM (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;
}
atk_gobj = ATK_GOBJECT_ACCESSIBLE (obj);
g_obj = atk_gobject_accessible_get_object (atk_gobj);
if (g_obj == NULL)
/* Object is defunct */
return -1;
item = GNOME_CANVAS_ITEM (g_obj);
if (item->parent)
{
return g_list_index (GNOME_CANVAS_GROUP (item->parent)->item_list, item);
}
else
{
g_return_val_if_fail (item->canvas->root == item, -1);
return 0;
}
}
static AtkStateSet*
gail_canvas_item_ref_state_set (AtkObject *obj)
{
AtkGObjectAccessible *atk_gobj;
GObject *g_obj;
GnomeCanvasItem *item;
AtkStateSet *state_set;
g_return_val_if_fail (GAIL_IS_CANVAS_ITEM (obj), NULL);
atk_gobj = ATK_GOBJECT_ACCESSIBLE (obj);
state_set = ATK_OBJECT_CLASS (parent_class)->ref_state_set (obj);
g_obj = atk_gobject_accessible_get_object (atk_gobj);
if (g_obj == NULL)
{
/* Object is defunct */
atk_state_set_add_state (state_set, ATK_STATE_DEFUNCT);
}
else
{
item = GNOME_CANVAS_ITEM (g_obj);
if (item->flags & GNOME_CANVAS_ITEM_VISIBLE)
{
atk_state_set_add_state (state_set, ATK_STATE_VISIBLE);
if (is_item_on_screen (item))
{
atk_state_set_add_state (state_set, ATK_STATE_SHOWING);
}
}
if (gtk_widget_get_can_focus (GTK_WIDGET (item->canvas)))
{
atk_state_set_add_state (state_set, ATK_STATE_FOCUSABLE);
if (item->canvas->focused_item == item)
{
atk_state_set_add_state (state_set, ATK_STATE_FOCUSED);
}
}
}
return state_set;
}
static void
gail_canvas_item_component_interface_init (AtkComponentIface *iface)
{
g_return_if_fail (iface != NULL);
iface->add_focus_handler = gail_canvas_item_add_focus_handler;
iface->get_extents = gail_canvas_item_get_extents;
iface->get_mdi_zorder = gail_canvas_item_get_mdi_zorder;
iface->grab_focus = gail_canvas_item_grab_focus;
iface->remove_focus_handler = gail_canvas_item_remove_focus_handler;
}
static guint
gail_canvas_item_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
gail_canvas_item_get_extents (AtkComponent *component,
gint *x,
gint *y,
gint *width,
gint *height,
AtkCoordType coord_type)
{
AtkGObjectAccessible *atk_gobj;
GObject *obj;
GnomeCanvasItem *item;
gint window_x, window_y;
gint toplevel_x, toplevel_y;
GdkRectangle extents;
g_return_if_fail (GAIL_IS_CANVAS_ITEM (component));
atk_gobj = ATK_GOBJECT_ACCESSIBLE (component);
obj = atk_gobject_accessible_get_object (atk_gobj);
if (obj == NULL)
/* item is defunct */
return;
/* Get the GnomeCanvasItem */
item = GNOME_CANVAS_ITEM (obj);
/* If this item has no parent canvas, something's broken */
g_return_if_fail (GTK_IS_WIDGET (item->canvas));
get_item_extents (item, &extents);
*width = extents.width;
*height = extents.height;
if (!is_item_in_window (item, &extents))
{
*x = G_MININT;
*y = G_MININT;
return;
}
gail_misc_get_origins (GTK_WIDGET (item->canvas), &window_x, &window_y,
&toplevel_x, &toplevel_y);
*x = extents.x + window_x - toplevel_x;
*y = extents.y + window_y - toplevel_y;
/* If screen coordinates are requested, modify x and y appropriately */
if (coord_type == ATK_XY_SCREEN)
{
*x += toplevel_x;
*y += toplevel_y;
}
return;
}
static gint
gail_canvas_item_get_mdi_zorder (AtkComponent *component)
{
g_return_val_if_fail (ATK_OBJECT (component), -1);
return gail_canvas_item_get_index_in_parent (ATK_OBJECT (component));
}
static gboolean
gail_canvas_item_grab_focus (AtkComponent *component)
{
AtkGObjectAccessible *atk_gobj;
GObject *obj;
GnomeCanvasItem *item;
GtkWidget *toplevel;
g_return_val_if_fail (GAIL_IS_CANVAS_ITEM (component), FALSE);
atk_gobj = ATK_GOBJECT_ACCESSIBLE (component);
obj = atk_gobject_accessible_get_object (atk_gobj);
/* Get the GnomeCanvasItem */
item = GNOME_CANVAS_ITEM (obj);
if (item == NULL)
/* item is defunct */
return FALSE;
gnome_canvas_item_grab_focus (item);
toplevel = gtk_widget_get_toplevel (GTK_WIDGET (item->canvas));
if (gtk_widget_is_toplevel (toplevel))
gtk_window_present (GTK_WINDOW (toplevel));
return TRUE;
}
static void
gail_canvas_item_remove_focus_handler (AtkComponent *component,
guint handler_id)
{
g_signal_handler_disconnect (ATK_OBJECT (component), handler_id);
}
static gboolean
is_item_on_screen (GnomeCanvasItem *item)
{
GdkRectangle extents;
get_item_extents (item, &extents);
return is_item_in_window (item, &extents);
}
static void
get_item_extents (GnomeCanvasItem *item,
GdkRectangle *extents)
{
double x1, x2, y1, y2;
cairo_matrix_t i2c;
x1 = y1 = x2 = y2 = 0.0;
if (GNOME_CANVAS_ITEM_CLASS (G_OBJECT_GET_CLASS (item))->bounds)
GNOME_CANVAS_ITEM_CLASS (G_OBJECT_GET_CLASS (item))->bounds (
item, &x1, &y1, &x2, &y2);
/* Get the item coordinates -> canvas pixel coordinates affine */
gnome_canvas_item_i2c_matrix (item, &i2c);
gnome_canvas_matrix_transform_rect (&i2c, &x1, &y1, &x2, &y2);
extents->x = floor (x1);
extents->y = floor (y1);
extents->width = ceil (x2) - extents->x;
extents->height = ceil (y2) - extents->y;
}
static gboolean
is_item_in_window (GnomeCanvasItem *item,
const GdkRectangle *extents)
{
GtkWidget *widget;
GdkWindow *window;
gboolean retval;
widget = GTK_WIDGET (item->canvas);
window = gtk_widget_get_window (widget);
if (window)
{
GdkRectangle window_rect;
window_rect.x = 0;
window_rect.y = 0;
window_rect.width = gdk_window_get_width (window);
window_rect.height = gdk_window_get_height (window);
retval = gdk_rectangle_intersect (extents, &window_rect, NULL);
}
else
{
retval = FALSE;
}
return retval;
}