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