]> Pileus Git - ~andy/gtk/blob - gtk/a11y/gailtoplevel.c
gail: Move from modules/other/gail to gtk/a11y
[~andy/gtk] / gtk / a11y / gailtoplevel.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 <stdlib.h>
23 #include <string.h>
24
25 #include <gtk/gtkx.h>
26
27 #include "gailtoplevel.h"
28
29 static void             gail_toplevel_class_init        (GailToplevelClass      *klass);
30 static void             gail_toplevel_init              (GailToplevel           *toplevel);
31 static void             gail_toplevel_initialize        (AtkObject              *accessible,
32                                                          gpointer                data);
33 static void             gail_toplevel_object_finalize   (GObject                *obj);
34
35 /* atkobject.h */
36
37 static gint             gail_toplevel_get_n_children    (AtkObject              *obj);
38 static AtkObject*       gail_toplevel_ref_child         (AtkObject              *obj,
39                                                         gint                    i);
40 static AtkObject*       gail_toplevel_get_parent        (AtkObject              *obj);
41
42 /* Callbacks */
43
44
45 static void             gail_toplevel_window_destroyed  (GtkWindow              *window,
46                                                         GailToplevel            *text);
47 static gboolean         gail_toplevel_hide_event_watcher (GSignalInvocationHint *ihint,
48                                                         guint                   n_param_values,
49                                                         const GValue            *param_values,
50                                                         gpointer                data);
51 static gboolean         gail_toplevel_show_event_watcher (GSignalInvocationHint *ihint,
52                                                         guint                   n_param_values,
53                                                         const GValue            *param_values,
54                                                         gpointer                data);
55
56 /* Misc */
57
58 static void      _gail_toplevel_remove_child            (GailToplevel           *toplevel,
59                                                         GtkWindow               *window);
60 static gboolean  is_attached_menu_window                (GtkWidget              *widget);
61 static gboolean  is_combo_window                        (GtkWidget              *widget);
62
63
64 G_DEFINE_TYPE (GailToplevel, gail_toplevel, ATK_TYPE_OBJECT)
65
66 static void
67 gail_toplevel_class_init (GailToplevelClass *klass)
68 {
69   AtkObjectClass *class = ATK_OBJECT_CLASS(klass);
70   GObjectClass *g_object_class = G_OBJECT_CLASS(klass);
71
72   class->initialize = gail_toplevel_initialize;
73   class->get_n_children = gail_toplevel_get_n_children;
74   class->ref_child = gail_toplevel_ref_child;
75   class->get_parent = gail_toplevel_get_parent;
76
77   g_object_class->finalize = gail_toplevel_object_finalize;
78 }
79
80 static void
81 gail_toplevel_init (GailToplevel *toplevel)
82 {
83   GtkWindow *window;
84   GtkWidget *widget;
85   GList *l;
86   guint signal_id;
87   
88   l = toplevel->window_list = gtk_window_list_toplevels ();
89
90   while (l)
91     {
92       window = GTK_WINDOW (l->data);
93       widget = GTK_WIDGET (window);
94       if (!window || 
95           !gtk_widget_get_visible (widget) ||
96           is_attached_menu_window (widget) ||
97 #ifdef GDK_WINDOWING_X11
98           GTK_IS_PLUG (window) ||
99 #endif
100           gtk_widget_get_parent (GTK_WIDGET (window)))
101         {
102           GList *temp_l  = l->next;
103
104           toplevel->window_list = g_list_delete_link (toplevel->window_list, l);
105           l = temp_l;
106         }
107       else
108         {
109           g_signal_connect (G_OBJECT (window), 
110                             "destroy",
111                             G_CALLBACK (gail_toplevel_window_destroyed),
112                             toplevel);
113           l = l->next;
114         }
115     }
116
117   g_type_class_ref (GTK_TYPE_WINDOW);
118
119   signal_id  = g_signal_lookup ("show", GTK_TYPE_WINDOW);
120   g_signal_add_emission_hook (signal_id, 0,
121     gail_toplevel_show_event_watcher, toplevel, (GDestroyNotify) NULL);
122
123   signal_id  = g_signal_lookup ("hide", GTK_TYPE_WINDOW);
124   g_signal_add_emission_hook (signal_id, 0,
125     gail_toplevel_hide_event_watcher, toplevel, (GDestroyNotify) NULL);
126 }
127
128 static void
129 gail_toplevel_initialize (AtkObject *accessible,
130                           gpointer  data)
131 {
132   ATK_OBJECT_CLASS (gail_toplevel_parent_class)->initialize (accessible, data);
133
134   accessible->role = ATK_ROLE_APPLICATION;
135   accessible->name = g_get_prgname();
136   accessible->accessible_parent = NULL;
137 }
138
139 static void
140 gail_toplevel_object_finalize (GObject *obj)
141 {
142   GailToplevel *toplevel = GAIL_TOPLEVEL (obj);
143
144   if (toplevel->window_list)
145     g_list_free (toplevel->window_list);
146
147   G_OBJECT_CLASS (gail_toplevel_parent_class)->finalize (obj);
148 }
149
150 static AtkObject*
151 gail_toplevel_get_parent (AtkObject *obj)
152 {
153     return NULL;
154 }
155
156 static gint
157 gail_toplevel_get_n_children (AtkObject *obj)
158 {
159   GailToplevel *toplevel = GAIL_TOPLEVEL (obj);
160
161   gint rc = g_list_length (toplevel->window_list);
162   return rc;
163 }
164
165 static AtkObject*
166 gail_toplevel_ref_child (AtkObject *obj,
167                          gint      i)
168 {
169   GailToplevel *toplevel;
170   gpointer ptr;
171   GtkWidget *widget;
172   AtkObject *atk_obj;
173
174   toplevel = GAIL_TOPLEVEL (obj);
175   ptr = g_list_nth_data (toplevel->window_list, i);
176   if (!ptr)
177     return NULL;
178   widget = GTK_WIDGET (ptr);
179   atk_obj = gtk_widget_get_accessible (widget);
180
181   g_object_ref (atk_obj);
182   return atk_obj;
183 }
184
185 /*
186  * Window destroy events on GtkWindow cause a child to be removed
187  * from the toplevel
188  */
189 static void
190 gail_toplevel_window_destroyed (GtkWindow    *window,
191                                 GailToplevel *toplevel)
192 {
193   _gail_toplevel_remove_child (toplevel, window);
194 }
195
196 /*
197  * Show events cause a child to be added to the toplevel
198  */
199 static gboolean
200 gail_toplevel_show_event_watcher (GSignalInvocationHint *ihint,
201                                   guint                  n_param_values,
202                                   const GValue          *param_values,
203                                   gpointer               data)
204 {
205   GailToplevel *toplevel = GAIL_TOPLEVEL (data);
206   AtkObject *atk_obj = ATK_OBJECT (toplevel);
207   GObject *object;
208   GtkWidget *widget;
209   gint n_children;
210   AtkObject *child;
211
212   object = g_value_get_object (param_values + 0);
213
214   if (!GTK_IS_WINDOW (object))
215     return TRUE;
216
217   widget = GTK_WIDGET (object);
218   if (gtk_widget_get_parent (widget) ||
219       is_attached_menu_window (widget) ||
220 #ifdef GDK_WINDOWING_X11
221       GTK_IS_PLUG (widget) ||
222 #endif
223       is_combo_window (widget))
224     return TRUE;
225
226   child = gtk_widget_get_accessible (widget);
227   if (atk_object_get_role (child) == ATK_ROLE_REDUNDANT_OBJECT)
228     {
229       return TRUE;
230     }
231
232   /* 
233    * Add the window to the list & emit the signal.
234    * Don't do this for tooltips (Bug #150649).
235    */
236   if (atk_object_get_role (child) == ATK_ROLE_TOOL_TIP)
237     {
238       return TRUE;
239     }
240
241   toplevel->window_list = g_list_append (toplevel->window_list, widget);
242
243   n_children = g_list_length (toplevel->window_list);
244
245   /*
246    * Must subtract 1 from the n_children since the index is 0-based
247    * but g_list_length is 1-based.
248    */
249   atk_object_set_parent (child, atk_obj);
250   g_signal_emit_by_name (atk_obj, "children-changed::add",
251                          n_children - 1, 
252                          child, NULL);
253
254   /* Connect destroy signal callback */
255   g_signal_connect (G_OBJECT(object), 
256                     "destroy",
257                     G_CALLBACK (gail_toplevel_window_destroyed),
258                     toplevel);
259
260   return TRUE;
261 }
262
263 /*
264  * Hide events on GtkWindow cause a child to be removed from the toplevel
265  */
266 static gboolean
267 gail_toplevel_hide_event_watcher (GSignalInvocationHint *ihint,
268                                   guint                  n_param_values,
269                                   const GValue          *param_values,
270                                   gpointer               data)
271 {
272   GailToplevel *toplevel = GAIL_TOPLEVEL (data);
273   GObject *object;
274
275   object = g_value_get_object (param_values + 0);
276
277   if (!GTK_IS_WINDOW (object))
278     return TRUE;
279
280   _gail_toplevel_remove_child (toplevel, GTK_WINDOW (object));
281   return TRUE;
282 }
283
284 /*
285  * Common code used by destroy and hide events on GtkWindow
286  */
287 static void
288 _gail_toplevel_remove_child (GailToplevel *toplevel, 
289                              GtkWindow    *window)
290 {
291   AtkObject *atk_obj = ATK_OBJECT (toplevel);
292   GList *l;
293   guint window_count = 0;
294   AtkObject *child;
295
296   if (toplevel->window_list)
297     {
298         GtkWindow *tmp_window;
299
300         /* Must loop through them all */
301         for (l = toplevel->window_list; l; l = l->next)
302         {
303           tmp_window = GTK_WINDOW (l->data);
304
305           if (window == tmp_window)
306             {
307               /* Remove the window from the window_list & emit the signal */
308               toplevel->window_list = g_list_remove (toplevel->window_list,
309                                                      l->data);
310               child = gtk_widget_get_accessible (GTK_WIDGET (window));
311               g_signal_emit_by_name (atk_obj, "children-changed::remove",
312                                      window_count, 
313                                      child, NULL);
314               atk_object_set_parent (child, NULL);
315               break;
316             }
317
318           window_count++;
319         }
320     }
321 }
322
323 static gboolean
324 is_attached_menu_window (GtkWidget *widget)
325 {
326   GtkWidget *child;
327   gboolean ret = FALSE;
328
329   child = gtk_bin_get_child (GTK_BIN (widget));
330   if (GTK_IS_MENU (child))
331     {
332       GtkWidget *attach;
333
334       attach = gtk_menu_get_attach_widget (GTK_MENU (child));
335       /* Allow for menu belonging to the Panel Menu, which is a GtkButton */
336       if (GTK_IS_MENU_ITEM (attach) ||
337           GTK_IS_BUTTON (attach))
338         ret = TRUE;
339     }
340   return ret;
341 }
342
343 static gboolean
344 is_combo_window (GtkWidget *widget)
345 {
346   GtkWidget *child;
347   AtkObject *obj;
348
349   child = gtk_bin_get_child (GTK_BIN (widget));
350
351   if (!GTK_IS_EVENT_BOX (child))
352     return FALSE;
353
354   child = gtk_bin_get_child (GTK_BIN (child));
355
356   if (!GTK_IS_FRAME (child))
357     return FALSE;
358
359   child = gtk_bin_get_child (GTK_BIN (child));
360
361   if (!GTK_IS_SCROLLED_WINDOW (child))
362     return FALSE;
363
364   obj = gtk_widget_get_accessible (child);
365   obj = atk_object_get_parent (obj);
366
367   return  FALSE;
368 }