]> Pileus Git - ~andy/gtk/blob - gtk/a11y/gtkwidgetaccessible.c
Initial conversion of GailWidget to GtkWidgetAccessible
[~andy/gtk] / gtk / a11y / gtkwidgetaccessible.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 "gtkwidgetaccessible.h"
29 #include "gtknotebookpageaccessible.h"
30
31 extern GtkWidget *focus_widget;
32
33
34 static gboolean gtk_widget_accessible_on_screen           (GtkWidget *widget);
35 static gboolean gtk_widget_accessible_all_parents_visible (GtkWidget *widget);
36
37 static void atk_component_interface_init (AtkComponentIface *iface);
38
39 G_DEFINE_TYPE_WITH_CODE (GtkWidgetAccessible, gtk_widget_accessible, GTK_TYPE_ACCESSIBLE,
40                          G_IMPLEMENT_INTERFACE (ATK_TYPE_COMPONENT, atk_component_interface_init))
41
42 /* Translate GtkWidget::focus-in/out-event to the focus_gtk vfunc */
43 static gboolean
44 focus_cb (GtkWidget     *widget,
45           GdkEventFocus *event)
46 {
47   GtkWidgetAccessible *accessible;
48   GtkWidgetAccessibleClass *klass;
49
50   accessible = GTK_WIDGET_ACCESSIBLE (gtk_widget_get_accessible (widget));
51   klass = GTK_WIDGET_ACCESSIBLE_GET_CLASS (accessible);
52   if (klass->focus_gtk)
53     return klass->focus_gtk (widget, event);
54   else
55     return FALSE;
56 }
57
58 /* Translate GtkWidget property change notification to the notify_gtk vfunc */
59 static void
60 notify_cb (GObject    *obj,
61            GParamSpec *pspec)
62 {
63   GtkWidgetAccessible *widget;
64   GtkWidgetAccessibleClass *klass;
65
66   widget = GTK_WIDGET_ACCESSIBLE (gtk_widget_get_accessible (GTK_WIDGET (obj)));
67   klass = GTK_WIDGET_ACCESSIBLE_GET_CLASS (widget);
68   if (klass->notify_gtk)
69     klass->notify_gtk (obj, pspec);
70 }
71
72 /* Translate GtkWidget::size-allocate to AtkComponent::bounds-changed */
73 static void
74 size_allocate_cb (GtkWidget     *widget,
75                   GtkAllocation *allocation)
76 {
77   AtkObject* accessible;
78   AtkRectangle rect;
79
80   accessible = gtk_widget_get_accessible (widget);
81   if (ATK_IS_COMPONENT (accessible))
82     {
83       rect.x = allocation->x;
84       rect.y = allocation->y;
85       rect.width = allocation->width;
86       rect.height = allocation->height;
87       g_signal_emit_by_name (accessible, "bounds_changed", &rect);
88     }
89 }
90
91 /* Translate GtkWidget mapped state into AtkObject showing */
92 static gint
93 map_cb (GtkWidget *widget)
94 {
95   AtkObject *accessible;
96
97   accessible = gtk_widget_get_accessible (widget);
98   atk_object_notify_state_change (accessible, ATK_STATE_SHOWING,
99                                   gtk_widget_get_mapped (widget));
100   return 1;
101 }
102
103 static void
104 focus_event (AtkObject *obj,
105              gboolean   focus_in)
106 {
107   AtkObject *focus_obj;
108
109   focus_obj = g_object_get_data (G_OBJECT (obj), "gail-focus-object");
110   if (focus_obj == NULL)
111     focus_obj = obj;
112   atk_object_notify_state_change (focus_obj, ATK_STATE_FOCUSED, focus_in);
113 }
114
115 static void
116 gtk_widget_accessible_initialize (AtkObject *obj,
117                                   gpointer   data)
118 {
119   GtkAccessible *accessible;
120   GtkWidget *widget;
121
122   widget = GTK_WIDGET (data);
123
124   accessible = GTK_ACCESSIBLE (obj);
125   gtk_accessible_set_widget (accessible, widget);
126   gtk_accessible_connect_widget_destroyed (accessible);
127   g_signal_connect_after (widget, "focus-in-event", G_CALLBACK (focus_cb), NULL);
128   g_signal_connect_after (widget, "focus-out-event", G_CALLBACK (focus_cb), NULL);
129   g_signal_connect (widget, "notify", G_CALLBACK (notify_cb), NULL);
130   g_signal_connect (widget, "size-allocate", G_CALLBACK (size_allocate_cb), NULL);
131   g_signal_connect (widget, "map", G_CALLBACK (map_cb), NULL);
132   g_signal_connect (widget, "unmap", G_CALLBACK (map_cb), NULL);
133
134   atk_component_add_focus_handler (ATK_COMPONENT (accessible), focus_event);
135   g_object_set_data (G_OBJECT (obj), "atk-component-layer", GINT_TO_POINTER (ATK_LAYER_WIDGET));
136
137   obj->role = ATK_ROLE_UNKNOWN;
138 }
139
140 static void
141 gtk_widget_accessible_destroyed (GtkWidget     *widget,
142                                  GtkAccessible *accessible)
143 {
144   gtk_accessible_set_widget (accessible, NULL);
145   atk_object_notify_state_change (ATK_OBJECT (accessible), ATK_STATE_DEFUNCT, TRUE);
146 }
147
148 static void
149 gtk_widget_accessible_connect_widget_destroyed (GtkAccessible *accessible)
150 {
151   GtkWidget *widget;
152
153   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
154   if (widget)
155     g_signal_connect_after (widget, "destroy",
156                             G_CALLBACK (gtk_widget_accessible_destroyed), accessible);
157 }
158
159 static const gchar *
160 gtk_widget_accessible_get_description (AtkObject *accessible)
161 {
162   GtkWidget *widget;
163
164   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
165   if (widget == NULL)
166     return NULL;
167
168   if (accessible->description)
169     return accessible->description;
170
171   return gtk_widget_get_tooltip_text (widget);
172 }
173
174 static AtkObject *
175 gtk_widget_accessible_get_parent (AtkObject *accessible)
176 {
177   AtkObject *parent;
178   GtkWidget *widget, *parent_widget;
179
180   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
181   if (widget == NULL)
182     return NULL;
183
184   parent = accessible->accessible_parent;
185   if (parent != NULL)
186     return parent;
187
188   parent_widget = gtk_widget_get_parent (widget);
189   if (parent_widget == NULL)
190     return NULL;
191
192   /* For a widget whose parent is a GtkNoteBook, we return the
193    * accessible object corresponding the GtkNotebookPage containing
194    * the widget as the accessible parent.
195    */
196   if (GTK_IS_NOTEBOOK (parent_widget))
197     {
198       gint page_num;
199       GtkWidget *child;
200       GtkNotebook *notebook;
201
202       page_num = 0;
203       notebook = GTK_NOTEBOOK (parent_widget);
204       while (TRUE)
205         {
206           child = gtk_notebook_get_nth_page (notebook, page_num);
207           if (!child)
208             break;
209           if (child == widget)
210             {
211               parent = gtk_widget_get_accessible (parent_widget);
212               parent = atk_object_ref_accessible_child (parent, page_num);
213               g_object_unref (parent);
214               return parent;
215             }
216           page_num++;
217         }
218     }
219   parent = gtk_widget_get_accessible (parent_widget);
220   return parent;
221 }
222
223 static GtkWidget *
224 find_label (GtkWidget *widget)
225 {
226   GList *labels;
227   GtkWidget *label;
228   GtkWidget *temp_widget;
229
230   labels = gtk_widget_list_mnemonic_labels (widget);
231   label = NULL;
232   if (labels)
233     {
234       if (labels->data)
235         {
236           if (labels->next)
237             g_warning ("Widget (%s) has more than one label", G_OBJECT_TYPE_NAME (widget));
238           else
239             label = labels->data;
240         }
241       g_list_free (labels);
242     }
243
244   /* Ignore a label within a button; bug #136602 */
245   if (label && GTK_IS_BUTTON (widget))
246     {
247       temp_widget = label;
248       while (temp_widget)
249         {
250           if (temp_widget == widget)
251             {
252               label = NULL;
253               break;
254             }
255           temp_widget = gtk_widget_get_parent (temp_widget);
256         }
257     }
258   return label;
259 }
260
261 static AtkRelationSet *
262 gtk_widget_accessible_ref_relation_set (AtkObject *obj)
263 {
264   GtkWidget *widget;
265   AtkRelationSet *relation_set;
266   GtkWidget *label;
267   AtkObject *array[1];
268   AtkRelation* relation;
269
270   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
271   if (widget == NULL)
272     return NULL;
273
274   relation_set = ATK_OBJECT_CLASS (gtk_widget_accessible_parent_class)->ref_relation_set (obj);
275
276   if (GTK_IS_BOX (widget))
277     return relation_set;
278
279   if (!atk_relation_set_contains (relation_set, ATK_RELATION_LABELLED_BY))
280     {
281       label = find_label (widget);
282       if (label == NULL)
283         {
284           if (GTK_IS_BUTTON (widget))
285             /*
286              * Handle the case where GnomeIconEntry is the mnemonic widget.
287              * The GtkButton which is a grandchild of the GnomeIconEntry
288              * should really be the mnemonic widget. See bug #133967.
289              */
290             {
291               GtkWidget *temp_widget;
292
293               temp_widget = gtk_widget_get_parent (widget);
294
295               if (GTK_IS_ALIGNMENT (temp_widget))
296                 {
297                   temp_widget = gtk_widget_get_parent (temp_widget);
298                   if (GTK_IS_BOX (temp_widget))
299                     {
300                       label = find_label (temp_widget);
301                       if (!label)
302                         label = find_label (gtk_widget_get_parent (temp_widget));
303                     }
304                 }
305             }
306           else if (GTK_IS_COMBO_BOX (widget))
307             /*
308              * Handle the case when GtkFileChooserButton is the mnemonic
309              * widget.  The GtkComboBox which is a child of the
310              * GtkFileChooserButton should be the mnemonic widget.
311              * See bug #359843.
312              */
313             {
314               GtkWidget *temp_widget;
315
316               temp_widget = gtk_widget_get_parent (widget);
317               if (GTK_IS_BOX (temp_widget))
318                 {
319                   label = find_label (temp_widget);
320                 }
321             }
322         }
323
324       if (label)
325         {
326           array[0] = gtk_widget_get_accessible (label);
327
328           relation = atk_relation_new (array, 1, ATK_RELATION_LABELLED_BY);
329           atk_relation_set_add (relation_set, relation);
330           g_object_unref (relation);
331         }
332     }
333
334   return relation_set;
335 }
336
337 static AtkStateSet *
338 gtk_widget_accessible_ref_state_set (AtkObject *accessible)
339 {
340   GtkWidget *widget;
341   AtkStateSet *state_set;
342
343   state_set = ATK_OBJECT_CLASS (gtk_widget_accessible_parent_class)->ref_state_set (accessible);
344
345   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
346   if (widget == NULL)
347     atk_state_set_add_state (state_set, ATK_STATE_DEFUNCT);
348   else
349     {
350       if (gtk_widget_is_sensitive (widget))
351         {
352           atk_state_set_add_state (state_set, ATK_STATE_SENSITIVE);
353           atk_state_set_add_state (state_set, ATK_STATE_ENABLED);
354         }
355   
356       if (gtk_widget_get_can_focus (widget))
357         {
358           atk_state_set_add_state (state_set, ATK_STATE_FOCUSABLE);
359         }
360       /*
361        * We do not currently generate notifications when an ATK object
362        * corresponding to a GtkWidget changes visibility by being scrolled
363        * on or off the screen.  The testcase for this is the main window
364        * of the testgtk application in which a set of buttons in a GtkVBox
365        * is in a scrolled window with a viewport.
366        *
367        * To generate the notifications we would need to do the following:
368        * 1) Find the GtkViewport among the ancestors of the objects
369        * 2) Create an accessible for the viewport
370        * 3) Connect to the value-changed signal on the viewport
371        * 4) When the signal is received we need to traverse the children
372        *    of the viewport and check whether the children are visible or not
373        *    visible; we may want to restrict this to the widgets for which
374        *    accessible objects have been created.
375        * 5) We probably need to store a variable on_screen in the
376        *    GtkWidgetAccessible data structure so we can determine whether
377        *    the value has changed.
378        */
379       if (gtk_widget_get_visible (widget))
380         {
381           atk_state_set_add_state (state_set, ATK_STATE_VISIBLE);
382           if (gtk_widget_accessible_on_screen (widget) &&
383               gtk_widget_get_mapped (widget) &&
384               gtk_widget_accessible_all_parents_visible (widget))
385             atk_state_set_add_state (state_set, ATK_STATE_SHOWING);
386         }
387
388       if (gtk_widget_has_focus (widget) && (widget == focus_widget))
389         {
390           AtkObject *focus_obj;
391
392           focus_obj = g_object_get_data (G_OBJECT (accessible), "gail-focus-object");
393           if (focus_obj == NULL)
394             atk_state_set_add_state (state_set, ATK_STATE_FOCUSED);
395         }
396
397       if (gtk_widget_has_default (widget))
398         atk_state_set_add_state (state_set, ATK_STATE_DEFAULT);
399
400       if (GTK_IS_ORIENTABLE (widget))
401         {
402           if (gtk_orientable_get_orientation (GTK_ORIENTABLE (widget)) == GTK_ORIENTATION_HORIZONTAL)
403             atk_state_set_add_state (state_set, ATK_STATE_HORIZONTAL);
404           else
405             atk_state_set_add_state (state_set, ATK_STATE_VERTICAL);
406         }
407     }
408   return state_set;
409 }
410
411 static gint
412 gtk_widget_accessible_get_index_in_parent (AtkObject *accessible)
413 {
414   GtkWidget *widget;
415   GtkWidget *parent_widget;
416   gint index;
417   GList *children;
418
419   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
420
421   if (widget == NULL)
422     return -1;
423
424   if (accessible->accessible_parent)
425     {
426       AtkObject *parent;
427
428       parent = accessible->accessible_parent;
429
430       if (GTK_IS_NOTEBOOK_PAGE_ACCESSIBLE (parent))
431         return 0;
432       else
433         {
434           gint n_children, i;
435           gboolean found = FALSE;
436
437           n_children = atk_object_get_n_accessible_children (parent);
438           for (i = 0; i < n_children; i++)
439             {
440               AtkObject *child;
441
442               child = atk_object_ref_accessible_child (parent, i);
443               if (child == accessible)
444                 found = TRUE;
445
446               g_object_unref (child);
447               if (found)
448                 return i;
449             }
450         }
451     }
452
453   if (!GTK_IS_WIDGET (widget))
454     return -1;
455   parent_widget = gtk_widget_get_parent (widget);
456   if (!GTK_IS_CONTAINER (parent_widget))
457     return -1;
458
459   children = gtk_container_get_children (GTK_CONTAINER (parent_widget));
460
461   index = g_list_index (children, widget);
462   g_list_free (children);
463   return index;
464 }
465
466 /* This function is the default implementation for the notify_gtk
467  * vfunc which gets called when a property changes value on the
468  * GtkWidget associated with a GtkWidgetAccessible. It constructs
469  * an AtkPropertyValues structure and emits a "property_changed"
470  * signal which causes the user specified AtkPropertyChangeHandler
471  * to be called.
472  */
473 static void
474 gtk_widget_accessible_notify_gtk (GObject    *obj,
475                                   GParamSpec *pspec)
476 {
477   GtkWidget* widget = GTK_WIDGET (obj);
478   AtkObject* atk_obj = gtk_widget_get_accessible (widget);
479   AtkState state;
480   gboolean value;
481
482   if (strcmp (pspec->name, "has-focus") == 0)
483     /*
484      * We use focus-in-event and focus-out-event signals to catch
485      * focus changes so we ignore this.
486      */
487     return;
488   else if (strcmp (pspec->name, "visible") == 0)
489     {
490       state = ATK_STATE_VISIBLE;
491       value = gtk_widget_get_visible (widget);
492     }
493   else if (strcmp (pspec->name, "sensitive") == 0)
494     {
495       state = ATK_STATE_SENSITIVE;
496       value = gtk_widget_get_sensitive (widget);
497     }
498   else if (strcmp (pspec->name, "orientation") == 0)
499     {
500       GtkOrientable *orientable;
501
502       orientable = GTK_ORIENTABLE (widget);
503
504       state = ATK_STATE_HORIZONTAL;
505       value = (gtk_orientable_get_orientation (orientable) == GTK_ORIENTATION_HORIZONTAL);
506     }
507   else
508     return;
509
510   atk_object_notify_state_change (atk_obj, state, value);
511   if (state == ATK_STATE_SENSITIVE)
512     atk_object_notify_state_change (atk_obj, ATK_STATE_ENABLED, value);
513
514   if (state == ATK_STATE_HORIZONTAL)
515     atk_object_notify_state_change (atk_obj, ATK_STATE_VERTICAL, !value);
516 }
517
518 /* This function is the default implementation for the focus_gtk
519  * vfunc which gets called for focus_in/out_event.
520  *
521  * It emits a focus-event signal on the GtkWidgetAccessible.
522  */
523 static gboolean
524 gtk_widget_accessible_focus_gtk (GtkWidget     *widget,
525                                  GdkEventFocus *event)
526 {
527   AtkObject* accessible;
528   gboolean return_val;
529   return_val = FALSE;
530
531   accessible = gtk_widget_get_accessible (widget);
532   g_signal_emit_by_name (accessible, "focus_event", event->in, &return_val);
533   return FALSE;
534 }
535
536 static AtkAttributeSet *
537 gtk_widget_accessible_get_attributes (AtkObject *obj)
538 {
539   AtkAttributeSet *attributes;
540   AtkAttribute *toolkit;
541
542   toolkit = g_new (AtkAttribute, 1);
543   toolkit->name = g_strdup ("toolkit");
544   toolkit->value = g_strdup ("gail");
545
546   attributes = g_slist_append (NULL, toolkit);
547
548   return attributes;
549 }
550
551 static void
552 gtk_widget_accessible_class_init (GtkWidgetAccessibleClass *klass)
553 {
554   AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
555   GtkAccessibleClass *accessible_class = GTK_ACCESSIBLE_CLASS (klass);
556
557   klass->notify_gtk = gtk_widget_accessible_notify_gtk;
558   klass->focus_gtk = gtk_widget_accessible_focus_gtk;
559
560   accessible_class->connect_widget_destroyed = gtk_widget_accessible_connect_widget_destroyed;
561
562   class->get_description = gtk_widget_accessible_get_description;
563   class->get_parent = gtk_widget_accessible_get_parent;
564   class->ref_relation_set = gtk_widget_accessible_ref_relation_set;
565   class->ref_state_set = gtk_widget_accessible_ref_state_set;
566   class->get_index_in_parent = gtk_widget_accessible_get_index_in_parent;
567   class->initialize = gtk_widget_accessible_initialize;
568   class->get_attributes = gtk_widget_accessible_get_attributes;
569 }
570
571 static void
572 gtk_widget_accessible_init (GtkWidgetAccessible *accessible)
573 {
574 }
575
576 static guint
577 gtk_widget_accessible_add_focus_handler (AtkComponent    *component,
578                                          AtkFocusHandler  handler)
579 {
580   GSignalMatchType match_type;
581   gulong ret;
582   guint signal_id;
583
584   match_type = G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_FUNC;
585   signal_id = g_signal_lookup ("focus-event", ATK_TYPE_OBJECT);
586
587   ret = g_signal_handler_find (component, match_type, signal_id, 0, NULL,
588                                (gpointer) handler, NULL);
589   if (!ret)
590     return g_signal_connect_closure_by_id (component,
591                                            signal_id, 0,
592                                            g_cclosure_new (G_CALLBACK (handler),
593                                            NULL,
594                                            (GClosureNotify) NULL),
595                                            FALSE);
596   else
597     return 0;
598 }
599
600 static void
601 gtk_widget_accessible_get_extents (AtkComponent   *component,
602                                    gint           *x,
603                                    gint           *y,
604                                    gint           *width,
605                                    gint           *height,
606                                    AtkCoordType    coord_type)
607 {
608   GdkWindow *window;
609   gint x_window, y_window;
610   gint x_toplevel, y_toplevel;
611   GtkWidget *widget;
612   GtkAllocation allocation;
613
614   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component));
615   if (widget == NULL)
616     return;
617
618   gtk_widget_get_allocation (widget, &allocation);
619   *width = allocation.width;
620   *height = allocation.height;
621   if (!gtk_widget_accessible_on_screen (widget) || (!gtk_widget_is_drawable (widget)))
622     {
623       *x = G_MININT;
624       *y = G_MININT;
625       return;
626     }
627
628   if (gtk_widget_get_parent (widget))
629     {
630       *x = allocation.x;
631       *y = allocation.y;
632       window = gtk_widget_get_parent_window (widget);
633     }
634   else
635     {
636       *x = 0;
637       *y = 0;
638       window = gtk_widget_get_window (widget);
639     }
640   gdk_window_get_origin (window, &x_window, &y_window);
641   *x += x_window;
642   *y += y_window;
643
644   if (coord_type == ATK_XY_WINDOW)
645     {
646       window = gdk_window_get_toplevel (gtk_widget_get_window (widget));
647       gdk_window_get_origin (window, &x_toplevel, &y_toplevel);
648
649       *x -= x_toplevel;
650       *y -= y_toplevel;
651     }
652 }
653
654 static void
655 gtk_widget_accessible_get_size (AtkComponent *component,
656                                 gint         *width,
657                                 gint         *height)
658 {
659   GtkWidget *widget;
660
661   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component));
662   if (widget == NULL)
663     return;
664
665   *width = gtk_widget_get_allocated_width (widget);
666   *height = gtk_widget_get_allocated_height (widget);
667 }
668
669 static AtkLayer
670 gtk_widget_accessible_get_layer (AtkComponent *component)
671 {
672   gint layer;
673
674   layer = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (component), "atk-component-layer"));
675
676   return (AtkLayer) layer;
677 }
678
679 static gboolean
680 gtk_widget_accessible_grab_focus (AtkComponent *component)
681 {
682   GtkWidget *widget;
683   GtkWidget *toplevel;
684
685   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component));
686   if (!widget)
687     return FALSE;
688
689   if (!gtk_widget_get_can_focus (widget))
690     return FALSE;
691
692   gtk_widget_grab_focus (widget);
693   toplevel = gtk_widget_get_toplevel (widget);
694   if (gtk_widget_is_toplevel (toplevel))
695     {
696 #ifdef GDK_WINDOWING_X11
697       gtk_window_present_with_time (GTK_WINDOW (toplevel),
698       gdk_x11_get_server_time (gtk_widget_get_window (widget)));
699 #else
700       gtk_window_present (GTK_WINDOW (toplevel));
701 #endif
702     }
703   return TRUE;
704 }
705
706 static void
707 gtk_widget_accessible_remove_focus_handler (AtkComponent *component,
708                                             guint         handler_id)
709 {
710   g_signal_handler_disconnect (component, handler_id);
711 }
712
713 static gboolean
714 gtk_widget_accessible_set_extents (AtkComponent *component,
715                                    gint          x,
716                                    gint          y,
717                                    gint          width,
718                                    gint          height,
719                                    AtkCoordType  coord_type)
720 {
721   GtkWidget *widget;
722
723   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component));
724   if (widget == NULL)
725     return FALSE;
726
727   if (!gtk_widget_is_toplevel (widget))
728     return FALSE;
729
730   if (coord_type == ATK_XY_WINDOW)
731     {
732       gint x_current, y_current;
733       GdkWindow *window = gtk_widget_get_window (widget);
734
735       gdk_window_get_origin (window, &x_current, &y_current);
736       x_current += x;
737       y_current += y;
738       if (x_current < 0 || y_current < 0)
739         return FALSE;
740       else
741         {
742           gtk_window_move (GTK_WINDOW (widget), x_current, y_current);
743           gtk_widget_set_size_request (widget, width, height);
744           return TRUE;
745         }
746     }
747   else if (coord_type == ATK_XY_SCREEN)
748     {
749       gtk_window_move (GTK_WINDOW (widget), x, y);
750       gtk_widget_set_size_request (widget, width, height);
751       return TRUE;
752     }
753   return FALSE;
754 }
755
756 static gboolean
757 gtk_widget_accessible_set_position (AtkComponent *component,
758                                     gint          x,
759                                     gint          y,
760                                     AtkCoordType  coord_type)
761 {
762   GtkWidget *widget;
763
764   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component));
765   if (widget == NULL)
766     return FALSE;
767
768   if (gtk_widget_is_toplevel (widget))
769     {
770       if (coord_type == ATK_XY_WINDOW)
771         {
772           gint x_current, y_current;
773           GdkWindow *window = gtk_widget_get_window (widget);
774
775           gdk_window_get_origin (window, &x_current, &y_current);
776           x_current += x;
777           y_current += y;
778           if (x_current < 0 || y_current < 0)
779             return FALSE;
780           else
781             {
782               gtk_window_move (GTK_WINDOW (widget), x_current, y_current);
783               return TRUE;
784             }
785         }
786       else if (coord_type == ATK_XY_SCREEN)
787         {
788           gtk_window_move (GTK_WINDOW (widget), x, y);
789           return TRUE;
790         }
791     }
792   return FALSE;
793 }
794
795 static gboolean
796 gtk_widget_accessible_set_size (AtkComponent *component,
797                                 gint          width,
798                                 gint          height)
799 {
800   GtkWidget *widget;
801
802   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component));
803   if (widget == NULL)
804     return FALSE;
805
806   if (gtk_widget_is_toplevel (widget))
807     {
808       gtk_widget_set_size_request (widget, width, height);
809       return TRUE;
810     }
811   else
812    return FALSE;
813 }
814
815 static void
816 atk_component_interface_init (AtkComponentIface *iface)
817 {
818   iface->add_focus_handler = gtk_widget_accessible_add_focus_handler;
819   iface->get_extents = gtk_widget_accessible_get_extents;
820   iface->get_size = gtk_widget_accessible_get_size;
821   iface->get_layer = gtk_widget_accessible_get_layer;
822   iface->grab_focus = gtk_widget_accessible_grab_focus;
823   iface->remove_focus_handler = gtk_widget_accessible_remove_focus_handler;
824   iface->set_extents = gtk_widget_accessible_set_extents;
825   iface->set_position = gtk_widget_accessible_set_position;
826   iface->set_size = gtk_widget_accessible_set_size;
827 }
828
829 /* This function checks whether the widget has an ancestor which is
830  * a GtkViewport and, if so, whether any part of the widget intersects
831  * the visible rectangle of the GtkViewport.
832  */
833 static gboolean
834 gtk_widget_accessible_on_screen (GtkWidget *widget)
835 {
836   GtkAllocation allocation;
837   GtkWidget *viewport;
838   gboolean return_value;
839
840   gtk_widget_get_allocation (widget, &allocation);
841
842   viewport = gtk_widget_get_ancestor (widget, GTK_TYPE_VIEWPORT);
843   if (viewport)
844     {
845       GtkAllocation viewport_allocation;
846       GtkAdjustment *adjustment;
847       GdkRectangle visible_rect;
848
849       gtk_widget_get_allocation (viewport, &viewport_allocation);
850
851       adjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (viewport));
852       visible_rect.y = gtk_adjustment_get_value (adjustment);
853       adjustment = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (viewport));
854       visible_rect.x = gtk_adjustment_get_value (adjustment);
855       visible_rect.width = viewport_allocation.width;
856       visible_rect.height = viewport_allocation.height;
857
858       if (((allocation.x + allocation.width) < visible_rect.x) ||
859          ((allocation.y + allocation.height) < visible_rect.y) ||
860          (allocation.x > (visible_rect.x + visible_rect.width)) ||
861          (allocation.y > (visible_rect.y + visible_rect.height)))
862         return_value = FALSE;
863       else
864         return_value = TRUE;
865     }
866   else
867     {
868       /* Check whether the widget has been placed of the screen.
869        * The widget may be MAPPED as when toolbar items do not
870        * fit on the toolbar.
871        */
872       if (allocation.x + allocation.width <= 0 &&
873           allocation.y + allocation.height <= 0)
874         return_value = FALSE;
875       else
876         return_value = TRUE;
877     }
878
879   return return_value;
880 }
881
882 /* Checks if all the predecessors (the parent widget, his parent, etc)
883  * are visible Used to check properly the SHOWING state.
884  */
885 static gboolean
886 gtk_widget_accessible_all_parents_visible (GtkWidget *widget)
887 {
888   GtkWidget *iter_parent = NULL;
889   gboolean result = TRUE;
890
891   for (iter_parent = gtk_widget_get_parent (widget); iter_parent;
892        iter_parent = gtk_widget_get_parent (iter_parent))
893     {
894       if (!gtk_widget_get_visible (iter_parent))
895         {
896           result = FALSE;
897           break;
898         }
899     }
900
901   return result;
902 }