]> Pileus Git - ~andy/gtk/blob - gtk/a11y/gtktoplevelaccessible.c
157abf015705cdaf5f1ae42d545c2d9b1047adfe
[~andy/gtk] / gtk / a11y / gtktoplevelaccessible.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 #include <gtk/gtkeventbox.h>
27 #include <gtk/gtkscrolledwindow.h>
28 #include <gtk/gtkframe.h>
29 #include <gtk/gtkmenu.h>
30 #include <gtk/gtkmenuitem.h>
31 #include <gtk/gtkbutton.h>
32
33 #include "gtktoplevelaccessible.h"
34
35
36 G_DEFINE_TYPE (GtkToplevelAccessible, _gtk_toplevel_accessible, ATK_TYPE_OBJECT)
37
38 static void
39 gtk_toplevel_accessible_initialize (AtkObject *accessible,
40                                     gpointer   data)
41 {
42   ATK_OBJECT_CLASS (_gtk_toplevel_accessible_parent_class)->initialize (accessible, data);
43
44   accessible->role = ATK_ROLE_APPLICATION;
45   accessible->name = g_get_prgname ();
46   accessible->accessible_parent = NULL;
47 }
48
49 static void
50 gtk_toplevel_accessible_object_finalize (GObject *obj)
51 {
52   GtkToplevelAccessible *toplevel = GTK_TOPLEVEL_ACCESSIBLE (obj);
53
54   if (toplevel->window_list)
55     g_list_free (toplevel->window_list);
56
57   G_OBJECT_CLASS (_gtk_toplevel_accessible_parent_class)->finalize (obj);
58 }
59
60 static gint
61 gtk_toplevel_accessible_get_n_children (AtkObject *obj)
62 {
63   GtkToplevelAccessible *toplevel = GTK_TOPLEVEL_ACCESSIBLE (obj);
64
65   return g_list_length (toplevel->window_list);
66 }
67
68 static AtkObject *
69 gtk_toplevel_accessible_ref_child (AtkObject *obj,
70                                    gint       i)
71 {
72   GtkToplevelAccessible *toplevel;
73   GtkWidget *widget;
74   AtkObject *atk_obj;
75
76   toplevel = GTK_TOPLEVEL_ACCESSIBLE (obj);
77   widget = g_list_nth_data (toplevel->window_list, i);
78   if (!widget)
79     return NULL;
80
81   atk_obj = gtk_widget_get_accessible (widget);
82
83   g_object_ref (atk_obj);
84
85   return atk_obj;
86 }
87
88 static gboolean
89 is_combo_window (GtkWidget *widget)
90 {
91   GtkWidget *child;
92   AtkObject *obj;
93
94   child = gtk_bin_get_child (GTK_BIN (widget));
95
96   if (!GTK_IS_EVENT_BOX (child))
97     return FALSE;
98
99   child = gtk_bin_get_child (GTK_BIN (child));
100
101   if (!GTK_IS_FRAME (child))
102     return FALSE;
103
104   child = gtk_bin_get_child (GTK_BIN (child));
105
106   if (!GTK_IS_SCROLLED_WINDOW (child))
107     return FALSE;
108
109   obj = gtk_widget_get_accessible (child);
110   obj = atk_object_get_parent (obj);
111
112   return FALSE;
113 }
114
115 static gboolean
116 is_attached_menu_window (GtkWidget *widget)
117 {
118   GtkWidget *child;
119
120   child = gtk_bin_get_child (GTK_BIN (widget));
121   if (GTK_IS_MENU (child))
122     {
123       GtkWidget *attach;
124
125       attach = gtk_menu_get_attach_widget (GTK_MENU (child));
126       /* Allow for menu belonging to the Panel Menu, which is a GtkButton */
127       if (GTK_IS_MENU_ITEM (attach) || GTK_IS_BUTTON (attach))
128         return TRUE;
129     }
130
131   return FALSE;
132 }
133
134 static void
135 _gtk_toplevel_accessible_class_init (GtkToplevelAccessibleClass *klass)
136 {
137   AtkObjectClass *class = ATK_OBJECT_CLASS(klass);
138   GObjectClass *g_object_class = G_OBJECT_CLASS(klass);
139
140   class->initialize = gtk_toplevel_accessible_initialize;
141   class->get_n_children = gtk_toplevel_accessible_get_n_children;
142   class->ref_child = gtk_toplevel_accessible_ref_child;
143   class->get_parent = NULL;
144
145   g_object_class->finalize = gtk_toplevel_accessible_object_finalize;
146 }
147
148 static void
149 remove_child (GtkToplevelAccessible *toplevel,
150               GtkWindow             *window)
151 {
152   AtkObject *atk_obj = ATK_OBJECT (toplevel);
153   GList *l;
154   guint window_count = 0;
155   AtkObject *child;
156
157   if (toplevel->window_list)
158     {
159       GtkWindow *tmp_window;
160
161       for (l = toplevel->window_list; l; l = l->next)
162         {
163           tmp_window = GTK_WINDOW (l->data);
164
165           if (window == tmp_window)
166             {
167               /* Remove the window from the window_list & emit the signal */
168               toplevel->window_list = g_list_delete_link (toplevel->window_list, l);
169               child = gtk_widget_get_accessible (GTK_WIDGET (window));
170               g_signal_emit_by_name (atk_obj, "children-changed::remove",
171                                      window_count, child, NULL);
172               atk_object_set_parent (child, NULL);
173               break;
174             }
175
176           window_count++;
177         }
178     }
179 }
180
181 static gboolean
182 show_event_watcher (GSignalInvocationHint *ihint,
183                     guint                  n_param_values,
184                     const GValue          *param_values,
185                     gpointer               data)
186 {
187   GtkToplevelAccessible *toplevel = GTK_TOPLEVEL_ACCESSIBLE (data);
188   AtkObject *atk_obj = ATK_OBJECT (toplevel);
189   GObject *object;
190   GtkWidget *widget;
191   gint n_children;
192   AtkObject *child;
193
194   object = g_value_get_object (param_values + 0);
195
196   if (!GTK_IS_WINDOW (object))
197     return TRUE;
198
199   widget = GTK_WIDGET (object);
200   if (gtk_widget_get_parent (widget) ||
201       is_attached_menu_window (widget) ||
202 #ifdef GDK_WINDOWING_X11
203       GTK_IS_PLUG (widget) ||
204 #endif
205       is_combo_window (widget))
206     return TRUE;
207
208   child = gtk_widget_get_accessible (widget);
209   if (atk_object_get_role (child) == ATK_ROLE_REDUNDANT_OBJECT ||
210       atk_object_get_role (child) == ATK_ROLE_TOOL_TIP)
211     return TRUE;
212
213   /* Add the window to the list & emit the signal */
214   toplevel->window_list = g_list_append (toplevel->window_list, widget);
215   n_children = g_list_length (toplevel->window_list);
216
217   atk_object_set_parent (child, atk_obj);
218   g_signal_emit_by_name (atk_obj, "children-changed::add",
219                          n_children - 1, child, NULL);
220
221   g_signal_connect_swapped (G_OBJECT(object), "destroy",
222                             G_CALLBACK (remove_child), toplevel);
223
224   return TRUE;
225 }
226
227 static gboolean
228 hide_event_watcher (GSignalInvocationHint *ihint,
229                     guint                  n_param_values,
230                     const GValue          *param_values,
231                     gpointer               data)
232 {
233   GtkToplevelAccessible *toplevel = GTK_TOPLEVEL_ACCESSIBLE (data);
234   GObject *object;
235
236   object = g_value_get_object (param_values + 0);
237
238   if (!GTK_IS_WINDOW (object))
239     return TRUE;
240
241   remove_child (toplevel, GTK_WINDOW (object));
242   return TRUE;
243 }
244
245 static void
246 _gtk_toplevel_accessible_init (GtkToplevelAccessible *toplevel)
247 {
248   GtkWindow *window;
249   GtkWidget *widget;
250   GList *l;
251   guint signal_id;
252
253   l = toplevel->window_list = gtk_window_list_toplevels ();
254
255   while (l)
256     {
257       window = GTK_WINDOW (l->data);
258       widget = GTK_WIDGET (window);
259       if (!window ||
260           !gtk_widget_get_visible (widget) ||
261           is_attached_menu_window (widget) ||
262 #ifdef GDK_WINDOWING_X11
263           GTK_IS_PLUG (window) ||
264 #endif
265           gtk_widget_get_parent (GTK_WIDGET (window)))
266         {
267           GList *temp_l  = l->next;
268
269           toplevel->window_list = g_list_delete_link (toplevel->window_list, l);
270           l = temp_l;
271         }
272       else
273         {
274           g_signal_connect_swapped (G_OBJECT (window), "destroy",
275                                     G_CALLBACK (remove_child), toplevel);
276           l = l->next;
277         }
278     }
279
280   g_type_class_ref (GTK_TYPE_WINDOW);
281
282   signal_id  = g_signal_lookup ("show", GTK_TYPE_WINDOW);
283   g_signal_add_emission_hook (signal_id, 0,
284                               show_event_watcher, toplevel, (GDestroyNotify) NULL);
285
286   signal_id  = g_signal_lookup ("hide", GTK_TYPE_WINDOW);
287   g_signal_add_emission_hook (signal_id, 0,
288                               hide_event_watcher, toplevel, (GDestroyNotify) NULL);
289 }