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