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 }