xref: /aoo42x/main/vcl/unx/gtk/a11y/atktext.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_vcl.hxx"
30 
31 #include "atkwrapper.hxx"
32 #include "atktextattributes.hxx"
33 #include <algorithm>
34 
35 #include <com/sun/star/accessibility/AccessibleTextType.hpp>
36 #include <com/sun/star/accessibility/TextSegment.hpp>
37 #include <com/sun/star/accessibility/XAccessibleMultiLineText.hpp>
38 #include <com/sun/star/accessibility/XAccessibleText.hpp>
39 #include <com/sun/star/accessibility/XAccessibleTextAttributes.hpp>
40 #include <com/sun/star/accessibility/XAccessibleTextMarkup.hpp>
41 #include <com/sun/star/text/TextMarkupType.hpp>
42 
43 // #define ENABLE_TRACING
44 
45 #ifdef ENABLE_TRACING
46 #include <stdio.h>
47 #endif
48 
49 using namespace ::com::sun::star;
50 
51 static sal_Int16
52 text_type_from_boundary(AtkTextBoundary boundary_type)
53 {
54     switch(boundary_type)
55     {
56         case ATK_TEXT_BOUNDARY_CHAR:
57             return accessibility::AccessibleTextType::CHARACTER;
58         case ATK_TEXT_BOUNDARY_WORD_START:
59         case ATK_TEXT_BOUNDARY_WORD_END:
60             return accessibility::AccessibleTextType::WORD;
61         case ATK_TEXT_BOUNDARY_SENTENCE_START:
62         case ATK_TEXT_BOUNDARY_SENTENCE_END:
63             return accessibility::AccessibleTextType::SENTENCE;
64         case ATK_TEXT_BOUNDARY_LINE_START:
65         case ATK_TEXT_BOUNDARY_LINE_END:
66             return accessibility::AccessibleTextType::LINE;
67         default:
68             return -1;
69     }
70 }
71 
72 /*****************************************************************************/
73 
74 static gchar *
75 adjust_boundaries( accessibility::XAccessibleText* pText,
76                    accessibility::TextSegment& rTextSegment,
77                    AtkTextBoundary  boundary_type,
78                    gint * start_offset, gint * end_offset )
79 {
80     accessibility::TextSegment aTextSegment;
81     rtl::OUString aString;
82     gint start = 0, end = 0;
83 
84     if( rTextSegment.SegmentText.getLength() > 0 )
85     {
86         switch(boundary_type)
87         {
88         case ATK_TEXT_BOUNDARY_CHAR:
89         case ATK_TEXT_BOUNDARY_LINE_START:
90         case ATK_TEXT_BOUNDARY_LINE_END:
91         case ATK_TEXT_BOUNDARY_SENTENCE_START:
92             start = rTextSegment.SegmentStart;
93             end = rTextSegment.SegmentEnd;
94             aString = rTextSegment.SegmentText;
95             break;
96 
97         // the OOo break iterator behaves as SENTENCE_START
98         case ATK_TEXT_BOUNDARY_SENTENCE_END:
99             start = rTextSegment.SegmentStart;
100             end = rTextSegment.SegmentEnd;
101 
102             if( start > 0 )
103                 --start;
104             if( end > 0 && end < pText->getCharacterCount() - 1 )
105                 --end;
106 
107             aString = pText->getTextRange(start, end);
108             break;
109 
110         case ATK_TEXT_BOUNDARY_WORD_START:
111             start = rTextSegment.SegmentStart;
112 
113             // Determine the start index of the next segment
114             aTextSegment = pText->getTextBehindIndex(rTextSegment.SegmentEnd,
115                                                      text_type_from_boundary(boundary_type));
116             if( aTextSegment.SegmentText.getLength() > 0 )
117                 end = aTextSegment.SegmentStart;
118             else
119                 end = pText->getCharacterCount();
120 
121             aString = pText->getTextRange(start, end);
122             break;
123 
124         case ATK_TEXT_BOUNDARY_WORD_END:
125             end = rTextSegment.SegmentEnd;
126 
127             // Determine the end index of the previous segment
128             aTextSegment = pText->getTextBeforeIndex(rTextSegment.SegmentStart,
129                                                      text_type_from_boundary(boundary_type));
130             if( aTextSegment.SegmentText.getLength() > 0 )
131                 start = aTextSegment.SegmentEnd;
132             else
133                 start = 0;
134 
135             aString = pText->getTextRange(start, end);
136             break;
137 
138         default:
139             return NULL;
140         }
141     }
142 
143     *start_offset = start;
144     *end_offset   = end;
145 
146 #ifdef ENABLE_TRACING
147     fprintf(stderr, "adjust_boundaries( %d, %d, %d ) returns %d, %d\n",
148         rTextSegment.SegmentStart, rTextSegment.SegmentEnd, boundary_type,
149         start, end);
150 #endif
151 
152     return OUStringToGChar(aString);
153 }
154 
155 /*****************************************************************************/
156 
157 static accessibility::XAccessibleText*
158     getText( AtkText *pText ) throw (uno::RuntimeException)
159 {
160     AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pText );
161     if( pWrap )
162     {
163         if( !pWrap->mpText && pWrap->mpContext )
164         {
165             uno::Any any = pWrap->mpContext->queryInterface( accessibility::XAccessibleText::static_type(NULL) );
166             pWrap->mpText = reinterpret_cast< accessibility::XAccessibleText * > (any.pReserved);
167             pWrap->mpText->acquire();
168         }
169 
170         return pWrap->mpText;
171     }
172 
173     return NULL;
174 }
175 
176 /*****************************************************************************/
177 
178 static accessibility::XAccessibleTextMarkup*
179     getTextMarkup( AtkText *pText ) throw (uno::RuntimeException)
180 {
181     AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pText );
182     if( pWrap )
183     {
184         if( !pWrap->mpTextMarkup && pWrap->mpContext )
185         {
186             uno::Any any = pWrap->mpContext->queryInterface( accessibility::XAccessibleTextMarkup::static_type(NULL) );
187             /* Since this not a dedicated interface in Atk and thus has not
188              * been queried during wrapper initialization, we need to check
189              * the return value here.
190              */
191             if( typelib_TypeClass_INTERFACE == any.pType->eTypeClass )
192             {
193 				pWrap->mpTextMarkup = reinterpret_cast< accessibility::XAccessibleTextMarkup * > (any.pReserved);
194 				if( pWrap->mpTextMarkup )
195 					pWrap->mpTextMarkup->acquire();
196 			}
197         }
198 
199         return pWrap->mpTextMarkup;
200     }
201 
202     return NULL;
203 }
204 
205 /*****************************************************************************/
206 
207 static accessibility::XAccessibleTextAttributes*
208     getTextAttributes( AtkText *pText ) throw (uno::RuntimeException)
209 {
210     AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pText );
211     if( pWrap )
212     {
213         if( !pWrap->mpTextAttributes && pWrap->mpContext )
214         {
215             uno::Any any = pWrap->mpContext->queryInterface( accessibility::XAccessibleTextAttributes::static_type(NULL) );
216             /* Since this not a dedicated interface in Atk and thus has not
217              * been queried during wrapper initialization, we need to check
218              * the return value here.
219              */
220             if( typelib_TypeClass_INTERFACE == any.pType->eTypeClass )
221             {
222                 pWrap->mpTextAttributes = reinterpret_cast< accessibility::XAccessibleTextAttributes * > (any.pReserved);
223                 pWrap->mpTextAttributes->acquire();
224             }
225         }
226 
227         return pWrap->mpTextAttributes;
228     }
229 
230     return NULL;
231 }
232 
233 /*****************************************************************************/
234 
235 static accessibility::XAccessibleMultiLineText*
236     getMultiLineText( AtkText *pText ) throw (uno::RuntimeException)
237 {
238     AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pText );
239     if( pWrap )
240     {
241         if( !pWrap->mpMultiLineText && pWrap->mpContext )
242         {
243             uno::Any any = pWrap->mpContext->queryInterface( accessibility::XAccessibleMultiLineText::static_type(NULL) );
244             /* Since this not a dedicated interface in Atk and thus has not
245              * been queried during wrapper initialization, we need to check
246              * the return value here.
247              */
248             if( typelib_TypeClass_INTERFACE == any.pType->eTypeClass )
249             {
250                 pWrap->mpMultiLineText = reinterpret_cast< accessibility::XAccessibleMultiLineText * > (any.pReserved);
251                 pWrap->mpMultiLineText->acquire();
252             }
253         }
254 
255         return pWrap->mpMultiLineText;
256     }
257 
258     return NULL;
259 }
260 
261 /*****************************************************************************/
262 
263 extern "C" {
264 
265 static gchar *
266 text_wrapper_get_text (AtkText *text,
267                        gint     start_offset,
268                        gint     end_offset)
269 {
270     gchar * ret = NULL;
271 
272     g_return_val_if_fail( (end_offset == -1) || (end_offset >= start_offset), NULL );
273 
274     /* at-spi expects the delete event to be send before the deletion happened
275      * so we save the deleted string object in the UNO event notification and
276      * fool libatk-bridge.so here ..
277      */
278     void * pData = g_object_get_data( G_OBJECT(text), "ooo::text_changed::delete" );
279     if( pData != NULL )
280     {
281         accessibility::TextSegment * pTextSegment =
282             reinterpret_cast <accessibility::TextSegment *> (pData);
283 
284         if( pTextSegment->SegmentStart == start_offset &&
285             pTextSegment->SegmentEnd == end_offset )
286         {
287             rtl::OString aUtf8 = rtl::OUStringToOString( pTextSegment->SegmentText, RTL_TEXTENCODING_UTF8 );
288             return g_strdup( aUtf8.getStr() );
289         }
290     }
291 
292     try {
293         accessibility::XAccessibleText* pText = getText( text );
294         if( pText )
295         {
296             rtl::OUString aText;
297             sal_Int32 n = pText->getCharacterCount();
298 
299             if( -1 == end_offset )
300                 aText = pText->getText();
301             else if( start_offset < n )
302                 aText = pText->getTextRange(start_offset, end_offset);
303 
304             ret = g_strdup( rtl::OUStringToOString(aText, RTL_TEXTENCODING_UTF8 ).getStr() );
305         }
306     }
307     catch(const uno::Exception& e) {
308         g_warning( "Exception in getText()" );
309     }
310 
311 #ifdef ENABLE_TRACING
312     fprintf(stderr, "text_wrapper_get_text( %d,%d ) returns %s\n", start_offset, end_offset, ret ? ret : "null" );
313 #endif
314     return ret;
315 }
316 
317 static gchar *
318 text_wrapper_get_text_after_offset (AtkText          *text,
319                                     gint             offset,
320                                     AtkTextBoundary  boundary_type,
321                                     gint             *start_offset,
322                                     gint             *end_offset)
323 {
324     try {
325         accessibility::XAccessibleText* pText = getText( text );
326         if( pText )
327         {
328             accessibility::TextSegment aTextSegment = pText->getTextBehindIndex(offset, text_type_from_boundary(boundary_type));
329             return adjust_boundaries(pText, aTextSegment, boundary_type, start_offset, end_offset);
330         }
331     }
332     catch(const uno::Exception& e) {
333         g_warning( "Exception in get_text_after_offset()" );
334     }
335 
336     return NULL;
337 }
338 
339 static gchar *
340 text_wrapper_get_text_at_offset (AtkText          *text,
341                                  gint             offset,
342                                  AtkTextBoundary  boundary_type,
343                                  gint             *start_offset,
344                                  gint             *end_offset)
345 {
346     try {
347         accessibility::XAccessibleText* pText = getText( text );
348         if( pText )
349         {
350             /* If the user presses the 'End' key, the caret will be placed behind the last character,
351              * which is the same index as the first character of the next line. In atk the magic offset
352              * '-2' is used to cover this special case.
353              */
354             if (
355                  -2 == offset &&
356                      (ATK_TEXT_BOUNDARY_LINE_START == boundary_type ||
357                       ATK_TEXT_BOUNDARY_LINE_END == boundary_type)
358                )
359             {
360                 accessibility::XAccessibleMultiLineText* pMultiLineText = getMultiLineText( text );
361                 if( pMultiLineText )
362                 {
363                     accessibility::TextSegment aTextSegment = pMultiLineText->getTextAtLineWithCaret();
364                     return adjust_boundaries(pText, aTextSegment, boundary_type, start_offset, end_offset);
365                 }
366             }
367 
368             accessibility::TextSegment aTextSegment = pText->getTextAtIndex(offset, text_type_from_boundary(boundary_type));
369             return adjust_boundaries(pText, aTextSegment, boundary_type, start_offset, end_offset);
370         }
371     }
372     catch(const uno::Exception& e) {
373         g_warning( "Exception in get_text_at_offset()" );
374     }
375 
376     return NULL;
377 }
378 
379 static gunichar
380 text_wrapper_get_character_at_offset (AtkText          *text,
381                                       gint             offset)
382 {
383     gint start, end;
384     gunichar uc = 0;
385 
386     gchar * char_as_string =
387         text_wrapper_get_text_at_offset(text, offset, ATK_TEXT_BOUNDARY_CHAR,
388                                         &start, &end);
389     if( char_as_string )
390     {
391         uc = g_utf8_get_char( char_as_string );
392         g_free( char_as_string );
393     }
394 
395     return uc;
396 }
397 
398 static gchar *
399 text_wrapper_get_text_before_offset (AtkText          *text,
400                                      gint             offset,
401                                      AtkTextBoundary  boundary_type,
402                                      gint             *start_offset,
403                                      gint             *end_offset)
404 {
405     try {
406         accessibility::XAccessibleText* pText = getText( text );
407         if( pText )
408         {
409             accessibility::TextSegment aTextSegment = pText->getTextBeforeIndex(offset, text_type_from_boundary(boundary_type));
410             return adjust_boundaries(pText, aTextSegment, boundary_type, start_offset, end_offset);
411         }
412     }
413     catch(const uno::Exception& e) {
414         g_warning( "Exception in text_before_offset()" );
415     }
416 
417     return NULL;
418 }
419 
420 static gint
421 text_wrapper_get_caret_offset (AtkText          *text)
422 {
423     gint offset = -1;
424 
425     try {
426         accessibility::XAccessibleText* pText = getText( text );
427         if( pText )
428             offset = pText->getCaretPosition();
429     }
430     catch(const uno::Exception& e) {
431         g_warning( "Exception in getCaretPosition()" );
432     }
433 
434 #ifdef ENABLE_TRACING
435     fprintf(stderr, "get_caret_offset(%p) returns %d\n", text, offset);
436 #endif
437 
438     return offset;
439 }
440 
441 static gboolean
442 text_wrapper_set_caret_offset (AtkText *text,
443                                gint     offset)
444 {
445     try {
446         accessibility::XAccessibleText* pText = getText( text );
447         if( pText )
448             return pText->setCaretPosition( offset );
449     }
450     catch(const uno::Exception& e) {
451         g_warning( "Exception in setCaretPosition()" );
452     }
453 
454     return FALSE;
455 }
456 
457 // --> OD 2010-03-04 #i92232#
458 AtkAttributeSet*
459 handle_text_markup_as_run_attribute( accessibility::XAccessibleTextMarkup* pTextMarkup,
460                                      const gint nTextMarkupType,
461                                      const gint offset,
462                                      AtkAttributeSet* pSet,
463                                      gint *start_offset,
464                                      gint *end_offset )
465 {
466     const gint nTextMarkupCount( pTextMarkup->getTextMarkupCount( nTextMarkupType ) );
467     if ( nTextMarkupCount > 0 )
468     {
469         for ( gint nTextMarkupIndex = 0;
470               nTextMarkupIndex < nTextMarkupCount;
471               ++nTextMarkupIndex )
472         {
473             accessibility::TextSegment aTextSegment =
474                 pTextMarkup->getTextMarkup( nTextMarkupIndex, nTextMarkupType );
475             const gint nStartOffsetTextMarkup = aTextSegment.SegmentStart;
476             const gint nEndOffsetTextMarkup = aTextSegment.SegmentEnd;
477             if ( nStartOffsetTextMarkup <= offset )
478             {
479                 if ( offset < nEndOffsetTextMarkup )
480                 {
481                     // text markup at <offset>
482                     *start_offset = ::std::max( *start_offset,
483                                                 nStartOffsetTextMarkup );
484                     *end_offset = ::std::min( *end_offset,
485                                               nEndOffsetTextMarkup );
486                     switch ( nTextMarkupType )
487                     {
488                         case com::sun::star::text::TextMarkupType::SPELLCHECK:
489                         {
490                             pSet = attribute_set_prepend_misspelled( pSet );
491                         }
492                         break;
493                         case com::sun::star::text::TextMarkupType::TRACK_CHANGE_INSERTION:
494                         {
495                             pSet = attribute_set_prepend_tracked_change_insertion( pSet );
496                         }
497                         break;
498                         case com::sun::star::text::TextMarkupType::TRACK_CHANGE_DELETION:
499                         {
500                             pSet = attribute_set_prepend_tracked_change_deletion( pSet );
501                         }
502                         break;
503                         case com::sun::star::text::TextMarkupType::TRACK_CHANGE_FORMATCHANGE:
504                         {
505                             pSet = attribute_set_prepend_tracked_change_formatchange( pSet );
506                         }
507                         break;
508                         default:
509                         {
510                             OSL_ASSERT( false );
511                         }
512                     }
513                     break; // no further iteration needed.
514                 }
515                 else
516                 {
517                     *start_offset = ::std::max( *start_offset,
518                                                 nEndOffsetTextMarkup );
519                     // continue iteration.
520                 }
521             }
522             else
523             {
524                 *end_offset = ::std::min( *end_offset,
525                                           nStartOffsetTextMarkup );
526                 break; // no further iteration.
527             }
528         } // eof iteration over text markups
529     }
530 
531     return pSet;
532 }
533 // <--
534 
535 static AtkAttributeSet *
536 text_wrapper_get_run_attributes( AtkText        *text,
537                                  gint           offset,
538                                  gint           *start_offset,
539                                  gint           *end_offset)
540 {
541     AtkAttributeSet *pSet = NULL;
542 
543     try {
544         bool bOffsetsAreValid = false;
545 
546         accessibility::XAccessibleText* pText = getText( text );
547         accessibility::XAccessibleTextAttributes* pTextAttributes = getTextAttributes( text );
548         if( pText && pTextAttributes )
549         {
550             uno::Sequence< beans::PropertyValue > aAttributeList =
551                 pTextAttributes->getRunAttributes( offset, uno::Sequence< rtl::OUString > () );
552 
553             pSet = attribute_set_new_from_property_values( aAttributeList, true, text );
554             // --> OD 2009-06-22 #i100938#
555             // - always provide start_offset and end_offset
556 //            if( pSet )
557             // <--
558             {
559                 accessibility::TextSegment aTextSegment =
560                     pText->getTextAtIndex(offset, accessibility::AccessibleTextType::ATTRIBUTE_RUN);
561 
562                 *start_offset = aTextSegment.SegmentStart;
563                 // --> OD 2009-06-22 #i100938#
564                 // Do _not_ increment the end_offset provide by <accessibility::TextSegment> instance
565 //                *end_offset = aTextSegment.SegmentEnd + 1; // FIXME: TESTME
566                 *end_offset = aTextSegment.SegmentEnd;
567                 // <--
568                 bOffsetsAreValid = true;
569             }
570         }
571 
572         // Special handling for misspelled text
573         // --> OD 2010-03-01 #i92232#
574         // - add special handling for tracked changes and refactor the
575         //   corresponding code for handling misspelled text.
576         accessibility::XAccessibleTextMarkup* pTextMarkup = getTextMarkup( text );
577         if( pTextMarkup )
578         {
579             // Get attribute run here if it hasn't been done before
580             if( !bOffsetsAreValid )
581             {
582                 accessibility::TextSegment aAttributeTextSegment =
583                     pText->getTextAtIndex(offset, accessibility::AccessibleTextType::ATTRIBUTE_RUN);
584                 *start_offset = aAttributeTextSegment.SegmentStart;
585                 *end_offset = aAttributeTextSegment.SegmentEnd;
586             }
587             // handle misspelled text
588             pSet = handle_text_markup_as_run_attribute(
589                     pTextMarkup,
590                     com::sun::star::text::TextMarkupType::SPELLCHECK,
591                     offset, pSet, start_offset, end_offset );
592             // handle tracked changes
593             pSet = handle_text_markup_as_run_attribute(
594                     pTextMarkup,
595                     com::sun::star::text::TextMarkupType::TRACK_CHANGE_INSERTION,
596                     offset, pSet, start_offset, end_offset );
597             pSet = handle_text_markup_as_run_attribute(
598                     pTextMarkup,
599                     com::sun::star::text::TextMarkupType::TRACK_CHANGE_DELETION,
600                     offset, pSet, start_offset, end_offset );
601             pSet = handle_text_markup_as_run_attribute(
602                     pTextMarkup,
603                     com::sun::star::text::TextMarkupType::TRACK_CHANGE_FORMATCHANGE,
604                     offset, pSet, start_offset, end_offset );
605         }
606         // <--
607     }
608     catch(const uno::Exception& e){
609 
610         g_warning( "Exception in get_run_attributes()" );
611 
612         if( pSet )
613         {
614             atk_attribute_set_free( pSet );
615             pSet = NULL;
616         }
617     }
618 
619     return pSet;
620 }
621 
622 /*****************************************************************************/
623 
624 static AtkAttributeSet *
625 text_wrapper_get_default_attributes( AtkText *text )
626 {
627     AtkAttributeSet *pSet = NULL;
628 
629     try {
630         accessibility::XAccessibleTextAttributes* pTextAttributes = getTextAttributes( text );
631         if( pTextAttributes )
632         {
633             uno::Sequence< beans::PropertyValue > aAttributeList =
634                 pTextAttributes->getDefaultAttributes( uno::Sequence< rtl::OUString > () );
635 
636             pSet = attribute_set_new_from_property_values( aAttributeList, false, text );
637         }
638     }
639     catch(const uno::Exception& e) {
640 
641         g_warning( "Exception in get_default_attributes()" );
642 
643         if( pSet )
644         {
645             atk_attribute_set_free( pSet );
646             pSet = NULL;
647         }
648     }
649 
650     return pSet;
651 }
652 
653 /*****************************************************************************/
654 
655 static void
656 text_wrapper_get_character_extents( AtkText          *text,
657                                     gint             offset,
658                                     gint             *x,
659                                     gint             *y,
660                                     gint             *width,
661                                     gint             *height,
662                                     AtkCoordType      coords )
663 {
664     try {
665         accessibility::XAccessibleText* pText = getText( text );
666         if( pText )
667         {
668             *x = *y = *width = *height = 0;
669             awt::Rectangle aRect = pText->getCharacterBounds( offset );
670 
671             gint origin_x = 0;
672             gint origin_y = 0;
673 
674             if( coords == ATK_XY_SCREEN )
675             {
676                 g_return_if_fail( ATK_IS_COMPONENT( text ) );
677                 atk_component_get_position( ATK_COMPONENT( text ), &origin_x, &origin_y, coords);
678             }
679 
680             *x = aRect.X + origin_x;
681             *y = aRect.Y + origin_y;
682             *width = aRect.Width;
683             *height = aRect.Height;
684 
685 #ifdef ENABLE_TRACING
686             fprintf(stderr, "get_character_extents(%d, %d) returns: %d,%d,%d,%d ",
687                 offset, coords, *x, *y, *width, *height);
688 #endif
689         }
690     }
691     catch(const uno::Exception& e) {
692         g_warning( "Exception in getCharacterBounds" );
693     }
694 }
695 
696 static gint
697 text_wrapper_get_character_count (AtkText *text)
698 {
699     gint rv = 0;
700 
701     try {
702         accessibility::XAccessibleText* pText = getText( text );
703         if( pText )
704             rv = pText->getCharacterCount();
705     }
706     catch(const uno::Exception& e) {
707         g_warning( "Exception in getCharacterCount" );
708     }
709 
710 #ifdef ENABLE_TRACING
711     fprintf(stderr, "get_character_count(%p) returns: %d\n", text, rv);
712 #endif
713 
714     return rv;
715 }
716 
717 static gint
718 text_wrapper_get_offset_at_point (AtkText     *text,
719                                   gint         x,
720                                   gint         y,
721                                   AtkCoordType coords)
722 {
723     try {
724         accessibility::XAccessibleText* pText = getText( text );
725         if( pText )
726         {
727             gint origin_x = 0;
728             gint origin_y = 0;
729 
730             if( coords == ATK_XY_SCREEN )
731             {
732                 g_return_val_if_fail( ATK_IS_COMPONENT( text ), -1 );
733                 atk_component_get_position( ATK_COMPONENT( text ), &origin_x, &origin_y, coords);
734             }
735 
736             return pText->getIndexAtPoint( awt::Point(x - origin_x, y - origin_y) );
737         }
738     }
739     catch(const uno::Exception& e) {
740         g_warning( "Exception in getIndexAtPoint" );
741     }
742 
743     return -1;
744 }
745 
746 // FIXME: the whole series of selections API is problematic ...
747 
748 static gint
749 text_wrapper_get_n_selections (AtkText *text)
750 {
751     gint rv = 0;
752 
753     try {
754         accessibility::XAccessibleText* pText = getText( text );
755         if( pText )
756             rv = ( pText->getSelectionEnd() > pText->getSelectionStart() ) ? 1 : 0;
757     }
758     catch(const uno::Exception& e) {
759         g_warning( "Exception in getSelectionEnd() or getSelectionStart()" );
760     }
761 
762 #ifdef ENABLE_TRACING
763     fprintf(stderr, "get_n_selections(%p) returns %d\n", text, rv);
764 #endif
765 
766     return rv;
767 }
768 
769 static gchar *
770 text_wrapper_get_selection (AtkText *text,
771                             gint     selection_num,
772                             gint    *start_offset,
773                             gint    *end_offset)
774 {
775     g_return_val_if_fail( selection_num == 0, FALSE );
776 
777     try {
778         accessibility::XAccessibleText* pText = getText( text );
779         if( pText )
780         {
781             *start_offset = pText->getSelectionStart();
782             *end_offset   = pText->getSelectionEnd();
783 
784             return OUStringToGChar( pText->getSelectedText() );
785         }
786     }
787     catch(const uno::Exception& e) {
788         g_warning( "Exception in getSelectionEnd(), getSelectionStart() or getSelectedText()" );
789     }
790 
791     return NULL;
792 }
793 
794 static gboolean
795 text_wrapper_add_selection (AtkText *text,
796                             gint     start_offset,
797                             gint     end_offset)
798 {
799     // FIXME: can we try to be more compatible by expanding an
800     //        existing adjacent selection ?
801 
802     try {
803         accessibility::XAccessibleText* pText = getText( text );
804         if( pText )
805             return pText->setSelection( start_offset, end_offset ); // ?
806     }
807     catch(const uno::Exception& e) {
808         g_warning( "Exception in setSelection()" );
809     }
810 
811     return FALSE;
812 }
813 
814 static gboolean
815 text_wrapper_remove_selection (AtkText *text,
816                                gint     selection_num)
817 {
818     g_return_val_if_fail( selection_num == 0, FALSE );
819 
820     try {
821         accessibility::XAccessibleText* pText = getText( text );
822         if( pText )
823             return pText->setSelection( 0, 0 ); // ?
824     }
825     catch(const uno::Exception& e) {
826         g_warning( "Exception in setSelection()" );
827     }
828 
829     return FALSE;
830 }
831 
832 static gboolean
833 text_wrapper_set_selection (AtkText *text,
834                             gint     selection_num,
835                             gint     start_offset,
836                             gint     end_offset)
837 {
838     g_return_val_if_fail( selection_num == 0, FALSE );
839 
840     try {
841         accessibility::XAccessibleText* pText = getText( text );
842         if( pText )
843             return pText->setSelection( start_offset, end_offset );
844     }
845     catch(const uno::Exception& e) {
846         g_warning( "Exception in setSelection()" );
847     }
848 
849     return FALSE;
850 }
851 
852 } // extern "C"
853 
854 void
855 textIfaceInit (AtkTextIface *iface)
856 {
857   g_return_if_fail (iface != NULL);
858 
859   iface->get_text = text_wrapper_get_text;
860   iface->get_character_at_offset = text_wrapper_get_character_at_offset;
861   iface->get_text_before_offset = text_wrapper_get_text_before_offset;
862   iface->get_text_at_offset = text_wrapper_get_text_at_offset;
863   iface->get_text_after_offset = text_wrapper_get_text_after_offset;
864   iface->get_caret_offset = text_wrapper_get_caret_offset;
865   iface->set_caret_offset = text_wrapper_set_caret_offset;
866   iface->get_character_count = text_wrapper_get_character_count;
867   iface->get_n_selections = text_wrapper_get_n_selections;
868   iface->get_selection = text_wrapper_get_selection;
869   iface->add_selection = text_wrapper_add_selection;
870   iface->remove_selection = text_wrapper_remove_selection;
871   iface->set_selection = text_wrapper_set_selection;
872   iface->get_run_attributes = text_wrapper_get_run_attributes;
873   iface->get_default_attributes = text_wrapper_get_default_attributes;
874   iface->get_character_extents = text_wrapper_get_character_extents;
875   iface->get_offset_at_point = text_wrapper_get_offset_at_point;
876 }
877