#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);
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;
}
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;
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;
}
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);
&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,
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
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 :-( */
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);
reviewBuffers[layer]->text_chunks,
text_chunk);
} else {
- clip_bounds[layer]->isEmpty = TRUE;
+ bounds.isEmpty =
+ IS_CLIPPING_CONTAINER (bounds.role);
}
}
Accessible_unref (component);
/* 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*
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);
iter = iter->next;
}
}
+#endif
chunk_list = buffers[SPI_LAYER_WIDGET]->text_chunks;
return text_chunk_list_to_string (chunk_list);
}
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);