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"
28 #define BOUNDS_CONTAIN_X(b, p) (((p)>=(b)->x) && ((p)<=((b)->x + (b)->width))\
29 && ((b)->width > 0) && ((b)->height > 0))
31 #define BOUNDS_CONTAIN_Y(b, p) (((p)>=(b)->y) && ((p)<=((b)->y + (b)->height))\
32 && ((b)->width > 0) && ((b)->height > 0))
34 #define CHUNK_BOUNDS_BEFORE_START(c, i) ((i) && ((c)->clip_bounds.x < \
37 #define CHUNK_BOUNDS_END_BEFORE_START(c, i) ((i) && \
38 (((c)->clip_bounds.x + \
39 (c)->clip_bounds.width) < \
42 #define CHUNK_BOUNDS_AFTER_END(c, i) ((i) && ((c)->clip_bounds.x >= \
43 ((i)->clip_bounds.x + \
44 (i)->clip_bounds.width)))
46 #define CHUNK_BOUNDS_SPANS_END(c, i) ((i) && (((c)->clip_bounds.x + \
47 (c)->clip_bounds.width) >= \
48 ((i)->clip_bounds.x + \
49 (i)->clip_bounds.width)))
51 //#define CHUNK_BOUNDS_WITHIN(c, i) ((i) && ((c)->clip_bounds.x >= \
52 // (i)->text_bounds.x) && \
53 // (((c)->clip_bounds.x + (c)->clip_bounds.width) \
54 // <= ((i)->text_bounds.x + (i)->text_bounds.width)))
56 static void report_screen_review_line (const AccessibleEvent *event, void *user_data);
58 static gint n_elements_traversed = 0;
59 static AccessibleEventListener *mouseclick_listener;
61 typedef struct _BoundaryRect {
66 AccessibleRole role; /* role of last clipping element */
71 typedef struct _TextChunk {
73 AccessibleText *source;
76 BoundaryRect clip_bounds;
77 BoundaryRect text_bounds;
78 BoundaryRect start_char_bounds;
79 BoundaryRect end_char_bounds;
82 typedef struct _ScreenReviewBuffer { /* TODO: implement */
86 static gboolean isJava = FALSE;
89 main (int argc, char **argv)
98 Accessible *application;
103 mouseclick_listener = SPI_createAccessibleEventListener (
104 report_screen_review_line, NULL);
106 SPI_registerGlobalEventListener (mouseclick_listener,
107 "Gtk:GtkWidget:button-press-event");
108 #define JAVA_TEST_HACK
109 #ifdef JAVA_TEST_HACK
110 SPI_registerGlobalEventListener (mouseclick_listener,
111 "object:text-caret-moved");
116 putenv ("AT_BRIDGE_SHUTDOWN=1");
119 * TODO: Add a key event listener that calls test_exit, to
120 * deregister and cleanup appropriately.
126 static inline gboolean
127 bounds_contain_y (BoundaryRect *bounds, int y)
129 return (y > bounds->y && y <= (bounds->y + bounds->height)
130 && bounds->width && bounds->height);
134 static inline gboolean
135 chunk_bounds_within (TextChunk *chunk, TextChunk *test_chunk)
137 int x1, x2, tx1, tx2;
140 x1 = chunk->clip_bounds.x;
141 x2 = x1 + chunk->clip_bounds.width;
142 tx1 = test_chunk->clip_bounds.x;
143 tx2 = tx1 + test_chunk->clip_bounds.width;
144 gtx1 = (chunk->clip_bounds.x >= test_chunk->clip_bounds.x);
145 ltx2 = (chunk->clip_bounds.x + chunk->clip_bounds.width
146 <= test_chunk->clip_bounds.x + test_chunk->clip_bounds.width);
148 // fprintf (stderr, "testing BOUNDS %d-%d WITHIN %d-%d: %s\n",
149 // x1, x2, tx1, tx2, ((gtx1 && ltx2) ? "T" : "F"));
154 #define CHUNK_BOUNDS_WITHIN(a, b) chunk_bounds_within(a, b)
156 static BoundaryRect **
157 clip_bounds_clone (BoundaryRect *bounds[])
160 BoundaryRect **bounds_clone;
161 bounds_clone = (BoundaryRect **)
162 g_new0 (gpointer, SPI_LAYER_LAST_DEFINED);
163 for (i = 0; i < SPI_LAYER_LAST_DEFINED; ++i) {
164 bounds_clone[i] = g_new0 (BoundaryRect, 1);
165 *bounds_clone[i] = *bounds[i];
171 boundary_clip (BoundaryRect *bounds, BoundaryRect *clipBounds)
173 int x2 = bounds->x + bounds->width;
174 int y2 = bounds->y + bounds->height;
176 fprintf (stderr, "bclip %d-%d, %d-%d; ",
178 clipBounds->x, clipBounds->x+clipBounds->width);
180 bounds->x = MAX (bounds->x, clipBounds->x);
181 bounds->y = MAX (bounds->y, clipBounds->y);
182 x2 = MIN (x2, clipBounds->x + clipBounds->width);
183 y2 = MIN (y2, clipBounds->y + clipBounds->height);
184 bounds->width = MAX (x2 - bounds->x, 0);
185 bounds->height = MAX (y2 - bounds->y, 0);
186 if (!bounds->width || !bounds->height)
187 bounds->isEmpty = TRUE;
189 fprintf (stderr, "%d-%d\n",
190 bounds->x, bounds->x+bounds->width);
195 boundary_xclip_head (BoundaryRect *bounds, BoundaryRect *clipBounds)
198 int cx2 = clipBounds->x + clipBounds->width;
199 if (cx2 < bounds->x) return;
200 x2 = bounds->x + bounds->width;
201 if (cx2 < x2) bounds->x = cx2;
202 bounds->width = MAX (0, x2 - cx2);
206 boundary_xclip_tail (BoundaryRect *bounds, BoundaryRect *clipBounds)
208 int x2 = bounds->x + bounds->width;
209 if (clipBounds->x > x2) return;
210 bounds->width = MAX (0, clipBounds->x - bounds->x);
214 text_chunk_copy (TextChunk *chunk)
216 TextChunk *copy = g_new0 (TextChunk, 1);
218 if (chunk->string) copy->string = g_strdup (chunk->string);
219 if (copy->source) AccessibleText_ref (copy->source);
224 text_chunk_tail_clip (TextChunk *bottom, TextChunk *top)
227 fprintf (stderr, "bottom %d-%d, top %d-%d;",
228 bottom->clip_bounds.x,
229 bottom->clip_bounds.x + bottom->clip_bounds.width,
231 top->clip_bounds.x + top->clip_bounds.width);
233 boundary_xclip_tail (&bottom->text_bounds, &top->clip_bounds);
234 boundary_xclip_tail (&bottom->clip_bounds, &top->clip_bounds);
235 bottom->text_bounds.isClipped = TRUE;
236 bottom->clip_bounds.isClipped = TRUE;
238 fprintf (stderr, "result %d-%d\n",
239 bottom->clip_bounds.x,
240 bottom->clip_bounds.x + bottom->clip_bounds.width);
245 text_chunk_head_clip (TextChunk *bottom, TextChunk *top)
248 fprintf (stderr, "bottom %d-%d, top %d-%d;",
249 bottom->clip_bounds.x,
250 bottom->clip_bounds.x + bottom->clip_bounds.width,
252 top->clip_bounds.x + top->clip_bounds.width);
254 boundary_xclip_head (&bottom->text_bounds, &top->clip_bounds);
255 boundary_xclip_head (&bottom->clip_bounds, &top->clip_bounds);
256 bottom->text_bounds.isClipped = TRUE;
257 bottom->clip_bounds.isClipped = TRUE;
259 fprintf (stderr, "result %d-%d\n",
260 bottom->clip_bounds.x,
261 bottom->clip_bounds.x + bottom->clip_bounds.width);
266 text_chunk_split_insert (GList *chunk_list, GList *iter, TextChunk *chunk)
268 TextChunk *iter_chunk = iter->data;
269 TextChunk *iter_copy = text_chunk_copy (iter_chunk);
270 /* TODO: FIXME something is wrong here */
272 fprintf (stderr, "***clip insert of %s into %s\n", chunk->string,
275 chunk_list = g_list_insert_before (chunk_list, iter, iter_copy);
276 text_chunk_tail_clip (iter_copy, chunk);
277 chunk_list = g_list_insert_before (chunk_list, iter, chunk);
278 text_chunk_head_clip (iter_chunk, chunk);
282 /* #define PRINT_CHUNK_DEBUG(a, b, c, d) print_chunk_debug(a, b, c, d) */
284 #define PRINT_CHUNK_DEBUG(a, b, c, d)
286 #ifdef PRINT_CHUNK_DEBUG
288 print_chunk_debug (TextChunk *chunk, char *opname, GList *prev, GList *next)
290 fprintf (stderr, "%sing chunk %s between %s and %s; %d-%d\n",
293 (prev ? ((TextChunk *) prev->data)->string : "<null>"),
294 (next ? ((TextChunk *) next->data)->string : "<null>"),
295 chunk->clip_bounds.x,
296 chunk->text_bounds.x + chunk->text_bounds.width);
301 text_chunk_list_head_clip (GList *text_chunk_list,
305 GList *target, *iter = next, *prev;
307 // if (chunk->string && strlen (chunk->string)) {
309 g_list_insert_before (text_chunk_list, next, chunk);
312 if (CHUNK_BOUNDS_SPANS_END (chunk, (TextChunk *)iter->data)) {
314 fprintf (stderr, "deleting %s\n",
315 ((TextChunk *)iter->data)->string);
320 g_list_delete_link (text_chunk_list, target);
322 if (!CHUNK_BOUNDS_END_BEFORE_START (chunk,
323 (TextChunk *)iter->data)) {
324 text_chunk_head_clip ((TextChunk *)iter->data,
328 !CHUNK_BOUNDS_AFTER_END (
330 (TextChunk *)prev->data)) {
331 text_chunk_tail_clip (
332 (TextChunk *)prev->data,
339 return text_chunk_list;
343 text_chunk_list_clip_and_insert (GList *text_chunk_list,
349 fprintf (stderr, "clip-and-insert for %s, between %s and %s\n",
351 (prev ? ((TextChunk *)prev->data)->string : "<null>"),
352 (next ? ((TextChunk *)next->data)->string : "<null>"));
355 if (!prev && !next) { /* first element in, no clip needed */
356 // if (chunk->string && strlen (chunk->string)) {
358 g_list_append (text_chunk_list, chunk);
359 PRINT_CHUNK_DEBUG (chunk, "append",
363 } else { /* check for clip with prev */
364 /* if we split the prev */
366 CHUNK_BOUNDS_WITHIN (chunk, (TextChunk *) prev->data)) {
368 text_chunk_split_insert (
372 /* we split the 'next' element */
373 if (CHUNK_BOUNDS_WITHIN (chunk, (TextChunk *)next->data)) {
375 text_chunk_split_insert (text_chunk_list,
378 /* do an insert + head clip */
380 text_chunk_list_head_clip (
386 if (!CHUNK_BOUNDS_AFTER_END (chunk,
387 (TextChunk *) prev->data)) {
388 text_chunk_tail_clip (prev->data, chunk);
390 // if (chunk->string && strlen (chunk->string)) {
392 g_list_append (text_chunk_list, chunk);
396 return text_chunk_list;
400 text_chunk_list_insert_chunk (GList *text_chunk_list, TextChunk *chunk)
402 GList *iter = g_list_first (text_chunk_list);
403 TextChunk *iter_chunk = NULL;
405 if (iter) iter_chunk = (TextChunk *) iter->data;
406 /* if we're ahead of the current element */
409 text_chunk_list_clip_and_insert (text_chunk_list,
414 } else if (CHUNK_BOUNDS_BEFORE_START (chunk, iter_chunk)) {
416 text_chunk_list_clip_and_insert (text_chunk_list,
421 } else if (!iter->next ) {
423 text_chunk_list_clip_and_insert (text_chunk_list,
429 if (iter) iter = iter->next;
431 return text_chunk_list;
435 review_buffer_get_text_chunk (ScreenReviewBuffer *reviewBuffer,
436 Accessible *accessible, BoundaryRect *bounds,
437 int screen_x, int screen_y)
439 AccessibleText *text = NULL;
441 TextChunk *text_chunk;
442 BoundaryRect start_char_bounds, end_char_bounds;
448 role = Accessible_getRole (accessible);
449 text_chunk = g_new0 (TextChunk, 1);
450 text_chunk->clip_bounds = *bounds;
451 if (Accessible_isText (accessible)) {
452 text = Accessible_getText (accessible);
453 offset = AccessibleText_getOffsetAtPoint (text,
456 SPI_COORD_TYPE_SCREEN);
457 s = AccessibleText_getTextAtOffset (text, offset,
458 SPI_TEXT_BOUNDARY_LINE_START,
461 AccessibleText_getCharacterExtents (
463 &text_chunk->start_char_bounds.x,
464 &text_chunk->start_char_bounds.y,
465 &text_chunk->start_char_bounds.width,
466 &text_chunk->start_char_bounds.height,
467 SPI_COORD_TYPE_SCREEN);
469 fprintf (stderr, "%s: start char (%d) x, width %d %d;",
472 text_chunk->start_char_bounds.x,
473 text_chunk->start_char_bounds.width);
475 end--; /* XXX: bug workaround */
476 AccessibleText_getCharacterExtents (
478 &text_chunk->end_char_bounds.x,
479 &text_chunk->end_char_bounds.y,
480 &text_chunk->end_char_bounds.width,
481 &text_chunk->end_char_bounds.height,
482 SPI_COORD_TYPE_SCREEN);
484 fprintf (stderr, "end char (%d) x, width %d %d\n",
486 text_chunk->end_char_bounds.x,
487 text_chunk->end_char_bounds.width);
490 text_chunk->text_bounds.x = MIN (text_chunk->start_char_bounds.x,
491 text_chunk->end_char_bounds.x);
492 text_chunk->text_bounds.y = MIN (text_chunk->start_char_bounds.y,
493 text_chunk->end_char_bounds.y);
494 x2 = MAX (text_chunk->start_char_bounds.x +
495 text_chunk->start_char_bounds.width,
496 text_chunk->end_char_bounds.x +
497 text_chunk->end_char_bounds.width);
498 text_chunk->text_bounds.width = x2 - text_chunk->text_bounds.x;
499 y2 = MAX (text_chunk->start_char_bounds.y +
500 text_chunk->start_char_bounds.height,
501 text_chunk->end_char_bounds.y +
502 text_chunk->end_char_bounds.height);
503 text_chunk->text_bounds.height = y2 - text_chunk->text_bounds.y;
504 text_chunk->start_offset = start;
505 text_chunk->end_offset = end;
506 if ((role != SPI_ROLE_TABLE_CELL) /* XXX bug workaround */
507 && !bounds_contain_y (&text_chunk->text_bounds,
512 if (role == SPI_ROLE_PUSH_BUTTON ||
513 role == SPI_ROLE_CHECK_BOX ||
514 role == SPI_ROLE_MENU ||
515 role == SPI_ROLE_MENU_ITEM) { /* don't like this
516 special casing :-( */
517 s = Accessible_getName (accessible);
518 /* use name instead */
519 text_chunk->text_bounds = text_chunk->clip_bounds;
520 text_chunk->start_offset = 0;
521 text_chunk->end_offset = strlen (s);
524 if (s && strlen (s)) {
525 if (s[strlen(s)-1] == '\n') s[strlen(s)-1] = ' ';
526 /* XXX: if last char is newline, aren't its bounds wrong now? */
527 text_chunk->string = s;
528 text_chunk->source = text;
529 if (text) AccessibleText_ref (text);
531 fprintf (stderr, "%s, bounds %d-%d; clip %d-%d\n",
533 text_chunk->text_bounds.x,
534 text_chunk->text_bounds.x+text_chunk->text_bounds.width,
535 text_chunk->clip_bounds.x,
536 text_chunk->clip_bounds.x+text_chunk->clip_bounds.width);
539 text_chunk->string = NULL;
540 text_chunk->source = NULL;
542 if (text) AccessibleText_unref (text);
547 debug_chunk_list (GList *iter)
551 chunk = (TextChunk *)iter->data;
552 fprintf (stderr, "Chunk %s, clip %d-%d, text %d-%d\n",
554 chunk->clip_bounds.x,
555 chunk->clip_bounds.x + chunk->clip_bounds.width,
556 chunk->text_bounds.x,
557 chunk->text_bounds.x + chunk->text_bounds.width);
563 clip_into_buffers (Accessible *accessible, BoundaryRect* parentClipBounds[],
564 ScreenReviewBuffer *reviewBuffers[],
565 int screen_x, int screen_y)
567 int n_children, child_n;
570 BoundaryRect** clip_bounds;
571 TextChunk *text_chunk;
572 AccessibleComponent *component;
575 clip_bounds = clip_bounds_clone (parentClipBounds);
576 if (Accessible_isComponent (accessible)) {
577 component = Accessible_getComponent (accessible);
578 layer = AccessibleComponent_getLayer (component);
579 bounds = *clip_bounds[layer];
580 if (!bounds.isEmpty || 1) {
581 AccessibleComponent_getExtents (component,
586 SPI_COORD_TYPE_SCREEN);
587 if (clip_bounds[layer])
588 boundary_clip (&bounds, clip_bounds[layer]);
589 if (bounds_contain_y (&bounds, screen_y)) {
590 text_chunk = review_buffer_get_text_chunk (
591 reviewBuffers[layer], accessible, &bounds,
593 reviewBuffers[layer]->text_chunks =
594 text_chunk_list_insert_chunk (
595 reviewBuffers[layer]->text_chunks,
598 clip_bounds[layer]->isEmpty = TRUE;
601 Accessible_unref (component);
604 * we always descend into children in case they are in a higher layer
605 * this can of course be optimized for the topmost layer...
606 * but nobody uses that one! (SPI_LAYER_OVERLAY)
608 n_children = Accessible_getChildCount (accessible);
609 for (child_n = 0; child_n < n_children; ++child_n) {
610 child = Accessible_getChildAtIndex (accessible, child_n);
611 clip_into_buffers (child, clip_bounds, reviewBuffers, screen_x, screen_y);
612 Accessible_unref (child);
614 /* TODO: free the parent clip bounds */
618 text_chunk_get_clipped_string (TextChunk *chunk)
624 GString *string = g_string_new ("");
625 BoundaryRect char_bounds;
626 if (!chunk->text_bounds.isClipped)
628 else if (chunk->source) {
629 len = chunk->end_offset - chunk->start_offset;
631 fprintf (stderr, "clipping %s\n", chunk->string);
633 for (i = 0; i < len; ++i) {
634 AccessibleText_getCharacterExtents (chunk->source,
640 SPI_COORD_TYPE_SCREEN);
641 #ifdef CHARACTER_CLIP_DEBUG
642 fprintf (stderr, "testing %d-%d against %d-%d\n",
643 char_bounds.x, char_bounds.x+char_bounds.width,
644 chunk->text_bounds.x,
645 chunk->text_bounds.x + chunk->text_bounds.width);
647 if (BOUNDS_CONTAIN_X (&chunk->text_bounds,
649 c = AccessibleText_getCharacterAtOffset (
652 fprintf (stderr, "[%c]", c);
654 g_string_append_unichar (string, c);
658 } else { /* we're clipped, but don't implement AccessibleText :-( */
659 /* punt for now, maybe we can do betterc someday */
662 g_string_free (string, FALSE);
667 text_chunk_list_to_string (GList *iter)
671 TextChunk *chunk = NULL;
673 chunk = (TextChunk *)iter->data;
674 if (chunk /* && chunk->string */) {
675 string = text_chunk_get_clipped_string (chunk);
677 s = g_strconcat (s, "|", string, NULL);
679 s = g_strconcat (s, ":", NULL);
683 s = g_strconcat (s, "|", NULL);
688 review_buffer_composite (ScreenReviewBuffer *buffers[])
691 GList *chunk_list, *iter;
693 chunk_list = buffers[0]->text_chunks;
694 for (i = 1; i < SPI_LAYER_LAST_DEFINED; ++i) {
695 iter = buffers[i]->text_chunks;
696 fprintf (stderr, "layer %d has %d chunks\n",
697 i, g_list_length (iter));
699 chunk = (TextChunk *) iter->data;
701 fprintf (stderr, "inserting chunk <%s>\n",
704 text_chunk_list_insert_chunk (chunk_list,
710 chunk_list = buffers[SPI_LAYER_WIDGET]->text_chunks;
711 return text_chunk_list_to_string (chunk_list);
715 get_screen_review_line_at (int x, int y)
718 Accessible *desktop, *app, *toplevel, *child;
719 AccessibleComponent *component;
720 AccessibleStateSet *states;
721 GList *toplevels = NULL, *actives = NULL, *iter;
722 ScreenReviewBuffer* reviewBuffers[SPI_LAYER_LAST_DEFINED];
723 BoundaryRect* clip_bounds[SPI_LAYER_LAST_DEFINED];
724 BoundaryRect toplevel_bounds;
725 int n_apps, n_toplevels, n_children, app_n, toplevel_n, child_n;
726 GTimer *timer = g_timer_new ();
729 for (i = 0; i < SPI_LAYER_LAST_DEFINED; ++i) {
730 reviewBuffers[i] = g_new0 (ScreenReviewBuffer, 1);
731 clip_bounds[i] = g_new0 (BoundaryRect, 1);
732 clip_bounds[i]->isClipped = FALSE;
733 clip_bounds[i]->isEmpty = FALSE;
736 /* how do we decide which desktop ? */
737 desktop = SPI_getDesktop (0);
740 n_apps = Accessible_getChildCount (desktop);
741 for (app_n = 0; app_n < n_apps; ++app_n) {
742 /* for each toplevel in app */
743 app = Accessible_getChildAtIndex (desktop, app_n);
744 n_toplevels = Accessible_getChildCount (app);
745 for (toplevel_n = 0; toplevel_n < n_toplevels; ++toplevel_n) {
746 Accessible *toplevel = Accessible_getChildAtIndex (app, toplevel_n);
747 if (Accessible_isComponent (toplevel))
748 toplevels = g_list_prepend (toplevels, toplevel);
750 Accessible_unref (toplevel);
751 fprintf (stderr, "warning, app toplevel not a component.\n");
756 /* sort: at the moment we don't have a good way to sort except to put actives on top */
757 for (iter = g_list_first (toplevels); iter; iter = iter->next) {
758 Accessible *toplevel =
759 (Accessible *) iter->data;
760 if (AccessibleStateSet_contains (Accessible_getStateSet (toplevel),
762 actives = g_list_prepend (actives, toplevel);
766 for (iter = g_list_first (actives); iter; iter = actives->next) {
767 toplevels = g_list_remove (toplevels, iter->data); /* place at end */
768 toplevels = g_list_append (toplevels, iter->data);
770 g_list_free (actives);
772 /* for each toplevel, ending with the active one(s),
773 * clip against children, putting results into appropriate charBuffer.
775 for (iter = g_list_first (toplevels); iter; iter = iter->next) {
776 toplevel = (Accessible *) iter->data;
777 if (Accessible_isComponent (toplevel)) {
778 /* make sure toplevel is visible and not iconified or shaded */
779 states = Accessible_getStateSet (toplevel);
780 if (AccessibleStateSet_contains (states, SPI_STATE_VISIBLE)
781 && !AccessibleStateSet_contains (states, SPI_STATE_ICONIFIED)
782 || isJava) { /* isJava hack! */
783 component = Accessible_getComponent (toplevel);
784 AccessibleComponent_getExtents (component,
787 &toplevel_bounds.width,
788 &toplevel_bounds.height,
789 SPI_COORD_TYPE_SCREEN);
790 toplevel_bounds.isEmpty = FALSE;
791 for (i = 0; i < SPI_LAYER_LAST_DEFINED; ++i) {
792 *clip_bounds[i] = toplevel_bounds;
795 fprintf (stderr, "toplevel clip starting\n");
796 debug_chunk_list (reviewBuffers[SPI_LAYER_WIDGET]->text_chunks);
798 clip_into_buffers (toplevel, clip_bounds,
799 reviewBuffers, x, y);
802 Accessible_unref (toplevel);
805 string = review_buffer_composite (reviewBuffers);
807 /* SIMPLE SINGLE-PASS ALGORITHM:*/
808 /* traverse the tree:
809 * keep a pointer to outermost instance of each layer
810 * clip against outermost in same layer
811 * when this clip occurs, store outermost clipped string in 2d string buffer.
812 * string buffer may have attributes to mark component bounds, line art,
813 * or attributes of text being reviewed.
814 * composite the layers, ignoring NULL chars in the string buffers.
817 * sibling clip not correct, text may overwrite if siblings intersect onscreen
818 * length of resulting text buffer may vary!
821 * no API for ordering toplevels yet, other than knowing which is ACTIVE.
822 * not much implementation for the LAYER API yet, other than menus.
824 g_timer_stop (timer);
825 fprintf (stderr, "elapsed time = %f s\n", g_timer_elapsed (timer, NULL));
831 report_screen_review_line (const AccessibleEvent *event, void *user_data)
833 static Display *display = NULL;
834 int x, y, win_x, win_y;
835 Window root_return, child_return;
836 unsigned int mask_return;
838 if (!display) display = XOpenDisplay (getenv ("DISPLAY"));
840 * we would prefer to get the x,y info in the above event.
841 * At the moment we don't get detail params for "toolkit" events,
842 * so for testing purposes we use XQueryPointer. Actual apps
843 * probably shouldn't do this.
845 XQueryPointer (display,
846 DefaultRootWindow (display),
847 &root_return, &child_return,
852 fprintf (stderr, "screen review event %s at %d, %d\n", event->type,
854 fprintf (stderr, "[%s]\n",
855 get_screen_review_line_at (x, y));
861 SPI_deregisterGlobalEventListenerAll (mouseclick_listener);
862 AccessibleEventListener_unref (mouseclick_listener);