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