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