1 /* GAIL - The GNOME Accessibility Implementation Library
2 * Copyright 2001, 2002, 2003 Sun Microsystems Inc.
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.
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.
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.
25 #include "gailtoplevel.h"
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,
31 static void gail_toplevel_object_finalize (GObject *obj);
35 static gint gail_toplevel_get_n_children (AtkObject *obj);
36 static AtkObject* gail_toplevel_ref_child (AtkObject *obj,
38 static AtkObject* gail_toplevel_get_parent (AtkObject *obj);
43 static void gail_toplevel_window_destroyed (GtkWindow *window,
45 static gboolean gail_toplevel_hide_event_watcher (GSignalInvocationHint *ihint,
47 const GValue *param_values,
49 static gboolean gail_toplevel_show_event_watcher (GSignalInvocationHint *ihint,
51 const GValue *param_values,
56 static void _gail_toplevel_remove_child (GailToplevel *toplevel,
58 static gboolean is_attached_menu_window (GtkWidget *widget);
59 static gboolean is_combo_window (GtkWidget *widget);
62 G_DEFINE_TYPE (GailToplevel, gail_toplevel, ATK_TYPE_OBJECT)
65 gail_toplevel_class_init (GailToplevelClass *klass)
67 AtkObjectClass *class = ATK_OBJECT_CLASS(klass);
68 GObjectClass *g_object_class = G_OBJECT_CLASS(klass);
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;
75 g_object_class->finalize = gail_toplevel_object_finalize;
79 gail_toplevel_init (GailToplevel *toplevel)
86 l = toplevel->window_list = gtk_window_list_toplevels ();
90 window = GTK_WINDOW (l->data);
91 widget = GTK_WIDGET (window);
93 !GTK_WIDGET_VISIBLE (widget) ||
94 is_attached_menu_window (widget) ||
95 GTK_WIDGET (window)->parent ||
98 GList *temp_l = l->next;
100 toplevel->window_list = g_list_delete_link (toplevel->window_list, l);
105 g_signal_connect (G_OBJECT (window),
107 G_CALLBACK (gail_toplevel_window_destroyed),
113 g_type_class_ref (GTK_TYPE_WINDOW);
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);
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);
125 gail_toplevel_initialize (AtkObject *accessible,
128 ATK_OBJECT_CLASS (gail_toplevel_parent_class)->initialize (accessible, data);
130 accessible->role = ATK_ROLE_APPLICATION;
131 accessible->name = g_get_prgname();
132 accessible->accessible_parent = NULL;
136 gail_toplevel_object_finalize (GObject *obj)
138 GailToplevel *toplevel = GAIL_TOPLEVEL (obj);
140 if (toplevel->window_list)
141 g_list_free (toplevel->window_list);
143 G_OBJECT_CLASS (gail_toplevel_parent_class)->finalize (obj);
147 gail_toplevel_get_parent (AtkObject *obj)
153 gail_toplevel_get_n_children (AtkObject *obj)
155 GailToplevel *toplevel = GAIL_TOPLEVEL (obj);
157 gint rc = g_list_length (toplevel->window_list);
162 gail_toplevel_ref_child (AtkObject *obj,
165 GailToplevel *toplevel;
170 toplevel = GAIL_TOPLEVEL (obj);
171 ptr = g_list_nth_data (toplevel->window_list, i);
174 widget = GTK_WIDGET (ptr);
175 atk_obj = gtk_widget_get_accessible (widget);
177 g_object_ref (atk_obj);
182 * Window destroy events on GtkWindow cause a child to be removed
186 gail_toplevel_window_destroyed (GtkWindow *window,
187 GailToplevel *toplevel)
189 _gail_toplevel_remove_child (toplevel, window);
193 * Show events cause a child to be added to the toplevel
196 gail_toplevel_show_event_watcher (GSignalInvocationHint *ihint,
197 guint n_param_values,
198 const GValue *param_values,
201 GailToplevel *toplevel = GAIL_TOPLEVEL (data);
202 AtkObject *atk_obj = ATK_OBJECT (toplevel);
208 object = g_value_get_object (param_values + 0);
210 if (!GTK_IS_WINDOW (object))
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))
220 child = gtk_widget_get_accessible (widget);
221 if (!strcmp (atk_role_get_name (atk_object_get_role (child)), "redundant object"))
226 child = gtk_widget_get_accessible (widget);
227 if (!strcmp (atk_role_get_name (atk_object_get_role (child)), "redundant object"))
233 * Add the window to the list & emit the signal.
234 * Don't do this for tooltips (Bug #150649).
236 if (atk_object_get_role (child) != ATK_ROLE_TOOL_TIP)
238 toplevel->window_list = g_list_append (toplevel->window_list, widget);
240 n_children = g_list_length (toplevel->window_list);
243 * Must subtract 1 from the n_children since the index is 0-based
244 * but g_list_length is 1-based.
246 atk_object_set_parent (child, atk_obj);
247 g_signal_emit_by_name (atk_obj, "children-changed::add",
252 /* Connect destroy signal callback */
253 g_signal_connect (G_OBJECT(object),
255 G_CALLBACK (gail_toplevel_window_destroyed),
262 * Hide events on GtkWindow cause a child to be removed from the toplevel
265 gail_toplevel_hide_event_watcher (GSignalInvocationHint *ihint,
266 guint n_param_values,
267 const GValue *param_values,
270 GailToplevel *toplevel = GAIL_TOPLEVEL (data);
273 object = g_value_get_object (param_values + 0);
275 if (!GTK_IS_WINDOW (object))
278 _gail_toplevel_remove_child (toplevel, GTK_WINDOW (object));
283 * Common code used by destroy and hide events on GtkWindow
286 _gail_toplevel_remove_child (GailToplevel *toplevel,
289 AtkObject *atk_obj = ATK_OBJECT (toplevel);
291 guint window_count = 0;
294 if (toplevel->window_list)
296 GtkWindow *tmp_window;
298 /* Must loop through them all */
299 for (l = toplevel->window_list; l; l = l->next)
301 tmp_window = GTK_WINDOW (l->data);
303 if (window == tmp_window)
305 /* Remove the window from the window_list & emit the signal */
306 toplevel->window_list = g_list_remove (toplevel->window_list,
308 child = gtk_widget_get_accessible (GTK_WIDGET (window));
309 g_signal_emit_by_name (atk_obj, "children-changed::remove",
312 atk_object_set_parent (child, NULL);
322 is_attached_menu_window (GtkWidget *widget)
324 GtkWidget *child = GTK_BIN (widget)->child;
325 gboolean ret = FALSE;
327 if (GTK_IS_MENU (child))
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))
342 is_combo_window (GtkWidget *widget)
344 GtkWidget *child = GTK_BIN (widget)->child;
346 GtkAccessible *accessible;
348 if (!GTK_IS_EVENT_BOX (child))
351 child = GTK_BIN (child)->child;
353 if (!GTK_IS_FRAME (child))
356 child = GTK_BIN (child)->child;
358 if (!GTK_IS_SCROLLED_WINDOW (child))
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))