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