2007-07-09 Li Yuan <li.yuan@sun.com>
[platform/core/uifw/at-spi2-atk.git] / test / screen-review-test.c
index 8ac47f2..1b5fa2e 100644 (file)
@@ -2,7 +2,8 @@
  * AT-SPI - Assistive Technology Service Provider Interface
  * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
  *
- * Copyright 2001 Sun Microsystems Inc.
+ * Copyright 2001, 2002 Sun Microsystems Inc.,
+ * Copyright 2001, 2002 Ximian, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -20,6 +21,8 @@
  * Boston, MA 02111-1307, USA.
  */
 
+#include <stdio.h>
+#include <string.h>
 #include <stdlib.h>
 #include "../cspi/spi-private.h"
 
@@ -30,8 +33,7 @@
  *
  * Issues:
  *
- * * bounds now fail to include window border decoration
- *         (which we can't really know about yet).
+ * * there are bugs in the compositing code.
  *
  * * brute-force algorithm uses no client-side cache; performance mediocre.
  *
  *          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.
+ * * text column alignment is crude since it relies on a hard-coded
+ *          (and arbitrary) ratio of horizontal pixels to character columns.
+ *          For small proportional fonts, overruns are likely to result in
+ *          occasional review lines that exceed the standard length.
  *
  * * (others).
  */
@@ -92,7 +90,6 @@
 
 static void report_screen_review_line  (const AccessibleEvent *event, void *user_data);
 
-static gint n_elements_traversed = 0;
 static AccessibleEventListener *mouseclick_listener;
 
 typedef struct _BoundaryRect {
@@ -116,7 +113,7 @@ typedef struct _TextChunk {
        BoundaryRect    end_char_bounds;
 } TextChunk;
 
-typedef struct _ScreenReviewBuffer { /* TODO: implement */
+typedef struct _ScreenReviewBuffer { 
        GList *text_chunks;
 } ScreenReviewBuffer;
 
@@ -125,16 +122,6 @@ static gboolean isJava = FALSE;
 int
 main (int argc, char **argv)
 {
-  int i, j;
-  int n_desktops;
-  int n_apps;
-  char *s;
-  GTimer *timer;
-  gdouble elapsed_time;
-  Accessible *desktop;
-  Accessible *application;
-  const char *modules;
-
   SPI_init ();
 
   mouseclick_listener = SPI_createAccessibleEventListener (
@@ -163,13 +150,11 @@ main (int argc, char **argv)
 static inline gboolean
 chunk_bounds_within (TextChunk *chunk, TextChunk *test_chunk)
 {
-       int x1, x2, tx1, tx2;
+       int x1, tx1;
        gboolean gtx1, ltx2;
 
        x1 = chunk->clip_bounds.x;
-       x2 = x1 + chunk->clip_bounds.width;
        tx1 = test_chunk->clip_bounds.x;
-       tx2 = tx1 + test_chunk->clip_bounds.width;
        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);
@@ -185,7 +170,7 @@ clip_bounds_clone (BoundaryRect *bounds[])
        BoundaryRect **bounds_clone;
        bounds_clone = (BoundaryRect **)
                g_new0 (gpointer, SPI_LAYER_LAST_DEFINED);
-       for (i = 0; i < SPI_LAYER_LAST_DEFINED; ++i) {
+       for (i = 1; i < SPI_LAYER_LAST_DEFINED; ++i) {
                bounds_clone[i] = g_new0 (BoundaryRect, 1);
                *bounds_clone[i] = *bounds[i];
        }
@@ -316,7 +301,7 @@ text_chunk_split_insert (GList *chunk_list, GList *iter, TextChunk *chunk)
 static void
 print_chunk_debug (TextChunk *chunk, char *opname, GList *prev, GList *next)
 {
-       fprintf (stderr, "%sing chunk %s between %s and %s; %d-%d\n",
+       fprintf (stderr, "%sing chunk %s between %s and %s; %ld-%ld\n",
                 opname,
                 chunk->string,
                 (prev ? ((TextChunk *) prev->data)->string : "<null>"),
@@ -333,10 +318,10 @@ text_chunk_list_head_clip (GList *text_chunk_list,
 {
        GList *target, *iter = next, *prev;
        prev = iter->prev;
-//     if (chunk->string && strlen (chunk->string)) { 
+/*     if (chunk->string && strlen (chunk->string)) { */
                text_chunk_list =
                        g_list_insert_before (text_chunk_list, next, chunk);
-//     }
+/*     }*/
        while (iter && CHUNK_BOUNDS_SPANS_END (chunk, (TextChunk *)iter->data)) {
 #ifdef CLIP_DEBUG                      
                        fprintf (stderr, "deleting %s\n",
@@ -463,7 +448,6 @@ review_buffer_get_text_chunk (ScreenReviewBuffer *reviewBuffer,
        AccessibleText *text = NULL;
        AccessibleRole role;
        TextChunk *text_chunk;
-       BoundaryRect start_char_bounds, end_char_bounds;
        char *s = NULL;
        int offset;
        long start, end;
@@ -530,8 +514,6 @@ 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;
-               fprintf (stderr, "text at offset %d, %s, %d, %d\n",
-                        offset, s, start, end);
                AccessibleText_unref (text);
        } else {
                if (role == SPI_ROLE_PUSH_BUTTON ||
@@ -591,13 +573,14 @@ review_buffer_get_text_chunk (ScreenReviewBuffer *reviewBuffer,
        return text_chunk;
 }
 
+#ifdef CHUNK_LIST_DEBUG
 static void
 debug_chunk_list (GList *iter)
 {
        TextChunk *chunk;
        while (iter) {
                chunk = (TextChunk *)iter->data;
-               fprintf (stderr, "Chunk %s, clip %d-%d, text %d-%d\n",
+               fprintf (stderr, "Chunk %s, clip %ld-%ld, text %ld-%ld\n",
                         chunk->string,
                         chunk->clip_bounds.x,
                         chunk->clip_bounds.x + chunk->clip_bounds.width,
@@ -606,6 +589,7 @@ debug_chunk_list (GList *iter)
                iter = iter->next;
        }
 }
+#endif
 
 static void
 clip_into_buffers (Accessible *accessible,  BoundaryRect* parentClipBounds[],
@@ -710,8 +694,8 @@ text_chunk_get_clipped_substring_by_char (TextChunk *chunk, int start, int end)
 
 /*
  * 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.
+ *       but at the moment it works around another bug (probably one in this
+ *       code).
  */
 static char *
 string_strip_newlines (char *s, long offset, long *start_offset, long *end_offset)
@@ -748,11 +732,10 @@ string_guess_clip (TextChunk *chunk)
                                                        &b.x, &b.y,
                                                        &b.width, &b.height,
                                                        SPI_COORD_TYPE_SCREEN);
-                       /* TODO: finish this! */
                        start_offset = len * (chunk->text_bounds.x - b.x) / b.width;
                        end_offset = len * (chunk->text_bounds.x +
                                            chunk->text_bounds.width - b.x) / b.width;
-                       fprintf (stderr, "String len %d, clipped to %d-%d\n",
+                       fprintf (stderr, "String len %ld, clipped to %ld-%ld\n",
                                 len, start_offset, end_offset);
                        len = end_offset - start_offset;
                        sp = g_utf8_offset_to_pointer (chunk->string, start_offset);
@@ -770,7 +753,6 @@ 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;
@@ -786,7 +768,7 @@ text_chunk_get_clipped_string (TextChunk *chunk)
                do {
                    s = AccessibleText_getTextAtOffset (text,
                                                        start,
-                                               SPI_TEXT_BOUNDARY_WORD_START,
+                                               SPI_TEXT_BOUNDARY_WORD_END,
                                                        &word_start,
                                                        &word_end);
                    range_end = word_end;
@@ -831,6 +813,69 @@ text_chunk_get_clipped_string (TextChunk *chunk)
        return string;
 }
 
+
+static char*
+text_chunk_pad_string (TextChunk *chunk, char *string, glong offset, const char *pad_chars)
+{
+       char *s = "";
+       char *cp;
+       char startbuf[6], padbuf[6], endbuf[6];
+       int pixels_per_column = 6;
+        /* this is an arbitrary pixel-to-textcolumn mapping at present */
+       glong end_padding;
+       gint howmany;
+       howmany = g_unichar_to_utf8 (g_utf8_get_char (pad_chars), startbuf);
+       startbuf[howmany] = '\0';
+       g_assert (howmany < 7 && howmany > 0);
+       cp = g_utf8_find_next_char (pad_chars, NULL);
+       howmany = g_unichar_to_utf8 (g_utf8_get_char (cp), padbuf);
+       padbuf[howmany] = '\0';
+       g_assert (howmany < 7 && howmany > 0);
+       cp = g_utf8_find_next_char (cp, NULL);
+       howmany = g_unichar_to_utf8 (g_utf8_get_char (cp), endbuf);
+       endbuf[howmany] = '\0';
+       g_assert (howmany < 7 && howmany > 0);
+       end_padding = chunk->clip_bounds.x / pixels_per_column; 
+       while (offset < end_padding - 1) {
+               s = g_strconcat (s, padbuf, NULL); /* could be more efficient */
+               ++offset;
+       }
+       s = g_strconcat (s, startbuf, string, NULL);
+       offset += g_utf8_strlen (string, -1) + 1;
+       end_padding = chunk->text_bounds.x / pixels_per_column; 
+       while (offset < end_padding) {
+               s = g_strconcat (s, padbuf, NULL); /* could be more efficient */
+               ++offset;
+       }
+       end_padding = (chunk->clip_bounds.x + chunk->clip_bounds.width) /
+               pixels_per_column;
+       while (offset < end_padding - 1) {
+               s = g_strconcat (s, padbuf, NULL); /* could be more efficient */
+               ++offset;
+       }
+       s = g_strconcat (s, endbuf, NULL);
+       return s;
+}
+
+static char*
+text_chunk_to_string (TextChunk *chunk, glong offset)
+{
+       char *s = NULL;
+       if (chunk->string) {
+               s = text_chunk_get_clipped_string (chunk);
+               if (chunk->clip_bounds.role == SPI_ROLE_PUSH_BUTTON) {
+                       s = text_chunk_pad_string (chunk, s, offset, "[ ]");
+               } else if (chunk->clip_bounds.role == SPI_ROLE_FRAME) {
+                       s = text_chunk_pad_string (chunk, s, offset, "| |");
+               } else if (chunk->clip_bounds.role == SPI_ROLE_TEXT) {
+                       s = text_chunk_pad_string (chunk, s, offset, "\" \"");
+               } else {
+                       s = text_chunk_pad_string (chunk, s, offset, "   ");
+               }
+       }
+       return s;
+}
+
 static char*
 text_chunk_list_to_string (GList *iter)
 {
@@ -839,12 +884,10 @@ text_chunk_list_to_string (GList *iter)
        TextChunk *chunk = NULL;
        while (iter) {
                chunk = (TextChunk *)iter->data;
-               if (chunk /* && chunk->string */) {
-                       string = text_chunk_get_clipped_string (chunk);
+               if (chunk) {
+                       string = text_chunk_to_string (chunk, g_utf8_strlen (s, -1));
                        if (string)
-                               s = g_strconcat (s, "|", string, NULL);
-                       else /* XXX test */
-                               s = g_strconcat (s, ":", NULL);
+                               s = g_strconcat (s, string, NULL);
                }
                iter = iter->next;
        }
@@ -852,15 +895,54 @@ text_chunk_list_to_string (GList *iter)
        return s;
 }
 
+#define COMPOSITE_DEBUG
+
+static void
+toplevel_composite (ScreenReviewBuffer *buffers[])
+{
+       int i;
+       GList *chunk_list, *iter;
+       TextChunk *chunk;
+
+       chunk_list = buffers[SPI_LAYER_CANVAS]->text_chunks;
+       for (i = SPI_LAYER_MDI; i < SPI_LAYER_OVERLAY; ++i) {
+               iter = buffers[i]->text_chunks;
+#ifdef COMPOSITE_DEBUG
+               fprintf (stderr, "layer %d has %d chunks\n",
+                        i, g_list_length (iter));
+#endif         
+               while (iter) {
+                       chunk = (TextChunk *) iter->data;
+                       if (chunk) {
+#ifdef COMPOSITE_DEBUG
+                               fprintf (stderr, "inserting chunk <%s>\n",
+                                        chunk->string ? chunk->string : "<null>");
+#endif
+                               chunk_list =
+                                       text_chunk_list_insert_chunk (chunk_list,
+                                                                     chunk);
+                       }
+                       iter = iter->next;
+               }
+       }
+}
+
 static char*
 review_buffer_composite (ScreenReviewBuffer *buffers[])
 {
+       /* TODO: FIXME: something is wrong here, compositing fails */
        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) {
+       chunk_list = buffers[SPI_LAYER_BACKGROUND]->text_chunks;
+       for (i = 2; i < SPI_LAYER_LAST_DEFINED; ++i) {
+               if (i == SPI_LAYER_WIDGET) i = SPI_LAYER_OVERLAY;
+               /*
+                * Q: why skip these layers ?
+                * A: since layers WIDGET, MDI, and POPUP have already been
+                *  composited into layer CANVAS for each toplevel before this
+                *  routine is called.
+                */
                iter = buffers[i]->text_chunks;
 #ifdef CLIP_DEBUG
                fprintf (stderr, "layer %d has %d chunks\n",
@@ -880,7 +962,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);
 }
@@ -889,18 +971,18 @@ static char *
 get_screen_review_line_at (int x, int y)
 {
   char *string;
-  Accessible *desktop, *app, *toplevel, *child;
+  Accessible *desktop, *app, *toplevel;
   AccessibleComponent *component;
   AccessibleStateSet *states;
   GList *toplevels = NULL, *actives = NULL, *iter;
   ScreenReviewBuffer* reviewBuffers[SPI_LAYER_LAST_DEFINED];
   BoundaryRect* clip_bounds[SPI_LAYER_LAST_DEFINED];
   BoundaryRect toplevel_bounds;
-  int n_apps, n_toplevels, n_children, app_n, toplevel_n, child_n;
+  int n_apps, n_toplevels, app_n, toplevel_n;
   GTimer *timer = g_timer_new ();
   int i;
 
-  for (i = 0; i < SPI_LAYER_LAST_DEFINED; ++i) {
+  for (i = 1; i < SPI_LAYER_LAST_DEFINED; ++i) {
          reviewBuffers[i] = g_new0 (ScreenReviewBuffer, 1);
          clip_bounds[i] = g_new0 (BoundaryRect, 1);
          clip_bounds[i]->isClipped = FALSE;
@@ -951,8 +1033,8 @@ get_screen_review_line_at (int x, int y)
          if (Accessible_isComponent (toplevel)) {
              /* make sure toplevel is visible and not iconified or shaded */
              states = Accessible_getStateSet (toplevel);
-             if (AccessibleStateSet_contains (states, SPI_STATE_VISIBLE)
-                 && !AccessibleStateSet_contains (states, SPI_STATE_ICONIFIED)
+             if ((AccessibleStateSet_contains (states, SPI_STATE_VISIBLE)
+                 && (!AccessibleStateSet_contains (states, SPI_STATE_ICONIFIED)))
                  || isJava) { /* isJava hack! */
                      component = Accessible_getComponent (toplevel);
                      AccessibleComponent_getExtents (component,
@@ -962,11 +1044,13 @@ get_screen_review_line_at (int x, int y)
                                                      &toplevel_bounds.height,
                                                      SPI_COORD_TYPE_SCREEN);
                      toplevel_bounds.isEmpty = FALSE;
-                     for (i = 0; i < SPI_LAYER_LAST_DEFINED; ++i) {
+                     for (i = 1; i < SPI_LAYER_LAST_DEFINED; ++i) {
                              *clip_bounds[i] = toplevel_bounds;
                      }
                      clip_into_buffers (toplevel, clip_bounds,
                                     reviewBuffers, x, y);
+
+                     toplevel_composite (reviewBuffers);
 #ifdef CHUNK_LIST_DEBUG
                      fprintf (stderr, "toplevel clip done\n");
                      debug_chunk_list (reviewBuffers[SPI_LAYER_WIDGET]->text_chunks);
@@ -1030,7 +1114,7 @@ report_screen_review_line (const AccessibleEvent *event, void *user_data)
 }
 
 void
-test_exit ()
+test_exit (void)
 {
   SPI_deregisterGlobalEventListenerAll (mouseclick_listener);
   AccessibleEventListener_unref (mouseclick_listener);