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