* 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
* Boston, MA 02111-1307, USA.
*/
+#include <stdio.h>
+#include <string.h>
#include <stdlib.h>
#include "../cspi/spi-private.h"
*
* 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).
*/
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 {
BoundaryRect end_char_bounds;
} TextChunk;
-typedef struct _ScreenReviewBuffer { /* TODO: implement */
+typedef struct _ScreenReviewBuffer {
GList *text_chunks;
} ScreenReviewBuffer;
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 (
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);
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];
}
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>"),
{
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",
AccessibleText *text = NULL;
AccessibleRole role;
TextChunk *text_chunk;
- BoundaryRect start_char_bounds, end_char_bounds;
char *s = NULL;
int offset;
long start, end;
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,
iter = iter->next;
}
}
+#endif
static void
clip_into_buffers (Accessible *accessible, BoundaryRect* parentClipBounds[],
&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);
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;
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)
{
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;
}
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",
iter = iter->next;
}
}
-#endif
+
chunk_list = buffers[SPI_LAYER_WIDGET]->text_chunks;
return text_chunk_list_to_string (chunk_list);
}
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;
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,
&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);
}
void
-test_exit ()
+test_exit (void)
{
SPI_deregisterGlobalEventListenerAll (mouseclick_listener);
AccessibleEventListener_unref (mouseclick_listener);