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