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