]> Pileus Git - ~andy/gtk/blob - modules/other/gail/gailwidget.c
b68c6bb87d6bac62fdbb4a8636f568a6a35d7898
[~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 const 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 AtkAttributeSet *gail_widget_get_attributes(AtkObject *obj);
106 static GtkWidget* gail_widget_find_viewport      (GtkWidget     *widget);
107 static gboolean   gail_widget_on_screen          (GtkWidget     *widget);
108 static gboolean   gail_widget_all_parents_visible(GtkWidget     *widget);
109
110 G_DEFINE_TYPE_WITH_CODE (GailWidget, gail_widget, GTK_TYPE_ACCESSIBLE,
111                          G_IMPLEMENT_INTERFACE (ATK_TYPE_COMPONENT, atk_component_interface_init))
112
113 static void
114 gail_widget_class_init (GailWidgetClass *klass)
115 {
116   AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
117   GtkAccessibleClass *accessible_class = GTK_ACCESSIBLE_CLASS (klass);
118
119   klass->notify_gtk = gail_widget_real_notify_gtk;
120   klass->focus_gtk = gail_widget_real_focus_gtk;
121
122   accessible_class->connect_widget_destroyed = gail_widget_connect_widget_destroyed;
123
124   class->get_description = gail_widget_get_description;
125   class->get_parent = gail_widget_get_parent;
126   class->ref_relation_set = gail_widget_ref_relation_set;
127   class->ref_state_set = gail_widget_ref_state_set;
128   class->get_index_in_parent = gail_widget_get_index_in_parent;
129   class->initialize = gail_widget_real_initialize;
130   class->get_attributes = gail_widget_get_attributes;
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   gtk_accessible_set_widget (accessible, 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   GtkWidget *widget;
215
216   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
217   if (widget)
218     {
219       g_signal_connect_after (widget,
220                               "destroy",
221                               G_CALLBACK (gail_widget_destroyed),
222                               accessible);
223     }
224 }
225
226 /*
227  * This function is called when the widget is destroyed.
228  * It sets the widget field in the GtkAccessible structure to NULL
229  * and emits a state-change signal for the state ATK_STATE_DEFUNCT
230  */
231 static void 
232 gail_widget_destroyed (GtkWidget     *widget,
233                        GtkAccessible *accessible)
234 {
235   gtk_accessible_set_widget (accessible, NULL);
236   atk_object_notify_state_change (ATK_OBJECT (accessible), ATK_STATE_DEFUNCT,
237                                   TRUE);
238 }
239
240 static const gchar*
241 gail_widget_get_description (AtkObject *accessible)
242 {
243   if (accessible->description)
244     return accessible->description;
245   else
246     {
247       GtkWidget *widget;
248
249       /* Get the tooltip from the widget */
250       widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
251       if (widget == NULL)
252         /*
253          * Object is defunct
254          */
255         return NULL;
256  
257       return gtk_widget_get_tooltip_text (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_get_widget (GTK_ACCESSIBLE (accessible));
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 = gtk_widget_get_parent (widget);
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_get_widget (GTK_ACCESSIBLE (obj));
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))
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_BOX (widget))
421             /*
422              * Handle the case when GtkFileChooserButton is the mnemonic
423              * widget.  The GtkComboBox which is a child of the
424              * GtkFileChooserButton should be the mnemonic widget.
425              * See bug #359843.
426              */
427             {
428               GtkWidget *temp_widget;
429
430               temp_widget = gtk_widget_get_parent (widget);
431               if (GTK_IS_BOX (temp_widget))
432                 {
433                   label = find_label (temp_widget);
434                 }
435             }
436         }
437
438       if (label)
439         {
440           array [0] = gtk_widget_get_accessible (label);
441
442           relation = atk_relation_new (array, 1, ATK_RELATION_LABELLED_BY);
443           atk_relation_set_add (relation_set, relation);
444           g_object_unref (relation);
445         }
446     }
447
448   return relation_set;
449 }
450
451 static AtkStateSet*
452 gail_widget_ref_state_set (AtkObject *accessible)
453 {
454   GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
455   AtkStateSet *state_set;
456
457   state_set = ATK_OBJECT_CLASS (gail_widget_parent_class)->ref_state_set (accessible);
458
459   if (widget == NULL)
460     {
461       atk_state_set_add_state (state_set, ATK_STATE_DEFUNCT);
462     }
463   else
464     {
465       if (gtk_widget_is_sensitive (widget))
466         {
467           atk_state_set_add_state (state_set, ATK_STATE_SENSITIVE);
468           atk_state_set_add_state (state_set, ATK_STATE_ENABLED);
469         }
470   
471       if (gtk_widget_get_can_focus (widget))
472         {
473           atk_state_set_add_state (state_set, ATK_STATE_FOCUSABLE);
474         }
475       /*
476        * We do not currently generate notifications when an ATK object 
477        * corresponding to a GtkWidget changes visibility by being scrolled 
478        * on or off the screen.  The testcase for this is the main window 
479        * of the testgtk application in which a set of buttons in a GtkVBox 
480        * is in a scrooled window with a viewport.
481        *
482        * To generate the notifications we would need to do the following: 
483        * 1) Find the GtkViewPort among the antecendents of the objects
484        * 2) Create an accesible for the GtkViewPort
485        * 3) Connect to the value-changed signal on the viewport
486        * 4) When the signal is received we need to traverse the children 
487        * of the viewport and check whether the children are visible or not 
488        * visible; we may want to restrict this to the widgets for which 
489        * accessible objects have been created.
490        * 5) We probably need to store a variable on_screen in the 
491        * GailWidget data structure so we can determine whether the value has 
492        * changed.
493        */
494       if (gtk_widget_get_visible (widget))
495         {
496           atk_state_set_add_state (state_set, ATK_STATE_VISIBLE);
497           if (gail_widget_on_screen (widget) && gtk_widget_get_mapped (widget) &&
498               gail_widget_all_parents_visible (widget))
499             {
500               atk_state_set_add_state (state_set, ATK_STATE_SHOWING);
501             }
502         }
503   
504       if (gtk_widget_has_focus (widget) && (widget == focus_widget))
505         {
506           AtkObject *focus_obj;
507
508           focus_obj = g_object_get_data (G_OBJECT (accessible), "gail-focus-object");
509           if (focus_obj == NULL)
510             atk_state_set_add_state (state_set, ATK_STATE_FOCUSED);
511         }
512       if (gtk_widget_has_default (widget))
513         {
514           atk_state_set_add_state (state_set, ATK_STATE_DEFAULT);
515         }
516
517       if (GTK_IS_ORIENTABLE(widget))
518         switch (gtk_orientable_get_orientation (GTK_ORIENTABLE (widget)))
519           {
520           case GTK_ORIENTATION_HORIZONTAL:
521             atk_state_set_add_state (state_set, ATK_STATE_HORIZONTAL);
522             break;
523
524           case GTK_ORIENTATION_VERTICAL:
525             atk_state_set_add_state (state_set, ATK_STATE_VERTICAL);
526             break;
527           }
528     }
529   return state_set;
530 }
531
532 static gint
533 gail_widget_get_index_in_parent (AtkObject *accessible)
534 {
535   GtkWidget *widget;
536   GtkWidget *parent_widget;
537   gint index;
538   GList *children;
539   GType type;
540
541   type = g_type_from_name ("GailCanvasWidget");
542   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
543
544   if (widget == NULL)
545     /*
546      * State is defunct
547      */
548     return -1;
549
550   if (accessible->accessible_parent)
551     {
552       AtkObject *parent;
553
554       parent = accessible->accessible_parent;
555
556       if (GAIL_IS_NOTEBOOK_PAGE (parent) ||
557           G_TYPE_CHECK_INSTANCE_TYPE ((parent), type))
558         return 0;
559       else
560         {
561           gint n_children, i;
562           gboolean found = FALSE;
563
564           n_children = atk_object_get_n_accessible_children (parent);
565           for (i = 0; i < n_children; i++)
566             {
567               AtkObject *child;
568
569               child = atk_object_ref_accessible_child (parent, i);
570               if (child == accessible)
571                 found = TRUE;
572
573               g_object_unref (child); 
574               if (found)
575                 return i;
576             }
577         }
578     }
579
580   gail_return_val_if_fail (GTK_IS_WIDGET (widget), -1);
581   parent_widget = gtk_widget_get_parent (widget);
582   if (parent_widget == NULL)
583     return -1;
584   gail_return_val_if_fail (GTK_IS_CONTAINER (parent_widget), -1);
585
586   children = gtk_container_get_children (GTK_CONTAINER (parent_widget));
587
588   index = g_list_index (children, widget);
589   g_list_free (children);
590   return index;  
591 }
592
593 static void 
594 atk_component_interface_init (AtkComponentIface *iface)
595 {
596   /*
597    * Use default implementation for contains and get_position
598    */
599   iface->add_focus_handler = gail_widget_add_focus_handler;
600   iface->get_extents = gail_widget_get_extents;
601   iface->get_size = gail_widget_get_size;
602   iface->get_layer = gail_widget_get_layer;
603   iface->grab_focus = gail_widget_grab_focus;
604   iface->remove_focus_handler = gail_widget_remove_focus_handler;
605   iface->set_extents = gail_widget_set_extents;
606   iface->set_position = gail_widget_set_position;
607   iface->set_size = gail_widget_set_size;
608 }
609
610 static guint 
611 gail_widget_add_focus_handler (AtkComponent    *component,
612                                AtkFocusHandler handler)
613 {
614   GSignalMatchType match_type;
615   gulong ret;
616   guint signal_id;
617
618   match_type = G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_FUNC;
619   signal_id = g_signal_lookup ("focus-event", ATK_TYPE_OBJECT);
620
621   ret = g_signal_handler_find (component, match_type, signal_id, 0, NULL,
622                                (gpointer) handler, NULL);
623   if (!ret)
624     {
625       return g_signal_connect_closure_by_id (component, 
626                                              signal_id, 0,
627                                              g_cclosure_new (
628                                              G_CALLBACK (handler), NULL,
629                                              (GClosureNotify) NULL),
630                                              FALSE);
631     }
632   else
633     {
634       return 0;
635     }
636 }
637
638 static void 
639 gail_widget_get_extents (AtkComponent   *component,
640                          gint           *x,
641                          gint           *y,
642                          gint           *width,
643                          gint           *height,
644                          AtkCoordType   coord_type)
645 {
646   GtkAllocation allocation;
647   GdkWindow *window;
648   gint x_window, y_window;
649   gint x_toplevel, y_toplevel;
650   GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component));
651
652   if (widget == NULL)
653     /*
654      * Object is defunct
655      */
656     return;
657
658   gail_return_if_fail (GTK_IS_WIDGET (widget));
659
660   gtk_widget_get_allocation (widget, &allocation);
661   *width = allocation.width;
662   *height = allocation.height;
663   if (!gail_widget_on_screen (widget) || (!gtk_widget_is_drawable (widget)))
664     {
665       *x = G_MININT;
666       *y = G_MININT;
667       return;
668     }
669
670   if (gtk_widget_get_parent (widget))
671     {
672       *x = allocation.x;
673       *y = allocation.y;
674       window = gtk_widget_get_parent_window (widget);
675     }
676   else
677     {
678       *x = 0;
679       *y = 0;
680       window = gtk_widget_get_window (widget);
681     }
682   gdk_window_get_origin (window, &x_window, &y_window);
683   *x += x_window;
684   *y += y_window;
685
686  
687  if (coord_type == ATK_XY_WINDOW) 
688     { 
689       window = gdk_window_get_toplevel (gtk_widget_get_window (widget));
690       gdk_window_get_origin (window, &x_toplevel, &y_toplevel);
691
692       *x -= x_toplevel;
693       *y -= y_toplevel;
694     }
695 }
696
697 static void 
698 gail_widget_get_size (AtkComponent   *component,
699                       gint           *width,
700                       gint           *height)
701 {
702   GtkAllocation allocation;
703   GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component));
704
705   if (widget == NULL)
706     /*
707      * Object is defunct
708      */
709     return;
710
711   gail_return_if_fail (GTK_IS_WIDGET (widget));
712
713   gtk_widget_get_allocation (widget, &allocation);
714   *width = allocation.width;
715   *height = allocation.height;
716 }
717
718 static AtkLayer
719 gail_widget_get_layer (AtkComponent *component)
720 {
721   gint layer;
722   layer = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (component), "atk-component-layer"));
723
724   return (AtkLayer) layer;
725 }
726
727 static gboolean 
728 gail_widget_grab_focus (AtkComponent   *component)
729 {
730   GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component));
731   GtkWidget *toplevel;
732
733   gail_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
734   if (gtk_widget_get_can_focus (widget))
735     {
736       gtk_widget_grab_focus (widget);
737       toplevel = gtk_widget_get_toplevel (widget);
738       if (gtk_widget_is_toplevel (toplevel))
739         {
740 #ifdef GDK_WINDOWING_X11
741           gtk_window_present_with_time (GTK_WINDOW (toplevel),
742           gdk_x11_get_server_time (gtk_widget_get_window (widget)));
743 #else
744           gtk_window_present (GTK_WINDOW (toplevel));
745 #endif
746         }
747       return TRUE;
748     }
749   else
750     return FALSE;
751 }
752
753 static void 
754 gail_widget_remove_focus_handler (AtkComponent   *component,
755                                   guint          handler_id)
756 {
757   g_signal_handler_disconnect (component, handler_id);
758 }
759
760 static gboolean 
761 gail_widget_set_extents (AtkComponent   *component,
762                          gint           x,
763                          gint           y,
764                          gint           width,
765                          gint           height,
766                          AtkCoordType   coord_type)
767 {
768   GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component));
769
770   if (widget == NULL)
771     /*
772      * Object is defunct
773      */
774     return FALSE;
775   gail_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
776
777   if (gtk_widget_is_toplevel (widget))
778     {
779       if (coord_type == ATK_XY_WINDOW)
780         {
781           gint x_current, y_current;
782           GdkWindow *window = gtk_widget_get_window (widget);
783
784           gdk_window_get_origin (window, &x_current, &y_current);
785           x_current += x;
786           y_current += y;
787           if (x_current < 0 || y_current < 0)
788             return FALSE;
789           else
790             {
791               gtk_window_move (GTK_WINDOW (widget), x_current, y_current);
792               gtk_widget_set_size_request (widget, width, height);
793               return TRUE;
794             }
795         }
796       else if (coord_type == ATK_XY_SCREEN)
797         {
798           gtk_window_move (GTK_WINDOW (widget), x, y);
799           gtk_widget_set_size_request (widget, width, height);
800           return TRUE;
801         }
802     }
803   return FALSE;
804 }
805
806 static gboolean
807 gail_widget_set_position (AtkComponent   *component,
808                           gint           x,
809                           gint           y,
810                           AtkCoordType   coord_type)
811 {
812   GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component));
813
814   if (widget == NULL)
815     /*
816      * Object is defunct
817      */
818     return FALSE;
819   gail_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
820
821   if (gtk_widget_is_toplevel (widget))
822     {
823       if (coord_type == ATK_XY_WINDOW)
824         {
825           gint x_current, y_current;
826           GdkWindow *window = gtk_widget_get_window (widget);
827
828           gdk_window_get_origin (window, &x_current, &y_current);
829           x_current += x;
830           y_current += y;
831           if (x_current < 0 || y_current < 0)
832             return FALSE;
833           else
834             {
835               gtk_window_move (GTK_WINDOW (widget), x_current, y_current);
836               return TRUE;
837             }
838         }
839       else if (coord_type == ATK_XY_SCREEN)
840         {
841           gtk_window_move (GTK_WINDOW (widget), x, y);
842           return TRUE;
843         }
844     }
845   return FALSE;
846 }
847
848 static gboolean 
849 gail_widget_set_size (AtkComponent   *component,
850                       gint           width,
851                       gint           height)
852 {
853   GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component));
854
855   if (widget == NULL)
856     /*
857      * Object is defunct
858      */
859     return FALSE;
860   gail_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
861
862   if (gtk_widget_is_toplevel (widget))
863     {
864       gtk_widget_set_size_request (widget, width, height);
865       return TRUE;
866     }
867   else
868    return FALSE;
869 }
870
871 /*
872  * This function is a signal handler for notify_in_event and focus_out_event
873  * signal which gets emitted on a GtkWidget.
874  */
875 static gboolean
876 gail_widget_focus_gtk (GtkWidget     *widget,
877                        GdkEventFocus *event)
878 {
879   GailWidget *gail_widget;
880   GailWidgetClass *klass;
881
882   gail_widget = GAIL_WIDGET (gtk_widget_get_accessible (widget));
883   klass = GAIL_WIDGET_GET_CLASS (gail_widget);
884   if (klass->focus_gtk)
885     return klass->focus_gtk (widget, event);
886   else
887     return FALSE;
888 }
889
890 /*
891  * This function is the signal handler defined for focus_in_event and
892  * focus_out_event got GailWidget.
893  *
894  * It emits a focus-event signal on the GailWidget.
895  */
896 static gboolean
897 gail_widget_real_focus_gtk (GtkWidget     *widget,
898                             GdkEventFocus *event)
899 {
900   AtkObject* accessible;
901   gboolean return_val;
902   return_val = FALSE;
903
904   accessible = gtk_widget_get_accessible (widget);
905   g_signal_emit_by_name (accessible, "focus_event", event->in, &return_val);
906   return FALSE;
907 }
908
909 static void
910 gail_widget_size_allocate_gtk (GtkWidget     *widget,
911                                GtkAllocation *allocation)
912 {
913   AtkObject* accessible;
914   AtkRectangle rect;
915
916   accessible = gtk_widget_get_accessible (widget);
917   if (ATK_IS_COMPONENT (accessible))
918     {
919       rect.x = allocation->x;
920       rect.y = allocation->y;
921       rect.width = allocation->width;
922       rect.height = allocation->height;
923       g_signal_emit_by_name (accessible, "bounds_changed", &rect);
924     }
925 }
926
927 /*
928  * This function is the signal handler defined for map and unmap signals.
929  */
930 static gint
931 gail_widget_map_gtk (GtkWidget     *widget)
932 {
933   AtkObject* accessible;
934
935   accessible = gtk_widget_get_accessible (widget);
936   atk_object_notify_state_change (accessible, ATK_STATE_SHOWING,
937                                   gtk_widget_get_mapped (widget));
938   return 1;
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 the object.
944  *
945  * It calls a function for the GailWidget type
946  */
947 static void 
948 gail_widget_notify_gtk (GObject     *obj,
949                         GParamSpec  *pspec)
950 {
951   GailWidget *widget;
952   GailWidgetClass *klass;
953
954   widget = GAIL_WIDGET (gtk_widget_get_accessible (GTK_WIDGET (obj)));
955   klass = GAIL_WIDGET_GET_CLASS (widget);
956   if (klass->notify_gtk)
957     klass->notify_gtk (obj, pspec);
958 }
959
960 /*
961  * This function is a signal handler for notify signal which gets emitted 
962  * when a property changes value on the GtkWidget associated with a GailWidget.
963  *
964  * It constructs an AtkPropertyValues structure and emits a "property_changed"
965  * signal which causes the user specified AtkPropertyChangeHandler
966  * to be called.
967  */
968 static void 
969 gail_widget_real_notify_gtk (GObject     *obj,
970                              GParamSpec  *pspec)
971 {
972   GtkWidget* widget = GTK_WIDGET (obj);
973   AtkObject* atk_obj = gtk_widget_get_accessible (widget);
974   AtkState state;
975   gboolean value;
976
977   if (strcmp (pspec->name, "has-focus") == 0)
978     /*
979      * We use focus-in-event and focus-out-event signals to catch
980      * focus changes so we ignore this.
981      */
982     return;
983   else if (strcmp (pspec->name, "visible") == 0)
984     {
985       state = ATK_STATE_VISIBLE;
986       value = gtk_widget_get_visible (widget);
987     }
988   else if (strcmp (pspec->name, "sensitive") == 0)
989     {
990       state = ATK_STATE_SENSITIVE;
991       value = gtk_widget_get_sensitive (widget);
992     }
993   else if (strcmp (pspec->name, "orientation") == 0)
994     {
995       GtkOrientable *orientable;
996
997       orientable = GTK_ORIENTABLE (widget);
998
999       state = ATK_STATE_HORIZONTAL;
1000       value = (gtk_orientable_get_orientation (orientable) == GTK_ORIENTATION_HORIZONTAL);
1001     }
1002   else
1003     return;
1004
1005   atk_object_notify_state_change (atk_obj, state, value);
1006   if (state == ATK_STATE_SENSITIVE)
1007     atk_object_notify_state_change (atk_obj, ATK_STATE_ENABLED, value);
1008
1009   if (state == ATK_STATE_HORIZONTAL)
1010     atk_object_notify_state_change (atk_obj, ATK_STATE_VERTICAL, !value);
1011 }
1012
1013 static void 
1014 gail_widget_focus_event (AtkObject   *obj,
1015                          gboolean    focus_in)
1016 {
1017   AtkObject *focus_obj;
1018
1019   focus_obj = g_object_get_data (G_OBJECT (obj), "gail-focus-object");
1020   if (focus_obj == NULL)
1021     focus_obj = obj;
1022   atk_object_notify_state_change (focus_obj, ATK_STATE_FOCUSED, focus_in);
1023 }
1024
1025 static GtkWidget*
1026 gail_widget_find_viewport (GtkWidget *widget)
1027 {
1028   /*
1029    * Find an antecedent which is a GtkViewPort
1030    */
1031   GtkWidget *parent;
1032
1033   parent = gtk_widget_get_parent (widget);
1034   while (parent != NULL)
1035     {
1036       if (GTK_IS_VIEWPORT (parent))
1037         break;
1038       parent = gtk_widget_get_parent (parent);
1039     }
1040   return parent;
1041 }
1042
1043 /*
1044  * This function checks whether the widget has an antecedent which is 
1045  * a GtkViewport and, if so, whether any part of the widget intersects
1046  * the visible rectangle of the GtkViewport.
1047  */ 
1048 static gboolean gail_widget_on_screen (GtkWidget *widget)
1049 {
1050   GtkAllocation allocation;
1051   GtkWidget *viewport;
1052   gboolean return_value;
1053
1054   gtk_widget_get_allocation (widget, &allocation);
1055
1056   viewport = gail_widget_find_viewport (widget);
1057   if (viewport)
1058     {
1059       GtkAllocation viewport_allocation;
1060       GtkAdjustment *adjustment;
1061       GdkRectangle visible_rect;
1062
1063       gtk_widget_get_allocation (viewport, &viewport_allocation);
1064
1065       adjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (viewport));
1066       visible_rect.y = gtk_adjustment_get_value (adjustment);
1067       adjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (viewport));
1068       visible_rect.x = gtk_adjustment_get_value (adjustment);
1069       visible_rect.width = viewport_allocation.width;
1070       visible_rect.height = viewport_allocation.height;
1071
1072       if (((allocation.x + allocation.width) < visible_rect.x) ||
1073          ((allocation.y + allocation.height) < visible_rect.y) ||
1074          (allocation.x > (visible_rect.x + visible_rect.width)) ||
1075          (allocation.y > (visible_rect.y + visible_rect.height)))
1076         return_value = FALSE;
1077       else
1078         return_value = TRUE;
1079     }
1080   else
1081     {
1082       /*
1083        * Check whether the widget has been placed of the screen. The
1084        * widget may be MAPPED as when toolbar items do not fit on the toolbar.
1085        */
1086       if (allocation.x + allocation.width <= 0 &&
1087           allocation.y + allocation.height <= 0)
1088         return_value = FALSE;
1089       else 
1090         return_value = TRUE;
1091     }
1092
1093   return return_value;
1094 }
1095
1096 /**
1097  * gail_widget_all_parents_visible:
1098  * @widget: a #GtkWidget
1099  *
1100  * Checks if all the predecesors (the parent widget, his parent, etc) are visible
1101  * Used to check properly the SHOWING state.
1102  *
1103  * Return value: TRUE if all the parent hierarchy is visible, FALSE otherwise
1104  **/
1105 static gboolean gail_widget_all_parents_visible (GtkWidget *widget)
1106 {
1107   GtkWidget *iter_parent = NULL;
1108   gboolean result = TRUE;
1109
1110   for (iter_parent = gtk_widget_get_parent (widget); iter_parent;
1111        iter_parent = gtk_widget_get_parent (iter_parent))
1112     {
1113       if (!gtk_widget_get_visible (iter_parent))
1114         {
1115           result = FALSE;
1116           break;
1117         }
1118     }
1119
1120   return result;
1121 }
1122
1123 static AtkAttributeSet *gail_widget_get_attributes(AtkObject *obj)
1124 {
1125         AtkAttributeSet *attributes;
1126         AtkAttribute *toolkit = g_malloc(sizeof(AtkAttribute));
1127
1128         toolkit->name = g_strdup("toolkit");
1129         toolkit->value = g_strdup("gail");
1130
1131         attributes = g_slist_append(NULL, toolkit);
1132
1133         return attributes;
1134 }