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