aboutsummaryrefslogblamecommitdiffstats
path: root/libgnomecanvas/gailcanvasitem.c
blob: 53e0e4666c816c69879d8fd895174be5384e1fab (plain) (tree)


















                                                                    



                    







                                                                                    
                                                                                     
                                                                                      
                                                                                     
                                                                                      

                                                                                       
                                                                                     
                                                                                        
                                                                                     




                                                                                           


                                                                                      
                                                                                           

                                                                                      
                                                                                         
                                                                                     
                                                                                          
 











                                                                                            
           













                                                          

                                            
 
                                                                           

                                                           
                                                      






                                                        











































                                                                                 

                           
 


                                                                              
 



                                 




















                                                                               

                                               








                                                         
                                                                                    










                                                             
                                                  






                                                                     
                                                               







                                                                     

















                                                                      
                                                            









                                                                         
                                                         

           





                                                   








                                                      



                                           






                                                      
                       




                                                     
                  








                                                             



                                          






                                                                         
                                                   

                                         












                                                                         
                                                    




                                                                       
                                                     

























                                                                 
                                                        



                                                                   
               

                                         
                       
 

                                            


           

                                        
 

                        
 
                          

                                                                  
                                                                 
                                


                                                                   

                                                                
 



                                           


               
                                         
                                               

                    
                    


                                     

                                          
     
                               
 

                        

                                                          

                                                                     






                     
/* 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.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#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);

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 (gail_canvas_item_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);

  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 (gail_canvas_item_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;
}