]> Pileus Git - ~andy/gtk/blob - gtk/a11y/gailwindow.c
Convert GailButton to GtkButtonAccessible
[~andy/gtk] / gtk / a11y / gailwindow.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 <string.h>
23
24 #include <gtk/gtkx.h>
25
26 #include "gailwindow.h"
27 #include "gailtoplevel.h"
28
29 enum {
30   ACTIVATE,
31   CREATE,
32   DEACTIVATE,
33   DESTROY,
34   MAXIMIZE,
35   MINIMIZE,
36   MOVE,
37   RESIZE,
38   RESTORE,
39   LAST_SIGNAL
40 };
41
42 static void gail_window_class_init (GailWindowClass *klass);
43
44 static void                  gail_window_init            (GailWindow   *accessible);
45
46 static void                  gail_window_real_initialize (AtkObject    *obj,
47                                                           gpointer     data);
48
49 static const gchar* gail_window_get_name       (AtkObject     *accessible);
50
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);
55
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,
59                                                           GParamSpec   *pspec);
60 static gint                  gail_window_get_mdi_zorder (AtkComponent  *component);
61
62 static gboolean              gail_window_state_event_gtk (GtkWidget           *widget,
63                                                           GdkEventWindowState *event);
64
65 /* atkcomponent.h */
66 static void                  atk_component_interface_init (AtkComponentIface    *iface);
67
68 static void                  gail_window_get_extents      (AtkComponent         *component,
69                                                            gint                 *x,
70                                                            gint                 *y,
71                                                            gint                 *width,
72                                                            gint                 *height,
73                                                            AtkCoordType         coord_type);
74 static void                  gail_window_get_size         (AtkComponent         *component,
75                                                            gint                 *width,
76                                                            gint                 *height);
77
78 static guint gail_window_signals [LAST_SIGNAL] = { 0, };
79
80 G_DEFINE_TYPE_WITH_CODE (GailWindow, gail_window, GAIL_TYPE_CONTAINER,
81                          G_IMPLEMENT_INTERFACE (ATK_TYPE_COMPONENT, atk_component_interface_init))
82
83 static void
84 gail_window_class_init (GailWindowClass *klass)
85 {
86   GailWidgetClass *widget_class;
87   AtkObjectClass  *class = ATK_OBJECT_CLASS (klass);
88
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;
92
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;
99
100   gail_window_signals [ACTIVATE] =
101     g_signal_new ("activate",
102                   G_TYPE_FROM_CLASS (klass),
103                   G_SIGNAL_RUN_LAST,
104                   0, /* default signal handler */
105                   NULL, NULL,
106                   g_cclosure_marshal_VOID__VOID,
107                   G_TYPE_NONE, 0);
108   gail_window_signals [CREATE] =
109     g_signal_new ("create",
110                   G_TYPE_FROM_CLASS (klass),
111                   G_SIGNAL_RUN_LAST,
112                   0, /* default signal handler */
113                   NULL, NULL,
114                   g_cclosure_marshal_VOID__VOID,
115                   G_TYPE_NONE, 0);
116   gail_window_signals [DEACTIVATE] =
117     g_signal_new ("deactivate",
118                   G_TYPE_FROM_CLASS (klass),
119                   G_SIGNAL_RUN_LAST,
120                   0, /* default signal handler */
121                   NULL, NULL,
122                   g_cclosure_marshal_VOID__VOID,
123                   G_TYPE_NONE, 0);
124   gail_window_signals [DESTROY] =
125     g_signal_new ("destroy",
126                   G_TYPE_FROM_CLASS (klass),
127                   G_SIGNAL_RUN_LAST,
128                   0, /* default signal handler */
129                   NULL, NULL,
130                   g_cclosure_marshal_VOID__VOID,
131                   G_TYPE_NONE, 0);
132   gail_window_signals [MAXIMIZE] =
133     g_signal_new ("maximize",
134                   G_TYPE_FROM_CLASS (klass),
135                   G_SIGNAL_RUN_LAST,
136                   0, /* default signal handler */
137                   NULL, NULL,
138                   g_cclosure_marshal_VOID__VOID,
139                   G_TYPE_NONE, 0);
140   gail_window_signals [MINIMIZE] =
141     g_signal_new ("minimize",
142                   G_TYPE_FROM_CLASS (klass),
143                   G_SIGNAL_RUN_LAST,
144                   0, /* default signal handler */
145                   NULL, NULL,
146                   g_cclosure_marshal_VOID__VOID,
147                   G_TYPE_NONE, 0);
148   gail_window_signals [MOVE] =
149     g_signal_new ("move",
150                   G_TYPE_FROM_CLASS (klass),
151                   G_SIGNAL_RUN_LAST,
152                   0, /* default signal handler */
153                   NULL, NULL,
154                   g_cclosure_marshal_VOID__VOID,
155                   G_TYPE_NONE, 0);
156   gail_window_signals [RESIZE] =
157     g_signal_new ("resize",
158                   G_TYPE_FROM_CLASS (klass),
159                   G_SIGNAL_RUN_LAST,
160                   0, /* default signal handler */
161                   NULL, NULL,
162                   g_cclosure_marshal_VOID__VOID,
163                   G_TYPE_NONE, 0);
164   gail_window_signals [RESTORE] =
165     g_signal_new ("restore",
166                   G_TYPE_FROM_CLASS (klass),
167                   G_SIGNAL_RUN_LAST,
168                   0, /* default signal handler */
169                   NULL, NULL,
170                   g_cclosure_marshal_VOID__VOID,
171                   G_TYPE_NONE, 0);
172 }
173
174 static void
175 gail_window_init (GailWindow   *accessible)
176 {
177 }
178
179 static void
180 gail_window_real_initialize (AtkObject *obj,
181                              gpointer  data)
182 {
183   GtkWidget *widget = GTK_WIDGET (data);
184
185   /*
186    * A GailWindow can be created for a GtkHandleBox or a GtkWindow
187    */
188   if (!GTK_IS_WINDOW (widget) && !GTK_IS_HANDLE_BOX (widget))
189     return;
190
191   ATK_OBJECT_CLASS (gail_window_parent_class)->initialize (obj, data);
192
193   g_signal_connect (data,
194                     "window_state_event",
195                     G_CALLBACK (gail_window_state_event_gtk),
196                     NULL);
197   g_object_set_data (G_OBJECT (obj), "atk-component-layer",
198                      GINT_TO_POINTER (ATK_LAYER_WINDOW));
199
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;
210   else
211     {
212       const gchar *name;
213
214       name = gtk_widget_get_name (widget);
215
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;
221 #endif
222       else if (gtk_window_get_window_type (GTK_WINDOW (widget)) == GTK_WINDOW_POPUP)
223         obj->role = ATK_ROLE_WINDOW;
224       else
225         obj->role = ATK_ROLE_FRAME;
226     }
227
228   /*
229    * Notify that tooltip is showing
230    */
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);
234 }
235
236 static const gchar*
237 gail_window_get_name (AtkObject *accessible)
238 {
239   const gchar* name;
240
241   name = ATK_OBJECT_CLASS (gail_window_parent_class)->get_name (accessible);
242   if (name == NULL)
243     {
244       /*
245        * Get the window title if it exists
246        */
247       GtkWidget* widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
248
249       if (widget == NULL)
250         return NULL;
251
252       if (GTK_IS_WINDOW (widget))
253         {
254           GtkWindow *window = GTK_WINDOW (widget);
255  
256           name = gtk_window_get_title (window);
257           if (name == NULL &&
258               accessible->role == ATK_ROLE_TOOL_TIP)
259             {
260               GtkWidget *child;
261
262               child = gtk_bin_get_child (GTK_BIN (window));
263               /* could be some kind of egg notification bubble thingy? */
264
265               /* Handle new GTK+ GNOME 2.20 tooltips */
266               if (GTK_IS_ALIGNMENT(child))
267                 {
268                   child = gtk_bin_get_child (GTK_BIN (child));
269                   if (GTK_IS_BOX(child)) 
270                     {
271                       GList *children;
272                       guint count;
273                       children = gtk_container_get_children (GTK_CONTAINER (child));
274                       count = g_list_length (children);
275                       if (count == 2) 
276                         {
277                           child = (GtkWidget *) g_list_nth_data (children, 1);
278                         }
279                       g_list_free (children);                
280                     }
281                 }
282
283               if (!GTK_IS_LABEL (child)) 
284               { 
285                   g_message ("ATK_ROLE_TOOLTIP object found, but doesn't look like a tooltip.");
286                   return NULL;
287               }
288               name = gtk_label_get_text (GTK_LABEL (child));
289             }
290         }
291     }
292   return name;
293 }
294
295 static AtkObject*
296 gail_window_get_parent (AtkObject *accessible)
297 {
298   AtkObject* parent;
299
300   parent = ATK_OBJECT_CLASS (gail_window_parent_class)->get_parent (accessible);
301
302   return parent;
303 }
304
305 static gint
306 gail_window_get_index_in_parent (AtkObject *accessible)
307 {
308   GtkWidget* widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
309   AtkObject* atk_obj = atk_get_root ();
310   gint index = -1;
311
312   if (widget == NULL)
313     return -1;
314
315   index = ATK_OBJECT_CLASS (gail_window_parent_class)->get_index_in_parent (accessible);
316   if (index != -1)
317     return index;
318
319   if (GTK_IS_WINDOW (widget))
320     {
321       GtkWindow *window = GTK_WINDOW (widget);
322       if (GAIL_IS_TOPLEVEL (atk_obj))
323         {
324           GailToplevel* toplevel = GAIL_TOPLEVEL (atk_obj);
325           index = g_list_index (toplevel->window_list, window);
326         }
327       else
328         {
329           int i, sibling_count = atk_object_get_n_accessible_children (atk_obj);
330           for (i = 0; i < sibling_count && index == -1; ++i)
331             {
332               AtkObject *child = atk_object_ref_accessible_child (atk_obj, i);
333               if (accessible == child) index = i;
334               g_object_unref (G_OBJECT (child));
335             }
336         }
337     }
338   return index;
339 }
340
341 static gboolean
342 gail_window_real_focus_gtk (GtkWidget     *widget,
343                             GdkEventFocus *event)
344 {
345   AtkObject* obj;
346
347   obj = gtk_widget_get_accessible (widget);
348   atk_object_notify_state_change (obj, ATK_STATE_ACTIVE, event->in);
349
350   return FALSE;
351 }
352
353 static AtkRelationSet*
354 gail_window_ref_relation_set (AtkObject *obj)
355 {
356   GtkWidget *widget;
357   AtkRelationSet *relation_set;
358   AtkObject *array[1];
359   AtkRelation* relation;
360   GtkWidget *current_widget;
361
362   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
363   if (widget == NULL)
364     return NULL;
365
366   relation_set = ATK_OBJECT_CLASS (gail_window_parent_class)->ref_relation_set (obj);
367
368   if (atk_object_get_role (obj) == ATK_ROLE_TOOL_TIP)
369     {
370       relation = atk_relation_set_get_relation_by_type (relation_set, ATK_RELATION_POPUP_FOR);
371
372       if (relation)
373         {
374           atk_relation_set_remove (relation_set, relation);
375         }
376       if (gtk_widget_get_visible(widget) && FALSE /* FIXME gtk_tooltips_get_info_from_tip_window (GTK_WINDOW (widget), NULL, &current_widget) */)
377         {
378           array [0] = gtk_widget_get_accessible (current_widget);
379
380           relation = atk_relation_new (array, 1, ATK_RELATION_POPUP_FOR);
381           atk_relation_set_add (relation_set, relation);
382           g_object_unref (relation);
383         }
384     }
385   return relation_set;
386 }
387
388 static AtkStateSet*
389 gail_window_ref_state_set (AtkObject *accessible)
390 {
391   AtkStateSet *state_set;
392   GtkWidget *widget;
393   GtkWindow *window;
394   GdkWindow *gdk_window;
395   GdkWindowState state;
396
397   state_set = ATK_OBJECT_CLASS (gail_window_parent_class)->ref_state_set (accessible);
398   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
399  
400   if (widget == NULL)
401     return state_set;
402
403   window = GTK_WINDOW (widget);
404
405   if (gtk_window_has_toplevel_focus (window) && gtk_window_is_active (window))
406     atk_state_set_add_state (state_set, ATK_STATE_ACTIVE);
407
408   gdk_window = gtk_widget_get_window (widget);
409   if (window)
410     {
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);
414     } 
415   if (gtk_window_get_modal (window))
416     atk_state_set_add_state (state_set, ATK_STATE_MODAL);
417
418   if (gtk_window_get_resizable (window))
419     atk_state_set_add_state (state_set, ATK_STATE_RESIZABLE);
420  
421   return state_set;
422 }
423
424 static void
425 gail_window_real_notify_gtk (GObject            *obj,
426                              GParamSpec         *pspec)
427 {
428   GtkWidget *widget = GTK_WIDGET (obj);
429   AtkObject* atk_obj = gtk_widget_get_accessible (widget);
430
431   if (strcmp (pspec->name, "title") == 0)
432     {
433       g_object_notify (G_OBJECT (atk_obj), "accessible-name");
434       g_signal_emit_by_name (atk_obj, "visible_data_changed");
435     }
436   else
437     GAIL_WIDGET_CLASS (gail_window_parent_class)->notify_gtk (obj, pspec);
438 }
439
440 static gboolean
441 gail_window_state_event_gtk (GtkWidget           *widget,
442                              GdkEventWindowState *event)
443 {
444   AtkObject* obj;
445
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);
449   return FALSE;
450 }
451
452 static void
453 atk_component_interface_init (AtkComponentIface *iface)
454 {
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;
458 }
459
460 static void
461 gail_window_get_extents (AtkComponent  *component,
462                          gint          *x,
463                          gint          *y,
464                          gint          *width,
465                          gint          *height,
466                          AtkCoordType  coord_type)
467 {
468   GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component)); 
469   GdkRectangle rect;
470   gint x_toplevel, y_toplevel;
471
472   if (widget == NULL)
473     return;
474
475   if (!gtk_widget_is_toplevel (widget))
476     {
477       AtkComponentIface *parent_iface;
478
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);
481       return;
482     }
483
484   gdk_window_get_frame_extents (gtk_widget_get_window (widget),
485                                 &rect);
486
487   *width = rect.width;
488   *height = rect.height;
489   if (!gtk_widget_is_drawable (widget))
490     {
491       *x = G_MININT;
492       *y = G_MININT;
493       return;
494     }
495   *x = rect.x;
496   *y = rect.y;
497   if (coord_type == ATK_XY_WINDOW)
498     {
499       gdk_window_get_origin (gtk_widget_get_window (widget),
500                              &x_toplevel, &y_toplevel);
501       *x -= x_toplevel;
502       *y -= y_toplevel;
503     }
504 }
505
506 static void
507 gail_window_get_size (AtkComponent *component,
508                       gint         *width,
509                       gint         *height)
510 {
511   GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component));
512   GdkRectangle rect;
513
514   if (widget == NULL)
515     return;
516
517   if (!gtk_widget_is_toplevel (widget))
518     {
519       AtkComponentIface *parent_iface;
520
521       parent_iface = (AtkComponentIface *) g_type_interface_peek_parent (ATK_COMPONENT_GET_IFACE (component));
522       parent_iface->get_size (component, width, height);
523       return;
524     }
525   gdk_window_get_frame_extents (gtk_widget_get_window (widget), &rect);
526
527   *width = rect.width;
528   *height = rect.height;
529 }
530
531 #if defined (GDK_WINDOWING_X11)
532
533 #include <X11/Xlib.h>
534 #include <X11/Xatom.h>
535 #include <gdk/x11/gdkx.h>
536
537 /* _NET_CLIENT_LIST_STACKING monitoring */
538
539 typedef struct {
540   Window     *stacked_windows;
541   int         stacked_windows_len;
542   GdkWindow  *root_window;
543   guint       update_handler;
544   int        *desktop;
545   guint       update_desktop_handler;
546   gboolean   *desktop_changed;
547
548   guint       screen_initialized : 1;
549   guint       update_stacked_windows : 1;
550 } GailScreenInfo;
551
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;
556
557 static gint
558 get_window_desktop (Window window)
559 {
560   Atom            ret_type;
561   int             format;
562   gulong          nitems;
563   gulong          bytes_after;
564   guchar         *cardinals;
565   int             error;
566   int             result;
567   int             desktop;
568
569   if (_net_wm_desktop == None)
570     _net_wm_desktop =
571                 XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "_NET_WM_DESKTOP", False);
572
573   gdk_error_trap_push ();
574   result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), window, _net_wm_desktop,
575                                0, G_MAXLONG,
576                                False, XA_CARDINAL,
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)
582     return -1;
583
584   desktop = *cardinals;
585
586   XFree (cardinals);
587   if (nitems != 1)
588     return -1;
589   return desktop;
590 }
591
592 static void
593 free_screen_info (GailScreenInfo *info)
594 {
595   if (info->stacked_windows)
596     XFree (info->stacked_windows);
597   if (info->desktop)
598     g_free (info->desktop);
599   if (info->desktop_changed)
600     g_free (info->desktop_changed);
601
602   info->stacked_windows = NULL;
603   info->stacked_windows_len = 0;
604   info->desktop = NULL;
605   info->desktop_changed = NULL;
606 }
607
608 static gboolean
609 get_stacked_windows (GailScreenInfo *info)
610 {
611   Atom    ret_type;
612   int     format;
613   gulong  nitems;
614   gulong  bytes_after;
615   guchar *data;
616   int     error;
617   int     result;
618   int     i;
619   int     j;
620   int    *desktops;
621   gboolean *desktops_changed;
622
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);
626
627   gdk_error_trap_push ();
628   ret_type = None;
629   result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()),
630                                GDK_WINDOW_XID (info->root_window),
631                                _net_client_list_stacking,
632                                0, G_MAXLONG,
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)
638     {
639       free_screen_info (info);
640       return FALSE;
641     }
642
643   if (ret_type != XA_WINDOW)
644     {
645       XFree (data);
646       free_screen_info (info);
647       return FALSE;
648     }
649
650   desktops = g_malloc0 (nitems * sizeof (int));
651   desktops_changed = g_malloc0 (nitems * sizeof (gboolean));
652   for (i = 0; i < nitems; i++)
653     {
654       gboolean window_found = FALSE;
655
656       for (j = 0; j < info->stacked_windows_len; j++)
657         {
658           if (info->stacked_windows [j] == data [i])
659             {
660               desktops [i] = info->desktop [j];
661               desktops_changed [i] = info->desktop_changed [j];
662               window_found = TRUE;
663               break;
664             }
665         }
666       if (!window_found)
667         {
668           desktops [i] = get_window_desktop (data [i]);
669           desktops_changed [i] = FALSE;
670         }
671     }
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;
677
678   return TRUE;
679 }
680
681 static gboolean
682 update_screen_info (gpointer data)
683 {
684   int screen_n = GPOINTER_TO_INT (data);
685
686   gail_screens [screen_n].update_handler = 0;
687   gail_screens [screen_n].update_stacked_windows = FALSE;
688
689   get_stacked_windows (&gail_screens [screen_n]);
690
691   return FALSE;
692 }
693
694 static gboolean
695 update_desktop_info (gpointer data)
696 {
697   int screen_n = GPOINTER_TO_INT (data);
698   GailScreenInfo *info;
699   int i;
700
701   info = &gail_screens [screen_n];
702   info->update_desktop_handler = 0;
703
704   for (i = 0; i < info->stacked_windows_len; i++)
705     {
706       if (info->desktop_changed [i])
707         {
708           info->desktop [i] = get_window_desktop (info->stacked_windows [i]);
709           info->desktop_changed [i] = FALSE;
710         }
711     }
712
713   return FALSE;
714 }
715
716 static GdkFilterReturn
717 filter_func (GdkXEvent *gdkxevent,
718              GdkEvent  *event,
719              gpointer   data)
720 {
721   XEvent *xevent = gdkxevent;
722
723   if (xevent->type == PropertyNotify)
724     {
725       if (xevent->xproperty.atom == _net_client_list_stacking)
726         {
727           int     screen_n;
728           GdkWindow *window;
729
730           window = event->any.window;
731
732           if (window)
733             {
734               screen_n = gdk_screen_get_number (gdk_window_get_screen (window));
735
736               gail_screens [screen_n].update_stacked_windows = TRUE;
737               if (!gail_screens [screen_n].update_handler)
738                 {
739                   gail_screens [screen_n].update_handler = gdk_threads_add_idle (update_screen_info,
740                                                                                  GINT_TO_POINTER (screen_n));
741                 }
742             }
743         }
744       else if (xevent->xproperty.atom == _net_wm_desktop)
745         {
746           int     i;
747           int     j;
748           GailScreenInfo *info;
749
750           for (i = 0; i < num_screens; i++)
751             {
752               info = &gail_screens [i];
753               for (j = 0; j < info->stacked_windows_len; j++)
754                 {
755                   if (xevent->xany.window == info->stacked_windows [j])
756                     {
757                       info->desktop_changed [j] = TRUE;
758                       if (!info->update_desktop_handler)
759                         {
760                           info->update_desktop_handler = gdk_threads_add_idle (update_desktop_info,
761                                                                                GINT_TO_POINTER (i));
762                         }
763                       break;
764                     }
765                 }
766             }
767         }
768     }
769   return GDK_FILTER_CONTINUE;
770 }
771
772 static void
773 display_closed (GdkDisplay *display,
774                 gboolean    is_error)
775 {
776   int i;
777
778   for (i = 0; i < num_screens; i++)
779     {
780       if (gail_screens [i].update_handler)
781         {
782           g_source_remove (gail_screens [i].update_handler);
783           gail_screens [i].update_handler = 0;
784         }
785
786       if (gail_screens [i].update_desktop_handler)
787         {
788           g_source_remove (gail_screens [i].update_desktop_handler);
789           gail_screens [i].update_desktop_handler = 0;
790         }
791
792       free_screen_info (&gail_screens [i]);
793     }
794
795   g_free (gail_screens);
796   gail_screens = NULL;
797   num_screens = 0;
798 }
799
800 static void
801 init_gail_screens (void)
802 {
803   GdkDisplay *display;
804
805   display = gdk_display_get_default ();
806
807   num_screens = gdk_display_get_n_screens (display);
808
809   gail_screens = g_new0 (GailScreenInfo, num_screens);
810   gdk_window_add_filter (NULL, filter_func, NULL);
811
812   g_signal_connect (display, "closed", G_CALLBACK (display_closed), NULL);
813 }
814
815 static void
816 init_gail_screen (GdkScreen *screen,
817                   int        screen_n)
818 {
819   XWindowAttributes attrs;
820
821   gail_screens [screen_n].root_window = gdk_screen_get_root_window (screen);
822
823   get_stacked_windows (&gail_screens [screen_n]);
824
825   XGetWindowAttributes (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()),
826                         GDK_WINDOW_XID (gail_screens [screen_n].root_window),
827                         &attrs); 
828
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);
832            
833   gail_screens [screen_n].screen_initialized = TRUE;
834 }
835
836 static GailScreenInfo *
837 get_screen_info (GdkScreen *screen)
838 {
839   gint screen_n;
840
841   screen_n = gdk_screen_get_number (screen);
842
843   if (gail_screens && gail_screens [screen_n].screen_initialized)
844     return &gail_screens [screen_n];
845
846   if (!gail_screens)
847     init_gail_screens ();
848
849   g_assert (gail_screens != NULL);
850
851   init_gail_screen (screen, screen_n);
852
853   g_assert (gail_screens [screen_n].screen_initialized);
854
855   return &gail_screens [screen_n];
856 }
857
858 static gint
859 get_window_zorder (GdkWindow *window)
860 {
861   GailScreenInfo *info;
862   Window          xid;
863   int             i;
864   int             zorder;
865   int             w_desktop;
866
867   info = get_screen_info (gdk_window_get_screen (window));
868
869   if (info->stacked_windows == NULL)
870     return -1;
871
872   xid = GDK_WINDOW_XID (window);
873
874   w_desktop = -1;
875   for (i = 0; i < info->stacked_windows_len; i++)
876     {
877       if (info->stacked_windows [i] == xid)
878         {
879           w_desktop = info->desktop[i];
880           break;
881         }
882     }
883   if (w_desktop < 0)
884     return w_desktop;
885
886   zorder = 0;
887   for (i = 0; i < info->stacked_windows_len; i++)
888     {
889       if (info->stacked_windows [i] == xid)
890         {
891           return zorder;
892         }
893       else
894         {
895           if (info->desktop[i] == w_desktop)
896             zorder++;
897         }
898      }
899
900   return -1;
901 }
902
903 static gint
904 gail_window_get_mdi_zorder (AtkComponent *component)
905 {
906   GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component));
907
908   if (widget == NULL)
909     return -1;
910
911   return get_window_zorder (gtk_widget_get_window (widget));
912 }
913
914 #elif defined (GDK_WINDOWING_WIN32)
915
916 static gint
917 gail_window_get_mdi_zorder (AtkComponent *component)
918 {
919   GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component));
920
921   if (widget == NULL)
922     return -1;
923
924   return 0;                     /* Punt, FIXME */
925 }
926
927 #else
928
929 static gint
930 gail_window_get_mdi_zorder (AtkComponent *component)
931 {
932   GtkWidget *widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component));
933
934   if (widget == NULL)
935     return -1;
936
937   return 0;                     /* Punt, FIXME */
938 }
939
940 #endif