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.
26 #include "gailwindow.h"
27 #include "gailtoplevel.h"
42 static void gail_window_class_init (GailWindowClass *klass);
44 static void gail_window_init (GailWindow *accessible);
46 static void gail_window_real_initialize (AtkObject *obj,
49 static const gchar* gail_window_get_name (AtkObject *accessible);
51 static AtkObject* gail_window_get_parent (AtkObject *accessible);
52 static gint gail_window_get_index_in_parent (AtkObject *accessible);
53 static gboolean gail_window_real_focus_gtk (GtkWidget *widget,
54 GdkEventFocus *event);
56 static AtkStateSet* gail_window_ref_state_set (AtkObject *accessible);
57 static AtkRelationSet* gail_window_ref_relation_set (AtkObject *accessible);
58 static void gail_window_real_notify_gtk (GObject *obj,
60 static gint gail_window_get_mdi_zorder (AtkComponent *component);
62 static gboolean gail_window_state_event_gtk (GtkWidget *widget,
63 GdkEventWindowState *event);
66 static void atk_component_interface_init (AtkComponentIface *iface);
68 static void gail_window_get_extents (AtkComponent *component,
73 AtkCoordType coord_type);
74 static void gail_window_get_size (AtkComponent *component,
78 static guint gail_window_signals [LAST_SIGNAL] = { 0, };
80 G_DEFINE_TYPE_WITH_CODE (GailWindow, gail_window, GAIL_TYPE_CONTAINER,
81 G_IMPLEMENT_INTERFACE (ATK_TYPE_COMPONENT, atk_component_interface_init))
84 gail_window_class_init (GailWindowClass *klass)
86 GailWidgetClass *widget_class;
87 AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
89 widget_class = (GailWidgetClass*)klass;
90 widget_class->focus_gtk = gail_window_real_focus_gtk;
91 widget_class->notify_gtk = gail_window_real_notify_gtk;
93 class->get_name = gail_window_get_name;
94 class->get_parent = gail_window_get_parent;
95 class->get_index_in_parent = gail_window_get_index_in_parent;
96 class->ref_relation_set = gail_window_ref_relation_set;
97 class->ref_state_set = gail_window_ref_state_set;
98 class->initialize = gail_window_real_initialize;
100 gail_window_signals [ACTIVATE] =
101 g_signal_new ("activate",
102 G_TYPE_FROM_CLASS (klass),
104 0, /* default signal handler */
106 g_cclosure_marshal_VOID__VOID,
108 gail_window_signals [CREATE] =
109 g_signal_new ("create",
110 G_TYPE_FROM_CLASS (klass),
112 0, /* default signal handler */
114 g_cclosure_marshal_VOID__VOID,
116 gail_window_signals [DEACTIVATE] =
117 g_signal_new ("deactivate",
118 G_TYPE_FROM_CLASS (klass),
120 0, /* default signal handler */
122 g_cclosure_marshal_VOID__VOID,
124 gail_window_signals [DESTROY] =
125 g_signal_new ("destroy",
126 G_TYPE_FROM_CLASS (klass),
128 0, /* default signal handler */
130 g_cclosure_marshal_VOID__VOID,
132 gail_window_signals [MAXIMIZE] =
133 g_signal_new ("maximize",
134 G_TYPE_FROM_CLASS (klass),
136 0, /* default signal handler */
138 g_cclosure_marshal_VOID__VOID,
140 gail_window_signals [MINIMIZE] =
141 g_signal_new ("minimize",
142 G_TYPE_FROM_CLASS (klass),
144 0, /* default signal handler */
146 g_cclosure_marshal_VOID__VOID,
148 gail_window_signals [MOVE] =
149 g_signal_new ("move",
150 G_TYPE_FROM_CLASS (klass),
152 0, /* default signal handler */
154 g_cclosure_marshal_VOID__VOID,
156 gail_window_signals [RESIZE] =
157 g_signal_new ("resize",
158 G_TYPE_FROM_CLASS (klass),
160 0, /* default signal handler */
162 g_cclosure_marshal_VOID__VOID,
164 gail_window_signals [RESTORE] =
165 g_signal_new ("restore",
166 G_TYPE_FROM_CLASS (klass),
168 0, /* default signal handler */
170 g_cclosure_marshal_VOID__VOID,
175 gail_window_init (GailWindow *accessible)
180 gail_window_real_initialize (AtkObject *obj,
183 GtkWidget *widget = GTK_WIDGET (data);
186 * A GailWindow can be created for a GtkHandleBox or a GtkWindow
188 if (!GTK_IS_WINDOW (widget) && !GTK_IS_HANDLE_BOX (widget))
191 ATK_OBJECT_CLASS (gail_window_parent_class)->initialize (obj, data);
193 g_signal_connect (data,
194 "window_state_event",
195 G_CALLBACK (gail_window_state_event_gtk),
197 g_object_set_data (G_OBJECT (obj), "atk-component-layer",
198 GINT_TO_POINTER (ATK_LAYER_WINDOW));
200 if (GTK_IS_FILE_CHOOSER_DIALOG (widget))
201 obj->role = ATK_ROLE_FILE_CHOOSER;
202 else if (GTK_IS_COLOR_SELECTION_DIALOG (widget))
203 obj->role = ATK_ROLE_COLOR_CHOOSER;
204 else if (GTK_IS_FONT_SELECTION_DIALOG (widget))
205 obj->role = ATK_ROLE_FONT_CHOOSER;
206 else if (GTK_IS_MESSAGE_DIALOG (widget))
207 obj->role = ATK_ROLE_ALERT;
208 else if (GTK_IS_DIALOG (widget))
209 obj->role = ATK_ROLE_DIALOG;
214 name = gtk_widget_get_name (widget);
216 if (!g_strcmp0 (name, "gtk-tooltip"))
217 obj->role = ATK_ROLE_TOOL_TIP;
218 #ifdef GDK_WINDOWING_X11
219 else if (GTK_IS_PLUG (widget))
220 obj->role = ATK_ROLE_PANEL;
222 else if (gtk_window_get_window_type (GTK_WINDOW (widget)) == GTK_WINDOW_POPUP)
223 obj->role = ATK_ROLE_WINDOW;
225 obj->role = ATK_ROLE_FRAME;
229 * Notify that tooltip is showing
231 if (obj->role == ATK_ROLE_TOOL_TIP &&
232 gtk_widget_get_mapped (widget))
233 atk_object_notify_state_change (obj, ATK_STATE_SHOWING, 1);
237 gail_window_get_name (AtkObject *accessible)
241 name = ATK_OBJECT_CLASS (gail_window_parent_class)->get_name (accessible);
245 * Get the window title if it exists
247 GtkWidget* widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
252 if (GTK_IS_WINDOW (widget))
254 GtkWindow *window = GTK_WINDOW (widget);
256 name = gtk_window_get_title (window);
258 accessible->role == ATK_ROLE_TOOL_TIP)
262 child = gtk_bin_get_child (GTK_BIN (window));
263 /* could be some kind of egg notification bubble thingy? */
265 /* Handle new GTK+ GNOME 2.20 tooltips */
266 if (GTK_IS_ALIGNMENT(child))
268 child = gtk_bin_get_child (GTK_BIN (child));
269 if (GTK_IS_BOX(child))
273 children = gtk_container_get_children (GTK_CONTAINER (child));
274 count = g_list_length (children);
277 child = (GtkWidget *) g_list_nth_data (children, 1);
279 g_list_free (children);
283 if (!GTK_IS_LABEL (child))
285 g_message ("ATK_ROLE_TOOLTIP object found, but doesn't look like a tooltip.");
288 name = gtk_label_get_text (GTK_LABEL (child));
296 gail_window_get_parent (AtkObject *accessible)
300 parent = ATK_OBJECT_CLASS (gail_window_parent_class)->get_parent (accessible);
306 gail_window_get_index_in_parent (AtkObject *accessible)
308 GtkWidget* widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
309 AtkObject* atk_obj = atk_get_root ();
315 index = ATK_OBJECT_CLASS (gail_window_parent_class)->get_index_in_parent (accessible);
319 if (GTK_IS_WINDOW (widget))
321 GtkWindow *window = GTK_WINDOW (widget);
322 if (GAIL_IS_TOPLEVEL (atk_obj))
324 GailToplevel* toplevel = GAIL_TOPLEVEL (atk_obj);
325 index = g_list_index (toplevel->window_list, window);
329 int i, sibling_count = atk_object_get_n_accessible_children (atk_obj);
330 for (i = 0; i < sibling_count && index == -1; ++i)
332 AtkObject *child = atk_object_ref_accessible_child (atk_obj, i);
333 if (accessible == child) index = i;
334 g_object_unref (G_OBJECT (child));
342 gail_window_real_focus_gtk (GtkWidget *widget,
343 GdkEventFocus *event)
347 obj = gtk_widget_get_accessible (widget);
348 atk_object_notify_state_change (obj, ATK_STATE_ACTIVE, event->in);
353 static AtkRelationSet*
354 gail_window_ref_relation_set (AtkObject *obj)
357 AtkRelationSet *relation_set;
359 AtkRelation* relation;
360 GtkWidget *current_widget;
362 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
366 relation_set = ATK_OBJECT_CLASS (gail_window_parent_class)->ref_relation_set (obj);
368 if (atk_object_get_role (obj) == ATK_ROLE_TOOL_TIP)
370 relation = atk_relation_set_get_relation_by_type (relation_set, ATK_RELATION_POPUP_FOR);
374 atk_relation_set_remove (relation_set, relation);
376 if (gtk_widget_get_visible(widget) && FALSE /* FIXME gtk_tooltips_get_info_from_tip_window (GTK_WINDOW (widget), NULL, ¤t_widget) */)
378 array [0] = gtk_widget_get_accessible (current_widget);
380 relation = atk_relation_new (array, 1, ATK_RELATION_POPUP_FOR);
381 atk_relation_set_add (relation_set, relation);
382 g_object_unref (relation);
389 gail_window_ref_state_set (AtkObject *accessible)
391 AtkStateSet *state_set;
394 GdkWindow *gdk_window;
395 GdkWindowState state;
397 state_set = ATK_OBJECT_CLASS (gail_window_parent_class)->ref_state_set (accessible);
398 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
403 window = GTK_WINDOW (widget);
405 if (gtk_window_has_toplevel_focus (window) && gtk_window_is_active (window))
406 atk_state_set_add_state (state_set, ATK_STATE_ACTIVE);
408 gdk_window = gtk_widget_get_window (widget);
411 state = gdk_window_get_state (gdk_window);
412 if (state & GDK_WINDOW_STATE_ICONIFIED)
413 atk_state_set_add_state (state_set, ATK_STATE_ICONIFIED);
415 if (gtk_window_get_modal (window))
416 atk_state_set_add_state (state_set, ATK_STATE_MODAL);
418 if (gtk_window_get_resizable (window))
419 atk_state_set_add_state (state_set, ATK_STATE_RESIZABLE);
425 gail_window_real_notify_gtk (GObject *obj,
428 GtkWidget *widget = GTK_WIDGET (obj);
429 AtkObject* atk_obj = gtk_widget_get_accessible (widget);
431 if (strcmp (pspec->name, "title") == 0)
433 g_object_notify (G_OBJECT (atk_obj), "accessible-name");
434 g_signal_emit_by_name (atk_obj, "visible_data_changed");
437 GAIL_WIDGET_CLASS (gail_window_parent_class)->notify_gtk (obj, pspec);
441 gail_window_state_event_gtk (GtkWidget *widget,
442 GdkEventWindowState *event)
446 obj = gtk_widget_get_accessible (widget);
447 atk_object_notify_state_change (obj, ATK_STATE_ICONIFIED,
448 (event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) != 0);
453 atk_component_interface_init (AtkComponentIface *iface)
455 iface->get_extents = gail_window_get_extents;
456 iface->get_size = gail_window_get_size;
457 iface->get_mdi_zorder = gail_window_get_mdi_zorder;
461 gail_window_get_extents (AtkComponent *component,
466 AtkCoordType coord_type)
468 GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component));
470 gint x_toplevel, y_toplevel;
475 if (!gtk_widget_is_toplevel (widget))
477 AtkComponentIface *parent_iface;
479 parent_iface = (AtkComponentIface *) g_type_interface_peek_parent (ATK_COMPONENT_GET_IFACE (component));
480 parent_iface->get_extents (component, x, y, width, height, coord_type);
484 gdk_window_get_frame_extents (gtk_widget_get_window (widget),
488 *height = rect.height;
489 if (!gtk_widget_is_drawable (widget))
497 if (coord_type == ATK_XY_WINDOW)
499 gdk_window_get_origin (gtk_widget_get_window (widget),
500 &x_toplevel, &y_toplevel);
507 gail_window_get_size (AtkComponent *component,
511 GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component));
517 if (!gtk_widget_is_toplevel (widget))
519 AtkComponentIface *parent_iface;
521 parent_iface = (AtkComponentIface *) g_type_interface_peek_parent (ATK_COMPONENT_GET_IFACE (component));
522 parent_iface->get_size (component, width, height);
525 gdk_window_get_frame_extents (gtk_widget_get_window (widget), &rect);
528 *height = rect.height;
531 #if defined (GDK_WINDOWING_X11)
533 #include <X11/Xlib.h>
534 #include <X11/Xatom.h>
535 #include <gdk/x11/gdkx.h>
537 /* _NET_CLIENT_LIST_STACKING monitoring */
540 Window *stacked_windows;
541 int stacked_windows_len;
542 GdkWindow *root_window;
543 guint update_handler;
545 guint update_desktop_handler;
546 gboolean *desktop_changed;
548 guint screen_initialized : 1;
549 guint update_stacked_windows : 1;
552 static GailScreenInfo *gail_screens = NULL;
553 static int num_screens = 0;
554 static Atom _net_client_list_stacking = None;
555 static Atom _net_wm_desktop = None;
558 get_window_desktop (Window window)
569 if (_net_wm_desktop == None)
571 XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "_NET_WM_DESKTOP", False);
573 gdk_error_trap_push ();
574 result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), window, _net_wm_desktop,
577 &ret_type, &format, &nitems,
578 &bytes_after, &cardinals);
579 error = gdk_error_trap_pop();
580 /* nitems < 1 will occur if the property is not set */
581 if (error != Success || result != Success || nitems < 1)
584 desktop = *cardinals;
593 free_screen_info (GailScreenInfo *info)
595 if (info->stacked_windows)
596 XFree (info->stacked_windows);
598 g_free (info->desktop);
599 if (info->desktop_changed)
600 g_free (info->desktop_changed);
602 info->stacked_windows = NULL;
603 info->stacked_windows_len = 0;
604 info->desktop = NULL;
605 info->desktop_changed = NULL;
609 get_stacked_windows (GailScreenInfo *info)
621 gboolean *desktops_changed;
623 if (_net_client_list_stacking == None)
624 _net_client_list_stacking =
625 XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "_NET_CLIENT_LIST_STACKING", False);
627 gdk_error_trap_push ();
629 result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()),
630 GDK_WINDOW_XID (info->root_window),
631 _net_client_list_stacking,
633 False, XA_WINDOW, &ret_type, &format, &nitems,
634 &bytes_after, &data);
635 error = gdk_error_trap_pop ();
636 /* nitems < 1 will occur if the property is not set */
637 if (error != Success || result != Success || nitems < 1)
639 free_screen_info (info);
643 if (ret_type != XA_WINDOW)
646 free_screen_info (info);
650 desktops = g_malloc0 (nitems * sizeof (int));
651 desktops_changed = g_malloc0 (nitems * sizeof (gboolean));
652 for (i = 0; i < nitems; i++)
654 gboolean window_found = FALSE;
656 for (j = 0; j < info->stacked_windows_len; j++)
658 if (info->stacked_windows [j] == data [i])
660 desktops [i] = info->desktop [j];
661 desktops_changed [i] = info->desktop_changed [j];
668 desktops [i] = get_window_desktop (data [i]);
669 desktops_changed [i] = FALSE;
672 free_screen_info (info);
673 info->stacked_windows = (Window*) data;
674 info->stacked_windows_len = nitems;
675 info->desktop = desktops;
676 info->desktop_changed = desktops_changed;
682 update_screen_info (gpointer data)
684 int screen_n = GPOINTER_TO_INT (data);
686 gail_screens [screen_n].update_handler = 0;
687 gail_screens [screen_n].update_stacked_windows = FALSE;
689 get_stacked_windows (&gail_screens [screen_n]);
695 update_desktop_info (gpointer data)
697 int screen_n = GPOINTER_TO_INT (data);
698 GailScreenInfo *info;
701 info = &gail_screens [screen_n];
702 info->update_desktop_handler = 0;
704 for (i = 0; i < info->stacked_windows_len; i++)
706 if (info->desktop_changed [i])
708 info->desktop [i] = get_window_desktop (info->stacked_windows [i]);
709 info->desktop_changed [i] = FALSE;
716 static GdkFilterReturn
717 filter_func (GdkXEvent *gdkxevent,
721 XEvent *xevent = gdkxevent;
723 if (xevent->type == PropertyNotify)
725 if (xevent->xproperty.atom == _net_client_list_stacking)
730 window = event->any.window;
734 screen_n = gdk_screen_get_number (gdk_window_get_screen (window));
736 gail_screens [screen_n].update_stacked_windows = TRUE;
737 if (!gail_screens [screen_n].update_handler)
739 gail_screens [screen_n].update_handler = gdk_threads_add_idle (update_screen_info,
740 GINT_TO_POINTER (screen_n));
744 else if (xevent->xproperty.atom == _net_wm_desktop)
748 GailScreenInfo *info;
750 for (i = 0; i < num_screens; i++)
752 info = &gail_screens [i];
753 for (j = 0; j < info->stacked_windows_len; j++)
755 if (xevent->xany.window == info->stacked_windows [j])
757 info->desktop_changed [j] = TRUE;
758 if (!info->update_desktop_handler)
760 info->update_desktop_handler = gdk_threads_add_idle (update_desktop_info,
761 GINT_TO_POINTER (i));
769 return GDK_FILTER_CONTINUE;
773 display_closed (GdkDisplay *display,
778 for (i = 0; i < num_screens; i++)
780 if (gail_screens [i].update_handler)
782 g_source_remove (gail_screens [i].update_handler);
783 gail_screens [i].update_handler = 0;
786 if (gail_screens [i].update_desktop_handler)
788 g_source_remove (gail_screens [i].update_desktop_handler);
789 gail_screens [i].update_desktop_handler = 0;
792 free_screen_info (&gail_screens [i]);
795 g_free (gail_screens);
801 init_gail_screens (void)
805 display = gdk_display_get_default ();
807 num_screens = gdk_display_get_n_screens (display);
809 gail_screens = g_new0 (GailScreenInfo, num_screens);
810 gdk_window_add_filter (NULL, filter_func, NULL);
812 g_signal_connect (display, "closed", G_CALLBACK (display_closed), NULL);
816 init_gail_screen (GdkScreen *screen,
819 XWindowAttributes attrs;
821 gail_screens [screen_n].root_window = gdk_screen_get_root_window (screen);
823 get_stacked_windows (&gail_screens [screen_n]);
825 XGetWindowAttributes (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()),
826 GDK_WINDOW_XID (gail_screens [screen_n].root_window),
829 XSelectInput (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()),
830 GDK_WINDOW_XID (gail_screens [screen_n].root_window),
831 attrs.your_event_mask | PropertyChangeMask);
833 gail_screens [screen_n].screen_initialized = TRUE;
836 static GailScreenInfo *
837 get_screen_info (GdkScreen *screen)
841 screen_n = gdk_screen_get_number (screen);
843 if (gail_screens && gail_screens [screen_n].screen_initialized)
844 return &gail_screens [screen_n];
847 init_gail_screens ();
849 g_assert (gail_screens != NULL);
851 init_gail_screen (screen, screen_n);
853 g_assert (gail_screens [screen_n].screen_initialized);
855 return &gail_screens [screen_n];
859 get_window_zorder (GdkWindow *window)
861 GailScreenInfo *info;
867 info = get_screen_info (gdk_window_get_screen (window));
869 if (info->stacked_windows == NULL)
872 xid = GDK_WINDOW_XID (window);
875 for (i = 0; i < info->stacked_windows_len; i++)
877 if (info->stacked_windows [i] == xid)
879 w_desktop = info->desktop[i];
887 for (i = 0; i < info->stacked_windows_len; i++)
889 if (info->stacked_windows [i] == xid)
895 if (info->desktop[i] == w_desktop)
904 gail_window_get_mdi_zorder (AtkComponent *component)
906 GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component));
911 return get_window_zorder (gtk_widget_get_window (widget));
914 #elif defined (GDK_WINDOWING_WIN32)
917 gail_window_get_mdi_zorder (AtkComponent *component)
919 GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component));
924 return 0; /* Punt, FIXME */
930 gail_window_get_mdi_zorder (AtkComponent *component)
932 GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component));
937 return 0; /* Punt, FIXME */