Tweaks to screen-review-test, and changed at-spi-registryd installation to
[platform/core/uifw/at-spi2-atk.git] / test / screen-review-test.c
index 4b41cd7..9588212 100644 (file)
 #include <stdlib.h>
 #include "../cspi/spi-private.h"
 
-#define CLIP_DEBUG
+#undef CLIP_DEBUG
 
-#define BOUNDS_CONTAIN_X(b, p)  (((p)>=(b)->x) && ((p)<=((b)->x + (b)->width))\
-           && ((b)->width > 0) && ((b)->height > 0))
+/*
+ * Screen Review Algorithm Demonstration, Benchmark, and Test-bed.
+ *
+ * Issues:
+ *
+ * * bounds now fail to include window border decoration
+ *         (which we can't really know about yet).
+ *
+ * * brute-force algorithm uses no client-side cache; performance mediocre.
+ *
+ * * we don't have good ordering information for the toplevel windows,
+ *          and our current heuristic is not guaranteed if
+ *          the active window is not always on top (i.e. autoraise is
+ *          not enabled).
+ *
+ * * text-bearing objects that don't implement AccessibleText (such as buttons)
+ *          don't get their text clipped since we don't know the character
+ *          bounding boxes.
+ *
+ * * can't know about "inaccessible" objects that may be obscuring the
+ *          accessible windows (inherent to API-based approach).
+ *
+ * * this implementation doesn't worry about text column-alignment, it generates
+ *          review lines of more-or-less arbitrary length.  The x-coordinate
+ *          info is preserved in the reviewBuffers list structures, but the
+ *          "buffer-to-string" algorithm (currently) ignores it.
+ *
+ * * (others).
+ */
+
+#undef CHUNK_LIST_DEBUG /* define to get list of text chunks before clip */
+
+#define BOUNDS_CONTAIN_X_BOUNDS(b, p)  ( ((p).x>=(b).x) &&\
+                                        (((p).x + (p).width) <= \
+                                         ((b).x + (b).width)) && \
+                                       ((b).width > 0) && ((b).height > 0))
 
 #define BOUNDS_CONTAIN_Y(b, p)  (((p)>=(b)->y) && ((p)<=((b)->y + (b)->height))\
            && ((b)->width > 0) && ((b)->height > 0))
                                              (c)->clip_bounds.width) >= \
                                       ((i)->clip_bounds.x + \
                                        (i)->clip_bounds.width)))
+/*
+ * #define CHUNK_BOUNDS_WITHIN(c, i)  ((i) && ((c)->clip_bounds.x >= \
+ *                               (i)->text_bounds.x) && \
+ *                               (((c)->clip_bounds.x + (c)->clip_bounds.width) \
+ *                             <= ((i)->text_bounds.x + (i)->text_bounds.width)))
+ */
 
-//#define CHUNK_BOUNDS_WITHIN(c, i)  ((i) && ((c)->clip_bounds.x >= \
-//                               (i)->text_bounds.x) && \
-//                               (((c)->clip_bounds.x + (c)->clip_bounds.width) \
-//                               <= ((i)->text_bounds.x + (i)->text_bounds.width)))
+#define IS_CLIPPING_CONTAINER(a) ((a) != SPI_ROLE_PAGE_TAB)
 
 static void report_screen_review_line  (const AccessibleEvent *event, void *user_data);
 
@@ -106,7 +143,7 @@ main (int argc, char **argv)
   SPI_registerGlobalEventListener (mouseclick_listener,
                                   "Gtk:GtkWidget:button-press-event");
 #define JAVA_TEST_HACK
-#ifdef JAVA_TEST_HACK
+#ifdef JAVA_TEST_HACK /* Only use this to test Java apps */
   SPI_registerGlobalEventListener (mouseclick_listener,
                                   "object:text-caret-moved");
   isJava = TRUE;
@@ -124,14 +161,6 @@ main (int argc, char **argv)
 }
 
 static inline gboolean
-bounds_contain_y (BoundaryRect *bounds, int y)
-{
-       return (y > bounds->y && y <= (bounds->y + bounds->height)
-               && bounds->width && bounds->height);
-}
-
-
-static inline gboolean
 chunk_bounds_within (TextChunk *chunk, TextChunk *test_chunk)
 {
        int x1, x2, tx1, tx2;
@@ -144,10 +173,6 @@ chunk_bounds_within (TextChunk *chunk, TextChunk *test_chunk)
        gtx1 = (chunk->clip_bounds.x >= test_chunk->clip_bounds.x);
        ltx2 = (chunk->clip_bounds.x + chunk->clip_bounds.width
                <= test_chunk->clip_bounds.x + test_chunk->clip_bounds.width);
-
-//     fprintf (stderr, "testing BOUNDS %d-%d WITHIN %d-%d: %s\n",
-//              x1, x2, tx1, tx2, ((gtx1 && ltx2) ? "T" : "F"));
-       
        return gtx1 && ltx2;
 }
 
@@ -185,6 +210,9 @@ boundary_clip (BoundaryRect *bounds, BoundaryRect *clipBounds)
        bounds->height = MAX (y2 - bounds->y, 0);
        if (!bounds->width || !bounds->height)
                bounds->isEmpty = TRUE;
+       if (IS_CLIPPING_CONTAINER (bounds->role)) {
+               *clipBounds = *bounds;
+       }
 #ifdef CLIP_DEBUG
        fprintf (stderr, "%d-%d\n",
                 bounds->x, bounds->x+bounds->width);
@@ -468,15 +496,16 @@ review_buffer_get_text_chunk (ScreenReviewBuffer *reviewBuffer,
                                &text_chunk->start_char_bounds.height,
                                SPI_COORD_TYPE_SCREEN);
 #ifdef CLIP_DEBUG
-                       fprintf (stderr, "%s: start char (%d) x, width %d %d;",
+                       fprintf (stderr, "%s: start char (%d) x, width %d %d; ",
                                 s,
                                 start,
                                 text_chunk->start_char_bounds.x,
                                 text_chunk->start_char_bounds.width);
 #endif
-                       end--; /* XXX: bug workaround */
+                       if (s && strlen (s) && s[strlen (s) - 1] == '\n')
+                               end--;
                        AccessibleText_getCharacterExtents (
-                               text, end--,
+                               text, end - 1,
                                &text_chunk->end_char_bounds.x,
                                &text_chunk->end_char_bounds.y,
                                &text_chunk->end_char_bounds.width,
@@ -484,7 +513,7 @@ review_buffer_get_text_chunk (ScreenReviewBuffer *reviewBuffer,
                                SPI_COORD_TYPE_SCREEN);
 #ifdef CLIP_DEBUG                      
                        fprintf (stderr, "end char (%d) x, width %d %d\n",
-                                end,
+                                end - 1,
                                 text_chunk->end_char_bounds.x,
                                 text_chunk->end_char_bounds.width);
 #endif
@@ -505,14 +534,34 @@ review_buffer_get_text_chunk (ScreenReviewBuffer *reviewBuffer,
                text_chunk->text_bounds.height = y2 - text_chunk->text_bounds.y;
                text_chunk->start_offset = start;
                text_chunk->end_offset = end;
-               if ((role != SPI_ROLE_TABLE_CELL) /* XXX bug workaround */
-                   && !bounds_contain_y (&text_chunk->text_bounds,
-                                         screen_y)) {
+               if (text_chunk->text_bounds.x < text_chunk->clip_bounds.x) {
+                       text_chunk->text_bounds.x = text_chunk->clip_bounds.x;
+                       text_chunk->text_bounds.isClipped = TRUE;
+               } 
+               if ((text_chunk->text_bounds.x +
+                    text_chunk->text_bounds.width)
+                   > (text_chunk->clip_bounds.x +
+                      text_chunk->clip_bounds.width)) {
+                       text_chunk->text_bounds.width =
+                               MAX (0, (text_chunk->clip_bounds.x +
+                                        text_chunk->clip_bounds.width) -
+                                    text_chunk->text_bounds.x);
+                       text_chunk->text_bounds.isClipped = TRUE;
+               }
+               if (!BOUNDS_CONTAIN_Y (&text_chunk->text_bounds,
+                                      screen_y)) {
+#ifdef CLIP_DEBUG                      
+                       fprintf (stderr, "%s out of bounds (%d-%d)\n", s,
+                                text_chunk->text_bounds.y,
+                                text_chunk->text_bounds.y +
+                                text_chunk->text_bounds.height);
+#endif                 
                        s = NULL;
                }
        } else {
                if (role == SPI_ROLE_PUSH_BUTTON ||
                    role == SPI_ROLE_CHECK_BOX ||
+                   role == SPI_ROLE_LABEL ||
                    role == SPI_ROLE_MENU ||
                    role == SPI_ROLE_MENU_ITEM) { /* don't like this
                                                     special casing :-( */
@@ -572,23 +621,26 @@ clip_into_buffers (Accessible *accessible,  BoundaryRect* parentClipBounds[],
        BoundaryRect** clip_bounds;
        TextChunk *text_chunk;
        AccessibleComponent *component;
+       AccessibleRole role;
        int layer;
 
         clip_bounds = clip_bounds_clone (parentClipBounds);
        if (Accessible_isComponent (accessible)) {
+               role = Accessible_getRole (accessible);
                component = Accessible_getComponent (accessible);
                layer = AccessibleComponent_getLayer (component);
                bounds = *clip_bounds[layer];
-               if (!bounds.isEmpty || 1) {
+               if (!bounds.isEmpty) {
                        AccessibleComponent_getExtents (component,
                                                        &bounds.x,
                                                        &bounds.y,
                                                        &bounds.width,
                                                        &bounds.height,
                                                        SPI_COORD_TYPE_SCREEN);
+                       bounds.role = role;
                        if (clip_bounds[layer])
                                boundary_clip (&bounds, clip_bounds[layer]);
-                       if (bounds_contain_y (&bounds, screen_y)) {
+                       if (BOUNDS_CONTAIN_Y (&bounds, screen_y)) {
                                text_chunk = review_buffer_get_text_chunk (
                                        reviewBuffers[layer], accessible, &bounds,
                                        screen_x, screen_y);
@@ -597,7 +649,8 @@ clip_into_buffers (Accessible *accessible,  BoundaryRect* parentClipBounds[],
                                                reviewBuffers[layer]->text_chunks,
                                                text_chunk);
                        } else {
-                               clip_bounds[layer]->isEmpty = TRUE;
+                               bounds.isEmpty =
+                                       IS_CLIPPING_CONTAINER (bounds.role);
                        }
                } 
                Accessible_unref (component);
@@ -616,53 +669,131 @@ clip_into_buffers (Accessible *accessible,  BoundaryRect* parentClipBounds[],
        /* TODO: free the parent clip bounds */
 }
 
+#undef CHARACTER_CLIP_DEBUG
+
 static char*
-text_chunk_get_clipped_string (TextChunk *chunk)
+text_chunk_get_clipped_substring_by_char (TextChunk *chunk, int start, int end)
 {
-       char *s;
+       BoundaryRect char_bounds;
        int i;
-       int len;
-       gunichar c;
        GString *string = g_string_new ("");
-       BoundaryRect char_bounds;
-       if (!chunk->text_bounds.isClipped)
-               s = chunk->string;
-       else if (chunk->source) {
-               len = chunk->end_offset - chunk->start_offset;
-#ifdef CLIP_DEBUG              
-               fprintf (stderr, "clipping %s\n", chunk->string);
-#endif         
-               for (i = 0; i < len; ++i) {
-                       AccessibleText_getCharacterExtents (chunk->source,
-                                                           i,
-                                                           &char_bounds.x,
-                                                           &char_bounds.y,
-                                                           &char_bounds.width,
-                                                           &char_bounds.height,
-                                                           SPI_COORD_TYPE_SCREEN);
+       gunichar c;
+       for (i = start; i < end; ++i) {
+               AccessibleText_getCharacterExtents (chunk->source,
+                                                   i,
+                                                   &char_bounds.x,
+                                                   &char_bounds.y,
+                                                   &char_bounds.width,
+                                                   &char_bounds.height,
+                                                   SPI_COORD_TYPE_SCREEN);
 #ifdef CHARACTER_CLIP_DEBUG
-                       fprintf (stderr, "testing %d-%d against %d-%d\n",
-                                char_bounds.x, char_bounds.x+char_bounds.width,
-                                chunk->text_bounds.x,
-                                chunk->text_bounds.x + chunk->text_bounds.width);
+               fprintf (stderr, "testing %d-%d against %d-%d\n",
+                        char_bounds.x, char_bounds.x+char_bounds.width,
+                        chunk->text_bounds.x,
+                        chunk->text_bounds.x + chunk->text_bounds.width);
 #endif
-                       if (BOUNDS_CONTAIN_X (&chunk->text_bounds,
-                                             char_bounds.x)) {
-                               c = AccessibleText_getCharacterAtOffset (
-                                       chunk->source, i);
+               if (BOUNDS_CONTAIN_X_BOUNDS (chunk->text_bounds,
+                                            char_bounds)) {
+                       c = AccessibleText_getCharacterAtOffset (
+                               chunk->source, i);
 #ifdef CLIP_DEBUG                              
-                               fprintf (stderr, "[%c]", c);
+                       fprintf (stderr, "[%c]", c);
 #endif                         
-                               g_string_append_unichar (string, c);
-                       }
+                       g_string_append_unichar (string, c);
+               }
+       }
+       return string->str;
+               g_string_free (string, FALSE);
+
+}
+
+
+/*
+ * Note: this routine shouldn't have to do as much as it currently does,
+ *       but at the moment it works around a pango?/gail? bug which
+ *       causes WORD boundary type queries to return incorrect strings.
+ */
+static char *
+string_strip_newlines (char *s, long offset, long *start_offset, long *end_offset)
+{
+       int i;
+       char *word_start = s;
+       /* FIXME: potential memory leak here */
+       for (i=0; s && s[i]; ++i)
+       {
+               if (s [i] == '\n' && i > (offset - *start_offset) ) {
+                       s [i] = '\0';
+                       *end_offset = *start_offset + i;
+                       return word_start;
+               } else if (s [i] == '\n') {
+                       word_start = &s[i + 1];
                }
-               s = string->str;
+       }
+       return word_start;
+}
+
+static char*
+text_chunk_get_clipped_string (TextChunk *chunk)
+{
+       char *s, *string = "";
+       int i;
+       long start = chunk->start_offset, end = chunk->end_offset;
+       long word_start, word_end, range_end;
+       BoundaryRect start_bounds, end_bounds;
+       gboolean start_inside, end_inside;
+       if (!chunk->text_bounds.isClipped)
+               string = chunk->string;
+       else if (chunk->source) {
+#ifdef CLIP_DEBUG              
+               fprintf (stderr, "clipping %s\n", chunk->string);
+#endif
+               /* while words at offset lie within the bounds, add them */
+               do {
+                   s = AccessibleText_getTextAtOffset (chunk->source,
+                                                       start,
+                                               SPI_TEXT_BOUNDARY_WORD_START,
+                                                       &word_start,
+                                                       &word_end);
+                   range_end = word_end;
+                   s = string_strip_newlines (s, start, &word_start, &word_end);
+                   AccessibleText_getCharacterExtents (chunk->source,
+                                                       word_start,
+                                                       &start_bounds.x,
+                                                       &start_bounds.y,
+                                                       &start_bounds.width,
+                                                       &start_bounds.height,
+                                                       SPI_COORD_TYPE_SCREEN);
+                   AccessibleText_getCharacterExtents (chunk->source,
+                                                       word_end - 1,
+                                                       &end_bounds.x,
+                                                       &end_bounds.y,
+                                                       &end_bounds.width,
+                                                       &end_bounds.height,
+                                                       SPI_COORD_TYPE_SCREEN);
+                   start_inside = BOUNDS_CONTAIN_X_BOUNDS (chunk->text_bounds,
+                                                           start_bounds);
+                   end_inside = BOUNDS_CONTAIN_X_BOUNDS (chunk->text_bounds,
+                                                         end_bounds);
+                   if (start_inside && end_inside) {
+                           /* word is contained in bounds */
+                           string = g_strconcat (string, s, NULL);
+                   } else if (start_inside || end_inside) {
+                           /* one end of word is in */
+                           if (word_end > end) word_end = end;
+                           s = text_chunk_get_clipped_substring_by_char (
+                                   chunk,
+                                   MAX (word_start, chunk->start_offset),
+                                   MIN (word_end, chunk->end_offset));
+                           string = g_strconcat (string, s, NULL);
+                   } else {
+                   }
+                   start = range_end;
+               } while (start < chunk->end_offset);
        } else { /* we're clipped, but don't implement AccessibleText :-( */
                /* punt for now, maybe we can do betterc someday */
-               s = chunk->string;
+               string = chunk->string;
        }
-       g_string_free (string, FALSE);
-       return s;
+       return string;
 }
 
 static char*
@@ -692,16 +823,21 @@ review_buffer_composite (ScreenReviewBuffer *buffers[])
        int i;
        GList *chunk_list, *iter;
        TextChunk *chunk;
+#ifdef NEED_TO_FIX_THIS        
        chunk_list = buffers[0]->text_chunks;
        for (i = 1; i < SPI_LAYER_LAST_DEFINED; ++i) {
                iter = buffers[i]->text_chunks;
+#ifdef CLIP_DEBUG
                fprintf (stderr, "layer %d has %d chunks\n",
                         i, g_list_length (iter));
+#endif         
                while (iter) {
                        chunk = (TextChunk *) iter->data;
                        if (chunk) {
+#ifdef CLIP_DEBUG
                                fprintf (stderr, "inserting chunk <%s>\n",
                                         chunk->string ? chunk->string : "<null>");
+#endif
                                chunk_list =
                                        text_chunk_list_insert_chunk (chunk_list,
                                                                      chunk);
@@ -709,6 +845,7 @@ review_buffer_composite (ScreenReviewBuffer *buffers[])
                        iter = iter->next;
                }
        }
+#endif
        chunk_list = buffers[SPI_LAYER_WIDGET]->text_chunks;
        return text_chunk_list_to_string (chunk_list);
 }
@@ -793,12 +930,12 @@ get_screen_review_line_at (int x, int y)
                      for (i = 0; i < SPI_LAYER_LAST_DEFINED; ++i) {
                              *clip_bounds[i] = toplevel_bounds;
                      }
-#ifdef CLIP_DEBUG                    
-                     fprintf (stderr, "toplevel clip starting\n");
-                     debug_chunk_list (reviewBuffers[SPI_LAYER_WIDGET]->text_chunks);
-#endif               
                      clip_into_buffers (toplevel, clip_bounds,
                                     reviewBuffers, x, y);
+#ifdef CHUNK_LIST_DEBUG
+                     fprintf (stderr, "toplevel clip done\n");
+                     debug_chunk_list (reviewBuffers[SPI_LAYER_WIDGET]->text_chunks);
+#endif               
              }
          }
          Accessible_unref (toplevel);