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