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