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