evolution-3.6.4/widgets/text/gal-a11y-e-text.c

No issues found

   1 /*
   2  * This program is free software; you can redistribute it and/or
   3  * modify it under the terms of the GNU Lesser General Public
   4  * License as published by the Free Software Foundation; either
   5  * version 2 of the License, or (at your option) version 3.
   6  *
   7  * This program is distributed in the hope that it will be useful,
   8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  10  * Lesser General Public License for more details.
  11  *
  12  * You should have received a copy of the GNU Lesser General Public
  13  * License along with the program; if not, see <http://www.gnu.org/licenses/>
  14  *
  15  *
  16  * Authors:
  17  *		Christopher James Lahey <clahey@ximian.com>
  18  *
  19  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  20  *
  21  */
  22 
  23 #ifdef HAVE_CONFIG_H
  24 #include <config.h>
  25 #endif
  26 
  27 #include <string.h>
  28 
  29 #include <gtk/gtk.h>
  30 
  31 #include "a11y/gal-a11y-util.h"
  32 #include "text/e-text.h"
  33 #include "text/e-text-model-repos.h"
  34 
  35 #include "gal-a11y-e-text.h"
  36 #include "gal-a11y-e-text-factory.h"
  37 
  38 static GObjectClass *parent_class;
  39 static AtkComponentIface *component_parent_iface;
  40 static GType parent_type;
  41 static gint priv_offset;
  42 static GQuark		quark_accessible_object = 0;
  43 #define PARENT_TYPE (parent_type)
  44 
  45 struct _GalA11yETextPrivate {
  46 	gint dummy;
  47 };
  48 
  49 static void
  50 et_dispose (GObject *object)
  51 {
  52 	if (parent_class->dispose)
  53 		parent_class->dispose (object);
  54 }
  55 
  56 /* Static functions */
  57 
  58 static void
  59 et_get_extents (AtkComponent *component,
  60                 gint *x,
  61                 gint *y,
  62                 gint *width,
  63                 gint *height,
  64                 AtkCoordType coord_type)
  65 {
  66 	EText *item = E_TEXT (atk_gobject_accessible_get_object (
  67 		ATK_GOBJECT_ACCESSIBLE (component)));
  68 	gdouble real_width;
  69 	gdouble real_height;
  70 	gint fake_width;
  71 	gint fake_height;
  72 
  73 	if (component_parent_iface &&
  74 	    component_parent_iface->get_extents)
  75 		component_parent_iface->get_extents (component,
  76 						     x,
  77 						     y,
  78 						     &fake_width,
  79 						     &fake_height,
  80 						     coord_type);
  81 
  82 	g_object_get (
  83 		item,
  84 		"text_width", &real_width,
  85 		"text_height", &real_height,
  86 		NULL);
  87 
  88 	if (width)
  89 		*width = real_width;
  90 	if (height)
  91 		*height = real_height;
  92 }
  93 
  94 static const gchar *
  95 et_get_full_text (AtkText *text)
  96 {
  97 	GObject *obj;
  98 	EText *etext;
  99 	ETextModel *model;
 100 	const gchar *full_text;
 101 
 102 	obj = atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (text));
 103 	if (obj == NULL)
 104 		return "";
 105 
 106 	etext = E_TEXT (obj);
 107 	g_object_get (etext, "model", &model, NULL);
 108 
 109 	full_text = e_text_model_get_text (model);
 110 
 111 	return full_text;
 112 }
 113 
 114 static void
 115 et_set_full_text (AtkEditableText *text,
 116                   const gchar *full_text)
 117 {
 118 	GObject *obj;
 119 	EText *etext;
 120 	ETextModel *model;
 121 
 122 	obj = atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (text));
 123 	if (obj == NULL)
 124 		return;
 125 
 126 	etext = E_TEXT (obj);
 127 	g_object_get (etext, "model", &model, NULL);
 128 
 129 	e_text_model_set_text (model, full_text);
 130 }
 131 
 132 static gchar *
 133 et_get_text (AtkText *text,
 134              gint start_offset,
 135              gint end_offset)
 136 {
 137 	gint start, end, real_start, real_end, len;
 138 	const gchar *full_text = et_get_full_text (text);
 139 	if (full_text == NULL)
 140 		return NULL;
 141 	len = g_utf8_strlen (full_text, -1);
 142 
 143 	start = MIN (MAX (0, start_offset), len);
 144 	end = MIN (MAX (-1, end_offset), len);
 145 
 146 	if (end_offset == -1)
 147 		end = strlen (full_text);
 148 	else
 149 		end = g_utf8_offset_to_pointer (full_text, end) - full_text;
 150 
 151 	start = g_utf8_offset_to_pointer (full_text, start) - full_text;
 152 
 153 	real_start = MIN (start, end);
 154 	real_end = MAX (start, end);
 155 
 156 	return g_strndup (full_text + real_start, real_end - real_start);
 157 }
 158 
 159 static gboolean
 160 is_a_seperator (gunichar c)
 161 {
 162 	return g_unichar_ispunct (c) || g_unichar_isspace (c);
 163 }
 164 
 165 static gint
 166 find_word_start (const gchar *text,
 167                  gint begin_offset,
 168                  gint step)
 169 {
 170 	gint offset;
 171 	gchar *at_offset;
 172 	gunichar current, previous;
 173 	gint len;
 174 
 175 	offset = begin_offset;
 176 	len = g_utf8_strlen (text, -1);
 177 
 178 	while (offset > 0 && offset < len) {
 179 		at_offset = g_utf8_offset_to_pointer (text, offset);
 180 		current = g_utf8_get_char_validated (at_offset, -1);
 181 		at_offset = g_utf8_offset_to_pointer (text, offset - 1);
 182 		previous = g_utf8_get_char_validated (at_offset, -1);
 183 		if ((!is_a_seperator (current)) && is_a_seperator (previous))
 184 			break;
 185 		offset += step;
 186 	}
 187 
 188 	return offset;
 189 }
 190 
 191 static gint
 192 find_word_end (const gchar *text,
 193                gint begin_offset,
 194                gint step)
 195 {
 196 	gint offset;
 197 	gchar *at_offset;
 198 	gunichar current, previous;
 199 	gint len;
 200 
 201 	offset = begin_offset;
 202 	len = g_utf8_strlen (text, -1);
 203 
 204 	while (offset > 0 && offset < len) {
 205 		at_offset = g_utf8_offset_to_pointer (text, offset);
 206 		current = g_utf8_get_char_validated (at_offset, -1);
 207 		at_offset = g_utf8_offset_to_pointer (text, offset - 1);
 208 		previous = g_utf8_get_char_validated (at_offset, -1);
 209 		if (is_a_seperator (current) && (!is_a_seperator (previous)))
 210 			break;
 211 		offset += step;
 212 	}
 213 
 214 	return offset;
 215 }
 216 
 217 static gint
 218 find_sentence_start (const gchar *text,
 219                      gint begin_offset,
 220                      gint step)
 221 {
 222 	gint offset, last_word_end, len;
 223 	gchar *at_offset;
 224 	gunichar ch;
 225 	gint i;
 226 
 227 	offset = find_word_start (text, begin_offset, step);
 228 	len = g_utf8_strlen (text, -1);
 229 
 230 	while (offset > 0 && offset <len) {
 231 		last_word_end = find_word_end (text, offset - 1, -1);
 232 		if (last_word_end == 0)
 233 			break;
 234 		for (i = last_word_end; i < offset; i++) {
 235 			at_offset = g_utf8_offset_to_pointer (text, i);
 236 			ch = g_utf8_get_char_validated (at_offset, -1);
 237 			if (ch == '.' || ch == '!' || ch == '?')
 238 				return offset;
 239 		}
 240 
 241 		offset = find_word_start (text, offset + step, step);
 242 	}
 243 
 244 	return offset;
 245 }
 246 
 247 static gint
 248 find_sentence_end (const gchar *text,
 249                    gint begin_offset,
 250                    gint step)
 251 {
 252 	gint offset;
 253 	gchar *at_offset;
 254 	gunichar previous;
 255 	gint len;
 256 
 257 	offset = begin_offset;
 258 	len = g_utf8_strlen (text, -1);
 259 
 260 	while (offset > 0 && offset < len) {
 261 		at_offset = g_utf8_offset_to_pointer (text, offset - 1);
 262 		previous = g_utf8_get_char_validated (at_offset, -1);
 263 		if (previous == '.' || previous == '!' || previous == '?')
 264 			break;
 265 		offset += step;
 266 	}
 267 
 268 	return offset;
 269 }
 270 
 271 static gint
 272 find_line_start (const gchar *text,
 273                      gint begin_offset,
 274                      gint step)
 275 {
 276 	gint offset;
 277 	gchar *at_offset;
 278 	gunichar previous;
 279 	gint len;
 280 
 281 	offset = begin_offset;
 282 	len = g_utf8_strlen (text, -1);
 283 
 284 	while (offset > 0 && offset < len) {
 285 		at_offset = g_utf8_offset_to_pointer (text, offset - 1);
 286 		previous = g_utf8_get_char_validated (at_offset, -1);
 287 		if (previous == '\n' || previous == '\r')
 288 			break;
 289 		offset += step;
 290 	}
 291 
 292 	return offset;
 293 }
 294 
 295 static gint
 296 find_line_end (const gchar *text,
 297                      gint begin_offset,
 298                      gint step)
 299 {
 300 	gint offset;
 301 	gchar *at_offset;
 302 	gunichar current;
 303 	gint len;
 304 
 305 	offset = begin_offset;
 306 	len = g_utf8_strlen (text, -1);
 307 
 308 	while (offset >= 0 && offset < len) {
 309 		at_offset = g_utf8_offset_to_pointer (text, offset);
 310 		current = g_utf8_get_char_validated (at_offset, -1);
 311 		if (current == '\n' || current == '\r')
 312 			break;
 313 		offset += step;
 314 	}
 315 
 316 	return offset;
 317 }
 318 
 319 static gchar *
 320 et_get_text_after_offset (AtkText *text,
 321                           gint offset,
 322                           AtkTextBoundary boundary_type,
 323                           gint *start_offset,
 324                           gint *end_offset)
 325 {
 326 	gint start, end, len;
 327 	const gchar *full_text = et_get_full_text (text);
 328 	g_return_val_if_fail (full_text, NULL);
 329 
 330 	switch (boundary_type)
 331 	{
 332 	case ATK_TEXT_BOUNDARY_CHAR:
 333 		start = offset + 1;
 334 		end = offset + 2;
 335 		break;
 336 	case ATK_TEXT_BOUNDARY_WORD_START:
 337 		start = find_word_start (full_text, offset + 1, 1);
 338 		end = find_word_start (full_text, start + 1, 1);
 339 		break;
 340 	case ATK_TEXT_BOUNDARY_WORD_END:
 341 		start = find_word_end (full_text, offset + 1, 1);
 342 		end = find_word_end (full_text, start + 1, 1);
 343 		break;
 344 	case ATK_TEXT_BOUNDARY_SENTENCE_START:
 345 		start = find_sentence_start (full_text, offset + 1, 1);
 346 		end = find_sentence_start (full_text, start + 1, 1);
 347 		break;
 348 	case ATK_TEXT_BOUNDARY_SENTENCE_END:
 349 		start = find_sentence_end (full_text, offset + 1, 1);
 350 		end = find_sentence_end (full_text, start + 1, 1);
 351 		break;
 352 	case ATK_TEXT_BOUNDARY_LINE_START:
 353 		start = find_line_start (full_text, offset + 1, 1);
 354 		end = find_line_start (full_text, start + 1, 1);
 355 		break;
 356 	case ATK_TEXT_BOUNDARY_LINE_END:
 357 		start = find_line_end (full_text, offset + 1, 1);
 358 		end = find_line_end (full_text, start + 1, 1);
 359 		break;
 360 	default:
 361 		return NULL;
 362 	}
 363 
 364 	len = g_utf8_strlen (full_text, -1);
 365 	if (start_offset)
 366 		*start_offset = MIN (MAX (0, start), len);
 367 	if (end_offset)
 368 		*end_offset = MIN (MAX (0, end), len);
 369 	return et_get_text (text, start, end);
 370 }
 371 
 372 static gchar *
 373 et_get_text_at_offset (AtkText *text,
 374                        gint offset,
 375                        AtkTextBoundary boundary_type,
 376                        gint *start_offset,
 377                        gint *end_offset)
 378 {
 379 	gint start, end, len;
 380 	const gchar *full_text = et_get_full_text (text);
 381 	g_return_val_if_fail (full_text, NULL);
 382 
 383 	switch (boundary_type)
 384 	{
 385 	case ATK_TEXT_BOUNDARY_CHAR:
 386 		start = offset;
 387 		end = offset + 1;
 388 		break;
 389 	case ATK_TEXT_BOUNDARY_WORD_START:
 390 		start = find_word_start (full_text, offset - 1, -1);
 391 		end = find_word_start (full_text, offset, 1);
 392 		break;
 393 	case ATK_TEXT_BOUNDARY_WORD_END:
 394 		start = find_word_end (full_text, offset, -1);
 395 		end = find_word_end (full_text, offset + 1, 1);
 396 		break;
 397 	case ATK_TEXT_BOUNDARY_SENTENCE_START:
 398 		start = find_sentence_start (full_text, offset - 1, -1);
 399 		end = find_sentence_start (full_text, offset, 1);
 400 		break;
 401 	case ATK_TEXT_BOUNDARY_SENTENCE_END:
 402 		start = find_sentence_end (full_text, offset, -1);
 403 		end = find_sentence_end (full_text, offset + 1, 1);
 404 		break;
 405 	case ATK_TEXT_BOUNDARY_LINE_START:
 406 		start = find_line_start (full_text, offset - 1, -1);
 407 		end = find_line_start (full_text, offset, 1);
 408 		break;
 409 	case ATK_TEXT_BOUNDARY_LINE_END:
 410 		start = find_line_end (full_text, offset, -1);
 411 		end = find_line_end (full_text, offset + 1, 1);
 412 		break;
 413 	default:
 414 		return NULL;
 415 	}
 416 
 417 	len = g_utf8_strlen (full_text, -1);
 418 	if (start_offset)
 419 		*start_offset = MIN (MAX (0, start), len);
 420 	if (end_offset)
 421 		*end_offset = MIN (MAX (0, end), len);
 422 	return et_get_text (text, start, end);
 423 }
 424 
 425 static gunichar
 426 et_get_character_at_offset (AtkText *text,
 427                             gint offset)
 428 {
 429 	const gchar *full_text = et_get_full_text (text);
 430 	gchar *at_offset;
 431 
 432 	at_offset = g_utf8_offset_to_pointer (full_text, offset);
 433 	return g_utf8_get_char_validated (at_offset, -1);
 434 }
 435 
 436 static gchar *
 437 et_get_text_before_offset (AtkText *text,
 438                            gint offset,
 439                            AtkTextBoundary boundary_type,
 440                            gint *start_offset,
 441                            gint *end_offset)
 442 {
 443 	gint start, end, len;
 444 	const gchar *full_text = et_get_full_text (text);
 445 	g_return_val_if_fail (full_text, NULL);
 446 
 447 	switch (boundary_type)
 448 	{
 449 	case ATK_TEXT_BOUNDARY_CHAR:
 450 		start = offset - 1;
 451 		end = offset;
 452 		break;
 453 	case ATK_TEXT_BOUNDARY_WORD_START:
 454 		end = find_word_start (full_text, offset - 1, -1);
 455 		start = find_word_start (full_text, end - 1, -1);
 456 		break;
 457 	case ATK_TEXT_BOUNDARY_WORD_END:
 458 		end = find_word_end (full_text, offset, -1);
 459 		start = find_word_end (full_text, end - 1, -1);
 460 		break;
 461 	case ATK_TEXT_BOUNDARY_SENTENCE_START:
 462 		end = find_sentence_start (full_text, offset, -1);
 463 		start = find_sentence_start (full_text, end - 1, -1);
 464 		break;
 465 	case ATK_TEXT_BOUNDARY_SENTENCE_END:
 466 		end = find_sentence_end (full_text, offset, -1);
 467 		start = find_sentence_end (full_text, end - 1, -1);
 468 		break;
 469 	case ATK_TEXT_BOUNDARY_LINE_START:
 470 		end = find_line_start (full_text, offset, -1);
 471 		start = find_line_start (full_text, end - 1, -1);
 472 		break;
 473 	case ATK_TEXT_BOUNDARY_LINE_END:
 474 		end = find_line_end (full_text, offset, -1);
 475 		start = find_line_end (full_text, end - 1, -1);
 476 		break;
 477 	default:
 478 		return NULL;
 479 	}
 480 
 481 	len = g_utf8_strlen (full_text, -1);
 482 	if (start_offset)
 483 		*start_offset = MIN (MAX (0, start), len);
 484 	if (end_offset)
 485 		*end_offset = MIN (MAX (0, end), len);
 486 	return et_get_text (text, start, end);
 487 }
 488 
 489 static gint
 490 et_get_caret_offset (AtkText *text)
 491 {
 492 	GObject *obj;
 493 	EText *etext;
 494 	gint offset;
 495 
 496 	g_return_val_if_fail (ATK_IS_GOBJECT_ACCESSIBLE (text), -1);
 497 	obj = atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (text));
 498 	if (obj == NULL)
 499 		return -1;
 500 
 501 	g_return_val_if_fail (E_IS_TEXT (obj), -1);
 502 	etext = E_TEXT (obj);
 503 
 504 	g_object_get (etext, "cursor_pos", &offset, NULL);
 505 	return offset;
 506 }
 507 
 508 static AtkAttributeSet *
 509 et_get_run_attributes (AtkText *text,
 510                        gint offset,
 511                        gint *start_offset,
 512                        gint *end_offset)
 513 {
 514 	/* Unimplemented */
 515 	return NULL;
 516 }
 517 
 518 static AtkAttributeSet *
 519 et_get_default_attributes (AtkText *text)
 520 {
 521 	/* Unimplemented */
 522 	return NULL;
 523 }
 524 
 525 static void
 526 et_get_character_extents (AtkText *text,
 527                           gint offset,
 528                           gint *x,
 529                           gint *y,
 530                           gint *width,
 531                           gint *height,
 532                           AtkCoordType coords)
 533 {
 534 	GObject *obj;
 535 	EText *etext;
 536 	GnomeCanvas *canvas;
 537 	gint x_widget, y_widget, x_window, y_window;
 538 	GdkWindow *window;
 539 	GtkWidget *widget;
 540 	PangoRectangle pango_pos;
 541 
 542 	g_return_if_fail (ATK_IS_GOBJECT_ACCESSIBLE (text));
 543 	obj = atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (text));
 544 	if (obj == NULL)
 545 	    return;
 546 	g_return_if_fail (E_IS_TEXT (obj));
 547 	etext = E_TEXT (obj);
 548 	canvas = GNOME_CANVAS_ITEM (etext)->canvas;
 549 	widget = GTK_WIDGET (canvas);
 550 	window = gtk_widget_get_window (widget);
 551 	gdk_window_get_origin (window, &x_widget, &y_widget);
 552 
 553 	pango_layout_index_to_pos (etext->layout, offset, &pango_pos);
 554 	pango_pos.x = PANGO_PIXELS (pango_pos.x);
 555 	pango_pos.y = PANGO_PIXELS (pango_pos.y);
 556 	pango_pos.width = (pango_pos.width + PANGO_SCALE / 2) / PANGO_SCALE;
 557 	pango_pos.height = (pango_pos.height + PANGO_SCALE / 2) / PANGO_SCALE;
 558 
 559         *x = pango_pos.x + x_widget;
 560         *y = pango_pos.y + y_widget;
 561 
 562 	*width  = pango_pos.width;
 563 	*height = pango_pos.height;
 564 
 565         *x += etext->xofs;
 566         *y += etext->yofs;
 567 
 568 	if (etext->editing) {
 569                 *x -= etext->xofs_edit;
 570                 *y -= etext->yofs_edit;
 571 	}
 572 
 573         *x += etext->cx;
 574         *y += etext->cy;
 575 
 576 	if (coords == ATK_XY_WINDOW) {
 577 		window = gdk_window_get_toplevel (window);
 578 		gdk_window_get_origin (window, &x_window, &y_window);
 579 		*x -= x_window;
 580 		*y -= y_window;
 581 	}
 582 	else if (coords == ATK_XY_SCREEN) {
 583 	}
 584 	else {
 585 		*x = 0;
 586 		*y = 0;
 587 		*height = 0;
 588 		*width = 0;
 589 	}
 590 }
 591 
 592 static gint
 593 et_get_character_count (AtkText *text)
 594 {
 595 	const gchar *full_text = et_get_full_text (text);
 596 
 597 	return g_utf8_strlen (full_text, -1);
 598 }
 599 
 600 static gint
 601 et_get_offset_at_point (AtkText *text,
 602                         gint x,
 603                         gint y,
 604                         AtkCoordType coords)
 605 {
 606 	GObject *obj;
 607 	EText *etext;
 608 	GnomeCanvas *canvas;
 609 	gint x_widget, y_widget, x_window, y_window;
 610 	GdkWindow *window;
 611 	GtkWidget *widget;
 612 	gint index;
 613 	gint trailing;
 614 
 615 	g_return_val_if_fail (ATK_IS_GOBJECT_ACCESSIBLE (text), -1);
 616 	obj = atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (text));
 617 	if (obj == NULL)
 618 	    return -1;
 619 	g_return_val_if_fail (E_IS_TEXT (obj), -1);
 620 	etext = E_TEXT (obj);
 621 	canvas = GNOME_CANVAS_ITEM (etext)->canvas;
 622 	widget = GTK_WIDGET (canvas);
 623 	window = gtk_widget_get_window (widget);
 624 	gdk_window_get_origin (window, &x_widget, &y_widget);
 625 
 626 	if (coords == ATK_XY_SCREEN) {
 627 		x = x - x_widget;
 628 		y = y - y_widget;
 629 	}
 630 	else if (coords == ATK_XY_WINDOW) {
 631 		window = gdk_window_get_toplevel (window);
 632 		gdk_window_get_origin (window, &x_window, &y_window);
 633 		x = x - x_widget + x_window;
 634 		y = y - y_widget + y_window;
 635 	}
 636 	else
 637 		return -1;
 638 
 639 	x -= etext->xofs;
 640 	y -= etext->yofs;
 641 
 642 	if (etext->editing) {
 643 		x += etext->xofs_edit;
 644 		y += etext->yofs_edit;
 645 	}
 646 
 647 	x -= etext->cx;
 648 	y -= etext->cy;
 649 
 650 	pango_layout_xy_to_index (
 651 		etext->layout,
 652 		x * PANGO_SCALE - PANGO_SCALE / 2,
 653 		y * PANGO_SCALE - PANGO_SCALE / 2,
 654 		&index,
 655 		&trailing);
 656 
 657 	return g_utf8_pointer_to_offset (etext->text, etext->text + index + trailing);
 658 }
 659 
 660 static gint
 661 et_get_n_selections (AtkText *text)
 662 {
 663 	EText *etext;
 664 	GObject *obj =  atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (text));
 665 
 666 	if (obj == NULL)
 667 		return -1;
 668 	etext = E_TEXT (obj);
 669 
 670 	if (etext->selection_start !=
 671 	    etext->selection_end)
 672 		return 1;
 673 	return 0;
 674 }
 675 
 676 static gchar *
 677 et_get_selection (AtkText *text,
 678                   gint selection_num,
 679                   gint *start_offset,
 680                   gint *end_offset)
 681 {
 682 	gint start, end, real_start, real_end, len;
 683 	EText *etext;
 684 	if (selection_num == 0) {
 685 		const gchar *full_text = et_get_full_text (text);
 686 		if (full_text == NULL)
 687 			return NULL;
 688 		len = g_utf8_strlen (full_text, -1);
 689 		etext = E_TEXT (atk_gobject_accessible_get_object (
 690 			ATK_GOBJECT_ACCESSIBLE (text)));
 691 		start = MIN (etext->selection_start, etext->selection_end);
 692 		end = MAX (etext->selection_start, etext->selection_end);
 693 		start = MIN (MAX (0, start), len);
 694 		end = MIN (MAX (0, end), len);
 695 		if (start != end) {
 696 			if (start_offset)
 697 				*start_offset = start;
 698 			if (end_offset)
 699 				*end_offset = end;
 700 			real_start = g_utf8_offset_to_pointer (full_text, start) - full_text;
 701 			real_end = g_utf8_offset_to_pointer (full_text, end) - full_text;
 702 			return g_strndup (full_text + real_start, real_end - real_start);
 703 		}
 704 	}
 705 
 706 	return NULL;
 707 }
 708 
 709 static gboolean
 710 et_add_selection (AtkText *text,
 711                   gint start_offset,
 712                   gint end_offset)
 713 {
 714 	GObject *obj;
 715 	EText *etext;
 716 
 717 	g_return_val_if_fail (ATK_IS_GOBJECT_ACCESSIBLE (text), FALSE);
 718 	obj = atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (text));
 719 	if (obj == NULL)
 720 		return FALSE;
 721 	g_return_val_if_fail (E_IS_TEXT (obj), FALSE);
 722 	etext = E_TEXT (obj);
 723 
 724 	g_return_val_if_fail (start_offset >= 0, FALSE);
 725 	g_return_val_if_fail (start_offset >= -1, FALSE);
 726 	if (end_offset == -1)
 727 		end_offset = et_get_character_count (text);
 728 
 729 	if (start_offset != end_offset) {
 730 		gint real_start, real_end;
 731 		real_start = MIN (start_offset, end_offset);
 732 		real_end = MAX (start_offset, end_offset);
 733 		etext->selection_start = real_start;
 734 		etext->selection_end = real_end;
 735 
 736 		gnome_canvas_item_grab_focus (GNOME_CANVAS_ITEM (etext));
 737 		gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (etext));
 738 
 739 		g_signal_emit_by_name (ATK_OBJECT (text), "text_selection_changed");
 740 
 741 		return TRUE;
 742 	}
 743 
 744 	return FALSE;
 745 }
 746 
 747 static gboolean
 748 et_remove_selection (AtkText *text,
 749                      gint selection_num)
 750 {
 751 	GObject *obj;
 752 	EText *etext;
 753 
 754 	g_return_val_if_fail (ATK_IS_GOBJECT_ACCESSIBLE (text), FALSE);
 755 	obj = atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (text));
 756 	if (obj == NULL)
 757 		return FALSE;
 758 	g_return_val_if_fail (E_IS_TEXT (obj), FALSE);
 759 	etext = E_TEXT (obj);
 760 
 761 	if (selection_num == 0
 762 	    && etext->selection_start != etext->selection_end) {
 763 		etext->selection_end = etext->selection_start;
 764 		g_signal_emit_by_name (ATK_OBJECT (text), "text_selection_changed");
 765 		return TRUE;
 766 	}
 767 
 768 	return FALSE;
 769 }
 770 
 771 static gboolean
 772 et_set_selection (AtkText *text,
 773                   gint selection_num,
 774                   gint start_offset,
 775                   gint end_offset)
 776 {
 777 	GObject *obj;
 778 
 779 	g_return_val_if_fail (ATK_IS_GOBJECT_ACCESSIBLE (text), FALSE);
 780 	obj = atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (text));
 781 	if (obj == NULL)
 782 		return FALSE;
 783 	g_return_val_if_fail (E_IS_TEXT (obj), FALSE);
 784 	if (selection_num == 0)
 785 		return et_add_selection (text, start_offset, end_offset);
 786 	return FALSE;
 787 }
 788 
 789 static gboolean
 790 et_set_caret_offset (AtkText *text,
 791                      gint offset)
 792 {
 793 	GObject *obj;
 794 	EText *etext;
 795 
 796 	g_return_val_if_fail (ATK_IS_GOBJECT_ACCESSIBLE (text), FALSE);
 797 	obj = atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (text));
 798 	if (obj == NULL)
 799 		return FALSE;
 800 
 801 	g_return_val_if_fail (E_IS_TEXT (obj), FALSE);
 802 	etext = E_TEXT (obj);
 803 
 804 	if (offset < -1)
 805 		return FALSE;
 806 	else {
 807 		ETextEventProcessorCommand command;
 808 
 809 		if (offset == -1)
 810 			offset = et_get_character_count (text);
 811 
 812 		command.action = E_TEP_MOVE;
 813 		command.position = E_TEP_VALUE;
 814 		command.value = offset;
 815 		command.time = GDK_CURRENT_TIME;
 816 		g_signal_emit_by_name (etext->tep, "command", &command);
 817 		return TRUE;
 818 	}
 819 }
 820 
 821 static gboolean
 822 et_set_run_attributes (AtkEditableText *text,
 823                        AtkAttributeSet *attrib_set,
 824                        gint start_offset,
 825                        gint end_offset)
 826 {
 827 	/* Unimplemented */
 828 	return FALSE;
 829 }
 830 
 831 static void
 832 et_set_text_contents (AtkEditableText *text,
 833                       const gchar *string)
 834 {
 835 	et_set_full_text (text, string);
 836 }
 837 
 838 static void
 839 et_insert_text (AtkEditableText *text,
 840                 const gchar *string,
 841                 gint length,
 842                 gint *position)
 843 {
 844 	/* Utf8 unimplemented */
 845 	gchar *result;
 846 
 847 	const gchar *full_text = et_get_full_text (ATK_TEXT (text));
 848 	if (full_text == NULL)
 849 		return;
 850 
 851 	result = g_strdup_printf (
 852 		"%.*s%.*s%s", *position, full_text,
 853 		length, string, full_text + *position);
 854 
 855 	et_set_full_text (text, result);
 856 
 857 	*position += length;
 858 
 859 	g_free (result);
 860 }
 861 
 862 static void
 863 et_copy_text (AtkEditableText *text,
 864               gint start_pos,
 865               gint end_pos)
 866 {
 867 	GObject *obj;
 868 	EText *etext;
 869 
 870 	g_return_if_fail (ATK_IS_GOBJECT_ACCESSIBLE (text));
 871 	obj = atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (text));
 872 	if (obj == NULL)
 873 		return;
 874 
 875 	g_return_if_fail (E_IS_TEXT (obj));
 876 	etext = E_TEXT (obj);
 877 
 878 	if (start_pos != end_pos) {
 879 		etext->selection_start = start_pos;
 880 		etext->selection_end = end_pos;
 881 		e_text_copy_clipboard (etext);
 882 	}
 883 }
 884 
 885 static void
 886 et_delete_text (AtkEditableText *text,
 887                 gint start_pos,
 888                 gint end_pos)
 889 {
 890 	GObject *obj;
 891 	EText *etext;
 892 
 893 	g_return_if_fail (ATK_IS_GOBJECT_ACCESSIBLE (text));
 894 	obj = atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (text));
 895 	if (obj == NULL)
 896 		return;
 897 
 898 	g_return_if_fail (E_IS_TEXT (obj));
 899 	etext = E_TEXT (obj);
 900 
 901 	etext->selection_start = start_pos;
 902 	etext->selection_end = end_pos;
 903 
 904 	e_text_delete_selection (etext);
 905 }
 906 
 907 static void
 908 et_cut_text (AtkEditableText *text,
 909              gint start_pos,
 910              gint end_pos)
 911 {
 912 	et_copy_text (text, start_pos, end_pos);
 913 	et_delete_text (text, start_pos, end_pos);
 914 }
 915 
 916 static void
 917 et_paste_text (AtkEditableText *text,
 918                gint position)
 919 {
 920 	GObject *obj;
 921 	EText *etext;
 922 
 923 	g_return_if_fail (ATK_IS_GOBJECT_ACCESSIBLE (text));
 924 	obj = atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (text));
 925 	if (obj == NULL)
 926 		return;
 927 
 928 	g_return_if_fail (E_IS_TEXT (obj));
 929 	etext = E_TEXT (obj);
 930 
 931 	g_object_set (etext, "cursor_pos", position, NULL);
 932 	e_text_paste_clipboard (etext);
 933 }
 934 
 935 static void
 936 et_atk_component_iface_init (AtkComponentIface *iface)
 937 {
 938 	iface->get_extents = et_get_extents;
 939 }
 940 
 941 static void
 942 et_atk_text_iface_init (AtkTextIface *iface)
 943 {
 944 	iface->get_text                = et_get_text;
 945 	iface->get_text_after_offset   = et_get_text_after_offset;
 946 	iface->get_text_at_offset      = et_get_text_at_offset;
 947 	iface->get_character_at_offset = et_get_character_at_offset;
 948 	iface->get_text_before_offset  = et_get_text_before_offset;
 949 	iface->get_caret_offset        = et_get_caret_offset;
 950 	iface->get_run_attributes      = et_get_run_attributes;
 951 	iface->get_default_attributes  = et_get_default_attributes;
 952 	iface->get_character_extents   = et_get_character_extents;
 953 	iface->get_character_count     = et_get_character_count;
 954 	iface->get_offset_at_point     = et_get_offset_at_point;
 955 	iface->get_n_selections        = et_get_n_selections;
 956 	iface->get_selection           = et_get_selection;
 957 	iface->add_selection           = et_add_selection;
 958 	iface->remove_selection        = et_remove_selection;
 959 	iface->set_selection           = et_set_selection;
 960 	iface->set_caret_offset        = et_set_caret_offset;
 961 }
 962 
 963 static void
 964 et_atk_editable_text_iface_init (AtkEditableTextIface *iface)
 965 {
 966 	iface->set_run_attributes = et_set_run_attributes;
 967 	iface->set_text_contents  = et_set_text_contents;
 968 	iface->insert_text        = et_insert_text;
 969 	iface->copy_text          = et_copy_text;
 970 	iface->cut_text           = et_cut_text;
 971 	iface->delete_text        = et_delete_text;
 972 	iface->paste_text         = et_paste_text;
 973 }
 974 
 975 static void
 976 _et_reposition_cb (ETextModel *model,
 977                    ETextModelReposFn fn,
 978                    gpointer repos_data,
 979                    gpointer user_data)
 980 {
 981 	AtkObject *accessible;
 982 	AtkText *text;
 983 
 984 	accessible = ATK_OBJECT (user_data);
 985 	text = ATK_TEXT (accessible);
 986 
 987 	if (fn == e_repos_delete_shift) {
 988 		EReposDeleteShift *info = (EReposDeleteShift *) repos_data;
 989 		g_signal_emit_by_name (text, "text-changed::delete", info->pos, info->len);
 990 	}
 991 	else if (fn == e_repos_insert_shift) {
 992 		EReposInsertShift *info = (EReposInsertShift *) repos_data;
 993 		g_signal_emit_by_name (text, "text-changed::insert", info->pos, info->len);
 994 	}
 995 }
 996 
 997 static void
 998 _et_command_cb (ETextEventProcessor *tep,
 999                 ETextEventProcessorCommand *command,
1000                 gpointer user_data)
1001 {
1002 	AtkObject *accessible;
1003 	AtkText *text;
1004 
1005 	accessible = ATK_OBJECT (user_data);
1006 	text = ATK_TEXT (accessible);
1007 
1008 	switch (command->action) {
1009 	case E_TEP_MOVE:
1010 		g_signal_emit_by_name (text, "text-caret-moved", et_get_caret_offset (text));
1011 		break;
1012 	case E_TEP_SELECT:
1013 		g_signal_emit_by_name (text, "text-selection-changed");
1014 		break;
1015 	default:
1016 		break;
1017 	}
1018 }
1019 
1020 static void
1021 et_real_initialize (AtkObject *obj,
1022                     gpointer data)
1023 {
1024 	EText *etext;
1025 
1026 	ATK_OBJECT_CLASS (parent_class)->initialize (obj, data);
1027 
1028 	g_return_if_fail (GAL_A11Y_IS_E_TEXT (obj));
1029 	g_return_if_fail (E_IS_TEXT (data));
1030 
1031 	etext = E_TEXT (data);
1032 
1033 	/* Set up signal callbacks */
1034 	g_signal_connect (
1035 		etext->model, "reposition",
1036 		G_CALLBACK (_et_reposition_cb), obj);
1037 
1038 	if (etext->tep)
1039 		g_signal_connect_after (
1040 			etext->tep, "command",
1041 			(GCallback) _et_command_cb, obj);
1042 
1043 	obj->role = ATK_ROLE_TEXT;
1044 }
1045 
1046 static void
1047 et_class_init (GalA11yETextClass *class)
1048 {
1049 	GObjectClass *object_class = G_OBJECT_CLASS (class);
1050 	AtkObjectClass *atk_class = ATK_OBJECT_CLASS (class);
1051 
1052 	quark_accessible_object =
1053 		g_quark_from_static_string ("gtk-accessible-object");
1054 	parent_class = g_type_class_ref (PARENT_TYPE);
1055 	component_parent_iface =
1056 		g_type_interface_peek (parent_class, ATK_TYPE_COMPONENT);
1057 	object_class->dispose = et_dispose;
1058 	atk_class->initialize = et_real_initialize;
1059 }
1060 
1061 static void
1062 et_init (GalA11yEText *a11y)
1063 {
1064 }
1065 
1066 /**
1067  * gal_a11y_e_text_get_type:
1068  * @void:
1069  *
1070  * Registers the &GalA11yEText class if necessary, and returns the type ID
1071  * associated to it.
1072  *
1073  * Return value: The type ID of the &GalA11yEText class.
1074  **/
1075 GType
1076 gal_a11y_e_text_get_type (void)
1077 {
1078 	static GType type = 0;
1079 
1080 	if (!type) {
1081 		AtkObjectFactory *factory;
1082 
1083 		GTypeInfo info = {
1084 			sizeof (GalA11yETextClass),
1085 			(GBaseInitFunc) NULL,
1086 			(GBaseFinalizeFunc) NULL,
1087 			(GClassInitFunc) et_class_init,
1088 			(GClassFinalizeFunc) NULL,
1089 			NULL, /* class_data */
1090 			sizeof (GalA11yEText),
1091 			0,
1092 			(GInstanceInitFunc) et_init,
1093 			NULL /* value_text */
1094 		};
1095 
1096 		static const GInterfaceInfo atk_component_info = {
1097 			(GInterfaceInitFunc) et_atk_component_iface_init,
1098 			(GInterfaceFinalizeFunc) NULL,
1099 			NULL
1100 		};
1101 		static const GInterfaceInfo atk_text_info = {
1102 			(GInterfaceInitFunc) et_atk_text_iface_init,
1103 			(GInterfaceFinalizeFunc) NULL,
1104 			NULL
1105 		};
1106 		static const GInterfaceInfo atk_editable_text_info = {
1107 			(GInterfaceInitFunc) et_atk_editable_text_iface_init,
1108 			(GInterfaceFinalizeFunc) NULL,
1109 			NULL
1110 		};
1111 
1112 		factory = atk_registry_get_factory (
1113 			atk_get_default_registry (), GNOME_TYPE_CANVAS_ITEM);
1114 		parent_type = atk_object_factory_get_accessible_type (factory);
1115 
1116 		type = gal_a11y_type_register_static_with_private (
1117 			PARENT_TYPE, "GalA11yEText", &info, 0,
1118 			sizeof (GalA11yETextPrivate), &priv_offset);
1119 
1120 		g_type_add_interface_static (
1121 			type, ATK_TYPE_COMPONENT, &atk_component_info);
1122 		g_type_add_interface_static (
1123 			type, ATK_TYPE_TEXT, &atk_text_info);
1124 		g_type_add_interface_static (
1125 			type, ATK_TYPE_EDITABLE_TEXT, &atk_editable_text_info);
1126 	}
1127 
1128 	return type;
1129 }
1130 
1131 void
1132 gal_a11y_e_text_init (void)
1133 {
1134 	if (atk_get_root ())
1135 		atk_registry_set_factory_type (
1136 			atk_get_default_registry (),
1137 			E_TYPE_TEXT,
1138 			gal_a11y_e_text_factory_get_type ());
1139 
1140 }