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