2 * AT-SPI - Assistive Technology Service Provider Interface
3 * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
5 * Copyright 2001, 2002 Sun Microsystems Inc.,
6 * Copyright 2001, 2002 Ximian, Inc.
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
24 /* text.c : implements the Text interface */
30 #include <atk/atktext.h>
31 #include <libspi/text.h>
32 #include <libspi/spi-private.h>
34 /* Our parent Gtk object type */
35 #define PARENT_TYPE SPI_TYPE_BASE
45 _spi_text_rect_union (SpiTextRect *aggregate, SpiTextRect *subrect)
49 /* 'normalize' subrect */
52 subrect->x += subrect->w;
57 subrect->y += subrect->h;
60 if (aggregate == NULL)
62 aggregate = g_new (SpiTextRect, 1);
63 memcpy (aggregate, subrect, sizeof (SpiTextRect));
67 gint ax2 = aggregate->x + aggregate->w;
68 gint ay2 = aggregate->y + aggregate->h;
69 gint sx2 = subrect->x + subrect->w;
70 gint sy2 = subrect->y + subrect->h;
71 if (subrect->x < aggregate->x)
73 aggregate->w += (aggregate->x - subrect->x);
74 aggregate->x = subrect->x;
78 aggregate->w += (sx2 - ax2);
80 if (subrect->y < aggregate->y)
82 aggregate->h += (aggregate->y - subrect->y);
83 aggregate->y = subrect->y;
87 aggregate->h += (sy2 - ay2);
95 get_text_from_servant (PortableServer_Servant servant)
97 SpiBase *object = SPI_BASE (bonobo_object_from_servant (servant));
99 g_return_val_if_fail (object, NULL);
100 g_return_val_if_fail (ATK_IS_OBJECT(object->gobj), NULL);
101 return ATK_TEXT (object->gobj);
105 impl_getText (PortableServer_Servant servant,
106 const CORBA_long startOffset,
107 const CORBA_long endOffset,
108 CORBA_Environment *ev)
112 AtkText *text = get_text_from_servant (servant);
114 g_return_val_if_fail (text != NULL, CORBA_string_dup (""));
116 txt = atk_text_get_text (text, startOffset, endOffset);
119 rv = CORBA_string_dup (txt);
123 rv = CORBA_string_dup ("");
130 impl_getTextAfterOffset (PortableServer_Servant servant,
131 const CORBA_long offset,
133 Accessibility_TEXT_BOUNDARY_TYPE
134 type, CORBA_long * startOffset,
135 CORBA_long * endOffset,
136 CORBA_Environment *ev)
140 gint intStartOffset, intEndOffset;
141 AtkText *text = get_text_from_servant (servant);
143 g_return_val_if_fail (text != NULL, CORBA_string_dup (""));
145 txt = atk_text_get_text_after_offset (text,
146 offset, (AtkTextBoundary) type,
147 &intStartOffset, &intEndOffset);
148 *startOffset = intStartOffset;
149 *endOffset = intEndOffset;
153 rv = CORBA_string_dup (txt);
157 rv = CORBA_string_dup ("");
164 impl_getTextAtOffset (PortableServer_Servant servant,
165 const CORBA_long offset,
166 const Accessibility_TEXT_BOUNDARY_TYPE type,
167 CORBA_long * startOffset,
168 CORBA_long * endOffset,
169 CORBA_Environment *ev)
173 gint intStartOffset, intEndOffset;
174 AtkText *text = get_text_from_servant (servant);
176 g_return_val_if_fail (text != NULL, CORBA_string_dup (""));
178 txt = atk_text_get_text_at_offset (
180 offset, (AtkTextBoundary) type,
181 &intStartOffset, &intEndOffset);
183 *startOffset = intStartOffset;
184 *endOffset = intEndOffset;
188 rv = CORBA_string_dup (txt);
192 rv = CORBA_string_dup ("");
198 static CORBA_unsigned_long
199 impl_getCharacterAtOffset (PortableServer_Servant servant,
200 const CORBA_long offset,
201 CORBA_Environment *ev)
203 AtkText *text = get_text_from_servant (servant);
205 g_return_val_if_fail (text != NULL, 0);
207 return atk_text_get_character_at_offset (text, offset);
212 impl_getTextBeforeOffset (PortableServer_Servant servant,
213 const CORBA_long offset,
215 Accessibility_TEXT_BOUNDARY_TYPE
216 type, CORBA_long * startOffset,
217 CORBA_long * endOffset,
218 CORBA_Environment *ev)
222 gint intStartOffset, intEndOffset;
223 AtkText *text = get_text_from_servant (servant);
225 g_return_val_if_fail (text != NULL, CORBA_string_dup (""));
227 txt = atk_text_get_text_before_offset (text,
228 offset, (AtkTextBoundary) type,
229 &intStartOffset, &intEndOffset);
231 *startOffset = intStartOffset;
232 *endOffset = intEndOffset;
236 rv = CORBA_string_dup (txt);
240 rv = CORBA_string_dup ("");
247 impl__get_caretOffset (PortableServer_Servant servant,
248 CORBA_Environment *ev)
250 AtkText *text = get_text_from_servant (servant);
252 g_return_val_if_fail (text != NULL, -1);
254 return atk_text_get_caret_offset (text);
259 _string_from_attribute_set (AtkAttributeSet *set)
261 gchar *attributes, *tmp, *tmp2;
266 attributes = g_strdup ("");
267 cur_attr = (GSList *) set;
270 at = (AtkAttribute *) cur_attr->data;
271 tmp = g_strdup_printf ("%s%s:%s%s",
272 ((GSList *)(set) == cur_attr) ? "" : " ",
274 (cur_attr->next) ? ";" : "");
275 tmp2 = g_strconcat (attributes, tmp, NULL);
279 cur_attr = cur_attr->next;
281 rv = CORBA_string_dup (attributes);
289 impl_getAttributes (PortableServer_Servant servant,
290 const CORBA_long offset,
291 CORBA_long * startOffset,
292 CORBA_long * endOffset,
293 CORBA_Environment *ev)
295 AtkAttributeSet *set;
296 gint intstart_offset, intend_offset;
298 AtkText *text = get_text_from_servant (servant);
300 g_return_val_if_fail (text != NULL, CORBA_string_dup (""));
302 set = atk_text_get_run_attributes (text, offset,
303 &intstart_offset, &intend_offset);
304 *startOffset = intstart_offset;
305 *endOffset = intend_offset;
306 rv = _string_from_attribute_set (set);
307 atk_attribute_set_free (set);
312 impl_getAttributeValue (PortableServer_Servant servant,
313 const CORBA_long offset,
314 const CORBA_char *attributename,
315 CORBA_long * startOffset,
316 CORBA_long * endOffset,
317 CORBA_boolean * defined,
318 CORBA_Environment *ev)
320 AtkAttributeSet *set;
321 gint intstart_offset, intend_offset;
323 CORBA_string rv = NULL;
324 AtkText *text = get_text_from_servant (servant);
327 g_return_val_if_fail (text != NULL, CORBA_string_dup (""));
329 set = atk_text_get_run_attributes (text, offset,
330 &intstart_offset, &intend_offset);
331 *startOffset = intstart_offset;
332 *endOffset = intend_offset;
334 cur_attr = (GSList *) set;
337 at = (AtkAttribute *) cur_attr->data;
338 if (!strcmp (at->name, attributename))
340 rv = CORBA_string_dup (at->value);
344 cur_attr = cur_attr->next;
346 atk_attribute_set_free (set);
347 return (rv ? rv : CORBA_string_dup (""));
351 impl_getDefaultAttributes (PortableServer_Servant servant,
352 CORBA_Environment *ev)
354 AtkAttributeSet *set;
356 AtkText *text = get_text_from_servant (servant);
358 g_return_val_if_fail (text != NULL, CORBA_string_dup (""));
360 set = atk_text_get_default_attributes (text);
362 rv = _string_from_attribute_set (set);
363 atk_attribute_set_free (set);
368 impl_getCharacterExtents (PortableServer_Servant servant,
369 const CORBA_long offset, CORBA_long * x,
370 CORBA_long * y, CORBA_long * width,
372 const CORBA_short coordType,
373 CORBA_Environment *ev)
375 AtkText *text = get_text_from_servant (servant);
378 g_return_if_fail (text != NULL);
380 atk_text_get_character_extents (
383 (AtkCoordType) coordType);
392 impl__get_characterCount (PortableServer_Servant servant,
393 CORBA_Environment *ev)
395 AtkText *text = get_text_from_servant (servant);
397 g_return_val_if_fail (text != NULL, 0);
399 return atk_text_get_character_count (text);
404 impl_getOffsetAtPoint (PortableServer_Servant servant,
405 const CORBA_long x, const CORBA_long y,
406 const CORBA_short coordType,
407 CORBA_Environment *ev)
409 AtkText *text = get_text_from_servant (servant);
411 g_return_val_if_fail (text != NULL, -1);
413 return atk_text_get_offset_at_point (text,
415 (AtkCoordType) coordType);
420 impl_getNSelections (PortableServer_Servant servant,
421 CORBA_Environment *ev)
423 AtkText *text = get_text_from_servant (servant);
425 g_return_val_if_fail (text != NULL, 0);
427 return atk_text_get_n_selections (text);
432 impl_getSelection (PortableServer_Servant servant,
433 const CORBA_long selectionNum,
434 CORBA_long * startOffset, CORBA_long * endOffset,
435 CORBA_Environment *ev)
437 AtkText *text = get_text_from_servant (servant);
438 gint intStartOffset, intEndOffset;
440 g_return_if_fail (text != NULL);
442 /* atk_text_get_selection returns gchar* which we discard */
443 g_free (atk_text_get_selection (text, selectionNum,
444 &intStartOffset, &intEndOffset));
446 *startOffset = intStartOffset;
447 *endOffset = intEndOffset;
452 impl_addSelection (PortableServer_Servant servant,
453 const CORBA_long startOffset,
454 const CORBA_long endOffset,
455 CORBA_Environment *ev)
457 AtkText *text = get_text_from_servant (servant);
459 g_return_val_if_fail (text != NULL, FALSE);
461 return atk_text_add_selection (text,
462 startOffset, endOffset);
467 impl_removeSelection (PortableServer_Servant servant,
468 const CORBA_long selectionNum,
469 CORBA_Environment *ev)
471 AtkText *text = get_text_from_servant (servant);
473 g_return_val_if_fail (text != NULL, FALSE);
475 return atk_text_remove_selection (text, selectionNum);
480 impl_setSelection (PortableServer_Servant servant,
481 const CORBA_long selectionNum,
482 const CORBA_long startOffset,
483 const CORBA_long endOffset,
484 CORBA_Environment *ev)
486 AtkText *text = get_text_from_servant (servant);
488 g_return_val_if_fail (text != NULL, FALSE);
490 return atk_text_set_selection (text,
491 selectionNum, startOffset, endOffset);
496 impl_setCaretOffset (PortableServer_Servant servant,
497 const CORBA_long value,
498 CORBA_Environment *ev)
500 AtkText *text = get_text_from_servant (servant);
502 g_return_val_if_fail (text != NULL, FALSE);
504 return atk_text_set_caret_offset (text, value);
507 #define SPI_TEXT_MIN_RANGE_FOR_LINE_CHECK 6
510 impl_getRangeExtents(PortableServer_Servant servant,
511 const CORBA_long startOffset,
512 const CORBA_long endOffset,
513 CORBA_long * x, CORBA_long * y,
516 const CORBA_short coordType,
517 CORBA_Environment * ev)
519 AtkText *text = get_text_from_servant (servant);
520 SpiTextRect cbounds, bounds;
523 g_return_if_fail (text != NULL);
525 atk_text_get_character_extents (text, startOffset,
526 &bounds.x, &bounds.y, &bounds.w, &bounds.h,
527 (AtkCoordType) coordType);
528 /* no equivalent ATK API yet, must do the hard way. :-( */
529 for (i = startOffset + 1; i < endOffset; i++)
531 atk_text_get_character_extents (text, i,
532 &cbounds.x, &cbounds.y, &cbounds.w, &cbounds.h,
533 (AtkCoordType) coordType);
534 _spi_text_rect_union (&bounds, &cbounds);
543 static Accessibility_Text_RangeList *
544 _spi_text_range_seq_from_gslist (GSList *range_list)
546 Accessibility_Text_RangeList *rangeList =
547 Accessibility_Text_RangeList__alloc ();
548 int i, len = g_slist_length (range_list);
549 GSList *list = range_list;
551 rangeList->_length = len;
552 rangeList->_buffer = Accessibility_Text_RangeList_allocbuf (len);
553 for (i = 0; i < len; ++i)
555 memcpy (&rangeList->_buffer[i], list->data, sizeof (Accessibility_Text_Range));
556 rangeList->_buffer[i].data._type = TC_null;
557 rangeList->_buffer[i].data._value = NULL;
558 rangeList->_buffer[i].data._release = TRUE;
560 list = g_slist_next (range_list);
562 g_slist_free (range_list);
568 _spi_bounds_contain (SpiTextRect *clip, SpiTextRect *cbounds,
569 Accessibility_TEXT_CLIP_TYPE xClipType,
570 Accessibility_TEXT_CLIP_TYPE yClipType)
572 gint clipx2 = clip->x + clip->w;
573 gint clipy2 = clip->y + clip->h;
574 gint charx2 = cbounds->x + cbounds->w;
575 gint chary2 = cbounds->y + cbounds->h;
576 gboolean x_min_ok, y_min_ok, x_max_ok, y_max_ok;
578 x_min_ok = (cbounds->x >= clip->x) ||
579 ((charx2 >= clip->x) &&
580 ((xClipType == Accessibility_TEXT_CLIP_NONE) ||
581 (xClipType == Accessibility_TEXT_CLIP_MAX)));
582 x_max_ok = (charx2 <= clipx2) ||
583 ((cbounds->x <= clipx2) &&
584 ((xClipType == Accessibility_TEXT_CLIP_NONE) ||
585 (xClipType == Accessibility_TEXT_CLIP_MIN)));
586 y_min_ok = (cbounds->y >= clip->y) ||
587 ((chary2 >= clip->y) &&
588 ((yClipType == Accessibility_TEXT_CLIP_NONE) ||
589 (yClipType == Accessibility_TEXT_CLIP_MAX)));
590 y_max_ok = (chary2 <= clipy2) ||
591 ((cbounds->y <= clipy2) &&
592 ((yClipType == Accessibility_TEXT_CLIP_NONE) ||
593 (yClipType == Accessibility_TEXT_CLIP_MIN)));
595 if (x_min_ok && y_min_ok && x_max_ok && y_max_ok)
601 static Accessibility_Text_RangeList *
602 impl_getBoundedRanges(PortableServer_Servant servant,
605 const CORBA_long width,
606 const CORBA_long height,
607 const CORBA_short coordType,
608 const Accessibility_TEXT_CLIP_TYPE xClipType,
609 const Accessibility_TEXT_CLIP_TYPE yClipType,
610 CORBA_Environment * ev)
612 AtkText *text = get_text_from_servant (servant);
613 GSList *range_list = NULL;
615 int startOffset = 0, endOffset = atk_text_get_character_count (text);
617 gint minLineStart, minLineEnd, maxLineStart, maxLineEnd;
618 long bounds_min_offset;
619 long bounds_max_offset;
626 /* for horizontal text layouts, at least, the following check helps. */
627 bounds_min_offset = atk_text_get_offset_at_point (text, x, y,
628 (AtkCoordType) coordType);
629 bounds_max_offset = atk_text_get_offset_at_point (text, x + width, y + height,
630 (AtkCoordType) coordType);
631 atk_text_get_text_at_offset (text, bounds_min_offset,
632 ATK_TEXT_BOUNDARY_LINE_START,
633 &minLineStart, &minLineEnd);
634 atk_text_get_text_at_offset (text, bounds_max_offset,
635 ATK_TEXT_BOUNDARY_LINE_START,
636 &maxLineStart, &maxLineEnd);
637 startOffset = MIN (minLineStart, maxLineStart);
638 endOffset = MAX (minLineEnd, maxLineEnd);
640 curr_offset = startOffset;
642 while (curr_offset < endOffset)
644 int offset = startOffset;
646 while (curr_offset < endOffset)
648 atk_text_get_character_extents (text, curr_offset,
649 &cbounds.x, &cbounds.y,
650 &cbounds.w, &cbounds.h,
651 (AtkCoordType) coordType);
652 if (!_spi_bounds_contain (&clip, &cbounds, xClipType, yClipType))
656 /* add the range to our list */
657 if (curr_offset > offset)
659 Accessibility_Text_Range *range = g_malloc (sizeof (Accessibility_Text_Range));
661 range->startOffset = offset;
662 range->endOffset = curr_offset;
663 s = atk_text_get_text (text, offset, curr_offset);
664 range->content = CORBA_string_dup (s ? s : "");
665 range_list = g_slist_append (range_list, range);
666 offset = curr_offset;
670 return _spi_text_range_seq_from_gslist (range_list); /* frees the GSList too */
675 static Accessibility_AttributeSet *
676 impl_getAttributeRun (PortableServer_Servant servant,
677 const CORBA_long offset,
678 CORBA_long *startOffset, CORBA_long *endOffset,
679 const CORBA_boolean includeDefaults,
680 CORBA_Environment *ev){
682 AtkAttributeSet *attributes, *default_attributes = NULL;
683 gint intstart_offset, intend_offset;
684 Accessibility_AttributeSet *retval = NULL;
685 AtkText *text = get_text_from_servant (servant);
686 gint n_attributes = 0, total_attributes = 0, n_default_attributes = 0;
689 g_return_val_if_fail (text != NULL, NULL);
691 attributes = atk_text_get_run_attributes (text, offset,
692 &intstart_offset, &intend_offset);
694 if (attributes) total_attributes = n_attributes = g_slist_length (attributes);
697 default_attributes = atk_text_get_default_attributes (text);
698 if (default_attributes)
699 n_default_attributes = g_slist_length (default_attributes);
700 total_attributes += n_default_attributes;
703 *startOffset = intstart_offset;
704 *endOffset = intend_offset;
706 if (total_attributes)
708 retval = CORBA_sequence_CORBA_string__alloc ();
709 retval->_length = retval->_maximum = total_attributes;
710 retval->_buffer = CORBA_sequence_CORBA_string_allocbuf (total_attributes);
711 CORBA_sequence_set_release (retval, CORBA_TRUE);
713 for (i = 0; i < n_attributes; ++i)
715 retval->_buffer[i] = CORBA_string_dup (g_slist_nth_data (attributes, i));
718 for (j = 0; j < n_default_attributes; ++i, ++j)
720 retval->_buffer[i] = CORBA_string_dup (g_slist_nth_data (default_attributes, j));
723 atk_attribute_set_free (attributes);
724 if (default_attributes)
725 atk_attribute_set_free (default_attributes);
730 static Accessibility_AttributeSet *
731 impl_getDefaultAttributeSet (PortableServer_Servant servant,
732 CORBA_Environment *ev){
733 AtkAttributeSet *attributes;
734 Accessibility_AttributeSet *retval = NULL;
735 AtkText *text = get_text_from_servant (servant);
736 gint n_attributes = 0;
739 g_return_val_if_fail (text != NULL, NULL);
741 attributes = atk_text_get_default_attributes (text);
745 n_attributes = g_slist_length (attributes);
747 retval = CORBA_sequence_CORBA_string__alloc ();
748 retval->_length = retval->_maximum = n_attributes;
749 retval->_buffer = CORBA_sequence_CORBA_string_allocbuf (n_attributes);
750 CORBA_sequence_set_release (retval, CORBA_TRUE);
752 for (i = 0; i < n_attributes; ++i)
754 retval->_buffer[i] = CORBA_string_dup (g_slist_nth_data (attributes, i));
756 atk_attribute_set_free (attributes);
763 spi_text_class_init (SpiTextClass *klass)
765 POA_Accessibility_Text__epv *epv = &klass->epv;
767 /* Initialize epv table */
769 epv->getText = impl_getText;
770 epv->getTextAfterOffset = impl_getTextAfterOffset;
771 epv->getCharacterAtOffset = impl_getCharacterAtOffset;
772 epv->getTextAtOffset = impl_getTextAtOffset;
773 epv->getTextBeforeOffset = impl_getTextBeforeOffset;
774 epv->_get_caretOffset = impl__get_caretOffset;
775 epv->getAttributes = impl_getAttributes;
776 epv->getDefaultAttributes = impl_getDefaultAttributes;
777 epv->getCharacterExtents = impl_getCharacterExtents;
778 epv->_get_characterCount = impl__get_characterCount;
779 epv->getOffsetAtPoint = impl_getOffsetAtPoint;
780 epv->getNSelections = impl_getNSelections;
781 epv->getSelection = impl_getSelection;
782 epv->addSelection = impl_addSelection;
783 epv->removeSelection = impl_removeSelection;
784 epv->setSelection = impl_setSelection;
785 epv->setCaretOffset = impl_setCaretOffset;
786 epv->getRangeExtents = impl_getRangeExtents;
787 epv->getBoundedRanges = impl_getBoundedRanges;
788 epv->getAttributeValue = impl_getAttributeValue;
789 epv->getAttributeRun = impl_getAttributeRun;
790 epv->getDefaultAttributeSet = impl_getDefaultAttributeSet;
794 spi_text_init (SpiText *text)
798 BONOBO_TYPE_FUNC_FULL (SpiText,
804 spi_text_construct (SpiText *text, AtkObject *obj)
806 spi_base_construct (SPI_BASE (text), G_OBJECT(obj));
811 spi_text_interface_new (AtkObject *obj)
815 g_return_val_if_fail (ATK_IS_TEXT (obj), NULL);
817 retval = g_object_new (SPI_TEXT_TYPE, NULL);
819 spi_text_construct (retval, obj);