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