]> Pileus Git - ~andy/gtk/blob - modules/other/gail/gailwidget.c
Add GtkScrollable interface
[~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   gtk_accessible_set_widget (accessible, 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   GtkWidget *widget;
213
214   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
215   if (widget)
216     {
217       g_signal_connect_after (widget,
218                               "destroy",
219                               G_CALLBACK (gail_widget_destroyed),
220                               accessible);
221     }
222 }
223
224 /*
225  * This function is called when the widget is destroyed.
226  * It sets the widget field in the GtkAccessible structure to NULL
227  * and emits a state-change signal for the state ATK_STATE_DEFUNCT
228  */
229 static void 
230 gail_widget_destroyed (GtkWidget     *widget,
231                        GtkAccessible *accessible)
232 {
233   gtk_accessible_set_widget (accessible, NULL);
234   atk_object_notify_state_change (ATK_OBJECT (accessible), ATK_STATE_DEFUNCT,
235                                   TRUE);
236 }
237
238 static G_CONST_RETURN gchar*
239 gail_widget_get_description (AtkObject *accessible)
240 {
241   if (accessible->description)
242     return accessible->description;
243   else
244     {
245       GtkWidget *widget;
246
247       /* Get the tooltip from the widget */
248       widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
249       if (widget == NULL)
250         /*
251          * Object is defunct
252          */
253         return NULL;
254  
255       return gtk_widget_get_tooltip_text (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_get_widget (GTK_ACCESSIBLE (accessible));
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 = gtk_widget_get_parent (widget);
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_get_widget (GTK_ACCESSIBLE (obj));
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_get_widget (GTK_ACCESSIBLE (accessible));
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_get_widget (GTK_ACCESSIBLE (accessible));
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 = gtk_widget_get_parent (widget);
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   GtkAllocation allocation;
633   GdkWindow *window;
634   gint x_window, y_window;
635   gint x_toplevel, y_toplevel;
636   GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component));
637
638   if (widget == NULL)
639     /*
640      * Object is defunct
641      */
642     return;
643
644   gail_return_if_fail (GTK_IS_WIDGET (widget));
645
646   gtk_widget_get_allocation (widget, &allocation);
647   *width = allocation.width;
648   *height = allocation.height;
649   if (!gail_widget_on_screen (widget) || (!gtk_widget_is_drawable (widget)))
650     {
651       *x = G_MININT;
652       *y = G_MININT;
653       return;
654     }
655
656   if (gtk_widget_get_parent (widget))
657     {
658       *x = allocation.x;
659       *y = allocation.y;
660       window = gtk_widget_get_parent_window (widget);
661     }
662   else
663     {
664       *x = 0;
665       *y = 0;
666       window = gtk_widget_get_window (widget);
667     }
668   gdk_window_get_origin (window, &x_window, &y_window);
669   *x += x_window;
670   *y += y_window;
671
672  
673  if (coord_type == ATK_XY_WINDOW) 
674     { 
675       window = gdk_window_get_toplevel (gtk_widget_get_window (widget));
676       gdk_window_get_origin (window, &x_toplevel, &y_toplevel);
677
678       *x -= x_toplevel;
679       *y -= y_toplevel;
680     }
681 }
682
683 static void 
684 gail_widget_get_size (AtkComponent   *component,
685                       gint           *width,
686                       gint           *height)
687 {
688   GtkAllocation allocation;
689   GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component));
690
691   if (widget == NULL)
692     /*
693      * Object is defunct
694      */
695     return;
696
697   gail_return_if_fail (GTK_IS_WIDGET (widget));
698
699   gtk_widget_get_allocation (widget, &allocation);
700   *width = allocation.width;
701   *height = allocation.height;
702 }
703
704 static AtkLayer
705 gail_widget_get_layer (AtkComponent *component)
706 {
707   gint layer;
708   layer = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (component), "atk-component-layer"));
709
710   return (AtkLayer) layer;
711 }
712
713 static gboolean 
714 gail_widget_grab_focus (AtkComponent   *component)
715 {
716   GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component));
717   GtkWidget *toplevel;
718
719   gail_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
720   if (gtk_widget_get_can_focus (widget))
721     {
722       gtk_widget_grab_focus (widget);
723       toplevel = gtk_widget_get_toplevel (widget);
724       if (gtk_widget_is_toplevel (toplevel))
725         {
726 #ifdef GDK_WINDOWING_X11
727           gtk_window_present_with_time (GTK_WINDOW (toplevel),
728           gdk_x11_get_server_time (gtk_widget_get_window (widget)));
729 #else
730           gtk_window_present (GTK_WINDOW (toplevel));
731 #endif
732         }
733       return TRUE;
734     }
735   else
736     return FALSE;
737 }
738
739 static void 
740 gail_widget_remove_focus_handler (AtkComponent   *component,
741                                   guint          handler_id)
742 {
743   g_signal_handler_disconnect (component, handler_id);
744 }
745
746 static gboolean 
747 gail_widget_set_extents (AtkComponent   *component,
748                          gint           x,
749                          gint           y,
750                          gint           width,
751                          gint           height,
752                          AtkCoordType   coord_type)
753 {
754   GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component));
755
756   if (widget == NULL)
757     /*
758      * Object is defunct
759      */
760     return FALSE;
761   gail_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
762
763   if (gtk_widget_is_toplevel (widget))
764     {
765       if (coord_type == ATK_XY_WINDOW)
766         {
767           gint x_current, y_current;
768           GdkWindow *window = gtk_widget_get_window (widget);
769
770           gdk_window_get_origin (window, &x_current, &y_current);
771           x_current += x;
772           y_current += y;
773           if (x_current < 0 || y_current < 0)
774             return FALSE;
775           else
776             {
777               gtk_window_move (GTK_WINDOW (widget), x_current, y_current);
778               gtk_widget_set_size_request (widget, width, height);
779               return TRUE;
780             }
781         }
782       else if (coord_type == ATK_XY_SCREEN)
783         {
784           gtk_window_move (GTK_WINDOW (widget), x, y);
785           gtk_widget_set_size_request (widget, width, height);
786           return TRUE;
787         }
788     }
789   return FALSE;
790 }
791
792 static gboolean
793 gail_widget_set_position (AtkComponent   *component,
794                           gint           x,
795                           gint           y,
796                           AtkCoordType   coord_type)
797 {
798   GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component));
799
800   if (widget == NULL)
801     /*
802      * Object is defunct
803      */
804     return FALSE;
805   gail_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
806
807   if (gtk_widget_is_toplevel (widget))
808     {
809       if (coord_type == ATK_XY_WINDOW)
810         {
811           gint x_current, y_current;
812           GdkWindow *window = gtk_widget_get_window (widget);
813
814           gdk_window_get_origin (window, &x_current, &y_current);
815           x_current += x;
816           y_current += y;
817           if (x_current < 0 || y_current < 0)
818             return FALSE;
819           else
820             {
821               gtk_window_move (GTK_WINDOW (widget), x_current, y_current);
822               return TRUE;
823             }
824         }
825       else if (coord_type == ATK_XY_SCREEN)
826         {
827           gtk_window_move (GTK_WINDOW (widget), x, y);
828           return TRUE;
829         }
830     }
831   return FALSE;
832 }
833
834 static gboolean 
835 gail_widget_set_size (AtkComponent   *component,
836                       gint           width,
837                       gint           height)
838 {
839   GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component));
840
841   if (widget == NULL)
842     /*
843      * Object is defunct
844      */
845     return FALSE;
846   gail_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
847
848   if (gtk_widget_is_toplevel (widget))
849     {
850       gtk_widget_set_size_request (widget, width, height);
851       return TRUE;
852     }
853   else
854    return FALSE;
855 }
856
857 /*
858  * This function is a signal handler for notify_in_event and focus_out_event
859  * signal which gets emitted on a GtkWidget.
860  */
861 static gboolean
862 gail_widget_focus_gtk (GtkWidget     *widget,
863                        GdkEventFocus *event)
864 {
865   GailWidget *gail_widget;
866   GailWidgetClass *klass;
867
868   gail_widget = GAIL_WIDGET (gtk_widget_get_accessible (widget));
869   klass = GAIL_WIDGET_GET_CLASS (gail_widget);
870   if (klass->focus_gtk)
871     return klass->focus_gtk (widget, event);
872   else
873     return FALSE;
874 }
875
876 /*
877  * This function is the signal handler defined for focus_in_event and
878  * focus_out_event got GailWidget.
879  *
880  * It emits a focus-event signal on the GailWidget.
881  */
882 static gboolean
883 gail_widget_real_focus_gtk (GtkWidget     *widget,
884                             GdkEventFocus *event)
885 {
886   AtkObject* accessible;
887   gboolean return_val;
888   return_val = FALSE;
889
890   accessible = gtk_widget_get_accessible (widget);
891   g_signal_emit_by_name (accessible, "focus_event", event->in, &return_val);
892   return FALSE;
893 }
894
895 static void
896 gail_widget_size_allocate_gtk (GtkWidget     *widget,
897                                GtkAllocation *allocation)
898 {
899   AtkObject* accessible;
900   AtkRectangle rect;
901
902   accessible = gtk_widget_get_accessible (widget);
903   if (ATK_IS_COMPONENT (accessible))
904     {
905       rect.x = allocation->x;
906       rect.y = allocation->y;
907       rect.width = allocation->width;
908       rect.height = allocation->height;
909       g_signal_emit_by_name (accessible, "bounds_changed", &rect);
910     }
911 }
912
913 /*
914  * This function is the signal handler defined for map and unmap signals.
915  */
916 static gint
917 gail_widget_map_gtk (GtkWidget     *widget)
918 {
919   AtkObject* accessible;
920
921   accessible = gtk_widget_get_accessible (widget);
922   atk_object_notify_state_change (accessible, ATK_STATE_SHOWING,
923                                   gtk_widget_get_mapped (widget));
924   return 1;
925 }
926
927 /*
928  * This function is a signal handler for notify signal which gets emitted 
929  * when a property changes value on the GtkWidget associated with the object.
930  *
931  * It calls a function for the GailWidget type
932  */
933 static void 
934 gail_widget_notify_gtk (GObject     *obj,
935                         GParamSpec  *pspec)
936 {
937   GailWidget *widget;
938   GailWidgetClass *klass;
939
940   widget = GAIL_WIDGET (gtk_widget_get_accessible (GTK_WIDGET (obj)));
941   klass = GAIL_WIDGET_GET_CLASS (widget);
942   if (klass->notify_gtk)
943     klass->notify_gtk (obj, pspec);
944 }
945
946 /*
947  * This function is a signal handler for notify signal which gets emitted 
948  * when a property changes value on the GtkWidget associated with a GailWidget.
949  *
950  * It constructs an AtkPropertyValues structure and emits a "property_changed"
951  * signal which causes the user specified AtkPropertyChangeHandler
952  * to be called.
953  */
954 static void 
955 gail_widget_real_notify_gtk (GObject     *obj,
956                              GParamSpec  *pspec)
957 {
958   GtkWidget* widget = GTK_WIDGET (obj);
959   AtkObject* atk_obj = gtk_widget_get_accessible (widget);
960   AtkState state;
961   gboolean value;
962
963   if (strcmp (pspec->name, "has-focus") == 0)
964     /*
965      * We use focus-in-event and focus-out-event signals to catch
966      * focus changes so we ignore this.
967      */
968     return;
969   else if (strcmp (pspec->name, "visible") == 0)
970     {
971       state = ATK_STATE_VISIBLE;
972       value = gtk_widget_get_visible (widget);
973     }
974   else if (strcmp (pspec->name, "sensitive") == 0)
975     {
976       state = ATK_STATE_SENSITIVE;
977       value = gtk_widget_get_sensitive (widget);
978     }
979   else
980     return;
981
982   atk_object_notify_state_change (atk_obj, state, value);
983   if (state == ATK_STATE_SENSITIVE)
984     atk_object_notify_state_change (atk_obj, ATK_STATE_ENABLED, value);
985
986 }
987
988 static void 
989 gail_widget_focus_event (AtkObject   *obj,
990                          gboolean    focus_in)
991 {
992   AtkObject *focus_obj;
993
994   focus_obj = g_object_get_data (G_OBJECT (obj), "gail-focus-object");
995   if (focus_obj == NULL)
996     focus_obj = obj;
997   atk_object_notify_state_change (focus_obj, ATK_STATE_FOCUSED, focus_in);
998 }
999
1000 static GtkWidget*
1001 gail_widget_find_viewport (GtkWidget *widget)
1002 {
1003   /*
1004    * Find an antecedent which is a GtkViewPort
1005    */
1006   GtkWidget *parent;
1007
1008   parent = gtk_widget_get_parent (widget);
1009   while (parent != NULL)
1010     {
1011       if (GTK_IS_VIEWPORT (parent))
1012         break;
1013       parent = gtk_widget_get_parent (parent);
1014     }
1015   return parent;
1016 }
1017
1018 /*
1019  * This function checks whether the widget has an antecedent which is 
1020  * a GtkViewport and, if so, whether any part of the widget intersects
1021  * the visible rectangle of the GtkViewport.
1022  */ 
1023 static gboolean gail_widget_on_screen (GtkWidget *widget)
1024 {
1025   GtkAllocation allocation;
1026   GtkWidget *viewport;
1027   gboolean return_value;
1028
1029   gtk_widget_get_allocation (widget, &allocation);
1030
1031   viewport = gail_widget_find_viewport (widget);
1032   if (viewport)
1033     {
1034       GtkAllocation viewport_allocation;
1035       GtkAdjustment *adjustment;
1036       GdkRectangle visible_rect;
1037
1038       gtk_widget_get_allocation (viewport, &viewport_allocation);
1039
1040       adjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (viewport));
1041       visible_rect.y = adjustment->value;
1042       adjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (viewport));
1043       visible_rect.x = adjustment->value;
1044       visible_rect.width = viewport_allocation.width;
1045       visible_rect.height = viewport_allocation.height;
1046
1047       if (((allocation.x + allocation.width) < visible_rect.x) ||
1048          ((allocation.y + allocation.height) < visible_rect.y) ||
1049          (allocation.x > (visible_rect.x + visible_rect.width)) ||
1050          (allocation.y > (visible_rect.y + visible_rect.height)))
1051         return_value = FALSE;
1052       else
1053         return_value = TRUE;
1054     }
1055   else
1056     {
1057       /*
1058        * Check whether the widget has been placed of the screen. The
1059        * widget may be MAPPED as when toolbar items do not fit on the toolbar.
1060        */
1061       if (allocation.x + allocation.width <= 0 &&
1062           allocation.y + allocation.height <= 0)
1063         return_value = FALSE;
1064       else 
1065         return_value = TRUE;
1066     }
1067
1068   return return_value;
1069 }
1070
1071 /**
1072  * gail_widget_all_parents_visible:
1073  * @widget: a #GtkWidget
1074  *
1075  * Checks if all the predecesors (the parent widget, his parent, etc) are visible
1076  * Used to check properly the SHOWING state.
1077  *
1078  * Return value: TRUE if all the parent hierarchy is visible, FALSE otherwise
1079  **/
1080 static gboolean gail_widget_all_parents_visible (GtkWidget *widget)
1081 {
1082   GtkWidget *iter_parent = NULL;
1083   gboolean result = TRUE;
1084
1085   for (iter_parent = gtk_widget_get_parent (widget); iter_parent;
1086        iter_parent = gtk_widget_get_parent (iter_parent))
1087     {
1088       if (!gtk_widget_get_visible (iter_parent))
1089         {
1090           result = FALSE;
1091           break;
1092         }
1093     }
1094
1095   return result;
1096 }