]> Pileus Git - ~andy/gtk/blob - gtk/a11y/gailwidget.c
2efdebf122e6c0a65867e6c5a4b82e2139a4d7dd
[~andy/gtk] / gtk / a11y / gailwidget.c
1 /* GAIL - The GNOME Accessibility Implementation Library
2  * Copyright 2001, 2002, 2003 Sun Microsystems Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include "config.h"
21
22 #include <string.h>
23
24 #include <gtk/gtk.h>
25 #ifdef GDK_WINDOWING_X11
26 #include <gdk/x11/gdkx.h>
27 #endif
28 #include "gailwidget.h"
29 #include "gtknotebookpageaccessible.h"
30
31 extern GtkWidget *focus_widget;
32
33 static void gail_widget_class_init (GailWidgetClass *klass);
34 static void gail_widget_init                     (GailWidget       *accessible);
35 static void gail_widget_connect_widget_destroyed (GtkAccessible    *accessible);
36 static void gail_widget_destroyed                (GtkWidget        *widget,
37                                                   GtkAccessible    *accessible);
38
39 static const gchar* gail_widget_get_description (AtkObject *accessible);
40 static AtkObject* gail_widget_get_parent (AtkObject *accessible);
41 static AtkStateSet* gail_widget_ref_state_set (AtkObject *accessible);
42 static AtkRelationSet* gail_widget_ref_relation_set (AtkObject *accessible);
43 static gint gail_widget_get_index_in_parent (AtkObject *accessible);
44
45 static void atk_component_interface_init (AtkComponentIface *iface);
46
47 static guint    gail_widget_add_focus_handler
48                                            (AtkComponent    *component,
49                                             AtkFocusHandler handler);
50
51 static void     gail_widget_get_extents    (AtkComponent    *component,
52                                             gint            *x,
53                                             gint            *y,
54                                             gint            *width,
55                                             gint            *height,
56                                             AtkCoordType    coord_type);
57
58 static void     gail_widget_get_size       (AtkComponent    *component,
59                                             gint            *width,
60                                             gint            *height);
61
62 static AtkLayer gail_widget_get_layer      (AtkComponent *component);
63
64 static gboolean gail_widget_grab_focus     (AtkComponent    *component);
65
66
67 static void     gail_widget_remove_focus_handler 
68                                            (AtkComponent    *component,
69                                             guint           handler_id);
70
71 static gboolean gail_widget_set_extents    (AtkComponent    *component,
72                                             gint            x,
73                                             gint            y,
74                                             gint            width,
75                                             gint            height,
76                                             AtkCoordType    coord_type);
77
78 static gboolean gail_widget_set_position   (AtkComponent    *component,
79                                             gint            x,
80                                             gint            y,
81                                             AtkCoordType    coord_type);
82
83 static gboolean gail_widget_set_size       (AtkComponent    *component,
84                                             gint            width,
85                                             gint            height);
86
87 static gint       gail_widget_map_gtk            (GtkWidget     *widget);
88 static void       gail_widget_real_notify_gtk    (GObject       *obj,
89                                                   GParamSpec    *pspec);
90 static void       gail_widget_notify_gtk         (GObject       *obj,
91                                                   GParamSpec    *pspec);
92 static gboolean   gail_widget_focus_gtk          (GtkWidget     *widget,
93                                                   GdkEventFocus *event);
94 static gboolean   gail_widget_real_focus_gtk     (GtkWidget     *widget,
95                                                   GdkEventFocus *event);
96 static void       gail_widget_size_allocate_gtk  (GtkWidget     *widget,
97                                                   GtkAllocation *allocation);
98
99 static void       gail_widget_focus_event        (AtkObject     *obj,
100                                                   gboolean      focus_in);
101
102 static void       gail_widget_real_initialize    (AtkObject     *obj,
103                                                   gpointer      data);
104 static AtkAttributeSet *gail_widget_get_attributes(AtkObject *obj);
105 static GtkWidget* gail_widget_find_viewport      (GtkWidget     *widget);
106 static gboolean   gail_widget_on_screen          (GtkWidget     *widget);
107 static gboolean   gail_widget_all_parents_visible(GtkWidget     *widget);
108
109 G_DEFINE_TYPE_WITH_CODE (GailWidget, gail_widget, GTK_TYPE_ACCESSIBLE,
110                          G_IMPLEMENT_INTERFACE (ATK_TYPE_COMPONENT, atk_component_interface_init))
111
112 static void
113 gail_widget_class_init (GailWidgetClass *klass)
114 {
115   AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
116   GtkAccessibleClass *accessible_class = GTK_ACCESSIBLE_CLASS (klass);
117
118   klass->notify_gtk = gail_widget_real_notify_gtk;
119   klass->focus_gtk = gail_widget_real_focus_gtk;
120
121   accessible_class->connect_widget_destroyed = gail_widget_connect_widget_destroyed;
122
123   class->get_description = gail_widget_get_description;
124   class->get_parent = gail_widget_get_parent;
125   class->ref_relation_set = gail_widget_ref_relation_set;
126   class->ref_state_set = gail_widget_ref_state_set;
127   class->get_index_in_parent = gail_widget_get_index_in_parent;
128   class->initialize = gail_widget_real_initialize;
129   class->get_attributes = gail_widget_get_attributes;
130 }
131
132 static void
133 gail_widget_init (GailWidget *accessible)
134 {
135 }
136
137 /**
138  * This function  specifies the GtkWidget for which the GailWidget was created 
139  * and specifies a handler to be called when the GtkWidget is destroyed.
140  **/
141 static void 
142 gail_widget_real_initialize (AtkObject *obj,
143                              gpointer  data)
144 {
145   GtkAccessible *accessible;
146   GtkWidget *widget;
147
148   g_return_if_fail (GTK_IS_WIDGET (data));
149
150   widget = GTK_WIDGET (data);
151
152   accessible = GTK_ACCESSIBLE (obj);
153   gtk_accessible_set_widget (accessible, widget);
154   gtk_accessible_connect_widget_destroyed (accessible);
155   g_signal_connect_after (widget,
156                           "focus-in-event",
157                           G_CALLBACK (gail_widget_focus_gtk),
158                           NULL);
159   g_signal_connect_after (widget,
160                           "focus-out-event",
161                           G_CALLBACK (gail_widget_focus_gtk),
162                           NULL);
163   g_signal_connect (widget,
164                     "notify",
165                     G_CALLBACK (gail_widget_notify_gtk),
166                     NULL);
167   g_signal_connect (widget,
168                     "size_allocate",
169                     G_CALLBACK (gail_widget_size_allocate_gtk),
170                     NULL);
171   atk_component_add_focus_handler (ATK_COMPONENT (accessible),
172                                    gail_widget_focus_event);
173   /*
174    * Add signal handlers for GTK signals required to support property changes
175    */
176   g_signal_connect (widget,
177                     "map",
178                     G_CALLBACK (gail_widget_map_gtk),
179                     NULL);
180   g_signal_connect (widget,
181                     "unmap",
182                     G_CALLBACK (gail_widget_map_gtk),
183                     NULL);
184   g_object_set_data (G_OBJECT (obj), "atk-component-layer",
185                      GINT_TO_POINTER (ATK_LAYER_WIDGET));
186
187   obj->role = ATK_ROLE_UNKNOWN;
188 }
189
190 /*
191  * This function specifies the function to be called when the widget
192  * is destroyed
193  */
194 static void
195 gail_widget_connect_widget_destroyed (GtkAccessible *accessible)
196 {
197   GtkWidget *widget;
198
199   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
200   if (widget)
201     {
202       g_signal_connect_after (widget,
203                               "destroy",
204                               G_CALLBACK (gail_widget_destroyed),
205                               accessible);
206     }
207 }
208
209 /*
210  * This function is called when the widget is destroyed.
211  * It sets the widget field in the GtkAccessible structure to NULL
212  * and emits a state-change signal for the state ATK_STATE_DEFUNCT
213  */
214 static void 
215 gail_widget_destroyed (GtkWidget     *widget,
216                        GtkAccessible *accessible)
217 {
218   gtk_accessible_set_widget (accessible, NULL);
219   atk_object_notify_state_change (ATK_OBJECT (accessible), ATK_STATE_DEFUNCT,
220                                   TRUE);
221 }
222
223 static const gchar*
224 gail_widget_get_description (AtkObject *accessible)
225 {
226   if (accessible->description)
227     return accessible->description;
228   else
229     {
230       GtkWidget *widget;
231
232       /* Get the tooltip from the widget */
233       widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
234       if (widget == NULL)
235         /*
236          * Object is defunct
237          */
238         return NULL;
239  
240       return gtk_widget_get_tooltip_text (widget);
241     }
242 }
243
244 static AtkObject* 
245 gail_widget_get_parent (AtkObject *accessible)
246 {
247   AtkObject *parent;
248
249   parent = accessible->accessible_parent;
250
251   if (parent != NULL)
252     g_return_val_if_fail (ATK_IS_OBJECT (parent), NULL);
253   else
254     {
255       GtkWidget *widget, *parent_widget;
256
257       widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
258       if (widget == NULL)
259         return NULL;
260
261       parent_widget = gtk_widget_get_parent (widget);
262       if (parent_widget == NULL)
263         return NULL;
264
265       /*
266        * For a widget whose parent is a GtkNoteBook, we return the
267        * accessible object corresponding the GtkNotebookPage containing
268        * the widget as the accessible parent.
269        */
270       if (GTK_IS_NOTEBOOK (parent_widget))
271         {
272           gint page_num;
273           GtkWidget *child;
274           GtkNotebook *notebook;
275
276           page_num = 0;
277           notebook = GTK_NOTEBOOK (parent_widget);
278           while (TRUE)
279             {
280               child = gtk_notebook_get_nth_page (notebook, page_num);
281               if (!child)
282                 break;
283               if (child == widget)
284                 {
285                   parent = gtk_widget_get_accessible (parent_widget);
286                   parent = atk_object_ref_accessible_child (parent, page_num);
287                   g_object_unref (parent);
288                   return parent;
289                 }
290               page_num++;
291             }
292         }
293
294       parent = gtk_widget_get_accessible (parent_widget);
295     }
296   return parent;
297 }
298
299 static GtkWidget*
300 find_label (GtkWidget *widget)
301 {
302   GList *labels;
303   GtkWidget *label;
304   GtkWidget *temp_widget;
305
306   labels = gtk_widget_list_mnemonic_labels (widget);
307   label = NULL;
308   if (labels)
309     {
310       if (labels->data)
311         {
312           if (labels->next)
313             {
314               g_warning ("Widget (%s) has more than one label", G_OBJECT_TYPE_NAME (widget));
315               
316             }
317           else
318             {
319               label = labels->data;
320             }
321         }
322       g_list_free (labels);
323     }
324
325   /*
326    * Ignore a label within a button; bug #136602
327    */
328   if (label && GTK_IS_BUTTON (widget))
329     {
330       temp_widget = label;
331       while (temp_widget)
332         {
333           if (temp_widget == widget)
334             {
335               label = NULL;
336               break;
337             }
338           temp_widget = gtk_widget_get_parent (temp_widget);
339         }
340     } 
341   return label;
342 }
343
344 static AtkRelationSet*
345 gail_widget_ref_relation_set (AtkObject *obj)
346 {
347   GtkWidget *widget;
348   AtkRelationSet *relation_set;
349   GtkWidget *label;
350   AtkObject *array[1];
351   AtkRelation* relation;
352
353   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
354   if (widget == NULL)
355     return NULL;
356
357   relation_set = ATK_OBJECT_CLASS (gail_widget_parent_class)->ref_relation_set (obj);
358
359   if (GTK_IS_BOX (widget))
360       /*
361        * Do not report labelled-by for a GtkBox which could be a 
362        * GnomeFileEntry.
363        */
364     return relation_set;
365
366   if (!atk_relation_set_contains (relation_set, ATK_RELATION_LABELLED_BY))
367     {
368       label = find_label (widget);
369       if (label == NULL)
370         {
371           if (GTK_IS_BUTTON (widget))
372             /*
373              * Handle the case where GnomeIconEntry is the mnemonic widget.
374              * The GtkButton which is a grandchild of the GnomeIconEntry
375              * should really be the mnemonic widget. See bug #133967.
376              */
377             {
378               GtkWidget *temp_widget;
379
380               temp_widget = gtk_widget_get_parent (widget);
381
382               if (GTK_IS_ALIGNMENT (temp_widget))
383                 {
384                   temp_widget = gtk_widget_get_parent (temp_widget);
385                   if (GTK_IS_BOX (temp_widget))
386                     {
387                       label = find_label (temp_widget);
388                  
389                       if (!label)
390                         label = find_label (gtk_widget_get_parent (temp_widget));
391                     }
392                 }
393             }
394           else if (GTK_IS_COMBO_BOX (widget))
395             /*
396              * Handle the case when GtkFileChooserButton is the mnemonic
397              * widget.  The GtkComboBox which is a child of the
398              * GtkFileChooserButton should be the mnemonic widget.
399              * See bug #359843.
400              */
401             {
402               GtkWidget *temp_widget;
403
404               temp_widget = gtk_widget_get_parent (widget);
405               if (GTK_IS_BOX (temp_widget))
406                 {
407                   label = find_label (temp_widget);
408                 }
409             }
410         }
411
412       if (label)
413         {
414           array [0] = gtk_widget_get_accessible (label);
415
416           relation = atk_relation_new (array, 1, ATK_RELATION_LABELLED_BY);
417           atk_relation_set_add (relation_set, relation);
418           g_object_unref (relation);
419         }
420     }
421
422   return relation_set;
423 }
424
425 static AtkStateSet*
426 gail_widget_ref_state_set (AtkObject *accessible)
427 {
428   GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
429   AtkStateSet *state_set;
430
431   state_set = ATK_OBJECT_CLASS (gail_widget_parent_class)->ref_state_set (accessible);
432
433   if (widget == NULL)
434     {
435       atk_state_set_add_state (state_set, ATK_STATE_DEFUNCT);
436     }
437   else
438     {
439       if (gtk_widget_is_sensitive (widget))
440         {
441           atk_state_set_add_state (state_set, ATK_STATE_SENSITIVE);
442           atk_state_set_add_state (state_set, ATK_STATE_ENABLED);
443         }
444   
445       if (gtk_widget_get_can_focus (widget))
446         {
447           atk_state_set_add_state (state_set, ATK_STATE_FOCUSABLE);
448         }
449       /*
450        * We do not currently generate notifications when an ATK object 
451        * corresponding to a GtkWidget changes visibility by being scrolled 
452        * on or off the screen.  The testcase for this is the main window 
453        * of the testgtk application in which a set of buttons in a GtkVBox 
454        * is in a scrooled window with a viewport.
455        *
456        * To generate the notifications we would need to do the following: 
457        * 1) Find the GtkViewPort among the antecendents of the objects
458        * 2) Create an accesible for the GtkViewPort
459        * 3) Connect to the value-changed signal on the viewport
460        * 4) When the signal is received we need to traverse the children 
461        * of the viewport and check whether the children are visible or not 
462        * visible; we may want to restrict this to the widgets for which 
463        * accessible objects have been created.
464        * 5) We probably need to store a variable on_screen in the 
465        * GailWidget data structure so we can determine whether the value has 
466        * changed.
467        */
468       if (gtk_widget_get_visible (widget))
469         {
470           atk_state_set_add_state (state_set, ATK_STATE_VISIBLE);
471           if (gail_widget_on_screen (widget) && gtk_widget_get_mapped (widget) &&
472               gail_widget_all_parents_visible (widget))
473             {
474               atk_state_set_add_state (state_set, ATK_STATE_SHOWING);
475             }
476         }
477   
478       if (gtk_widget_has_focus (widget) && (widget == focus_widget))
479         {
480           AtkObject *focus_obj;
481
482           focus_obj = g_object_get_data (G_OBJECT (accessible), "gail-focus-object");
483           if (focus_obj == NULL)
484             atk_state_set_add_state (state_set, ATK_STATE_FOCUSED);
485         }
486       if (gtk_widget_has_default (widget))
487         {
488           atk_state_set_add_state (state_set, ATK_STATE_DEFAULT);
489         }
490
491       if (GTK_IS_ORIENTABLE(widget))
492         switch (gtk_orientable_get_orientation (GTK_ORIENTABLE (widget)))
493           {
494           case GTK_ORIENTATION_HORIZONTAL:
495             atk_state_set_add_state (state_set, ATK_STATE_HORIZONTAL);
496             break;
497
498           case GTK_ORIENTATION_VERTICAL:
499             atk_state_set_add_state (state_set, ATK_STATE_VERTICAL);
500             break;
501           }
502     }
503   return state_set;
504 }
505
506 static gint
507 gail_widget_get_index_in_parent (AtkObject *accessible)
508 {
509   GtkWidget *widget;
510   GtkWidget *parent_widget;
511   gint index;
512   GList *children;
513
514   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
515
516   if (widget == NULL)
517     /*
518      * State is defunct
519      */
520     return -1;
521
522   if (accessible->accessible_parent)
523     {
524       AtkObject *parent;
525
526       parent = accessible->accessible_parent;
527
528       if (GTK_IS_NOTEBOOK_PAGE_ACCESSIBLE (parent))
529         return 0;
530       else
531         {
532           gint n_children, i;
533           gboolean found = FALSE;
534
535           n_children = atk_object_get_n_accessible_children (parent);
536           for (i = 0; i < n_children; i++)
537             {
538               AtkObject *child;
539
540               child = atk_object_ref_accessible_child (parent, i);
541               if (child == accessible)
542                 found = TRUE;
543
544               g_object_unref (child); 
545               if (found)
546                 return i;
547             }
548         }
549     }
550
551   if (!GTK_IS_WIDGET (widget))
552     return -1;
553   parent_widget = gtk_widget_get_parent (widget);
554   if (!GTK_IS_CONTAINER (parent_widget))
555     return -1;
556
557   children = gtk_container_get_children (GTK_CONTAINER (parent_widget));
558
559   index = g_list_index (children, widget);
560   g_list_free (children);
561   return index;  
562 }
563
564 static void 
565 atk_component_interface_init (AtkComponentIface *iface)
566 {
567   /*
568    * Use default implementation for contains and get_position
569    */
570   iface->add_focus_handler = gail_widget_add_focus_handler;
571   iface->get_extents = gail_widget_get_extents;
572   iface->get_size = gail_widget_get_size;
573   iface->get_layer = gail_widget_get_layer;
574   iface->grab_focus = gail_widget_grab_focus;
575   iface->remove_focus_handler = gail_widget_remove_focus_handler;
576   iface->set_extents = gail_widget_set_extents;
577   iface->set_position = gail_widget_set_position;
578   iface->set_size = gail_widget_set_size;
579 }
580
581 static guint 
582 gail_widget_add_focus_handler (AtkComponent    *component,
583                                AtkFocusHandler handler)
584 {
585   GSignalMatchType match_type;
586   gulong ret;
587   guint signal_id;
588
589   match_type = G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_FUNC;
590   signal_id = g_signal_lookup ("focus-event", ATK_TYPE_OBJECT);
591
592   ret = g_signal_handler_find (component, match_type, signal_id, 0, NULL,
593                                (gpointer) handler, NULL);
594   if (!ret)
595     {
596       return g_signal_connect_closure_by_id (component, 
597                                              signal_id, 0,
598                                              g_cclosure_new (
599                                              G_CALLBACK (handler), NULL,
600                                              (GClosureNotify) NULL),
601                                              FALSE);
602     }
603   else
604     {
605       return 0;
606     }
607 }
608
609 static void 
610 gail_widget_get_extents (AtkComponent   *component,
611                          gint           *x,
612                          gint           *y,
613                          gint           *width,
614                          gint           *height,
615                          AtkCoordType   coord_type)
616 {
617   GtkAllocation allocation;
618   GdkWindow *window;
619   gint x_window, y_window;
620   gint x_toplevel, y_toplevel;
621   GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component));
622
623   if (widget == NULL)
624     return;
625
626   gtk_widget_get_allocation (widget, &allocation);
627   *width = allocation.width;
628   *height = allocation.height;
629   if (!gail_widget_on_screen (widget) || (!gtk_widget_is_drawable (widget)))
630     {
631       *x = G_MININT;
632       *y = G_MININT;
633       return;
634     }
635
636   if (gtk_widget_get_parent (widget))
637     {
638       *x = allocation.x;
639       *y = allocation.y;
640       window = gtk_widget_get_parent_window (widget);
641     }
642   else
643     {
644       *x = 0;
645       *y = 0;
646       window = gtk_widget_get_window (widget);
647     }
648   gdk_window_get_origin (window, &x_window, &y_window);
649   *x += x_window;
650   *y += y_window;
651
652  
653  if (coord_type == ATK_XY_WINDOW) 
654     { 
655       window = gdk_window_get_toplevel (gtk_widget_get_window (widget));
656       gdk_window_get_origin (window, &x_toplevel, &y_toplevel);
657
658       *x -= x_toplevel;
659       *y -= y_toplevel;
660     }
661 }
662
663 static void 
664 gail_widget_get_size (AtkComponent   *component,
665                       gint           *width,
666                       gint           *height)
667 {
668   GtkAllocation allocation;
669   GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component));
670
671   if (widget == NULL)
672     return;
673
674   gtk_widget_get_allocation (widget, &allocation);
675   *width = allocation.width;
676   *height = allocation.height;
677 }
678
679 static AtkLayer
680 gail_widget_get_layer (AtkComponent *component)
681 {
682   gint layer;
683   layer = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (component), "atk-component-layer"));
684
685   return (AtkLayer) layer;
686 }
687
688 static gboolean
689 gail_widget_grab_focus (AtkComponent   *component)
690 {
691   GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component));
692   GtkWidget *toplevel;
693
694   if (!widget)
695     return FALSE;
696
697   if (gtk_widget_get_can_focus (widget))
698     {
699       gtk_widget_grab_focus (widget);
700       toplevel = gtk_widget_get_toplevel (widget);
701       if (gtk_widget_is_toplevel (toplevel))
702         {
703 #ifdef GDK_WINDOWING_X11
704           gtk_window_present_with_time (GTK_WINDOW (toplevel),
705           gdk_x11_get_server_time (gtk_widget_get_window (widget)));
706 #else
707           gtk_window_present (GTK_WINDOW (toplevel));
708 #endif
709         }
710       return TRUE;
711     }
712   else
713     return FALSE;
714 }
715
716 static void 
717 gail_widget_remove_focus_handler (AtkComponent   *component,
718                                   guint          handler_id)
719 {
720   g_signal_handler_disconnect (component, handler_id);
721 }
722
723 static gboolean 
724 gail_widget_set_extents (AtkComponent   *component,
725                          gint           x,
726                          gint           y,
727                          gint           width,
728                          gint           height,
729                          AtkCoordType   coord_type)
730 {
731   GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component));
732
733   if (widget == NULL)
734     return FALSE;
735
736   if (gtk_widget_is_toplevel (widget))
737     {
738       if (coord_type == ATK_XY_WINDOW)
739         {
740           gint x_current, y_current;
741           GdkWindow *window = gtk_widget_get_window (widget);
742
743           gdk_window_get_origin (window, &x_current, &y_current);
744           x_current += x;
745           y_current += y;
746           if (x_current < 0 || y_current < 0)
747             return FALSE;
748           else
749             {
750               gtk_window_move (GTK_WINDOW (widget), x_current, y_current);
751               gtk_widget_set_size_request (widget, width, height);
752               return TRUE;
753             }
754         }
755       else if (coord_type == ATK_XY_SCREEN)
756         {
757           gtk_window_move (GTK_WINDOW (widget), x, y);
758           gtk_widget_set_size_request (widget, width, height);
759           return TRUE;
760         }
761     }
762   return FALSE;
763 }
764
765 static gboolean
766 gail_widget_set_position (AtkComponent   *component,
767                           gint           x,
768                           gint           y,
769                           AtkCoordType   coord_type)
770 {
771   GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component));
772
773   if (widget == NULL)
774     return FALSE;
775
776   if (gtk_widget_is_toplevel (widget))
777     {
778       if (coord_type == ATK_XY_WINDOW)
779         {
780           gint x_current, y_current;
781           GdkWindow *window = gtk_widget_get_window (widget);
782
783           gdk_window_get_origin (window, &x_current, &y_current);
784           x_current += x;
785           y_current += y;
786           if (x_current < 0 || y_current < 0)
787             return FALSE;
788           else
789             {
790               gtk_window_move (GTK_WINDOW (widget), x_current, y_current);
791               return TRUE;
792             }
793         }
794       else if (coord_type == ATK_XY_SCREEN)
795         {
796           gtk_window_move (GTK_WINDOW (widget), x, y);
797           return TRUE;
798         }
799     }
800   return FALSE;
801 }
802
803 static gboolean 
804 gail_widget_set_size (AtkComponent   *component,
805                       gint           width,
806                       gint           height)
807 {
808   GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component));
809
810   if (widget == NULL)
811     return FALSE;
812
813   if (gtk_widget_is_toplevel (widget))
814     {
815       gtk_widget_set_size_request (widget, width, height);
816       return TRUE;
817     }
818   else
819    return FALSE;
820 }
821
822 /*
823  * This function is a signal handler for notify_in_event and focus_out_event
824  * signal which gets emitted on a GtkWidget.
825  */
826 static gboolean
827 gail_widget_focus_gtk (GtkWidget     *widget,
828                        GdkEventFocus *event)
829 {
830   GailWidget *gail_widget;
831   GailWidgetClass *klass;
832
833   gail_widget = GAIL_WIDGET (gtk_widget_get_accessible (widget));
834   klass = GAIL_WIDGET_GET_CLASS (gail_widget);
835   if (klass->focus_gtk)
836     return klass->focus_gtk (widget, event);
837   else
838     return FALSE;
839 }
840
841 /*
842  * This function is the signal handler defined for focus_in_event and
843  * focus_out_event got GailWidget.
844  *
845  * It emits a focus-event signal on the GailWidget.
846  */
847 static gboolean
848 gail_widget_real_focus_gtk (GtkWidget     *widget,
849                             GdkEventFocus *event)
850 {
851   AtkObject* accessible;
852   gboolean return_val;
853   return_val = FALSE;
854
855   accessible = gtk_widget_get_accessible (widget);
856   g_signal_emit_by_name (accessible, "focus_event", event->in, &return_val);
857   return FALSE;
858 }
859
860 static void
861 gail_widget_size_allocate_gtk (GtkWidget     *widget,
862                                GtkAllocation *allocation)
863 {
864   AtkObject* accessible;
865   AtkRectangle rect;
866
867   accessible = gtk_widget_get_accessible (widget);
868   if (ATK_IS_COMPONENT (accessible))
869     {
870       rect.x = allocation->x;
871       rect.y = allocation->y;
872       rect.width = allocation->width;
873       rect.height = allocation->height;
874       g_signal_emit_by_name (accessible, "bounds_changed", &rect);
875     }
876 }
877
878 /*
879  * This function is the signal handler defined for map and unmap signals.
880  */
881 static gint
882 gail_widget_map_gtk (GtkWidget     *widget)
883 {
884   AtkObject* accessible;
885
886   accessible = gtk_widget_get_accessible (widget);
887   atk_object_notify_state_change (accessible, ATK_STATE_SHOWING,
888                                   gtk_widget_get_mapped (widget));
889   return 1;
890 }
891
892 /*
893  * This function is a signal handler for notify signal which gets emitted 
894  * when a property changes value on the GtkWidget associated with the object.
895  *
896  * It calls a function for the GailWidget type
897  */
898 static void 
899 gail_widget_notify_gtk (GObject     *obj,
900                         GParamSpec  *pspec)
901 {
902   GailWidget *widget;
903   GailWidgetClass *klass;
904
905   widget = GAIL_WIDGET (gtk_widget_get_accessible (GTK_WIDGET (obj)));
906   klass = GAIL_WIDGET_GET_CLASS (widget);
907   if (klass->notify_gtk)
908     klass->notify_gtk (obj, pspec);
909 }
910
911 /*
912  * This function is a signal handler for notify signal which gets emitted 
913  * when a property changes value on the GtkWidget associated with a GailWidget.
914  *
915  * It constructs an AtkPropertyValues structure and emits a "property_changed"
916  * signal which causes the user specified AtkPropertyChangeHandler
917  * to be called.
918  */
919 static void 
920 gail_widget_real_notify_gtk (GObject     *obj,
921                              GParamSpec  *pspec)
922 {
923   GtkWidget* widget = GTK_WIDGET (obj);
924   AtkObject* atk_obj = gtk_widget_get_accessible (widget);
925   AtkState state;
926   gboolean value;
927
928   if (strcmp (pspec->name, "has-focus") == 0)
929     /*
930      * We use focus-in-event and focus-out-event signals to catch
931      * focus changes so we ignore this.
932      */
933     return;
934   else if (strcmp (pspec->name, "visible") == 0)
935     {
936       state = ATK_STATE_VISIBLE;
937       value = gtk_widget_get_visible (widget);
938     }
939   else if (strcmp (pspec->name, "sensitive") == 0)
940     {
941       state = ATK_STATE_SENSITIVE;
942       value = gtk_widget_get_sensitive (widget);
943     }
944   else if (strcmp (pspec->name, "orientation") == 0)
945     {
946       GtkOrientable *orientable;
947
948       orientable = GTK_ORIENTABLE (widget);
949
950       state = ATK_STATE_HORIZONTAL;
951       value = (gtk_orientable_get_orientation (orientable) == GTK_ORIENTATION_HORIZONTAL);
952     }
953   else
954     return;
955
956   atk_object_notify_state_change (atk_obj, state, value);
957   if (state == ATK_STATE_SENSITIVE)
958     atk_object_notify_state_change (atk_obj, ATK_STATE_ENABLED, value);
959
960   if (state == ATK_STATE_HORIZONTAL)
961     atk_object_notify_state_change (atk_obj, ATK_STATE_VERTICAL, !value);
962 }
963
964 static void 
965 gail_widget_focus_event (AtkObject   *obj,
966                          gboolean    focus_in)
967 {
968   AtkObject *focus_obj;
969
970   focus_obj = g_object_get_data (G_OBJECT (obj), "gail-focus-object");
971   if (focus_obj == NULL)
972     focus_obj = obj;
973   atk_object_notify_state_change (focus_obj, ATK_STATE_FOCUSED, focus_in);
974 }
975
976 static GtkWidget*
977 gail_widget_find_viewport (GtkWidget *widget)
978 {
979   /*
980    * Find an antecedent which is a GtkViewPort
981    */
982   GtkWidget *parent;
983
984   parent = gtk_widget_get_parent (widget);
985   while (parent != NULL)
986     {
987       if (GTK_IS_VIEWPORT (parent))
988         break;
989       parent = gtk_widget_get_parent (parent);
990     }
991   return parent;
992 }
993
994 /*
995  * This function checks whether the widget has an antecedent which is 
996  * a GtkViewport and, if so, whether any part of the widget intersects
997  * the visible rectangle of the GtkViewport.
998  */ 
999 static gboolean gail_widget_on_screen (GtkWidget *widget)
1000 {
1001   GtkAllocation allocation;
1002   GtkWidget *viewport;
1003   gboolean return_value;
1004
1005   gtk_widget_get_allocation (widget, &allocation);
1006
1007   viewport = gail_widget_find_viewport (widget);
1008   if (viewport)
1009     {
1010       GtkAllocation viewport_allocation;
1011       GtkAdjustment *adjustment;
1012       GdkRectangle visible_rect;
1013
1014       gtk_widget_get_allocation (viewport, &viewport_allocation);
1015
1016       adjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (viewport));
1017       visible_rect.y = gtk_adjustment_get_value (adjustment);
1018       adjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (viewport));
1019       visible_rect.x = gtk_adjustment_get_value (adjustment);
1020       visible_rect.width = viewport_allocation.width;
1021       visible_rect.height = viewport_allocation.height;
1022
1023       if (((allocation.x + allocation.width) < visible_rect.x) ||
1024          ((allocation.y + allocation.height) < visible_rect.y) ||
1025          (allocation.x > (visible_rect.x + visible_rect.width)) ||
1026          (allocation.y > (visible_rect.y + visible_rect.height)))
1027         return_value = FALSE;
1028       else
1029         return_value = TRUE;
1030     }
1031   else
1032     {
1033       /*
1034        * Check whether the widget has been placed of the screen. The
1035        * widget may be MAPPED as when toolbar items do not fit on the toolbar.
1036        */
1037       if (allocation.x + allocation.width <= 0 &&
1038           allocation.y + allocation.height <= 0)
1039         return_value = FALSE;
1040       else 
1041         return_value = TRUE;
1042     }
1043
1044   return return_value;
1045 }
1046
1047 /**
1048  * gail_widget_all_parents_visible:
1049  * @widget: a #GtkWidget
1050  *
1051  * Checks if all the predecesors (the parent widget, his parent, etc) are visible
1052  * Used to check properly the SHOWING state.
1053  *
1054  * Return value: TRUE if all the parent hierarchy is visible, FALSE otherwise
1055  **/
1056 static gboolean gail_widget_all_parents_visible (GtkWidget *widget)
1057 {
1058   GtkWidget *iter_parent = NULL;
1059   gboolean result = TRUE;
1060
1061   for (iter_parent = gtk_widget_get_parent (widget); iter_parent;
1062        iter_parent = gtk_widget_get_parent (iter_parent))
1063     {
1064       if (!gtk_widget_get_visible (iter_parent))
1065         {
1066           result = FALSE;
1067           break;
1068         }
1069     }
1070
1071   return result;
1072 }
1073
1074 static AtkAttributeSet *gail_widget_get_attributes(AtkObject *obj)
1075 {
1076         AtkAttributeSet *attributes;
1077         AtkAttribute *toolkit = g_malloc(sizeof(AtkAttribute));
1078
1079         toolkit->name = g_strdup("toolkit");
1080         toolkit->value = g_strdup("gail");
1081
1082         attributes = g_slist_append(NULL, toolkit);
1083
1084         return attributes;
1085 }