* Issues:
*
* * bounds now fail to include window border decoration
- * (which we can't really know about).
+ * (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.
+ * 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(b, p) (((p)>=(b)->x) && ((p)<=((b)->x + (b)->width))\
- && ((b)->width > 0) && ((b)->height > 0))
+#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))
typedef struct _TextChunk {
char *string;
- AccessibleText *source;
+ Accessible *source;
int start_offset;
int end_offset;
BoundaryRect clip_bounds;
int cx2 = clipBounds->x + clipBounds->width;
if (cx2 < bounds->x) return;
x2 = bounds->x + bounds->width;
- if (cx2 < x2) bounds->x = cx2;
+ if (cx2 <= x2) bounds->x = cx2;
bounds->width = MAX (0, x2 - cx2);
}
TextChunk *copy = g_new0 (TextChunk, 1);
*copy = *chunk;
if (chunk->string) copy->string = g_strdup (chunk->string);
- if (copy->source) AccessibleText_ref (copy->source);
+ if (copy->source) Accessible_ref (copy->source);
return copy;
}
/* #define PRINT_CHUNK_DEBUG(a, b, c, d) print_chunk_debug(a, b, c, d) */
-#define PRINT_CHUNK_DEBUG(a, b, c, d)
+#define PRINT_CHUNK_DEBUG(a, b, c, d)
#ifdef PRINT_CHUNK_DEBUG
static void
text_chunk_list =
g_list_insert_before (text_chunk_list, next, chunk);
// }
- while (iter) {
- if (CHUNK_BOUNDS_SPANS_END (chunk, (TextChunk *)iter->data)) {
+ while (iter && CHUNK_BOUNDS_SPANS_END (chunk, (TextChunk *)iter->data)) {
#ifdef CLIP_DEBUG
fprintf (stderr, "deleting %s\n",
((TextChunk *)iter->data)->string);
iter = iter->next;
text_chunk_list =
g_list_delete_link (text_chunk_list, target);
- } else {
- if (!CHUNK_BOUNDS_END_BEFORE_START (chunk,
- (TextChunk *)iter->data)) {
- text_chunk_head_clip ((TextChunk *)iter->data,
- chunk);
- }
- if (prev &&
- !CHUNK_BOUNDS_AFTER_END (
- chunk,
- (TextChunk *)prev->data)) {
- text_chunk_tail_clip (
- (TextChunk *)prev->data,
- chunk);
- }
- break;
- }
+ }
+ if (iter && !CHUNK_BOUNDS_END_BEFORE_START (chunk,
+ (TextChunk *)iter->data)) {
+ text_chunk_head_clip ((TextChunk *)iter->data,
+ chunk);
+ }
+ if (prev &&
+ !CHUNK_BOUNDS_AFTER_END (
+ chunk,
+ (TextChunk *)prev->data)) {
+ text_chunk_tail_clip (
+ (TextChunk *)prev->data,
+ chunk);
}
return text_chunk_list;
GList *next)
{
#ifdef CLIP_DEBUG
- if (chunk->string)
- fprintf (stderr, "clip-and-insert for %s, between %s and %s\n",
+/* if (chunk->string) */
+ fprintf (stderr, "clip-and-insert for %s, between %s (%d) and %s (%d)\n",
chunk->string,
(prev && ((TextChunk *)prev->data)->string ? ((TextChunk *)prev->data)->string : "<null>"),
- (next && ((TextChunk *)next->data)->string ? ((TextChunk *)next->data)->string : "<null>"));
+ (prev ? ((TextChunk *)prev->data)->text_bounds.x : -1),
+ (next && ((TextChunk *)next->data)->string ? ((TextChunk *)next->data)->string : "<null>"),
+ (next ? ((TextChunk *)next->data)->text_bounds.x : -1));
#endif
/* cases: */
if (!prev && !next) { /* first element in, no clip needed */
-// if (chunk->string && strlen (chunk->string)) {
- text_chunk_list =
- g_list_append (text_chunk_list, chunk);
- PRINT_CHUNK_DEBUG (chunk, "append",
- prev,
- NULL);
-// }
+ text_chunk_list =
+ g_list_append (text_chunk_list, chunk);
+ PRINT_CHUNK_DEBUG (chunk, "append",
+ prev,
+ NULL);
} else { /* check for clip with prev */
/* if we split the prev */
if (prev &&
(TextChunk *) prev->data)) {
text_chunk_tail_clip (prev->data, chunk);
}
-// if (chunk->string && strlen (chunk->string)) {
- text_chunk_list =
- g_list_append (text_chunk_list, chunk);
-// }
+ text_chunk_list =
+ g_list_append (text_chunk_list, chunk);
}
}
return text_chunk_list;
role = Accessible_getRole (accessible);
text_chunk = g_new0 (TextChunk, 1);
text_chunk->clip_bounds = *bounds;
+ text_chunk->source = accessible;
+ Accessible_ref (accessible);
if (Accessible_isText (accessible)) {
text = Accessible_getText (accessible);
offset = AccessibleText_getOffsetAtPoint (text,
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 (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;
- }
+ fprintf (stderr, "text at offset %d, %s, %d, %d\n",
+ offset, s, start, end);
+ AccessibleText_unref (text);
} 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 :-( */
text_chunk->end_offset = strlen (s);
}
}
+ 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;
+ text_chunk->start_offset = offset;
+ text_chunk->end_offset = offset;
+ }
if (s && strlen (s)) {
if (s[strlen(s)-1] == '\n') s[strlen(s)-1] = ' ';
/* XXX: if last char is newline, aren't its bounds wrong now? */
text_chunk->string = s;
- text_chunk->source = text;
- if (text) AccessibleText_ref (text);
#ifdef CLIP_DEBUG
fprintf (stderr, "%s, bounds %d-%d; clip %d-%d\n",
s,
#endif
} else {
text_chunk->string = NULL;
- text_chunk->source = NULL;
}
- if (text) AccessibleText_unref (text);
return text_chunk;
}
#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;
+ char *s;
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 CHARACTER_CLIP_DEBUG
- fprintf (stderr, "clipping %s\n", chunk->string);
-#endif
- for (i = chunk->start_offset; i < chunk->end_offset; ++i) {
- AccessibleText_getCharacterExtents (chunk->source,
- i,
- &char_bounds.x,
- &char_bounds.y,
- &char_bounds.width,
- &char_bounds.height,
- SPI_COORD_TYPE_SCREEN);
+ gunichar c;
+ AccessibleText *text = Accessible_getText (chunk->source);
+ for (i = start; i < end; ++i) {
+ AccessibleText_getCharacterExtents (text,
+ 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 (
+ text, 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);
}
- s = string->str;
- } else { /* we're clipped, but don't implement AccessibleText :-( */
- /* punt for now, maybe we can do betterc someday */
- s = chunk->string;
}
+ AccessibleText_unref (text);
+ s = string->str;
g_string_free (string, FALSE);
return s;
}
+
+/*
+ * 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];
+ }
+ }
+ return word_start;
+}
+
+static char *
+string_guess_clip (TextChunk *chunk)
+{
+ BoundaryRect b;
+ char *s = NULL, *sp = chunk->string, *ep;
+ long start_offset, end_offset, len;
+ if (sp) {
+ AccessibleComponent *component =
+ Accessible_getComponent (chunk->source);
+ ep = sp + (strlen (sp));
+ len = g_utf8_strlen (chunk->string, -1);
+ if (component) {
+ AccessibleComponent_getExtents (component,
+ &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",
+ len, start_offset, end_offset);
+ len = end_offset - start_offset;
+ sp = g_utf8_offset_to_pointer (chunk->string, start_offset);
+ ep = g_utf8_offset_to_pointer (chunk->string, end_offset);
+ }
+ s = g_new0 (char, ep - sp + 1);
+ s = g_utf8_strncpy (s, sp, len);
+ s [sp - ep] = '\0';
+ g_assert (g_utf8_validate (s, -1, NULL));
+ }
+ return s;
+}
+
+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 || !chunk->string)
+ string = chunk->string;
+ else if (chunk->source && Accessible_isText (chunk->source)) {
+ /* while words at offset lie within the bounds, add them */
+ AccessibleText *text = Accessible_getText (chunk->source);
+#ifdef CLIP_DEBUG
+ fprintf (stderr, "clipping %s\n", chunk->string);
+#endif
+ do {
+ s = AccessibleText_getTextAtOffset (text,
+ 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 (text,
+ word_start,
+ &start_bounds.x,
+ &start_bounds.y,
+ &start_bounds.width,
+ &start_bounds.height,
+ SPI_COORD_TYPE_SCREEN);
+ AccessibleText_getCharacterExtents (text,
+ 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 :-( */
+ /* guess for now, maybe we can do better someday */
+ string = string_guess_clip (chunk);
+ }
+ return string;
+}
+
static char*
text_chunk_list_to_string (GList *iter)
{
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) {
+ 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;
}
}
-*/ chunk_list = buffers[SPI_LAYER_WIDGET]->text_chunks;
+#endif
+ chunk_list = buffers[SPI_LAYER_WIDGET]->text_chunks;
return text_chunk_list_to_string (chunk_list);
}
}
clip_into_buffers (toplevel, clip_bounds,
reviewBuffers, x, y);
-#ifdef CLIP_DEBUG
+#ifdef CHUNK_LIST_DEBUG
fprintf (stderr, "toplevel clip done\n");
debug_chunk_list (reviewBuffers[SPI_LAYER_WIDGET]->text_chunks);
#endif