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