2 * AT-SPI - Assistive Technology Service Provider Interface
3 * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
5 * Copyright 2001 Sun Microsystems Inc.
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
24 #include "../cspi/spi-private.h"
29 * Screen Review Algorithm Demonstration, Benchmark, and Test-bed.
33 * * bounds now fail to include window border decoration
34 * (which we can't really know about).
36 * * brute-force algorithm uses no client-side cache; performance mediocre.
38 * * we don't have good ordering information for the toplevel windows,
39 * and our current heuristic is not guaranteed if
40 * the active window is not always on top.
42 * * text-bearing objects that don't implement AccessibleText (such as buttons)
43 * don't get their text clipped since we don't know the character
45 * * can't know about "inaccessible" objects that may be obscuring the
46 * accessible windows (inherent to API-based approach).
52 #define BOUNDS_CONTAIN_X(b, p) (((p)>=(b)->x) && ((p)<=((b)->x + (b)->width))\
53 && ((b)->width > 0) && ((b)->height > 0))
55 #define BOUNDS_CONTAIN_Y(b, p) (((p)>=(b)->y) && ((p)<=((b)->y + (b)->height))\
56 && ((b)->width > 0) && ((b)->height > 0))
58 #define CHUNK_BOUNDS_BEFORE_START(c, i) ((i) && ((c)->clip_bounds.x < \
61 #define CHUNK_BOUNDS_END_BEFORE_START(c, i) ((i) && \
62 (((c)->clip_bounds.x + \
63 (c)->clip_bounds.width) < \
66 #define CHUNK_BOUNDS_AFTER_END(c, i) ((i) && ((c)->clip_bounds.x >= \
67 ((i)->clip_bounds.x + \
68 (i)->clip_bounds.width)))
70 #define CHUNK_BOUNDS_SPANS_END(c, i) ((i) && (((c)->clip_bounds.x + \
71 (c)->clip_bounds.width) >= \
72 ((i)->clip_bounds.x + \
73 (i)->clip_bounds.width)))
75 * #define CHUNK_BOUNDS_WITHIN(c, i) ((i) && ((c)->clip_bounds.x >= \
76 * (i)->text_bounds.x) && \
77 * (((c)->clip_bounds.x + (c)->clip_bounds.width) \
78 * <= ((i)->text_bounds.x + (i)->text_bounds.width)))
81 #define IS_CLIPPING_CONTAINER(a) ((a) != SPI_ROLE_PAGE_TAB)
83 static void report_screen_review_line (const AccessibleEvent *event, void *user_data);
85 static gint n_elements_traversed = 0;
86 static AccessibleEventListener *mouseclick_listener;
88 typedef struct _BoundaryRect {
93 AccessibleRole role; /* role of last clipping element */
98 typedef struct _TextChunk {
100 AccessibleText *source;
103 BoundaryRect clip_bounds;
104 BoundaryRect text_bounds;
105 BoundaryRect start_char_bounds;
106 BoundaryRect end_char_bounds;
109 typedef struct _ScreenReviewBuffer { /* TODO: implement */
111 } ScreenReviewBuffer;
113 static gboolean isJava = FALSE;
116 main (int argc, char **argv)
123 gdouble elapsed_time;
125 Accessible *application;
130 mouseclick_listener = SPI_createAccessibleEventListener (
131 report_screen_review_line, NULL);
133 SPI_registerGlobalEventListener (mouseclick_listener,
134 "Gtk:GtkWidget:button-press-event");
135 #undef JAVA_TEST_HACK
136 #ifdef JAVA_TEST_HACK /* Only use this to test Java apps */
137 SPI_registerGlobalEventListener (mouseclick_listener,
138 "object:text-caret-moved");
143 putenv ("AT_BRIDGE_SHUTDOWN=1");
146 * TODO: Add a key event listener that calls test_exit, to
147 * deregister and cleanup appropriately.
153 static inline gboolean
154 chunk_bounds_within (TextChunk *chunk, TextChunk *test_chunk)
156 int x1, x2, tx1, tx2;
159 x1 = chunk->clip_bounds.x;
160 x2 = x1 + chunk->clip_bounds.width;
161 tx1 = test_chunk->clip_bounds.x;
162 tx2 = tx1 + test_chunk->clip_bounds.width;
163 gtx1 = (chunk->clip_bounds.x >= test_chunk->clip_bounds.x);
164 ltx2 = (chunk->clip_bounds.x + chunk->clip_bounds.width
165 <= test_chunk->clip_bounds.x + test_chunk->clip_bounds.width);
169 #define CHUNK_BOUNDS_WITHIN(a, b) chunk_bounds_within(a, b)
171 static BoundaryRect **
172 clip_bounds_clone (BoundaryRect *bounds[])
175 BoundaryRect **bounds_clone;
176 bounds_clone = (BoundaryRect **)
177 g_new0 (gpointer, SPI_LAYER_LAST_DEFINED);
178 for (i = 0; i < SPI_LAYER_LAST_DEFINED; ++i) {
179 bounds_clone[i] = g_new0 (BoundaryRect, 1);
180 *bounds_clone[i] = *bounds[i];
186 boundary_clip (BoundaryRect *bounds, BoundaryRect *clipBounds)
188 int x2 = bounds->x + bounds->width;
189 int y2 = bounds->y + bounds->height;
191 fprintf (stderr, "bclip %d-%d, %d-%d; ",
193 clipBounds->x, clipBounds->x+clipBounds->width);
195 bounds->x = MAX (bounds->x, clipBounds->x);
196 bounds->y = MAX (bounds->y, clipBounds->y);
197 x2 = MIN (x2, clipBounds->x + clipBounds->width);
198 y2 = MIN (y2, clipBounds->y + clipBounds->height);
199 bounds->width = MAX (x2 - bounds->x, 0);
200 bounds->height = MAX (y2 - bounds->y, 0);
201 if (!bounds->width || !bounds->height)
202 bounds->isEmpty = TRUE;
203 if (IS_CLIPPING_CONTAINER (bounds->role)) {
204 *clipBounds = *bounds;
207 fprintf (stderr, "%d-%d\n",
208 bounds->x, bounds->x+bounds->width);
213 boundary_xclip_head (BoundaryRect *bounds, BoundaryRect *clipBounds)
216 int cx2 = clipBounds->x + clipBounds->width;
217 if (cx2 < bounds->x) return;
218 x2 = bounds->x + bounds->width;
219 if (cx2 < x2) bounds->x = cx2;
220 bounds->width = MAX (0, x2 - cx2);
224 boundary_xclip_tail (BoundaryRect *bounds, BoundaryRect *clipBounds)
226 int x2 = bounds->x + bounds->width;
227 if (clipBounds->x > x2) return;
228 bounds->width = MAX (0, clipBounds->x - bounds->x);
232 text_chunk_copy (TextChunk *chunk)
234 TextChunk *copy = g_new0 (TextChunk, 1);
236 if (chunk->string) copy->string = g_strdup (chunk->string);
237 if (copy->source) AccessibleText_ref (copy->source);
242 text_chunk_tail_clip (TextChunk *bottom, TextChunk *top)
245 fprintf (stderr, "bottom %d-%d, top %d-%d;",
246 bottom->clip_bounds.x,
247 bottom->clip_bounds.x + bottom->clip_bounds.width,
249 top->clip_bounds.x + top->clip_bounds.width);
251 boundary_xclip_tail (&bottom->text_bounds, &top->clip_bounds);
252 boundary_xclip_tail (&bottom->clip_bounds, &top->clip_bounds);
253 bottom->text_bounds.isClipped = TRUE;
254 bottom->clip_bounds.isClipped = TRUE;
256 fprintf (stderr, "result %d-%d\n",
257 bottom->clip_bounds.x,
258 bottom->clip_bounds.x + bottom->clip_bounds.width);
263 text_chunk_head_clip (TextChunk *bottom, TextChunk *top)
266 fprintf (stderr, "bottom %d-%d, top %d-%d;",
267 bottom->clip_bounds.x,
268 bottom->clip_bounds.x + bottom->clip_bounds.width,
270 top->clip_bounds.x + top->clip_bounds.width);
272 boundary_xclip_head (&bottom->text_bounds, &top->clip_bounds);
273 boundary_xclip_head (&bottom->clip_bounds, &top->clip_bounds);
274 bottom->text_bounds.isClipped = TRUE;
275 bottom->clip_bounds.isClipped = TRUE;
277 fprintf (stderr, "result %d-%d\n",
278 bottom->clip_bounds.x,
279 bottom->clip_bounds.x + bottom->clip_bounds.width);
284 text_chunk_split_insert (GList *chunk_list, GList *iter, TextChunk *chunk)
286 TextChunk *iter_chunk = iter->data;
287 TextChunk *iter_copy = text_chunk_copy (iter_chunk);
288 /* TODO: FIXME something is wrong here */
290 fprintf (stderr, "***clip insert of %s into %s\n",
291 chunk->string ? chunk->string : "<null>",
292 iter_chunk->string ? iter_chunk->string : "<null>");
294 chunk_list = g_list_insert_before (chunk_list, iter, iter_copy);
295 text_chunk_tail_clip (iter_copy, chunk);
296 chunk_list = g_list_insert_before (chunk_list, iter, chunk);
297 text_chunk_head_clip (iter_chunk, chunk);
301 /* #define PRINT_CHUNK_DEBUG(a, b, c, d) print_chunk_debug(a, b, c, d) */
303 #define PRINT_CHUNK_DEBUG(a, b, c, d)
305 #ifdef PRINT_CHUNK_DEBUG
307 print_chunk_debug (TextChunk *chunk, char *opname, GList *prev, GList *next)
309 fprintf (stderr, "%sing chunk %s between %s and %s; %d-%d\n",
312 (prev ? ((TextChunk *) prev->data)->string : "<null>"),
313 (next ? ((TextChunk *) next->data)->string : "<null>"),
314 chunk->clip_bounds.x,
315 chunk->text_bounds.x + chunk->text_bounds.width);
320 text_chunk_list_head_clip (GList *text_chunk_list,
324 GList *target, *iter = next, *prev;
326 // if (chunk->string && strlen (chunk->string)) {
328 g_list_insert_before (text_chunk_list, next, chunk);
331 if (CHUNK_BOUNDS_SPANS_END (chunk, (TextChunk *)iter->data)) {
333 fprintf (stderr, "deleting %s\n",
334 ((TextChunk *)iter->data)->string);
339 g_list_delete_link (text_chunk_list, target);
341 if (!CHUNK_BOUNDS_END_BEFORE_START (chunk,
342 (TextChunk *)iter->data)) {
343 text_chunk_head_clip ((TextChunk *)iter->data,
347 !CHUNK_BOUNDS_AFTER_END (
349 (TextChunk *)prev->data)) {
350 text_chunk_tail_clip (
351 (TextChunk *)prev->data,
358 return text_chunk_list;
362 text_chunk_list_clip_and_insert (GList *text_chunk_list,
369 fprintf (stderr, "clip-and-insert for %s, between %s and %s\n",
371 (prev && ((TextChunk *)prev->data)->string ? ((TextChunk *)prev->data)->string : "<null>"),
372 (next && ((TextChunk *)next->data)->string ? ((TextChunk *)next->data)->string : "<null>"));
375 if (!prev && !next) { /* first element in, no clip needed */
376 // if (chunk->string && strlen (chunk->string)) {
378 g_list_append (text_chunk_list, chunk);
379 PRINT_CHUNK_DEBUG (chunk, "append",
383 } else { /* check for clip with prev */
384 /* if we split the prev */
386 CHUNK_BOUNDS_WITHIN (chunk, (TextChunk *) prev->data)) {
388 text_chunk_split_insert (
392 /* we split the 'next' element */
393 if (CHUNK_BOUNDS_WITHIN (chunk, (TextChunk *)next->data)) {
395 text_chunk_split_insert (text_chunk_list,
398 /* do an insert + head clip */
400 text_chunk_list_head_clip (
406 if (!CHUNK_BOUNDS_AFTER_END (chunk,
407 (TextChunk *) prev->data)) {
408 text_chunk_tail_clip (prev->data, chunk);
410 // if (chunk->string && strlen (chunk->string)) {
412 g_list_append (text_chunk_list, chunk);
416 return text_chunk_list;
420 text_chunk_list_insert_chunk (GList *text_chunk_list, TextChunk *chunk)
422 GList *iter = g_list_first (text_chunk_list);
423 TextChunk *iter_chunk = NULL;
425 if (iter) iter_chunk = (TextChunk *) iter->data;
426 /* if we're ahead of the current element */
429 text_chunk_list_clip_and_insert (text_chunk_list,
434 } else if (CHUNK_BOUNDS_BEFORE_START (chunk, iter_chunk)) {
436 text_chunk_list_clip_and_insert (text_chunk_list,
441 } else if (!iter->next ) {
443 text_chunk_list_clip_and_insert (text_chunk_list,
449 if (iter) iter = iter->next;
451 return text_chunk_list;
455 review_buffer_get_text_chunk (ScreenReviewBuffer *reviewBuffer,
456 Accessible *accessible, BoundaryRect *bounds,
457 int screen_x, int screen_y)
459 AccessibleText *text = NULL;
461 TextChunk *text_chunk;
462 BoundaryRect start_char_bounds, end_char_bounds;
468 role = Accessible_getRole (accessible);
469 text_chunk = g_new0 (TextChunk, 1);
470 text_chunk->clip_bounds = *bounds;
471 if (Accessible_isText (accessible)) {
472 text = Accessible_getText (accessible);
473 offset = AccessibleText_getOffsetAtPoint (text,
476 SPI_COORD_TYPE_SCREEN);
477 s = AccessibleText_getTextAtOffset (text, offset,
478 SPI_TEXT_BOUNDARY_LINE_START,
481 AccessibleText_getCharacterExtents (
483 &text_chunk->start_char_bounds.x,
484 &text_chunk->start_char_bounds.y,
485 &text_chunk->start_char_bounds.width,
486 &text_chunk->start_char_bounds.height,
487 SPI_COORD_TYPE_SCREEN);
489 fprintf (stderr, "%s: start char (%d) x, width %d %d; ",
492 text_chunk->start_char_bounds.x,
493 text_chunk->start_char_bounds.width);
495 end--; /* XXX: bug workaround */
496 AccessibleText_getCharacterExtents (
498 &text_chunk->end_char_bounds.x,
499 &text_chunk->end_char_bounds.y,
500 &text_chunk->end_char_bounds.width,
501 &text_chunk->end_char_bounds.height,
502 SPI_COORD_TYPE_SCREEN);
504 fprintf (stderr, "end char (%d) x, width %d %d\n",
506 text_chunk->end_char_bounds.x,
507 text_chunk->end_char_bounds.width);
510 text_chunk->text_bounds.x = MIN (text_chunk->start_char_bounds.x,
511 text_chunk->end_char_bounds.x);
512 text_chunk->text_bounds.y = MIN (text_chunk->start_char_bounds.y,
513 text_chunk->end_char_bounds.y);
514 x2 = MAX (text_chunk->start_char_bounds.x +
515 text_chunk->start_char_bounds.width,
516 text_chunk->end_char_bounds.x +
517 text_chunk->end_char_bounds.width);
518 text_chunk->text_bounds.width = x2 - text_chunk->text_bounds.x;
519 y2 = MAX (text_chunk->start_char_bounds.y +
520 text_chunk->start_char_bounds.height,
521 text_chunk->end_char_bounds.y +
522 text_chunk->end_char_bounds.height);
523 text_chunk->text_bounds.height = y2 - text_chunk->text_bounds.y;
524 text_chunk->start_offset = start;
525 text_chunk->end_offset = end;
526 if (text_chunk->text_bounds.x < text_chunk->clip_bounds.x) {
527 text_chunk->text_bounds.x = text_chunk->clip_bounds.x;
528 text_chunk->text_bounds.isClipped = TRUE;
530 if ((text_chunk->text_bounds.x +
531 text_chunk->text_bounds.width)
532 > (text_chunk->clip_bounds.x +
533 text_chunk->clip_bounds.width)) {
534 text_chunk->text_bounds.width =
535 MAX (0, (text_chunk->clip_bounds.x +
536 text_chunk->clip_bounds.width) -
537 text_chunk->text_bounds.x);
538 text_chunk->text_bounds.isClipped = TRUE;
540 if (!BOUNDS_CONTAIN_Y (&text_chunk->text_bounds,
543 fprintf (stderr, "%s out of bounds (%d-%d)\n", s,
544 text_chunk->text_bounds.y,
545 text_chunk->text_bounds.y +
546 text_chunk->text_bounds.height);
551 if (role == SPI_ROLE_PUSH_BUTTON ||
552 role == SPI_ROLE_CHECK_BOX ||
553 role == SPI_ROLE_MENU ||
554 role == SPI_ROLE_MENU_ITEM) { /* don't like this
555 special casing :-( */
556 s = Accessible_getName (accessible);
557 /* use name instead */
558 text_chunk->text_bounds = text_chunk->clip_bounds;
559 text_chunk->start_offset = 0;
560 text_chunk->end_offset = strlen (s);
563 if (s && strlen (s)) {
564 if (s[strlen(s)-1] == '\n') s[strlen(s)-1] = ' ';
565 /* XXX: if last char is newline, aren't its bounds wrong now? */
566 text_chunk->string = s;
567 text_chunk->source = text;
568 if (text) AccessibleText_ref (text);
570 fprintf (stderr, "%s, bounds %d-%d; clip %d-%d\n",
572 text_chunk->text_bounds.x,
573 text_chunk->text_bounds.x+text_chunk->text_bounds.width,
574 text_chunk->clip_bounds.x,
575 text_chunk->clip_bounds.x+text_chunk->clip_bounds.width);
578 text_chunk->string = NULL;
579 text_chunk->source = NULL;
581 if (text) AccessibleText_unref (text);
586 debug_chunk_list (GList *iter)
590 chunk = (TextChunk *)iter->data;
591 fprintf (stderr, "Chunk %s, clip %d-%d, text %d-%d\n",
593 chunk->clip_bounds.x,
594 chunk->clip_bounds.x + chunk->clip_bounds.width,
595 chunk->text_bounds.x,
596 chunk->text_bounds.x + chunk->text_bounds.width);
602 clip_into_buffers (Accessible *accessible, BoundaryRect* parentClipBounds[],
603 ScreenReviewBuffer *reviewBuffers[],
604 int screen_x, int screen_y)
606 int n_children, child_n;
609 BoundaryRect** clip_bounds;
610 TextChunk *text_chunk;
611 AccessibleComponent *component;
615 clip_bounds = clip_bounds_clone (parentClipBounds);
616 if (Accessible_isComponent (accessible)) {
617 role = Accessible_getRole (accessible);
618 component = Accessible_getComponent (accessible);
619 layer = AccessibleComponent_getLayer (component);
620 bounds = *clip_bounds[layer];
621 if (!bounds.isEmpty) {
622 AccessibleComponent_getExtents (component,
627 SPI_COORD_TYPE_SCREEN);
629 if (clip_bounds[layer])
630 boundary_clip (&bounds, clip_bounds[layer]);
631 if (BOUNDS_CONTAIN_Y (&bounds, screen_y)) {
632 text_chunk = review_buffer_get_text_chunk (
633 reviewBuffers[layer], accessible, &bounds,
635 reviewBuffers[layer]->text_chunks =
636 text_chunk_list_insert_chunk (
637 reviewBuffers[layer]->text_chunks,
641 IS_CLIPPING_CONTAINER (bounds.role);
644 Accessible_unref (component);
647 * we always descend into children in case they are in a higher layer
648 * this can of course be optimized for the topmost layer...
649 * but nobody uses that one! (SPI_LAYER_OVERLAY)
651 n_children = Accessible_getChildCount (accessible);
652 for (child_n = 0; child_n < n_children; ++child_n) {
653 child = Accessible_getChildAtIndex (accessible, child_n);
654 clip_into_buffers (child, clip_bounds, reviewBuffers, screen_x, screen_y);
655 Accessible_unref (child);
657 /* TODO: free the parent clip bounds */
660 #undef CHARACTER_CLIP_DEBUG
663 text_chunk_get_clipped_string (TextChunk *chunk)
669 GString *string = g_string_new ("");
670 BoundaryRect char_bounds;
671 if (!chunk->text_bounds.isClipped)
673 else if (chunk->source) {
674 len = chunk->end_offset - chunk->start_offset;
675 #ifdef CHARACTER_CLIP_DEBUG
676 fprintf (stderr, "clipping %s\n", chunk->string);
678 for (i = chunk->start_offset; i < chunk->end_offset; ++i) {
679 AccessibleText_getCharacterExtents (chunk->source,
685 SPI_COORD_TYPE_SCREEN);
686 #ifdef CHARACTER_CLIP_DEBUG
687 fprintf (stderr, "testing %d-%d against %d-%d\n",
688 char_bounds.x, char_bounds.x+char_bounds.width,
689 chunk->text_bounds.x,
690 chunk->text_bounds.x + chunk->text_bounds.width);
692 if (BOUNDS_CONTAIN_X (&chunk->text_bounds,
694 c = AccessibleText_getCharacterAtOffset (
697 fprintf (stderr, "[%c]", c);
699 g_string_append_unichar (string, c);
703 } else { /* we're clipped, but don't implement AccessibleText :-( */
704 /* punt for now, maybe we can do betterc someday */
707 g_string_free (string, FALSE);
712 text_chunk_list_to_string (GList *iter)
716 TextChunk *chunk = NULL;
718 chunk = (TextChunk *)iter->data;
719 if (chunk /* && chunk->string */) {
720 string = text_chunk_get_clipped_string (chunk);
722 s = g_strconcat (s, "|", string, NULL);
724 s = g_strconcat (s, ":", NULL);
728 s = g_strconcat (s, "|", NULL);
733 review_buffer_composite (ScreenReviewBuffer *buffers[])
736 GList *chunk_list, *iter;
738 chunk_list = buffers[0]->text_chunks;
739 /* for (i = 1; i < SPI_LAYER_LAST_DEFINED; ++i) {
740 iter = buffers[i]->text_chunks;
741 fprintf (stderr, "layer %d has %d chunks\n",
742 i, g_list_length (iter));
744 chunk = (TextChunk *) iter->data;
746 fprintf (stderr, "inserting chunk <%s>\n",
747 chunk->string ? chunk->string : "<null>");
749 text_chunk_list_insert_chunk (chunk_list,
755 */ chunk_list = buffers[SPI_LAYER_WIDGET]->text_chunks;
756 return text_chunk_list_to_string (chunk_list);
760 get_screen_review_line_at (int x, int y)
763 Accessible *desktop, *app, *toplevel, *child;
764 AccessibleComponent *component;
765 AccessibleStateSet *states;
766 GList *toplevels = NULL, *actives = NULL, *iter;
767 ScreenReviewBuffer* reviewBuffers[SPI_LAYER_LAST_DEFINED];
768 BoundaryRect* clip_bounds[SPI_LAYER_LAST_DEFINED];
769 BoundaryRect toplevel_bounds;
770 int n_apps, n_toplevels, n_children, app_n, toplevel_n, child_n;
771 GTimer *timer = g_timer_new ();
774 for (i = 0; i < SPI_LAYER_LAST_DEFINED; ++i) {
775 reviewBuffers[i] = g_new0 (ScreenReviewBuffer, 1);
776 clip_bounds[i] = g_new0 (BoundaryRect, 1);
777 clip_bounds[i]->isClipped = FALSE;
778 clip_bounds[i]->isEmpty = FALSE;
781 /* how do we decide which desktop ? */
782 desktop = SPI_getDesktop (0);
785 n_apps = Accessible_getChildCount (desktop);
786 for (app_n = 0; app_n < n_apps; ++app_n) {
787 /* for each toplevel in app */
788 app = Accessible_getChildAtIndex (desktop, app_n);
789 n_toplevels = Accessible_getChildCount (app);
790 for (toplevel_n = 0; toplevel_n < n_toplevels; ++toplevel_n) {
791 Accessible *toplevel = Accessible_getChildAtIndex (app, toplevel_n);
792 if (Accessible_isComponent (toplevel))
793 toplevels = g_list_prepend (toplevels, toplevel);
795 Accessible_unref (toplevel);
796 fprintf (stderr, "warning, app toplevel not a component.\n");
801 /* sort: at the moment we don't have a good way to sort except to put actives on top */
802 for (iter = g_list_first (toplevels); iter; iter = iter->next) {
803 Accessible *toplevel =
804 (Accessible *) iter->data;
805 if (AccessibleStateSet_contains (Accessible_getStateSet (toplevel),
807 actives = g_list_prepend (actives, toplevel);
811 for (iter = g_list_first (actives); iter; iter = actives->next) {
812 toplevels = g_list_remove (toplevels, iter->data); /* place at end */
813 toplevels = g_list_append (toplevels, iter->data);
815 g_list_free (actives);
817 /* for each toplevel, ending with the active one(s),
818 * clip against children, putting results into appropriate charBuffer.
820 for (iter = g_list_first (toplevels); iter; iter = iter->next) {
821 toplevel = (Accessible *) iter->data;
822 if (Accessible_isComponent (toplevel)) {
823 /* make sure toplevel is visible and not iconified or shaded */
824 states = Accessible_getStateSet (toplevel);
825 if (AccessibleStateSet_contains (states, SPI_STATE_VISIBLE)
826 && !AccessibleStateSet_contains (states, SPI_STATE_ICONIFIED)
827 || isJava) { /* isJava hack! */
828 component = Accessible_getComponent (toplevel);
829 AccessibleComponent_getExtents (component,
832 &toplevel_bounds.width,
833 &toplevel_bounds.height,
834 SPI_COORD_TYPE_SCREEN);
835 toplevel_bounds.isEmpty = FALSE;
836 for (i = 0; i < SPI_LAYER_LAST_DEFINED; ++i) {
837 *clip_bounds[i] = toplevel_bounds;
839 clip_into_buffers (toplevel, clip_bounds,
840 reviewBuffers, x, y);
842 fprintf (stderr, "toplevel clip done\n");
843 debug_chunk_list (reviewBuffers[SPI_LAYER_WIDGET]->text_chunks);
847 Accessible_unref (toplevel);
850 string = review_buffer_composite (reviewBuffers);
852 /* SIMPLE SINGLE-PASS ALGORITHM:*/
853 /* traverse the tree:
854 * keep a pointer to outermost instance of each layer
855 * clip against outermost in same layer
856 * when this clip occurs, store outermost clipped string in 2d string buffer.
857 * string buffer may have attributes to mark component bounds, line art,
858 * or attributes of text being reviewed.
859 * composite the layers, ignoring NULL chars in the string buffers.
862 * sibling clip not correct, text may overwrite if siblings intersect onscreen
863 * length of resulting text buffer may vary!
866 * no API for ordering toplevels yet, other than knowing which is ACTIVE.
867 * not much implementation for the LAYER API yet, other than menus.
869 g_timer_stop (timer);
870 fprintf (stderr, "elapsed time = %f s\n", g_timer_elapsed (timer, NULL));
876 report_screen_review_line (const AccessibleEvent *event, void *user_data)
878 static Display *display = NULL;
879 int x, y, win_x, win_y;
880 Window root_return, child_return;
881 unsigned int mask_return;
883 if (!display) display = XOpenDisplay (getenv ("DISPLAY"));
885 * we would prefer to get the x,y info in the above event.
886 * At the moment we don't get detail params for "toolkit" events,
887 * so for testing purposes we use XQueryPointer. Actual apps
888 * probably shouldn't do this.
890 XQueryPointer (display,
891 DefaultRootWindow (display),
892 &root_return, &child_return,
897 fprintf (stderr, "screen review event %s at %d, %d\n", event->type,
899 fprintf (stderr, "[%s]\n",
900 get_screen_review_line_at (x, y));
906 SPI_deregisterGlobalEventListenerAll (mouseclick_listener);
907 AccessibleEventListener_unref (mouseclick_listener);