1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: atktext.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_vcl.hxx"
34 #include "atkwrapper.hxx"
35 #include "atktextattributes.hxx"
38 #include <com/sun/star/accessibility/AccessibleTextType.hpp>
39 #include <com/sun/star/accessibility/TextSegment.hpp>
40 #include <com/sun/star/accessibility/XAccessibleMultiLineText.hpp>
41 #include <com/sun/star/accessibility/XAccessibleText.hpp>
42 #include <com/sun/star/accessibility/XAccessibleTextAttributes.hpp>
43 #include <com/sun/star/accessibility/XAccessibleTextMarkup.hpp>
44 #include <com/sun/star/text/TextMarkupType.hpp>
46 // #define ENABLE_TRACING
52 using namespace ::com::sun::star
;
55 text_type_from_boundary(AtkTextBoundary boundary_type
)
59 case ATK_TEXT_BOUNDARY_CHAR
:
60 return accessibility::AccessibleTextType::CHARACTER
;
61 case ATK_TEXT_BOUNDARY_WORD_START
:
62 case ATK_TEXT_BOUNDARY_WORD_END
:
63 return accessibility::AccessibleTextType::WORD
;
64 case ATK_TEXT_BOUNDARY_SENTENCE_START
:
65 case ATK_TEXT_BOUNDARY_SENTENCE_END
:
66 return accessibility::AccessibleTextType::SENTENCE
;
67 case ATK_TEXT_BOUNDARY_LINE_START
:
68 case ATK_TEXT_BOUNDARY_LINE_END
:
69 return accessibility::AccessibleTextType::LINE
;
75 /*****************************************************************************/
78 adjust_boundaries( accessibility::XAccessibleText
* pText
,
79 accessibility::TextSegment
& rTextSegment
,
80 AtkTextBoundary boundary_type
,
81 gint
* start_offset
, gint
* end_offset
)
83 accessibility::TextSegment aTextSegment
;
84 rtl::OUString aString
;
85 gint start
= 0, end
= 0;
87 if( rTextSegment
.SegmentText
.getLength() > 0 )
91 case ATK_TEXT_BOUNDARY_CHAR
:
92 case ATK_TEXT_BOUNDARY_LINE_START
:
93 case ATK_TEXT_BOUNDARY_LINE_END
:
94 case ATK_TEXT_BOUNDARY_SENTENCE_START
:
95 start
= rTextSegment
.SegmentStart
;
96 end
= rTextSegment
.SegmentEnd
;
97 aString
= rTextSegment
.SegmentText
;
100 // the OOo break iterator behaves as SENTENCE_START
101 case ATK_TEXT_BOUNDARY_SENTENCE_END
:
102 start
= rTextSegment
.SegmentStart
;
103 end
= rTextSegment
.SegmentEnd
;
107 if( end
> 0 && end
< pText
->getCharacterCount() - 1 )
110 aString
= pText
->getTextRange(start
, end
);
113 case ATK_TEXT_BOUNDARY_WORD_START
:
114 start
= rTextSegment
.SegmentStart
;
116 // Determine the start index of the next segment
117 aTextSegment
= pText
->getTextBehindIndex(rTextSegment
.SegmentEnd
,
118 text_type_from_boundary(boundary_type
));
119 if( aTextSegment
.SegmentText
.getLength() > 0 )
120 end
= aTextSegment
.SegmentStart
;
122 end
= pText
->getCharacterCount();
124 aString
= pText
->getTextRange(start
, end
);
127 case ATK_TEXT_BOUNDARY_WORD_END
:
128 end
= rTextSegment
.SegmentEnd
;
130 // Determine the end index of the previous segment
131 aTextSegment
= pText
->getTextBeforeIndex(rTextSegment
.SegmentStart
,
132 text_type_from_boundary(boundary_type
));
133 if( aTextSegment
.SegmentText
.getLength() > 0 )
134 start
= aTextSegment
.SegmentEnd
;
138 aString
= pText
->getTextRange(start
, end
);
146 *start_offset
= start
;
149 #ifdef ENABLE_TRACING
150 fprintf(stderr
, "adjust_boundaries( %d, %d, %d ) returns %d, %d\n",
151 rTextSegment
.SegmentStart
, rTextSegment
.SegmentEnd
, boundary_type
,
155 return OUStringToGChar(aString
);
158 /*****************************************************************************/
160 static accessibility::XAccessibleText
*
161 getText( AtkText
*pText
) throw (uno::RuntimeException
)
163 AtkObjectWrapper
*pWrap
= ATK_OBJECT_WRAPPER( pText
);
166 if( !pWrap
->mpText
&& pWrap
->mpContext
)
168 uno::Any any
= pWrap
->mpContext
->queryInterface( accessibility::XAccessibleText::static_type(NULL
) );
169 pWrap
->mpText
= reinterpret_cast< accessibility::XAccessibleText
* > (any
.pReserved
);
170 pWrap
->mpText
->acquire();
173 return pWrap
->mpText
;
179 /*****************************************************************************/
181 static accessibility::XAccessibleTextMarkup
*
182 getTextMarkup( AtkText
*pText
) throw (uno::RuntimeException
)
184 AtkObjectWrapper
*pWrap
= ATK_OBJECT_WRAPPER( pText
);
187 if( !pWrap
->mpTextMarkup
&& pWrap
->mpContext
)
189 uno::Any any
= pWrap
->mpContext
->queryInterface( accessibility::XAccessibleTextMarkup::static_type(NULL
) );
190 pWrap
->mpTextMarkup
= reinterpret_cast< accessibility::XAccessibleTextMarkup
* > (any
.pReserved
);
191 pWrap
->mpTextMarkup
->acquire();
194 return pWrap
->mpTextMarkup
;
200 /*****************************************************************************/
202 static accessibility::XAccessibleTextAttributes
*
203 getTextAttributes( AtkText
*pText
) throw (uno::RuntimeException
)
205 AtkObjectWrapper
*pWrap
= ATK_OBJECT_WRAPPER( pText
);
208 if( !pWrap
->mpTextAttributes
&& pWrap
->mpContext
)
210 uno::Any any
= pWrap
->mpContext
->queryInterface( accessibility::XAccessibleTextAttributes::static_type(NULL
) );
211 /* Since this not a dedicated interface in Atk and thus has not
212 * been queried during wrapper initialization, we need to check
213 * the return value here.
215 if( typelib_TypeClass_INTERFACE
== any
.pType
->eTypeClass
)
217 pWrap
->mpTextAttributes
= reinterpret_cast< accessibility::XAccessibleTextAttributes
* > (any
.pReserved
);
218 pWrap
->mpTextAttributes
->acquire();
222 return pWrap
->mpTextAttributes
;
228 /*****************************************************************************/
230 static accessibility::XAccessibleMultiLineText
*
231 getMultiLineText( AtkText
*pText
) throw (uno::RuntimeException
)
233 AtkObjectWrapper
*pWrap
= ATK_OBJECT_WRAPPER( pText
);
236 if( !pWrap
->mpMultiLineText
&& pWrap
->mpContext
)
238 uno::Any any
= pWrap
->mpContext
->queryInterface( accessibility::XAccessibleMultiLineText::static_type(NULL
) );
239 /* Since this not a dedicated interface in Atk and thus has not
240 * been queried during wrapper initialization, we need to check
241 * the return value here.
243 if( typelib_TypeClass_INTERFACE
== any
.pType
->eTypeClass
)
245 pWrap
->mpMultiLineText
= reinterpret_cast< accessibility::XAccessibleMultiLineText
* > (any
.pReserved
);
246 pWrap
->mpMultiLineText
->acquire();
250 return pWrap
->mpMultiLineText
;
256 /*****************************************************************************/
261 text_wrapper_get_text (AtkText
*text
,
267 g_return_val_if_fail( (end_offset
== -1) || (end_offset
>= start_offset
), NULL
);
269 /* at-spi expects the delete event to be send before the deletion happened
270 * so we save the deleted string object in the UNO event notification and
271 * fool libatk-bridge.so here ..
273 void * pData
= g_object_get_data( G_OBJECT(text
), "ooo::text_changed::delete" );
276 accessibility::TextSegment
* pTextSegment
=
277 reinterpret_cast <accessibility::TextSegment
*> (pData
);
279 if( pTextSegment
->SegmentStart
== start_offset
&&
280 pTextSegment
->SegmentEnd
== end_offset
)
282 rtl::OString aUtf8
= rtl::OUStringToOString( pTextSegment
->SegmentText
, RTL_TEXTENCODING_UTF8
);
283 return g_strdup( aUtf8
.getStr() );
288 accessibility::XAccessibleText
* pText
= getText( text
);
292 sal_Int32 n
= pText
->getCharacterCount();
294 if( -1 == end_offset
)
295 aText
= pText
->getText();
296 else if( start_offset
< n
)
297 aText
= pText
->getTextRange(start_offset
, end_offset
);
299 ret
= g_strdup( rtl::OUStringToOString(aText
, RTL_TEXTENCODING_UTF8
).getStr() );
302 catch(const uno::Exception
& e
) {
303 g_warning( "Exception in getText()" );
306 #ifdef ENABLE_TRACING
307 fprintf(stderr
, "text_wrapper_get_text( %d,%d ) returns %s\n", start_offset
, end_offset
, ret
? ret
: "null" );
313 text_wrapper_get_text_after_offset (AtkText
*text
,
315 AtkTextBoundary boundary_type
,
320 accessibility::XAccessibleText
* pText
= getText( text
);
323 accessibility::TextSegment aTextSegment
= pText
->getTextBehindIndex(offset
, text_type_from_boundary(boundary_type
));
324 return adjust_boundaries(pText
, aTextSegment
, boundary_type
, start_offset
, end_offset
);
327 catch(const uno::Exception
& e
) {
328 g_warning( "Exception in get_text_after_offset()" );
335 text_wrapper_get_text_at_offset (AtkText
*text
,
337 AtkTextBoundary boundary_type
,
342 accessibility::XAccessibleText
* pText
= getText( text
);
345 /* If the user presses the 'End' key, the caret will be placed behind the last character,
346 * which is the same index as the first character of the next line. In atk the magic offset
347 * '-2' is used to cover this special case.
351 (ATK_TEXT_BOUNDARY_LINE_START
== boundary_type
||
352 ATK_TEXT_BOUNDARY_LINE_END
== boundary_type
)
355 accessibility::XAccessibleMultiLineText
* pMultiLineText
= getMultiLineText( text
);
358 accessibility::TextSegment aTextSegment
= pMultiLineText
->getTextAtLineWithCaret();
359 return adjust_boundaries(pText
, aTextSegment
, boundary_type
, start_offset
, end_offset
);
363 accessibility::TextSegment aTextSegment
= pText
->getTextAtIndex(offset
, text_type_from_boundary(boundary_type
));
364 return adjust_boundaries(pText
, aTextSegment
, boundary_type
, start_offset
, end_offset
);
367 catch(const uno::Exception
& e
) {
368 g_warning( "Exception in get_text_at_offset()" );
375 text_wrapper_get_character_at_offset (AtkText
*text
,
381 gchar
* char_as_string
=
382 text_wrapper_get_text_at_offset(text
, offset
, ATK_TEXT_BOUNDARY_CHAR
,
386 uc
= g_utf8_get_char( char_as_string
);
387 g_free( char_as_string
);
394 text_wrapper_get_text_before_offset (AtkText
*text
,
396 AtkTextBoundary boundary_type
,
401 accessibility::XAccessibleText
* pText
= getText( text
);
404 accessibility::TextSegment aTextSegment
= pText
->getTextBeforeIndex(offset
, text_type_from_boundary(boundary_type
));
405 return adjust_boundaries(pText
, aTextSegment
, boundary_type
, start_offset
, end_offset
);
408 catch(const uno::Exception
& e
) {
409 g_warning( "Exception in text_before_offset()" );
416 text_wrapper_get_caret_offset (AtkText
*text
)
421 accessibility::XAccessibleText
* pText
= getText( text
);
423 offset
= pText
->getCaretPosition();
425 catch(const uno::Exception
& e
) {
426 g_warning( "Exception in getCaretPosition()" );
429 #ifdef ENABLE_TRACING
430 fprintf(stderr
, "get_caret_offset(%p) returns %d\n", text
, offset
);
437 text_wrapper_set_caret_offset (AtkText
*text
,
441 accessibility::XAccessibleText
* pText
= getText( text
);
443 return pText
->setCaretPosition( offset
);
445 catch(const uno::Exception
& e
) {
446 g_warning( "Exception in setCaretPosition()" );
452 static AtkAttributeSet
*
453 text_wrapper_get_run_attributes( AtkText
*text
,
458 AtkAttributeSet
*pSet
= NULL
;
461 bool bOffsetsAreValid
= false;
463 accessibility::XAccessibleText
* pText
= getText( text
);
464 accessibility::XAccessibleTextAttributes
* pTextAttributes
= getTextAttributes( text
);
465 if( pText
&& pTextAttributes
)
467 uno::Sequence
< beans::PropertyValue
> aAttributeList
=
468 pTextAttributes
->getRunAttributes( offset
, uno::Sequence
< rtl::OUString
> () );
470 pSet
= attribute_set_new_from_property_values( aAttributeList
, true, text
);
471 // --> OD 2009-06-22 #i100938#
472 // - always provide start_offset and end_offset
476 accessibility::TextSegment aTextSegment
=
477 pText
->getTextAtIndex(offset
, accessibility::AccessibleTextType::ATTRIBUTE_RUN
);
479 *start_offset
= aTextSegment
.SegmentStart
;
480 // --> OD 2009-06-22 #i100938#
481 // Do _not_ increment the end_offset provide by <accessibility::TextSegment> instance
482 // *end_offset = aTextSegment.SegmentEnd + 1; // FIXME: TESTME
483 *end_offset
= aTextSegment
.SegmentEnd
;
485 bOffsetsAreValid
= true;
489 // Special handling for missspelled
490 accessibility::XAccessibleTextMarkup
* pTextMarkup
= getTextMarkup( text
);
493 uno::Sequence
< accessibility::TextSegment
> aTextSegmentSeq
=
494 pTextMarkup
->getTextMarkupAtIndex( offset
, com::sun::star::text::TextMarkupType::SPELLCHECK
);
495 if( aTextSegmentSeq
.getLength() > 0 )
497 accessibility::TextSegment aTextSegment
= aTextSegmentSeq
[0];
498 gint nStartOffsetMisspelled
= aTextSegment
.SegmentStart
;
499 gint nEndOffsetMisspelled
= aTextSegment
.SegmentEnd
;
501 // Get attribute run here if it hasn't been done before
502 if( !bOffsetsAreValid
)
504 accessibility::TextSegment aAttributeTextSegment
=
505 pText
->getTextAtIndex(offset
, accessibility::AccessibleTextType::ATTRIBUTE_RUN
);
506 *start_offset
= aAttributeTextSegment
.SegmentStart
;
507 *end_offset
= aAttributeTextSegment
.SegmentEnd
;
510 if( nEndOffsetMisspelled
<= offset
)
511 *start_offset
= ::std::max( *start_offset
, nEndOffsetMisspelled
);
512 else if( nStartOffsetMisspelled
<= offset
)
513 *start_offset
= ::std::max( *start_offset
, nStartOffsetMisspelled
);
515 if( nStartOffsetMisspelled
> offset
)
516 *end_offset
= ::std::min( *end_offset
, nStartOffsetMisspelled
);
517 else if( nEndOffsetMisspelled
> offset
)
518 *end_offset
= ::std::min( *end_offset
, nEndOffsetMisspelled
);
520 if( nStartOffsetMisspelled
<= offset
&& nEndOffsetMisspelled
> offset
)
521 pSet
= attribute_set_prepend_misspelled( pSet
);
525 catch(const uno::Exception
& e
){
527 g_warning( "Exception in get_run_attributes()" );
531 atk_attribute_set_free( pSet
);
539 /*****************************************************************************/
541 static AtkAttributeSet
*
542 text_wrapper_get_default_attributes( AtkText
*text
)
544 AtkAttributeSet
*pSet
= NULL
;
547 accessibility::XAccessibleTextAttributes
* pTextAttributes
= getTextAttributes( text
);
548 if( pTextAttributes
)
550 uno::Sequence
< beans::PropertyValue
> aAttributeList
=
551 pTextAttributes
->getDefaultAttributes( uno::Sequence
< rtl::OUString
> () );
553 pSet
= attribute_set_new_from_property_values( aAttributeList
, false, text
);
556 catch(const uno::Exception
& e
) {
558 g_warning( "Exception in get_default_attributes()" );
562 atk_attribute_set_free( pSet
);
570 /*****************************************************************************/
573 text_wrapper_get_character_extents( AtkText
*text
,
579 AtkCoordType coords
)
582 accessibility::XAccessibleText
* pText
= getText( text
);
585 *x
= *y
= *width
= *height
= 0;
586 awt::Rectangle aRect
= pText
->getCharacterBounds( offset
);
591 if( coords
== ATK_XY_SCREEN
)
593 g_return_if_fail( ATK_IS_COMPONENT( text
) );
594 atk_component_get_position( ATK_COMPONENT( text
), &origin_x
, &origin_y
, coords
);
597 *x
= aRect
.X
+ origin_x
;
598 *y
= aRect
.Y
+ origin_y
;
599 *width
= aRect
.Width
;
600 *height
= aRect
.Height
;
602 #ifdef ENABLE_TRACING
603 fprintf(stderr
, "get_character_extents(%d, %d) returns: %d,%d,%d,%d ",
604 offset
, coords
, *x
, *y
, *width
, *height
);
608 catch(const uno::Exception
& e
) {
609 g_warning( "Exception in getCharacterBounds" );
614 text_wrapper_get_character_count (AtkText
*text
)
619 accessibility::XAccessibleText
* pText
= getText( text
);
621 rv
= pText
->getCharacterCount();
623 catch(const uno::Exception
& e
) {
624 g_warning( "Exception in getCharacterCount" );
627 #ifdef ENABLE_TRACING
628 fprintf(stderr
, "get_character_count(%p) returns: %d\n", text
, rv
);
635 text_wrapper_get_offset_at_point (AtkText
*text
,
641 accessibility::XAccessibleText
* pText
= getText( text
);
647 if( coords
== ATK_XY_SCREEN
)
649 g_return_val_if_fail( ATK_IS_COMPONENT( text
), -1 );
650 atk_component_get_position( ATK_COMPONENT( text
), &origin_x
, &origin_y
, coords
);
653 return pText
->getIndexAtPoint( awt::Point(x
- origin_x
, y
- origin_y
) );
656 catch(const uno::Exception
& e
) {
657 g_warning( "Exception in getIndexAtPoint" );
663 // FIXME: the whole series of selections API is problematic ...
666 text_wrapper_get_n_selections (AtkText
*text
)
671 accessibility::XAccessibleText
* pText
= getText( text
);
673 rv
= ( pText
->getSelectionEnd() > pText
->getSelectionStart() ) ? 1 : 0;
675 catch(const uno::Exception
& e
) {
676 g_warning( "Exception in getSelectionEnd() or getSelectionStart()" );
679 #ifdef ENABLE_TRACING
680 fprintf(stderr
, "get_n_selections(%p) returns %d\n", text
, rv
);
687 text_wrapper_get_selection (AtkText
*text
,
692 g_return_val_if_fail( selection_num
== 0, FALSE
);
695 accessibility::XAccessibleText
* pText
= getText( text
);
698 *start_offset
= pText
->getSelectionStart();
699 *end_offset
= pText
->getSelectionEnd();
701 return OUStringToGChar( pText
->getSelectedText() );
704 catch(const uno::Exception
& e
) {
705 g_warning( "Exception in getSelectionEnd(), getSelectionStart() or getSelectedText()" );
712 text_wrapper_add_selection (AtkText
*text
,
716 // FIXME: can we try to be more compatible by expanding an
717 // existing adjacent selection ?
720 accessibility::XAccessibleText
* pText
= getText( text
);
722 return pText
->setSelection( start_offset
, end_offset
); // ?
724 catch(const uno::Exception
& e
) {
725 g_warning( "Exception in setSelection()" );
732 text_wrapper_remove_selection (AtkText
*text
,
735 g_return_val_if_fail( selection_num
== 0, FALSE
);
738 accessibility::XAccessibleText
* pText
= getText( text
);
740 return pText
->setSelection( 0, 0 ); // ?
742 catch(const uno::Exception
& e
) {
743 g_warning( "Exception in setSelection()" );
750 text_wrapper_set_selection (AtkText
*text
,
755 g_return_val_if_fail( selection_num
== 0, FALSE
);
758 accessibility::XAccessibleText
* pText
= getText( text
);
760 return pText
->setSelection( start_offset
, end_offset
);
762 catch(const uno::Exception
& e
) {
763 g_warning( "Exception in setSelection()" );
772 textIfaceInit (AtkTextIface
*iface
)
774 g_return_if_fail (iface
!= NULL
);
776 iface
->get_text
= text_wrapper_get_text
;
777 iface
->get_character_at_offset
= text_wrapper_get_character_at_offset
;
778 iface
->get_text_before_offset
= text_wrapper_get_text_before_offset
;
779 iface
->get_text_at_offset
= text_wrapper_get_text_at_offset
;
780 iface
->get_text_after_offset
= text_wrapper_get_text_after_offset
;
781 iface
->get_caret_offset
= text_wrapper_get_caret_offset
;
782 iface
->set_caret_offset
= text_wrapper_set_caret_offset
;
783 iface
->get_character_count
= text_wrapper_get_character_count
;
784 iface
->get_n_selections
= text_wrapper_get_n_selections
;
785 iface
->get_selection
= text_wrapper_get_selection
;
786 iface
->add_selection
= text_wrapper_add_selection
;
787 iface
->remove_selection
= text_wrapper_remove_selection
;
788 iface
->set_selection
= text_wrapper_set_selection
;
789 iface
->get_run_attributes
= text_wrapper_get_run_attributes
;
790 iface
->get_default_attributes
= text_wrapper_get_default_attributes
;
791 iface
->get_character_extents
= text_wrapper_get_character_extents
;
792 iface
->get_offset_at_point
= text_wrapper_get_offset_at_point
;