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