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