1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_editeng.hxx"
30 
31 //------------------------------------------------------------------------
32 //
33 // Global header
34 //
35 //------------------------------------------------------------------------
36 
37 #include <limits.h>
38 #include <vector>
39 #include <algorithm>
40 #include <boost/bind.hpp>
41 #include <vos/mutex.hxx>
42 #include <vcl/window.hxx>
43 #include <vcl/svapp.hxx>
44 #include <comphelper/sequenceasvector.hxx>
45 #include <com/sun/star/uno/Any.hxx>
46 #include <com/sun/star/uno/Reference.hxx>
47 #include <com/sun/star/awt/Point.hpp>
48 #include <com/sun/star/awt/Rectangle.hpp>
49 #include <com/sun/star/accessibility/AccessibleTextType.hpp>
50 
51 //------------------------------------------------------------------------
52 //
53 // Project-local header
54 //
55 //------------------------------------------------------------------------
56 
57 #include <editeng/editdata.hxx>
58 #include <editeng/unopracc.hxx>
59 #include "editeng/unoedprx.hxx"
60 #include <editeng/AccessibleStaticTextBase.hxx>
61 #include "editeng/AccessibleEditableTextPara.hxx"
62 
63 
64 using namespace ::com::sun::star;
65 using namespace ::com::sun::star::accessibility;
66 
67 /* TODO:
68    =====
69 
70    - separate adapter functionality from AccessibleStaticText class
71 
72    - refactor common loops into templates, using mem_fun
73 
74  */
75 
76 namespace accessibility
77 {
78     typedef ::comphelper::SequenceAsVector< beans::PropertyValue > PropertyValueVector;
79 
80     class PropertyValueEqualFunctor : public ::std::binary_function< beans::PropertyValue, beans::PropertyValue, bool >
81     {
82     public:
83         PropertyValueEqualFunctor()
84         {}
85         bool operator() ( const beans::PropertyValue& lhs, const beans::PropertyValue& rhs ) const
86         {
87             return ( lhs.Name == rhs.Name && lhs.Value == rhs.Value );
88         }
89     };
90 
91 	//------------------------------------------------------------------------
92 	//
93 	// Static Helper
94 	//
95 	//------------------------------------------------------------------------
96     ESelection MakeSelection( sal_Int32 nStartPara, sal_Int32 nStartIndex,
97                               sal_Int32 nEndPara, sal_Int32 nEndIndex )
98     {
99         DBG_ASSERT(nStartPara >= 0 && nStartPara <= USHRT_MAX &&
100                    nStartIndex >= 0 && nStartIndex <= USHRT_MAX &&
101                    nEndPara >= 0 && nEndPara <= USHRT_MAX &&
102                    nEndIndex >= 0 && nEndIndex <= USHRT_MAX ,
103                    "AccessibleStaticTextBase_Impl::MakeSelection: index value overflow");
104 
105         return ESelection( static_cast< sal_uInt16 >(nStartPara), static_cast< sal_uInt16 >(nStartIndex),
106                            static_cast< sal_uInt16 >(nEndPara), static_cast< sal_uInt16 >(nEndIndex) );
107     }
108 
109 	//------------------------------------------------------------------------
110 	//
111 	// AccessibleStaticTextBase_Impl declaration
112 	//
113 	//------------------------------------------------------------------------
114 
115     DBG_NAME( AccessibleStaticTextBase_Impl );
116 
117     /** AccessibleStaticTextBase_Impl
118 
119     	This class implements the AccessibleStaticTextBase
120     	functionality, mainly by forwarding the calls to an aggregated
121     	AccessibleEditableTextPara. As this is a therefore non-trivial
122     	adapter, factoring out the common functionality from
123     	AccessibleEditableTextPara might be a profitable future task.
124      */
125     class AccessibleStaticTextBase_Impl
126     {
127 
128     public:
129 
130         // receive pointer to our frontend class and view window
131         AccessibleStaticTextBase_Impl();
132         ~AccessibleStaticTextBase_Impl();
133 
134         SvxEditSourceAdapter& GetEditSource() const SAL_THROW((uno::RuntimeException))
135         {
136             DBG_CHKTHIS( AccessibleStaticTextBase_Impl, NULL );
137 
138             return maEditSource;
139         }
140         void SetEditSource( ::std::auto_ptr< SvxEditSource > pEditSource ) SAL_THROW((uno::RuntimeException));
141 
142         void SetEventSource( const uno::Reference< XAccessible >& rInterface )
143         {
144             DBG_CHKTHIS( AccessibleStaticTextBase_Impl, NULL );
145 
146             mxThis = rInterface;
147         }
148         uno::Reference< XAccessible > GetEventSource() const
149         {
150             DBG_CHKTHIS( AccessibleStaticTextBase_Impl, NULL );
151 
152             return mxThis;
153         }
154 
155         void SetOffset( const Point& );
156         Point GetOffset() const
157         {
158             DBG_CHKTHIS( AccessibleStaticTextBase_Impl, NULL );
159 
160             ::osl::MutexGuard aGuard( maMutex ); Point aPoint( maOffset );
161             return aPoint;
162         }
163 
164         void UpdateChildren();
165         void Dispose();
166 
167 #ifdef DBG_UTIL
168         void CheckInvariants() const;
169 #endif
170 
171         AccessibleEditableTextPara& GetParagraph( sal_Int32 nPara ) const;
172         sal_Int32 					GetParagraphCount() const;
173         sal_Int32                   GetParagraphIndex() const;
174         sal_Int32                   GetLineCount( sal_Int32 nParagraph ) const;
175 
176         EPosition                   Index2Internal( sal_Int32 nFlatIndex ) const
177         {
178             DBG_CHKTHIS( AccessibleStaticTextBase_Impl, NULL );
179 
180             return ImpCalcInternal( nFlatIndex, false );
181         }
182 
183         EPosition                   Range2Internal( sal_Int32 nFlatIndex ) const
184         {
185             DBG_CHKTHIS( AccessibleStaticTextBase_Impl, NULL );
186 
187             return ImpCalcInternal( nFlatIndex, true );
188         }
189 
190         sal_Int32					Internal2Index( EPosition nEEIndex ) const;
191 
192         void						CorrectTextSegment( TextSegment&	aTextSegment,
193                                                         int				nPara	) const;
194 
195         sal_Bool					SetSelection( sal_Int32 nStartPara, sal_Int32 nStartIndex,
196                                                   sal_Int32 nEndPara, sal_Int32 nEndIndex );
197         sal_Bool					CopyText( sal_Int32 nStartPara, sal_Int32 nStartIndex,
198                                               sal_Int32 nEndPara, sal_Int32 nEndIndex );
199 
200         Rectangle                   GetParagraphBoundingBox() const;
201 
202     private:
203 
204         EPosition 					ImpCalcInternal( sal_Int32 nFlatIndex, bool bExclusive ) const;
205 
206         // our frontend class (the one implementing the actual
207         // interface). That's not necessarily the one containing the impl
208         // pointer
209         uno::Reference< XAccessible > mxThis;
210 
211         // implements our functionality, we're just an adapter (guarded by solar mutex)
212         mutable AccessibleEditableTextPara* mpTextParagraph;
213 
214         uno::Reference< XAccessible > mxParagraph;
215 
216         // a wrapper for the text forwarders (guarded by solar mutex)
217         mutable SvxEditSourceAdapter maEditSource;
218 
219         // guard for maOffset
220         mutable ::osl::Mutex maMutex;
221 
222         /// our current offset to the containing shape/cell (guarded by maMutex)
223         Point maOffset;
224 
225     };
226 
227 	//------------------------------------------------------------------------
228 	//
229 	// AccessibleStaticTextBase_Impl implementation
230 	//
231 	//------------------------------------------------------------------------
232 
233     AccessibleStaticTextBase_Impl::AccessibleStaticTextBase_Impl() :
234         mxThis( NULL ),
235         mpTextParagraph( new AccessibleEditableTextPara(NULL) ),
236         mxParagraph( mpTextParagraph ),
237         maEditSource(),
238         maMutex(),
239         maOffset(0,0)
240     {
241         DBG_CTOR( AccessibleStaticTextBase_Impl, NULL );
242 
243         // TODO: this is still somewhat of a hack, all the more since
244         // now the maTextParagraph has an empty parent reference set
245     }
246 
247     AccessibleStaticTextBase_Impl::~AccessibleStaticTextBase_Impl()
248     {
249         DBG_DTOR( AccessibleStaticTextBase_Impl, NULL );
250     }
251 
252     void AccessibleStaticTextBase_Impl::SetEditSource( ::std::auto_ptr< SvxEditSource > pEditSource ) SAL_THROW((uno::RuntimeException))
253     {
254         DBG_CHKTHIS( AccessibleStaticTextBase_Impl, NULL );
255 
256         maEditSource.SetEditSource( pEditSource );
257         if( mpTextParagraph )
258             mpTextParagraph->SetEditSource( &maEditSource );
259     }
260 
261     void AccessibleStaticTextBase_Impl::SetOffset( const Point& rPoint )
262     {
263         DBG_CHKTHIS( AccessibleStaticTextBase_Impl, NULL );
264 
265         // guard against non-atomic access to maOffset data structure
266         {
267             ::osl::MutexGuard aGuard( maMutex );
268             maOffset = rPoint;
269         }
270 
271         if( mpTextParagraph )
272             mpTextParagraph->SetEEOffset( rPoint );
273 
274         // in all cases, check visibility afterwards.
275         UpdateChildren();
276     }
277 
278     void AccessibleStaticTextBase_Impl::UpdateChildren()
279     {
280         DBG_CHKTHIS( AccessibleStaticTextBase_Impl, NULL );
281 
282         // currently no children
283     }
284 
285     void AccessibleStaticTextBase_Impl::Dispose()
286     {
287         DBG_CHKTHIS( AccessibleStaticTextBase_Impl, NULL );
288 
289         // we're the owner of the paragraph, so destroy it, too
290         if( mpTextParagraph )
291             mpTextParagraph->Dispose();
292 
293         // drop references
294         mxParagraph = NULL;
295         mxThis = NULL;
296         mpTextParagraph = NULL;
297     }
298 
299 #ifdef DBG_UTIL
300     void AccessibleStaticTextBase_Impl::CheckInvariants() const
301     {
302         // TODO
303     }
304 #endif
305 
306     AccessibleEditableTextPara& AccessibleStaticTextBase_Impl::GetParagraph( sal_Int32 nPara ) const
307     {
308         DBG_CHKTHIS( AccessibleStaticTextBase_Impl, NULL );
309 
310         if( !mpTextParagraph )
311             throw lang::DisposedException (
312                 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("object has been already disposed")), mxThis );
313 
314         // TODO: Have a differnt method on AccessibleEditableTextPara
315         // that does not care about state changes
316         mpTextParagraph->SetParagraphIndex( nPara );
317 
318         return *mpTextParagraph;
319     }
320 
321     sal_Int32 AccessibleStaticTextBase_Impl::GetParagraphCount() const
322     {
323         DBG_CHKTHIS( AccessibleStaticTextBase_Impl, NULL );
324 
325         if( !mpTextParagraph )
326             return 0;
327         else
328             return mpTextParagraph->GetTextForwarder().GetParagraphCount();
329     }
330 
331     sal_Int32 AccessibleStaticTextBase_Impl::GetParagraphIndex() const
332     {
333         DBG_CHKTHIS( AccessibleStaticTextBase_Impl, NULL );
334 
335         sal_Int32 nIndex = -1;
336         if( mpTextParagraph )
337             nIndex = mpTextParagraph->GetParagraphIndex();
338         return nIndex;
339     }
340 
341     sal_Int32 AccessibleStaticTextBase_Impl::GetLineCount( sal_Int32 nParagraph ) const
342     {
343         DBG_CHKTHIS( AccessibleStaticTextBase_Impl, NULL );
344 
345         sal_Int32 nIndex = 0;
346         if( mpTextParagraph )
347             nIndex = mpTextParagraph->GetTextForwarder().GetLineCount( static_cast< sal_uInt16 >(nParagraph) );
348         return nIndex;
349     }
350 
351     sal_Int32 AccessibleStaticTextBase_Impl::Internal2Index( EPosition nEEIndex ) const
352     {
353         sal_Int32 aRes(0);
354         int i;
355         for(i=0; i<nEEIndex.nPara; ++i)
356             aRes += GetParagraph(i).getCharacterCount();
357 
358         return aRes + nEEIndex.nIndex;
359     }
360 
361     void AccessibleStaticTextBase_Impl::CorrectTextSegment( TextSegment&	aTextSegment,
362                                                             int				nPara	) const
363     {
364         // Keep 'invalid' values at the TextSegment
365         if( aTextSegment.SegmentStart != -1 &&
366             aTextSegment.SegmentStart != -1 )
367         {
368             // #112814# Correct TextSegment by paragraph offset
369             sal_Int32 nOffset(0);
370             int i;
371             for(i=0; i<nPara; ++i)
372                 nOffset += GetParagraph(i).getCharacterCount();
373 
374             aTextSegment.SegmentStart += nOffset;
375             aTextSegment.SegmentEnd += nOffset;
376         }
377     }
378 
379     EPosition AccessibleStaticTextBase_Impl::ImpCalcInternal( sal_Int32 nFlatIndex, bool bExclusive ) const
380     {
381         DBG_CHKTHIS( AccessibleStaticTextBase_Impl, NULL );
382 
383         if( nFlatIndex < 0 )
384             throw lang::IndexOutOfBoundsException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("AccessibleStaticTextBase_Impl::Index2Internal: character index out of bounds")),
385                                                   mxThis);
386         // gratuitously accepting larger indices here, AccessibleEditableTextPara will throw eventually
387 
388         sal_Int32 nCurrPara, nCurrIndex, nParas, nCurrCount;
389         for( nCurrPara=0, nParas=GetParagraphCount(), nCurrCount=0, nCurrIndex=0; nCurrPara<nParas; ++nCurrPara )
390         {
391             nCurrCount = GetParagraph( nCurrPara ).getCharacterCount();
392             nCurrIndex += nCurrCount;
393 
394             if( nCurrIndex > nFlatIndex )
395             {
396                 // check overflow
397                 DBG_ASSERT(nCurrPara >= 0 && nCurrPara <= USHRT_MAX &&
398                            nFlatIndex - nCurrIndex + nCurrCount >= 0 && nFlatIndex - nCurrIndex + nCurrCount <= USHRT_MAX ,
399                            "AccessibleStaticTextBase_Impl::Index2Internal: index value overflow");
400 
401                 return EPosition( static_cast< sal_uInt16 >(nCurrPara), static_cast< sal_uInt16 >(nFlatIndex - nCurrIndex + nCurrCount) );
402             }
403         }
404 
405         // #102170# Allow one-past the end for ranges
406         if( bExclusive && nCurrIndex == nFlatIndex )
407         {
408             // check overflow
409             DBG_ASSERT(nCurrPara >= 0 && nCurrPara <= USHRT_MAX &&
410                        nFlatIndex - nCurrIndex + nCurrCount >= 0 && nFlatIndex - nCurrIndex + nCurrCount <= USHRT_MAX ,
411                        "AccessibleStaticTextBase_Impl::Index2Internal: index value overflow");
412 
413             return EPosition( static_cast< sal_uInt16 >(nCurrPara-1), static_cast< sal_uInt16 >(nFlatIndex - nCurrIndex + nCurrCount) );
414         }
415 
416         // not found? Out of bounds
417         throw lang::IndexOutOfBoundsException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("AccessibleStaticTextBase_Impl::Index2Internal: character index out of bounds")),
418                                               mxThis);
419     }
420 
421     sal_Bool AccessibleStaticTextBase_Impl::SetSelection( sal_Int32 nStartPara, sal_Int32 nStartIndex,
422                                                           sal_Int32 nEndPara, sal_Int32 nEndIndex )
423     {
424         DBG_CHKTHIS( AccessibleStaticTextBase_Impl, NULL );
425 
426         if( !mpTextParagraph )
427             return sal_False;
428 
429         try
430         {
431             SvxEditViewForwarder& rCacheVF = mpTextParagraph->GetEditViewForwarder( sal_True );
432             return rCacheVF.SetSelection( MakeSelection(nStartPara, nStartIndex, nEndPara, nEndIndex) );
433         }
434         catch( const uno::RuntimeException& )
435         {
436             return sal_False;
437         }
438     }
439 
440     sal_Bool AccessibleStaticTextBase_Impl::CopyText( sal_Int32 nStartPara, sal_Int32 nStartIndex,
441                                                       sal_Int32 nEndPara, sal_Int32 nEndIndex )
442     {
443         DBG_CHKTHIS( AccessibleStaticTextBase_Impl, NULL );
444 
445         if( !mpTextParagraph )
446             return sal_False;
447 
448         try
449         {
450             SvxEditViewForwarder& rCacheVF = mpTextParagraph->GetEditViewForwarder( sal_True );
451             mpTextParagraph->GetTextForwarder();	// MUST be after GetEditViewForwarder(), see method docs
452             sal_Bool aRetVal;
453 
454             // save current selection
455             ESelection aOldSelection;
456 
457             rCacheVF.GetSelection( aOldSelection );
458             rCacheVF.SetSelection( MakeSelection(nStartPara, nStartIndex, nEndPara, nEndIndex) );
459             aRetVal = rCacheVF.Copy();
460             rCacheVF.SetSelection( aOldSelection ); // restore
461 
462             return aRetVal;
463         }
464         catch( const uno::RuntimeException& )
465         {
466             return sal_False;
467         }
468     }
469 
470     Rectangle AccessibleStaticTextBase_Impl::GetParagraphBoundingBox() const
471     {
472         Rectangle aRect;
473         if( mpTextParagraph )
474         {
475             awt::Rectangle aAwtRect = mpTextParagraph->getBounds();
476             aRect = Rectangle( Point( aAwtRect.X, aAwtRect.Y ), Size( aAwtRect.Width, aAwtRect.Height ) );
477         }
478         else
479         {
480             aRect.SetEmpty();
481         }
482         return aRect;
483     }
484 
485 	//------------------------------------------------------------------------
486 	//
487 	// AccessibleStaticTextBase implementation
488 	//
489 	//------------------------------------------------------------------------
490 
491     AccessibleStaticTextBase::AccessibleStaticTextBase( ::std::auto_ptr< SvxEditSource > 		pEditSource ) :
492         mpImpl( new AccessibleStaticTextBase_Impl() )
493     {
494         ::vos::OGuard aGuard( Application::GetSolarMutex() );
495 
496         SetEditSource( pEditSource );
497     }
498 
499     AccessibleStaticTextBase::~AccessibleStaticTextBase()
500     {
501     }
502 
503     const SvxEditSource& AccessibleStaticTextBase::GetEditSource() const SAL_THROW((::com::sun::star::uno::RuntimeException))
504     {
505 #ifdef DBG_UTIL
506         mpImpl->CheckInvariants();
507 
508         const SvxEditSource& aEditSource = mpImpl->GetEditSource();
509 
510         mpImpl->CheckInvariants();
511 
512         return aEditSource;
513 #else
514         return mpImpl->GetEditSource();
515 #endif
516     }
517 
518     void AccessibleStaticTextBase::SetEditSource( ::std::auto_ptr< SvxEditSource > pEditSource ) SAL_THROW((::com::sun::star::uno::RuntimeException))
519     {
520 #ifdef DBG_UTIL
521         // precondition: solar mutex locked
522         DBG_TESTSOLARMUTEX();
523 
524         mpImpl->CheckInvariants();
525 
526         mpImpl->SetEditSource( pEditSource );
527 
528         mpImpl->CheckInvariants();
529 #else
530         mpImpl->SetEditSource( pEditSource );
531 #endif
532     }
533 
534     void AccessibleStaticTextBase::SetEventSource( const uno::Reference< XAccessible >& rInterface )
535     {
536 #ifdef DBG_UTIL
537         mpImpl->CheckInvariants();
538 #endif
539 
540         mpImpl->SetEventSource( rInterface );
541 
542 #ifdef DBG_UTIL
543         mpImpl->CheckInvariants();
544 #endif
545     }
546 
547     uno::Reference< XAccessible > AccessibleStaticTextBase::GetEventSource() const
548     {
549 #ifdef DBG_UTIL
550         mpImpl->CheckInvariants();
551 
552         uno::Reference< XAccessible > xRet( mpImpl->GetEventSource() );
553 
554         mpImpl->CheckInvariants();
555 
556         return xRet;
557 #else
558         return mpImpl->GetEventSource();
559 #endif
560     }
561 
562     void AccessibleStaticTextBase::SetOffset( const Point& rPoint )
563     {
564 #ifdef DBG_UTIL
565         // precondition: solar mutex locked
566         DBG_TESTSOLARMUTEX();
567 
568         mpImpl->CheckInvariants();
569 
570         mpImpl->SetOffset( rPoint );
571 
572         mpImpl->CheckInvariants();
573 #else
574         mpImpl->SetOffset( rPoint );
575 #endif
576     }
577 
578     Point AccessibleStaticTextBase::GetOffset() const
579     {
580 #ifdef DBG_UTIL
581         mpImpl->CheckInvariants();
582 
583         Point aPoint( mpImpl->GetOffset() );
584 
585         mpImpl->CheckInvariants();
586 
587         return aPoint;
588 #else
589         return mpImpl->GetOffset();
590 #endif
591     }
592 
593     void AccessibleStaticTextBase::UpdateChildren() SAL_THROW((::com::sun::star::uno::RuntimeException))
594     {
595 #ifdef DBG_UTIL
596         // precondition: solar mutex locked
597         DBG_TESTSOLARMUTEX();
598 
599         mpImpl->CheckInvariants();
600 
601         mpImpl->UpdateChildren();
602 
603         mpImpl->CheckInvariants();
604 #else
605         mpImpl->UpdateChildren();
606 #endif
607     }
608 
609     void AccessibleStaticTextBase::Dispose()
610     {
611 #ifdef DBG_UTIL
612         mpImpl->CheckInvariants();
613 #endif
614 
615         mpImpl->Dispose();
616 
617 #ifdef DBG_UTIL
618         mpImpl->CheckInvariants();
619 #endif
620     }
621 
622 	// XAccessibleContext
623     sal_Int32 SAL_CALL AccessibleStaticTextBase::getAccessibleChildCount() throw (uno::RuntimeException)
624     {
625         // no children at all
626         return 0;
627     }
628 
629     uno::Reference< XAccessible > SAL_CALL AccessibleStaticTextBase::getAccessibleChild( sal_Int32 /*i*/ ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException)
630     {
631         // no children at all
632         return uno::Reference< XAccessible >();
633     }
634 
635     uno::Reference< XAccessible > SAL_CALL AccessibleStaticTextBase::getAccessibleAtPoint( const awt::Point& /*_aPoint*/ ) throw (uno::RuntimeException)
636     {
637         // no children at all
638         return uno::Reference< XAccessible >();
639     }
640 
641 	// XAccessibleText
642     sal_Int32 SAL_CALL AccessibleStaticTextBase::getCaretPosition() throw (uno::RuntimeException)
643     {
644         ::vos::OGuard aGuard( Application::GetSolarMutex() );
645 
646         sal_Int32 i, nPos, nParas;
647         for( i=0, nPos=-1, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i )
648         {
649             if( (nPos=mpImpl->GetParagraph(i).getCaretPosition()) != -1 )
650                 return nPos;
651         }
652 
653         return nPos;
654     }
655 
656     sal_Bool SAL_CALL AccessibleStaticTextBase::setCaretPosition( sal_Int32 nIndex ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException)
657     {
658         return setSelection(nIndex, nIndex);
659     }
660 
661     sal_Unicode SAL_CALL AccessibleStaticTextBase::getCharacter( sal_Int32 nIndex ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException)
662     {
663         ::vos::OGuard aGuard( Application::GetSolarMutex() );
664 
665         EPosition aPos( mpImpl->Index2Internal(nIndex) );
666 
667         return mpImpl->GetParagraph( aPos.nPara ).getCharacter( aPos.nIndex );
668     }
669 
670     uno::Sequence< beans::PropertyValue > SAL_CALL AccessibleStaticTextBase::getCharacterAttributes( sal_Int32 nIndex, const ::com::sun::star::uno::Sequence< ::rtl::OUString >& aRequestedAttributes ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException)
671     {
672         ::vos::OGuard aGuard( Application::GetSolarMutex() );
673 
674         EPosition aPos( mpImpl->Index2Internal(nIndex) );
675 
676         return mpImpl->GetParagraph( aPos.nPara ).getCharacterAttributes( aPos.nIndex, aRequestedAttributes );
677     }
678 
679     awt::Rectangle SAL_CALL AccessibleStaticTextBase::getCharacterBounds( sal_Int32 nIndex ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException)
680     {
681         ::vos::OGuard aGuard( Application::GetSolarMutex() );
682 
683         // #108900# Allow ranges for nIndex, as one-past-the-end
684         // values are now legal, too.
685         EPosition aPos( mpImpl->Range2Internal(nIndex) );
686 
687         // #i70916# Text in spread sheet cells return the wrong extents
688         AccessibleEditableTextPara& rPara = mpImpl->GetParagraph( aPos.nPara );
689         awt::Rectangle aParaBounds( rPara.getBounds() );
690         awt::Rectangle aBounds( rPara.getCharacterBounds( aPos.nIndex ) );
691         aBounds.X += aParaBounds.X;
692         aBounds.Y += aParaBounds.Y;
693 
694         return aBounds;
695     }
696 
697     sal_Int32 SAL_CALL AccessibleStaticTextBase::getCharacterCount() throw (uno::RuntimeException)
698     {
699         ::vos::OGuard aGuard( Application::GetSolarMutex() );
700 
701         sal_Int32 i, nCount, nParas;
702         for( i=0, nCount=0, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i )
703             nCount += mpImpl->GetParagraph(i).getCharacterCount();
704 
705         return nCount;
706     }
707 
708     sal_Int32 SAL_CALL AccessibleStaticTextBase::getIndexAtPoint( const awt::Point& rPoint ) throw (uno::RuntimeException)
709     {
710         ::vos::OGuard aGuard( Application::GetSolarMutex() );
711 
712         const sal_Int32 nParas( mpImpl->GetParagraphCount() );
713         sal_Int32 nIndex;
714         int i;
715         for( i=0; i<nParas; ++i )
716         {
717             // TODO: maybe exploit the fact that paragraphs are
718             // ordered vertically for early exit
719 
720             // #i70916# Text in spread sheet cells return the wrong extents
721             AccessibleEditableTextPara& rPara = mpImpl->GetParagraph( i );
722             awt::Rectangle aParaBounds( rPara.getBounds() );
723             awt::Point aPoint( rPoint );
724             aPoint.X -= aParaBounds.X;
725             aPoint.Y -= aParaBounds.Y;
726 
727             // #112814# Use correct index offset
728             if ( ( nIndex = rPara.getIndexAtPoint( aPoint ) ) != -1 )
729                 return mpImpl->Internal2Index( EPosition(sal::static_int_cast<sal_uInt16>(i),
730                                                          sal::static_int_cast<sal_uInt16>(nIndex)) );
731         }
732 
733         return -1;
734     }
735 
736     ::rtl::OUString SAL_CALL AccessibleStaticTextBase::getSelectedText() throw (uno::RuntimeException)
737     {
738         ::vos::OGuard aGuard( Application::GetSolarMutex() );
739 
740         sal_Int32 nStart( getSelectionStart() );
741         sal_Int32 nEnd( getSelectionEnd() );
742 
743         // #104481# Return the empty string for 'no selection'
744         if( nStart < 0 || nEnd < 0 )
745             return ::rtl::OUString();
746 
747         return getTextRange( nStart, nEnd );
748     }
749 
750     sal_Int32 SAL_CALL AccessibleStaticTextBase::getSelectionStart() throw (uno::RuntimeException)
751     {
752         ::vos::OGuard aGuard( Application::GetSolarMutex() );
753 
754         sal_Int32 i, nPos, nParas;
755         for( i=0, nPos=-1, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i )
756         {
757             if( (nPos=mpImpl->GetParagraph(i).getSelectionStart()) != -1 )
758                 return nPos;
759         }
760 
761         return nPos;
762     }
763 
764     sal_Int32 SAL_CALL AccessibleStaticTextBase::getSelectionEnd() throw (uno::RuntimeException)
765     {
766         ::vos::OGuard aGuard( Application::GetSolarMutex() );
767 
768         sal_Int32 i, nPos, nParas;
769         for( i=0, nPos=-1, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i )
770         {
771             if( (nPos=mpImpl->GetParagraph(i).getSelectionEnd()) != -1 )
772                 return nPos;
773         }
774 
775         return nPos;
776     }
777 
778     sal_Bool SAL_CALL AccessibleStaticTextBase::setSelection( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException)
779     {
780         ::vos::OGuard aGuard( Application::GetSolarMutex() );
781 
782         EPosition aStartIndex( mpImpl->Range2Internal(nStartIndex) );
783         EPosition aEndIndex( mpImpl->Range2Internal(nEndIndex) );
784 
785         return mpImpl->SetSelection( aStartIndex.nPara, aStartIndex.nIndex,
786                                      aEndIndex.nPara, aEndIndex.nIndex );
787     }
788 
789     ::rtl::OUString SAL_CALL AccessibleStaticTextBase::getText() throw (uno::RuntimeException)
790     {
791         ::vos::OGuard aGuard( Application::GetSolarMutex() );
792 
793         sal_Int32 i, nParas;
794         ::rtl::OUString aRes;
795         for( i=0, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i )
796             aRes += mpImpl->GetParagraph(i).getText();
797 
798         return aRes;
799     }
800 
801     ::rtl::OUString SAL_CALL AccessibleStaticTextBase::getTextRange( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException)
802     {
803         ::vos::OGuard aGuard( Application::GetSolarMutex() );
804 
805         if( nStartIndex > nEndIndex )
806             ::std::swap(nStartIndex, nEndIndex);
807 
808         EPosition aStartIndex( mpImpl->Range2Internal(nStartIndex) );
809         EPosition aEndIndex( mpImpl->Range2Internal(nEndIndex) );
810 
811         // #102170# Special case: start and end paragraph are identical
812         if( aStartIndex.nPara == aEndIndex.nPara )
813         {
814             return mpImpl->GetParagraph( aStartIndex.nPara ).getTextRange( aStartIndex.nIndex, aEndIndex.nIndex );
815         }
816         else
817         {
818             sal_Int32 i( aStartIndex.nPara );
819             ::rtl::OUString aRes( mpImpl->GetParagraph(i).getTextRange( aStartIndex.nIndex,
820                                                                         mpImpl->GetParagraph(i).getCharacterCount()-1) );
821             ++i;
822 
823             // paragraphs inbetween are fully included
824             for( ; i<aEndIndex.nPara; ++i )
825                 aRes += mpImpl->GetParagraph(i).getText();
826 
827             if( i<=aEndIndex.nPara )
828                 aRes += mpImpl->GetParagraph(i).getTextRange( 0, aEndIndex.nIndex );
829 
830             return aRes;
831         }
832     }
833 
834     ::com::sun::star::accessibility::TextSegment SAL_CALL AccessibleStaticTextBase::getTextAtIndex( sal_Int32 nIndex, sal_Int16 aTextType ) throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException)
835     {
836         ::vos::OGuard aGuard( Application::GetSolarMutex() );
837 
838         EPosition aPos( mpImpl->Range2Internal(nIndex) );
839 
840         ::com::sun::star::accessibility::TextSegment aResult;
841 
842         if( AccessibleTextType::PARAGRAPH == aTextType )
843         {
844             // #106393# Special casing one behind last paragraph is
845             // not necessary, since then, we return the content and
846             // boundary of that last paragraph. Range2Internal is
847             // tolerant against that, and returns the last paragraph
848             // in aPos.nPara.
849 
850             // retrieve full text of the paragraph
851             aResult.SegmentText = mpImpl->GetParagraph( aPos.nPara ).getText();
852 
853             // #112814# Adapt the start index with the paragraph offset
854             aResult.SegmentStart = mpImpl->Internal2Index( EPosition( aPos.nPara, 0 ) );
855             aResult.SegmentEnd = aResult.SegmentStart + aResult.SegmentText.getLength();
856         }
857         else
858         {
859             // No special handling required, forward to wrapped class
860             aResult = mpImpl->GetParagraph( aPos.nPara ).getTextAtIndex( aPos.nIndex, aTextType );
861 
862             // #112814# Adapt the start index with the paragraph offset
863             mpImpl->CorrectTextSegment( aResult, aPos.nPara );
864         }
865 
866         return aResult;
867     }
868 
869     ::com::sun::star::accessibility::TextSegment SAL_CALL AccessibleStaticTextBase::getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 aTextType ) throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException)
870     {
871         ::vos::OGuard aGuard( Application::GetSolarMutex() );
872 
873         EPosition aPos( mpImpl->Range2Internal(nIndex) );
874 
875         ::com::sun::star::accessibility::TextSegment aResult;
876 
877         if( AccessibleTextType::PARAGRAPH == aTextType )
878         {
879             if( aPos.nIndex == mpImpl->GetParagraph( aPos.nPara ).getCharacterCount() )
880             {
881                 // #103589# Special casing one behind the last paragraph
882                 aResult.SegmentText = mpImpl->GetParagraph( aPos.nPara ).getText();
883 
884                 // #112814# Adapt the start index with the paragraph offset
885                 aResult.SegmentStart = mpImpl->Internal2Index( EPosition( aPos.nPara, 0 ) );
886             }
887             else if( aPos.nPara > 0 )
888             {
889                 aResult.SegmentText = mpImpl->GetParagraph( aPos.nPara - 1 ).getText();
890 
891                 // #112814# Adapt the start index with the paragraph offset
892                 aResult.SegmentStart = mpImpl->Internal2Index( EPosition( aPos.nPara - 1, 0 ) );
893             }
894 
895             aResult.SegmentEnd = aResult.SegmentStart + aResult.SegmentText.getLength();
896         }
897         else
898         {
899             // No special handling required, forward to wrapped class
900             aResult = mpImpl->GetParagraph( aPos.nPara ).getTextBeforeIndex( aPos.nIndex, aTextType );
901 
902             // #112814# Adapt the start index with the paragraph offset
903             mpImpl->CorrectTextSegment( aResult, aPos.nPara );
904         }
905 
906         return aResult;
907     }
908 
909     ::com::sun::star::accessibility::TextSegment SAL_CALL AccessibleStaticTextBase::getTextBehindIndex( sal_Int32 nIndex, sal_Int16 aTextType ) throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException)
910     {
911         ::vos::OGuard aGuard( Application::GetSolarMutex() );
912 
913         EPosition aPos( mpImpl->Range2Internal(nIndex) );
914 
915         ::com::sun::star::accessibility::TextSegment aResult;
916 
917         if( AccessibleTextType::PARAGRAPH == aTextType )
918         {
919             // Special casing one behind the last paragraph is not
920             // necessary, this case is invalid here for
921             // getTextBehindIndex
922             if( aPos.nPara + 1 < mpImpl->GetParagraphCount() )
923             {
924                 aResult.SegmentText = mpImpl->GetParagraph( aPos.nPara + 1 ).getText();
925 
926                 // #112814# Adapt the start index with the paragraph offset
927                 aResult.SegmentStart = mpImpl->Internal2Index( EPosition( aPos.nPara + 1, 0 ) );
928                 aResult.SegmentEnd = aResult.SegmentStart + aResult.SegmentText.getLength();
929             }
930         }
931         else
932         {
933             // No special handling required, forward to wrapped class
934             aResult = mpImpl->GetParagraph( aPos.nPara ).getTextBehindIndex( aPos.nIndex, aTextType );
935 
936             // #112814# Adapt the start index with the paragraph offset
937             mpImpl->CorrectTextSegment( aResult, aPos.nPara );
938        }
939 
940         return aResult;
941     }
942 
943     sal_Bool SAL_CALL AccessibleStaticTextBase::copyText( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException)
944     {
945         ::vos::OGuard aGuard( Application::GetSolarMutex() );
946 
947         if( nStartIndex > nEndIndex )
948             ::std::swap(nStartIndex, nEndIndex);
949 
950         EPosition aStartIndex( mpImpl->Range2Internal(nStartIndex) );
951         EPosition aEndIndex( mpImpl->Range2Internal(nEndIndex) );
952 
953         return mpImpl->CopyText( aStartIndex.nPara, aStartIndex.nIndex,
954                                  aEndIndex.nPara, aEndIndex.nIndex );
955     }
956 
957     // XAccessibleTextAttributes
958     uno::Sequence< beans::PropertyValue > AccessibleStaticTextBase::getDefaultAttributes( const uno::Sequence< ::rtl::OUString >& RequestedAttributes ) throw (uno::RuntimeException)
959     {
960         // get the intersection of the default attributes of all paragraphs
961 
962         ::vos::OGuard aGuard( Application::GetSolarMutex() );
963 
964         PropertyValueVector aDefAttrVec( mpImpl->GetParagraph( 0 ).getDefaultAttributes( RequestedAttributes ) );
965 
966         const sal_Int32 nParaCount = mpImpl->GetParagraphCount();
967         for ( sal_Int32 nPara = 1; nPara < nParaCount; ++nPara )
968         {
969             uno::Sequence< beans::PropertyValue > aSeq = mpImpl->GetParagraph( nPara ).getDefaultAttributes( RequestedAttributes );
970             PropertyValueVector aIntersectionVec;
971 
972             PropertyValueVector::const_iterator aEnd = aDefAttrVec.end();
973             for ( PropertyValueVector::const_iterator aItr = aDefAttrVec.begin(); aItr != aEnd; ++aItr )
974             {
975                 const beans::PropertyValue* pItr = aSeq.getConstArray();
976                 const beans::PropertyValue* pEnd  = pItr + aSeq.getLength();
977                 const beans::PropertyValue* pFind = ::std::find_if( pItr, pEnd, ::std::bind2nd( PropertyValueEqualFunctor(), boost::cref( *aItr ) ) );
978                 if ( pFind != pEnd )
979                 {
980                     aIntersectionVec.push_back( *pFind );
981                 }
982             }
983 
984             aDefAttrVec.swap( aIntersectionVec );
985 
986             if ( aDefAttrVec.empty() )
987             {
988                 break;
989             }
990         }
991 
992         return aDefAttrVec.getAsConstList();
993     }
994 
995     uno::Sequence< beans::PropertyValue > SAL_CALL AccessibleStaticTextBase::getRunAttributes( sal_Int32 nIndex, const uno::Sequence< ::rtl::OUString >& RequestedAttributes ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException)
996     {
997         // get those default attributes of the paragraph, which are not part
998         // of the intersection of all paragraphs and add them to the run attributes
999 
1000         ::vos::OGuard aGuard( Application::GetSolarMutex() );
1001 
1002         EPosition aPos( mpImpl->Index2Internal( nIndex ) );
1003         AccessibleEditableTextPara& rPara = mpImpl->GetParagraph( aPos.nPara );
1004         uno::Sequence< beans::PropertyValue > aDefAttrSeq = rPara.getDefaultAttributes( RequestedAttributes );
1005         uno::Sequence< beans::PropertyValue > aRunAttrSeq = rPara.getRunAttributes( aPos.nIndex, RequestedAttributes );
1006         uno::Sequence< beans::PropertyValue > aIntersectionSeq = getDefaultAttributes( RequestedAttributes );
1007         PropertyValueVector aDiffVec;
1008 
1009         const beans::PropertyValue* pDefAttr = aDefAttrSeq.getConstArray();
1010         const sal_Int32 nLength = aDefAttrSeq.getLength();
1011         for ( sal_Int32 i = 0; i < nLength; ++i )
1012         {
1013             const beans::PropertyValue* pItr = aIntersectionSeq.getConstArray();
1014             const beans::PropertyValue* pEnd  = pItr + aIntersectionSeq.getLength();
1015             const beans::PropertyValue* pFind = ::std::find_if( pItr, pEnd, ::std::bind2nd( PropertyValueEqualFunctor(), boost::cref( pDefAttr[i] ) ) );
1016             if ( pFind == pEnd && pDefAttr[i].Handle != 0)
1017             {
1018                 aDiffVec.push_back( pDefAttr[i] );
1019             }
1020         }
1021 
1022         return ::comphelper::concatSequences( aRunAttrSeq, aDiffVec.getAsConstList() );
1023     }
1024 
1025     Rectangle AccessibleStaticTextBase::GetParagraphBoundingBox() const
1026     {
1027         return mpImpl->GetParagraphBoundingBox();
1028     }
1029 
1030     sal_Int32 AccessibleStaticTextBase::GetParagraphIndex() const
1031     {
1032         return mpImpl->GetParagraphIndex();
1033     }
1034 
1035     sal_Int32 AccessibleStaticTextBase::GetParagraphCount() const
1036     {
1037         return mpImpl->GetParagraphCount();
1038     }
1039 
1040     sal_Int32 AccessibleStaticTextBase::GetLineCount( sal_Int32 nParagraph ) const
1041     {
1042         return mpImpl->GetLineCount( nParagraph );
1043     }
1044 
1045 }  // end of namespace accessibility
1046 
1047 //------------------------------------------------------------------------
1048