Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/gal/ChangeLog,v
retrieving revision 1.808
diff -u -r1.808 ChangeLog
--- ChangeLog	24 Sep 2003 11:25:59 -0000	1.808
+++ ChangeLog	27 Sep 2003 07:46:09 -0000
@@ -1,3 +1,31 @@
+2003-09-27  Tim Wo <tim.wo@sun.com>
+
+	* gal/a11y/e-text/gal-a11y-e-text-factory.c
+	(gal_a11y_e_text_factory_create_accessible): set the role of the
+	atk object in the initialization function of GalA11yEText
+	* gal/a11y/e-text/gal-a11y-e-text.c (is_a_seperator),
+	(find_word_start), (find_word_end), (find_sentence_start),
+	(find_sentence_end), (find_line_start), (find_line_end): 7 new
+	private functions, They are all used by text retrieving functions
+	below.
+	(et_get_text_after_offset): implementation added
+	(et_get_text_at_offset): implementation added
+	(et_get_text_before_offset): implementation added
+	(et_get_character_extents): implementation added
+	(et_get_offset_at_point): implementation added
+	(et_set_caret_offset): use command to modify the cursor position,
+	so that we can be notified and emit "text-caret-moved" signal in
+	function _et_command_cb.
+	(_et_reposition_cb): new function to emit "text-changed" signal
+	for the atk object
+	(_et_command_cb): new function to emit "text-caret-moved" and
+	"text-selection-changed" signals for the atk object
+	(et_real_initialize): new function to deal with initialization of
+	GalA11yEText. It set some signal callbacks and the atk role of
+	the atk object (set GalA11yEText's role to ATK_ROLE_TEXT).
+	(et_class_init): override the virtual function "initialize" in
+	baseclass (AtkObject) with "et_real_initialize"
+
 2003-09-22  Tim Wo <tim.wo@sun.com>
 
 	* gal/a11y/e-text/gal-a11y-e-text.c (et_get_text): some checking
Index: gal/a11y/e-text/gal-a11y-e-text-factory.c
===================================================================
RCS file: /cvs/gnome/gal/gal/a11y/e-text/gal-a11y-e-text-factory.c,v
retrieving revision 1.1
diff -u -r1.1 gal-a11y-e-text-factory.c
--- gal/a11y/e-text/gal-a11y-e-text-factory.c	30 Nov 2002 07:54:16 -0000	1.1
+++ gal/a11y/e-text/gal-a11y-e-text-factory.c	27 Sep 2003 07:46:09 -0000
@@ -32,7 +32,6 @@
 
 	atk_object = g_object_new (GAL_A11Y_TYPE_E_TEXT, NULL);
 	atk_object_initialize (atk_object, obj);
-	atk_object->role = ATK_ROLE_UNKNOWN;
 
 	return atk_object;
 }
Index: gal/a11y/e-text/gal-a11y-e-text.c
===================================================================
RCS file: /cvs/gnome/gal/gal/a11y/e-text/gal-a11y-e-text.c,v
retrieving revision 1.4
diff -u -r1.4 gal-a11y-e-text.c
--- gal/a11y/e-text/gal-a11y-e-text.c	24 Sep 2003 11:26:00 -0000	1.4
+++ gal/a11y/e-text/gal-a11y-e-text.c	27 Sep 2003 07:46:10 -0000
@@ -16,6 +16,7 @@
 #include <atk/atkregistry.h>
 #include <atk/atkgobjectaccessible.h>
 #include "gal/e-text/e-text.h"
+#include "gal/e-text/e-text-model-repos.h"
 #include <gtk/gtkmain.h>
 
 #define CS_CLASS(a11y) (G_TYPE_INSTANCE_GET_CLASS ((a11y), C_TYPE_STREAM, GalA11yETextClass))
@@ -131,6 +132,166 @@
 	return g_strndup (full_text + real_start, real_end - real_start);
 }
 
+static gboolean
+is_a_seperator (gunichar c)
+{
+	return g_unichar_ispunct(c) || g_unichar_isspace(c);
+}
+
+static gint
+find_word_start (const char *text,
+		 gint begin_offset,
+		 gint step)
+{
+	gint offset;
+	char *at_offset;
+	gunichar current, previous;
+	gint len;
+
+	offset = begin_offset;
+	len = g_utf8_strlen (text, -1);
+
+	while (offset > 0 && offset < len) {
+		at_offset = g_utf8_offset_to_pointer (text, offset);
+		current = g_utf8_get_char_validated (at_offset, -1);
+		at_offset = g_utf8_offset_to_pointer (text, offset-1);
+		previous = g_utf8_get_char_validated (at_offset, -1);
+		if ((! is_a_seperator (current)) && is_a_seperator (previous))
+			break;
+		offset += step;
+	}
+
+	return offset;
+}
+
+static gint
+find_word_end (const char *text,
+	       gint begin_offset,
+	       gint step)
+{
+	gint offset;
+	char *at_offset;
+	gunichar current, previous;
+	gint len;
+
+	offset = begin_offset;
+	len = g_utf8_strlen (text, -1);
+
+	while (offset > 0 && offset < len) {
+		at_offset = g_utf8_offset_to_pointer (text, offset);
+		current = g_utf8_get_char_validated (at_offset, -1);
+		at_offset = g_utf8_offset_to_pointer (text, offset-1);
+		previous = g_utf8_get_char_validated (at_offset, -1);
+		if (is_a_seperator (current) && (! is_a_seperator (previous)))
+			break;
+		offset += step;
+	}
+
+	return offset;
+}
+
+static gint
+find_sentence_start (const char *text,
+		     gint begin_offset,
+		     gint step)
+{
+	gint offset, last_word_end, len;
+	char *at_offset;
+	gunichar ch;
+	int i;
+	
+	offset = find_word_start (text, begin_offset, step);
+	len = g_utf8_strlen (text, -1);
+
+	while (offset>0 && offset <len) {
+		last_word_end = find_word_end (text, offset - 1, -1);
+		if (last_word_end == 0)
+			break;
+		for (i = last_word_end; i < offset; i++) {
+			at_offset = g_utf8_offset_to_pointer (text, i);
+			ch = g_utf8_get_char_validated (at_offset, -1);
+			if (ch == '.' || ch == '!' || ch == '?')
+				return offset;
+		}
+
+		offset = find_word_start (text, offset + step, step);
+	}
+
+	return offset;
+}
+
+static gint
+find_sentence_end (const char *text,
+                   gint begin_offset,
+                   gint step)
+{
+        gint offset;
+        char *at_offset;
+        gunichar previous;
+        gint len;
+
+        offset = begin_offset;
+        len = g_utf8_strlen (text, -1);
+
+        while (offset > 0 && offset < len) {
+                at_offset = g_utf8_offset_to_pointer (text, offset - 1);
+                previous = g_utf8_get_char_validated (at_offset, -1);
+                if (previous == '.' || previous == '!' || previous == '?')
+                        break;
+                offset += step;
+        }
+
+        return offset;
+}
+
+static gint
+find_line_start (const char *text,
+                     gint begin_offset,
+                     gint step)
+{
+        gint offset;
+        char *at_offset;
+        gunichar previous;
+        gint len;
+
+        offset = begin_offset;
+        len = g_utf8_strlen (text, -1);
+
+        while (offset > 0 && offset < len) {
+                at_offset = g_utf8_offset_to_pointer (text, offset - 1);
+                previous = g_utf8_get_char_validated (at_offset, -1);
+                if (previous == '\n' || previous == '\r')
+                        break;
+                offset += step;
+        }
+
+        return offset;
+}
+
+static gint
+find_line_end (const char *text,
+                     gint begin_offset,
+                     gint step)
+{
+        gint offset;
+        char *at_offset;
+        gunichar current;
+        gint len;
+
+        offset = begin_offset;
+        len = g_utf8_strlen (text, -1);
+
+        while (offset >= 0 && offset < len) {
+                at_offset = g_utf8_offset_to_pointer (text, offset);
+                current = g_utf8_get_char_validated (at_offset, -1);
+                if (current == '\n' || current == '\r')
+                        break;
+                offset += step;
+        }
+
+        return offset;
+}
+
 static gchar *
 et_get_text_after_offset (AtkText *text,
 			  gint offset,
@@ -138,8 +299,50 @@
 			  gint *start_offset,
 			  gint *end_offset)
 {
-	/* Unimplemented */
-	return NULL;
+        gint start, end, len;
+        const char *full_text = et_get_full_text (text);
+        g_return_val_if_fail (full_text, NULL);
+
+	switch (boundary_type)
+	{
+	case ATK_TEXT_BOUNDARY_CHAR:
+		start = offset + 1;
+		end = offset + 2;
+		break;
+	case ATK_TEXT_BOUNDARY_WORD_START:
+		start = find_word_start (full_text, offset + 1, 1);
+		end = find_word_start (full_text, start + 1, 1);
+		break;
+	case ATK_TEXT_BOUNDARY_WORD_END:
+		start = find_word_end (full_text, offset + 1, 1);
+		end = find_word_end (full_text, start + 1, 1);
+		break;
+	case ATK_TEXT_BOUNDARY_SENTENCE_START:
+		start = find_sentence_start (full_text, offset + 1, 1);
+		end = find_sentence_start (full_text, start + 1, 1);
+		break;
+	case ATK_TEXT_BOUNDARY_SENTENCE_END:
+		start = find_sentence_end (full_text, offset + 1, 1);
+		end = find_sentence_end (full_text, start + 1, 1);
+		break;
+	case ATK_TEXT_BOUNDARY_LINE_START:
+		start = find_line_start (full_text, offset + 1, 1);
+		end = find_line_start (full_text, start + 1, 1);
+		break;
+	case ATK_TEXT_BOUNDARY_LINE_END:
+		start = find_line_end (full_text, offset + 1, 1);
+		end = find_line_end (full_text, start + 1, 1);
+		break;
+	defalut:
+		return NULL;
+	}
+
+	len = g_utf8_strlen (full_text, -1);
+	if (start_offset)
+		*start_offset = MIN (MAX (0, start), len);
+	if (end_offset)
+		*end_offset = MIN (MAX (0, end), len);
+	return et_get_text (text, start, end);
 }
 
 static gchar *
@@ -149,8 +352,50 @@
 		       gint *start_offset,
 		       gint *end_offset)
 {
-	/* Unimplemented */
-	return NULL;
+	gint start, end, len;
+        const char *full_text = et_get_full_text (text);
+        g_return_val_if_fail (full_text, NULL);
+
+	switch (boundary_type)
+	{
+	case ATK_TEXT_BOUNDARY_CHAR:
+		start = offset;
+		end = offset + 1;
+		break;
+	case ATK_TEXT_BOUNDARY_WORD_START:
+		start = find_word_start (full_text, offset - 1, -1);
+		end = find_word_start (full_text, offset, 1);
+		break;
+	case ATK_TEXT_BOUNDARY_WORD_END:
+		start = find_word_end (full_text, offset, -1);
+		end = find_word_end (full_text, offset + 1, 1);
+		break;
+	case ATK_TEXT_BOUNDARY_SENTENCE_START:
+		start = find_sentence_start (full_text, offset - 1, -1);
+                end = find_sentence_start (full_text, offset, 1);
+ 		break;
+	case ATK_TEXT_BOUNDARY_SENTENCE_END:
+	        start = find_sentence_end (full_text, offset, -1);
+                end = find_sentence_end (full_text, offset + 1, 1);
+		break;
+	case ATK_TEXT_BOUNDARY_LINE_START:
+	        start = find_line_start (full_text, offset - 1, -1);
+                end = find_line_start (full_text, offset, 1);
+ 		break;
+	case ATK_TEXT_BOUNDARY_LINE_END:
+		start = find_line_end (full_text, offset, -1);
+                end = find_line_end (full_text, offset + 1, 1);
+ 		break;
+	defalut:
+		return NULL;
+	}
+
+	len = g_utf8_strlen (full_text, -1);
+	if (start_offset)
+		*start_offset = MIN (MAX (0, start), len);
+	if (end_offset)
+		*end_offset = MIN (MAX (0, end), len);
+	return et_get_text (text, start, end);
 }
 
 static gunichar
@@ -172,10 +417,51 @@
 			   gint *start_offset,
 			   gint *end_offset)
 {
-	/* Unimplemented */
-	return NULL;
-}
+        gint start, end, len;
+        const char *full_text = et_get_full_text (text);
+        g_return_val_if_fail (full_text, NULL);
+
+	switch (boundary_type)
+	{
+	case ATK_TEXT_BOUNDARY_CHAR:
+		start = offset - 1;
+		end = offset;
+		break;
+	case ATK_TEXT_BOUNDARY_WORD_START:
+		end = find_word_start (full_text, offset - 1, -1);
+		start = find_word_start (full_text, end - 1, -1) ;
+		break;
+	case ATK_TEXT_BOUNDARY_WORD_END:
+		end = find_word_end (full_text, offset, -1);
+		start = find_word_end (full_text, end - 1, -1);
+		break;
+	case ATK_TEXT_BOUNDARY_SENTENCE_START:
+		end = find_sentence_start (full_text, offset, -1);
+		start = find_sentence_start (full_text, end - 1, -1);
+ 		break;
+	case ATK_TEXT_BOUNDARY_SENTENCE_END:
+	        end = find_sentence_end (full_text, offset, -1);
+                start = find_sentence_end (full_text, end - 1, -1);
+ 		break;
+	case ATK_TEXT_BOUNDARY_LINE_START:
+	        end = find_line_start (full_text, offset, -1);
+                start = find_line_start (full_text, end - 1, -1);
+ 		break;
+	case ATK_TEXT_BOUNDARY_LINE_END:
+	        end = find_line_end (full_text, offset, -1);
+                start = find_line_end (full_text, end - 1, -1);
+ 		break;
+	default:
+		return NULL;
+	}
 
+	len = g_utf8_strlen (full_text, -1);
+	if (start_offset)
+		*start_offset = MIN (MAX (0, start), len);
+	if (end_offset)
+		*end_offset = MIN (MAX (0, end), len);
+	return et_get_text (text, start, end);
+}
 
 static gint
 et_get_caret_offset (AtkText *text)
@@ -227,7 +513,69 @@
 			  gint *height,
 			  AtkCoordType coords)
 {
-	/* Unimplemented */
+        GObject *obj;
+        EText *etext;
+        GnomeCanvas *canvas;
+        gint x_widget, y_widget, x_window, y_window;
+        GdkWindow *window;
+        GtkWidget *widget;
+        int index;
+        int trailing;
+	PangoRectangle pango_pos;
+
+        g_return_if_fail (ATK_IS_GOBJECT_ACCESSIBLE(text));
+        obj = atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (text));
+        if (obj == NULL)
+            return;
+        g_return_if_fail (E_IS_TEXT (obj));
+        etext = E_TEXT(obj);
+        canvas = GNOME_CANVAS_ITEM(etext)->canvas;
+        widget = GTK_WIDGET(canvas);
+        window = widget->window;
+        gdk_window_get_origin (window, &x_widget, &y_widget);
+
+	pango_layout_index_to_pos (etext->layout, offset, &pango_pos);
+	pango_pos.x = PANGO_PIXELS (pango_pos.x);
+	pango_pos.y = PANGO_PIXELS (pango_pos.y);
+	pango_pos.width = (pango_pos.width + PANGO_SCALE / 2) / PANGO_SCALE;
+	pango_pos.height = (pango_pos.height + PANGO_SCALE / 2) / PANGO_SCALE;
+
+        *x = pango_pos.x + x_widget;
+        *y = pango_pos.y + y_widget;
+
+	*width  = pango_pos.width;
+	*height = pango_pos.height;
+
+        if (etext->draw_borders) {
+                *x += 3; /*BORDER_INDENT;*/
+                *y += 3; /*BORDER_INDENT;*/
+        }
+
+        *x += etext->xofs;
+        *y += etext->yofs;
+
+        if (etext->editing) {
+                *x -= etext->xofs_edit;
+                *y -= etext->yofs_edit;
+        }
+
+        *x += etext->cx;
+        *y += etext->cy;
+
+	if (coords == ATK_XY_WINDOW) {
+		window = gdk_window_get_toplevel (window);
+		gdk_window_get_origin (window, &x_window, &y_window);
+		*x -= x_window;
+	        *y -= y_window;
+    	}
+	else if (coords == ATK_XY_SCREEN) {
+	}
+	else {
+		*x = 0;
+		*y = 0;
+		*height = 0;
+		*width = 0;
+	}
 }
 
 
@@ -246,8 +594,62 @@
 			gint y,
 			AtkCoordType coords)
 {
-	/* Unimplemented */
-	return 0;
+        GObject *obj;
+        EText *etext;
+        GnomeCanvas *canvas;
+	gint x_widget, y_widget, x_window, y_window;
+	GdkWindow *window;
+        GtkWidget *widget;
+        int index;
+        int trailing;
+
+        g_return_val_if_fail (ATK_IS_GOBJECT_ACCESSIBLE(text), -1);
+        obj = atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (text));
+        if (obj == NULL)
+            return -1;
+        g_return_val_if_fail (E_IS_TEXT (obj), -1);
+        etext = E_TEXT(obj);
+        canvas = GNOME_CANVAS_ITEM(etext)->canvas;
+        widget = GTK_WIDGET(canvas);
+	window = widget->window;
+	gdk_window_get_origin (window, &x_widget, &y_widget);
+
+	if (coords == ATK_XY_SCREEN) {
+		x = x - x_widget;
+		y = y - y_widget;
+	}
+	else if (coords == ATK_XY_WINDOW) {
+		window = gdk_window_get_toplevel (window);
+		gdk_window_get_origin (window, &x_window, &y_window);
+		x = x - x_widget + x_window;
+		y = y - y_widget + y_window;
+	}
+	else
+		return -1;
+
+        if (etext->draw_borders) {
+                x -= 3; /*BORDER_INDENT;*/
+                y -= 3; /*BORDER_INDENT;*/
+        }
+
+        x -= etext->xofs;
+        y -= etext->yofs;
+
+        if (etext->editing) {
+                x += etext->xofs_edit;
+                y += etext->yofs_edit;
+        }
+
+        x -= etext->cx;
+        y -= etext->cy;
+
+        pango_layout_xy_to_index (etext->layout,
+				  x * PANGO_SCALE - PANGO_SCALE / 2,
+				  y * PANGO_SCALE - PANGO_SCALE / 2,
+				  &index,
+				  &trailing);
+
+        return g_utf8_pointer_to_offset (etext->text, etext->text + index + trailing);
 }
 
 
@@ -348,8 +750,8 @@
 	g_return_val_if_fail (E_IS_TEXT (obj), FALSE);
 	etext = E_TEXT (obj);
 
-	if( selection_num == 0 
-	    && etext->selection_start != etext->selection_end ) {
+	if (selection_num == 0 
+	    && etext->selection_start != etext->selection_end) {
 		etext->selection_end = etext->selection_start;
 		g_signal_emit_by_name (ATK_OBJECT(text), "text_selection_changed");
 		return TRUE;
@@ -398,15 +800,18 @@
                                                                                 
 	if (offset < -1)
 		return FALSE;
-	else if (offset == -1)
-		gtk_object_set (GTK_OBJECT (etext),
-				"cursor_pos", et_get_character_count (text),
-				NULL);
-	else
-		gtk_object_set (GTK_OBJECT (etext),
-				"cursor_pos", offset,
-				NULL);
-	return TRUE;
+	else {
+		if (offset == -1)
+			offset = et_get_character_count (text);
+
+		ETextEventProcessorCommand command;
+		command.action = E_TEP_MOVE;
+		command.position = E_TEP_VALUE;
+		command.value = offset;
+		command.time = GDK_CURRENT_TIME;
+		g_signal_emit_by_name (etext->tep, "command", &command);
+		return TRUE;
+	}
 }
 
 static gboolean
@@ -564,17 +969,84 @@
 }
 
 static void
+_et_reposition_cb (ETextModel *model,
+		   ETextModelReposFn fn,
+		   gpointer repos_data,
+		   gpointer user_data)
+{
+	AtkObject *accessible;
+	AtkText *text;
+
+	accessible = ATK_OBJECT (user_data);
+	text = ATK_TEXT (accessible);
+
+	if (fn == e_repos_delete_shift) {
+		EReposDeleteShift *info = (EReposDeleteShift *) repos_data;
+		g_signal_emit_by_name (text, "text-changed::delete", info->pos, info->len);
+	}
+	else if (fn == e_repos_insert_shift) {
+		EReposInsertShift *info = (EReposInsertShift *) repos_data;
+		g_signal_emit_by_name (text, "text-changed::insert", info->pos, info->len);
+	}
+}
+
+static void
+_et_command_cb (ETextEventProcessor *tep,
+		ETextEventProcessorCommand *command,
+		gpointer user_data)
+{
+	AtkObject *accessible;
+	AtkText *text;
+
+	accessible = ATK_OBJECT (user_data);
+	text = ATK_TEXT (accessible);
+
+	switch (command->action) {
+	case E_TEP_MOVE:
+		g_signal_emit_by_name (text, "text-caret-moved", et_get_caret_offset (text));
+		break;
+	case E_TEP_SELECT:
+		g_signal_emit_by_name (text, "text-selection-changed");
+		break;
+	}
+}
+
+static void
+et_real_initialize (AtkObject *obj,
+                    gpointer  data)
+{
+	GalA11yEText *a11y;
+	EText *etext;
+
+	ATK_OBJECT_CLASS (parent_class)->initialize (obj, data);
+
+	g_return_if_fail (GAL_A11Y_IS_E_TEXT (obj));
+	g_return_if_fail (E_IS_TEXT (data));
+
+	a11y = GAL_A11Y_E_TEXT (obj);
+	etext = E_TEXT (data);
+
+	/* Set up signal callbacks */
+	g_signal_connect (etext->model, "reposition",
+		G_CALLBACK (_et_reposition_cb), obj);
+
+	g_signal_connect_after (etext->tep, "command",
+		(GCallback) _et_command_cb, obj);
+                                                                              
+	obj->role = ATK_ROLE_TEXT;
+}
+
+static void
 et_class_init (GalA11yETextClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	AtkObjectClass *atk_class = ATK_OBJECT_CLASS (klass);
 
 	quark_accessible_object               = g_quark_from_static_string ("gtk-accessible-object");
-
 	parent_class                          = g_type_class_ref (PARENT_TYPE);
-
 	component_parent_iface                = g_type_interface_peek(parent_class, ATK_TYPE_COMPONENT);
-	
 	object_class->dispose                 = et_dispose;
+	atk_class->initialize                 = et_real_initialize;
 }
 
 static void
