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 */
29 #include <atk/atktext.h>
30 #include <libspi/text.h>
32 /* Our parent Gtk object type */
33 #define PARENT_TYPE SPI_TYPE_BASE
43 _spi_text_rect_union (SpiTextRect *aggregate, SpiTextRect *subrect)
47 /* 'normalize' subrect */
50 subrect->x += subrect->w;
55 subrect->y += subrect->h;
58 if (aggregate == NULL)
60 aggregate = g_new (SpiTextRect, 1);
61 memcpy (aggregate, subrect, sizeof (SpiTextRect));
65 gint ax2 = aggregate->x + aggregate->w;
66 gint ay2 = aggregate->y + aggregate->h;
67 gint sx2 = subrect->x + subrect->w;
68 gint sy2 = subrect->y + subrect->h;
69 if (subrect->x < aggregate->x)
71 aggregate->w += (aggregate->x - subrect->x);
72 aggregate->x = subrect->x;
76 aggregate->w += (sx2 - ax2);
78 if (subrect->y < aggregate->y)
80 aggregate->h += (aggregate->y - subrect->y);
81 aggregate->y = subrect->y;
85 aggregate->h += (sy2 - ay2);
93 get_text_from_servant (PortableServer_Servant servant)
95 SpiBase *object = SPI_BASE (bonobo_object_from_servant (servant));
97 g_return_val_if_fail (object, NULL);
98 g_return_val_if_fail (ATK_IS_OBJECT(object->gobj), NULL);
99 return ATK_TEXT (object->gobj);
103 impl_getText (PortableServer_Servant servant,
104 const CORBA_long startOffset,
105 const CORBA_long endOffset,
106 CORBA_Environment *ev)
110 AtkText *text = get_text_from_servant (servant);
112 g_return_val_if_fail (text != NULL, CORBA_string_dup (""));
114 txt = atk_text_get_text (text, startOffset, endOffset);
117 rv = CORBA_string_dup (txt);
121 rv = CORBA_string_dup ("");
128 impl_getTextAfterOffset (PortableServer_Servant servant,
129 const CORBA_long offset,
131 Accessibility_TEXT_BOUNDARY_TYPE
132 type, CORBA_long * startOffset,
133 CORBA_long * endOffset,
134 CORBA_Environment *ev)
138 gint intStartOffset, intEndOffset;
139 AtkText *text = get_text_from_servant (servant);
141 g_return_val_if_fail (text != NULL, CORBA_string_dup (""));
143 txt = atk_text_get_text_after_offset (text,
144 offset, (AtkTextBoundary) type,
145 &intStartOffset, &intEndOffset);
146 *startOffset = intStartOffset;
147 *endOffset = intEndOffset;
151 rv = CORBA_string_dup (txt);
155 rv = CORBA_string_dup ("");
162 impl_getTextAtOffset (PortableServer_Servant servant,
163 const CORBA_long offset,
164 const Accessibility_TEXT_BOUNDARY_TYPE type,
165 CORBA_long * startOffset,
166 CORBA_long * endOffset,
167 CORBA_Environment *ev)
171 gint intStartOffset, intEndOffset;
172 AtkText *text = get_text_from_servant (servant);
174 g_return_val_if_fail (text != NULL, CORBA_string_dup (""));
176 txt = atk_text_get_text_at_offset (
178 offset, (AtkTextBoundary) type,
179 &intStartOffset, &intEndOffset);
181 *startOffset = intStartOffset;
182 *endOffset = intEndOffset;
186 rv = CORBA_string_dup (txt);
190 rv = CORBA_string_dup ("");
196 static CORBA_unsigned_long
197 impl_getCharacterAtOffset (PortableServer_Servant servant,
198 const CORBA_long offset,
199 CORBA_Environment *ev)
201 AtkText *text = get_text_from_servant (servant);
203 g_return_val_if_fail (text != NULL, 0);
205 return atk_text_get_character_at_offset (text, offset);
210 impl_getTextBeforeOffset (PortableServer_Servant servant,
211 const CORBA_long offset,
213 Accessibility_TEXT_BOUNDARY_TYPE
214 type, CORBA_long * startOffset,
215 CORBA_long * endOffset,
216 CORBA_Environment *ev)
220 gint intStartOffset, intEndOffset;
221 AtkText *text = get_text_from_servant (servant);
223 g_return_val_if_fail (text != NULL, CORBA_string_dup (""));
225 txt = atk_text_get_text_before_offset (text,
226 offset, (AtkTextBoundary) type,
227 &intStartOffset, &intEndOffset);
229 *startOffset = intStartOffset;
230 *endOffset = intEndOffset;
234 rv = CORBA_string_dup (txt);
238 rv = CORBA_string_dup ("");
245 impl__get_caretOffset (PortableServer_Servant servant,
246 CORBA_Environment *ev)
248 AtkText *text = get_text_from_servant (servant);
250 g_return_val_if_fail (text != NULL, -1);
252 return atk_text_get_caret_offset (text);
257 _string_from_attribute_set (AtkAttributeSet *set)
259 gchar *attributes, *tmp, *tmp2;
264 attributes = g_strdup ("");
265 cur_attr = (GSList *) set;
268 at = (AtkAttribute *) cur_attr->data;
269 tmp = g_strdup_printf ("%s%s:%s%s",
270 ((GSList *)(set) == cur_attr) ? "" : " ",
272 (cur_attr->next) ? ";" : "");
273 tmp2 = g_strconcat (attributes, tmp, NULL);
277 cur_attr = cur_attr->next;
279 rv = CORBA_string_dup (attributes);
287 impl_getAttributes (PortableServer_Servant servant,
288 const CORBA_long offset,
289 CORBA_long * startOffset,
290 CORBA_long * endOffset,
291 CORBA_Environment *ev)
293 AtkAttributeSet *set;
294 gint intstart_offset, intend_offset;
296 AtkText *text = get_text_from_servant (servant);
298 g_return_val_if_fail (text != NULL, CORBA_string_dup (""));
300 set = atk_text_get_run_attributes (text, offset,
301 &intstart_offset, &intend_offset);
302 *startOffset = intstart_offset;
303 *endOffset = intend_offset;
304 rv = _string_from_attribute_set (set);
305 atk_attribute_set_free (set);
311 impl_getCharacterExtents (PortableServer_Servant servant,
312 const CORBA_long offset, CORBA_long * x,
313 CORBA_long * y, CORBA_long * width,
315 const CORBA_short coordType,
316 CORBA_Environment *ev)
318 AtkText *text = get_text_from_servant (servant);
321 g_return_if_fail (text != NULL);
323 atk_text_get_character_extents (
326 (AtkCoordType) coordType);
335 impl__get_characterCount (PortableServer_Servant servant,
336 CORBA_Environment *ev)
338 AtkText *text = get_text_from_servant (servant);
340 g_return_val_if_fail (text != NULL, 0);
342 return atk_text_get_character_count (text);
347 impl_getOffsetAtPoint (PortableServer_Servant servant,
348 const CORBA_long x, const CORBA_long y,
349 const CORBA_short coordType,
350 CORBA_Environment *ev)
352 AtkText *text = get_text_from_servant (servant);
354 g_return_val_if_fail (text != NULL, -1);
356 return atk_text_get_offset_at_point (text,
358 (AtkCoordType) coordType);
363 impl_getNSelections (PortableServer_Servant servant,
364 CORBA_Environment *ev)
366 AtkText *text = get_text_from_servant (servant);
368 g_return_val_if_fail (text != NULL, 0);
370 return atk_text_get_n_selections (text);
375 impl_getSelection (PortableServer_Servant servant,
376 const CORBA_long selectionNum,
377 CORBA_long * startOffset, CORBA_long * endOffset,
378 CORBA_Environment *ev)
380 AtkText *text = get_text_from_servant (servant);
381 gint intStartOffset, intEndOffset;
383 g_return_if_fail (text != NULL);
385 /* atk_text_get_selection returns gchar* which we discard */
386 g_free (atk_text_get_selection (text, selectionNum,
387 &intStartOffset, &intEndOffset));
389 *startOffset = intStartOffset;
390 *endOffset = intEndOffset;
395 impl_addSelection (PortableServer_Servant servant,
396 const CORBA_long startOffset,
397 const CORBA_long endOffset,
398 CORBA_Environment *ev)
400 AtkText *text = get_text_from_servant (servant);
402 g_return_val_if_fail (text != NULL, FALSE);
404 return atk_text_add_selection (text,
405 startOffset, endOffset);
410 impl_removeSelection (PortableServer_Servant servant,
411 const CORBA_long selectionNum,
412 CORBA_Environment *ev)
414 AtkText *text = get_text_from_servant (servant);
416 g_return_val_if_fail (text != NULL, FALSE);
418 return atk_text_remove_selection (text, selectionNum);
423 impl_setSelection (PortableServer_Servant servant,
424 const CORBA_long selectionNum,
425 const CORBA_long startOffset,
426 const CORBA_long endOffset,
427 CORBA_Environment *ev)
429 AtkText *text = get_text_from_servant (servant);
431 g_return_val_if_fail (text != NULL, FALSE);
433 return atk_text_set_selection (text,
434 selectionNum, startOffset, endOffset);
439 impl_setCaretOffset (PortableServer_Servant servant,
440 const CORBA_long value,
441 CORBA_Environment *ev)
443 AtkText *text = get_text_from_servant (servant);
445 g_return_val_if_fail (text != NULL, FALSE);
447 return atk_text_set_caret_offset (text, value);
450 #define SPI_TEXT_MIN_RANGE_FOR_LINE_CHECK 6
453 impl_getRangeExtents(PortableServer_Servant servant,
454 const CORBA_long startOffset,
455 const CORBA_long endOffset,
456 CORBA_long * x, CORBA_long * y,
459 const CORBA_short coordType,
460 CORBA_Environment * ev)
462 AtkText *text = get_text_from_servant (servant);
463 SpiTextRect cbounds, bounds;
466 g_return_if_fail (text != NULL);
468 /* no equivalent ATK API yet, must do the hard way. :-( */
469 for (i = startOffset; i > endOffset; i++)
471 atk_text_get_character_extents (text, i,
472 &cbounds.x, &cbounds.y, &cbounds.w, &cbounds.h,
473 (AtkCoordType) coordType);
474 _spi_text_rect_union (&bounds, &cbounds);
483 static Accessibility_Text_RangeList *
484 _spi_text_range_seq_from_gslist (GSList *range_list)
486 Accessibility_Text_RangeList *rangeList =
487 Accessibility_Text_RangeList__alloc ();
488 int i, len = g_slist_length (range_list);
489 GSList *list = range_list;
491 rangeList->_length = len;
492 rangeList->_buffer = Accessibility_Text_RangeList_allocbuf (len);
493 for (i = 0; i < len; ++i)
495 memcpy (&rangeList->_buffer[i], list->data, sizeof (Accessibility_Text_Range));
496 spi_init_any_nil (&rangeList->_buffer[i].data);
498 list = g_slist_next (range_list);
500 g_slist_free (range_list);
506 _spi_bounds_contain (SpiTextRect *clip, SpiTextRect *cbounds,
507 Accessibility_TEXT_CLIP_TYPE xClipType,
508 Accessibility_TEXT_CLIP_TYPE yClipType)
510 gint clipx2 = clip->x + clip->w;
511 gint clipy2 = clip->y + clip->h;
512 gint charx2 = cbounds->x + cbounds->w;
513 gint chary2 = cbounds->y + cbounds->h;
514 gboolean x_min_ok, y_min_ok, x_max_ok, y_max_ok;
516 x_min_ok = (cbounds->x >= clip->x) ||
517 ((charx2 >= clip->x) &&
518 ((xClipType == Accessibility_TEXT_CLIP_NONE) ||
519 (xClipType == Accessibility_TEXT_CLIP_MAX)));
520 x_max_ok = (charx2 <= clipx2) ||
521 ((cbounds->x <= clipx2) &&
522 ((xClipType == Accessibility_TEXT_CLIP_NONE) ||
523 (xClipType == Accessibility_TEXT_CLIP_MIN)));
524 y_min_ok = (cbounds->y >= clip->y) ||
525 ((chary2 >= clip->y) &&
526 ((yClipType == Accessibility_TEXT_CLIP_NONE) ||
527 (yClipType == Accessibility_TEXT_CLIP_MAX)));
528 y_max_ok = (chary2 <= clipy2) ||
529 ((cbounds->y <= clipy2) &&
530 ((yClipType == Accessibility_TEXT_CLIP_NONE) ||
531 (yClipType == Accessibility_TEXT_CLIP_MIN)));
533 if (x_min_ok && y_min_ok && x_max_ok && y_max_ok)
539 Accessibility_Text_RangeList *
540 impl_getBoundedRanges(PortableServer_Servant servant,
543 const CORBA_long width,
544 const CORBA_long height,
545 const CORBA_short coordType,
546 const Accessibility_TEXT_CLIP_TYPE xClipType,
547 const Accessibility_TEXT_CLIP_TYPE yClipType,
548 CORBA_Environment * ev)
550 AtkText *text = get_text_from_servant (servant);
551 GSList *range_list = NULL;
553 int startOffset = 0, endOffset = atk_text_get_character_count (text);
555 gint minLineStart, minLineEnd, maxLineStart, maxLineEnd;
556 long bounds_min_offset;
563 /* for horizontal text layouts, at least, the following check helps. */
564 bounds_min_offset = atk_text_get_offset_at_point (text, x, y,
565 (AtkCoordType) coordType);
566 atk_text_get_text_at_offset (text, bounds_min_offset,
567 ATK_TEXT_BOUNDARY_LINE_START,
568 &minLineStart, &minLineEnd);
569 atk_text_get_text_at_offset (text, bounds_min_offset,
570 ATK_TEXT_BOUNDARY_LINE_START,
571 &maxLineStart, &maxLineEnd);
572 startOffset = MIN (minLineStart, maxLineStart);
573 endOffset = MIN (minLineEnd, maxLineEnd);
575 curr_offset = startOffset;
577 while (curr_offset < endOffset)
579 int offset = startOffset;
581 while (curr_offset < endOffset)
583 atk_text_get_character_extents (text, curr_offset,
584 &cbounds.x, &cbounds.y,
585 &cbounds.w, &cbounds.h,
586 (AtkCoordType) coordType);
587 if (!_spi_bounds_contain (&clip, &cbounds, xClipType, yClipType))
591 /* add the range to our list */
592 if (curr_offset > offset)
594 Accessibility_Text_Range *range = g_malloc (sizeof (Accessibility_Text_Range));
596 range->startOffset = offset;
597 range->endOffset = curr_offset;
598 s = atk_text_get_text (text, offset, curr_offset);
599 range->content = CORBA_string_dup (s ? s : "");
600 range_list = g_slist_append (range_list, range);
601 offset = curr_offset;
605 return _spi_text_range_seq_from_gslist (range_list); /* frees the GSList too */
610 spi_text_class_init (SpiTextClass *klass)
612 POA_Accessibility_Text__epv *epv = &klass->epv;
614 /* Initialize epv table */
616 epv->getText = impl_getText;
617 epv->getTextAfterOffset = impl_getTextAfterOffset;
618 epv->getCharacterAtOffset = impl_getCharacterAtOffset;
619 epv->getTextAtOffset = impl_getTextAtOffset;
620 epv->getTextBeforeOffset = impl_getTextBeforeOffset;
621 epv->_get_caretOffset = impl__get_caretOffset;
622 epv->getAttributes = impl_getAttributes;
623 epv->getCharacterExtents = impl_getCharacterExtents;
624 epv->_get_characterCount = impl__get_characterCount;
625 epv->getOffsetAtPoint = impl_getOffsetAtPoint;
626 epv->getNSelections = impl_getNSelections;
627 epv->getSelection = impl_getSelection;
628 epv->addSelection = impl_addSelection;
629 epv->removeSelection = impl_removeSelection;
630 epv->setSelection = impl_setSelection;
631 epv->setCaretOffset = impl_setCaretOffset;
632 epv->getRangeExtents = impl_getRangeExtents;
633 epv->getBoundedRanges = impl_getBoundedRanges;
637 spi_text_init (SpiText *text)
641 BONOBO_TYPE_FUNC_FULL (SpiText,
647 spi_text_construct (SpiText *text, AtkObject *obj)
649 spi_base_construct (SPI_BASE (text), G_OBJECT(obj));
654 spi_text_interface_new (AtkObject *obj)
658 g_return_val_if_fail (ATK_IS_TEXT (obj), NULL);
660 retval = g_object_new (SPI_TEXT_TYPE, NULL);
662 spi_text_construct (retval, obj);