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