xref: /trunk/main/svx/source/accessibility/AccessibleTextHelper.cxx (revision ffd38472365e95f6a578737bc9a5eb0fac624a86)
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 // MARKER(update_precomp.py): autogen include statement, do not remove
23 #include "precompiled_svx.hxx"
24 
25 //------------------------------------------------------------------------
26 // Global header
27 //------------------------------------------------------------------------
28 
29 #include <limits.h>
30 #include <memory>
31 #include <algorithm>
32 #include <deque>
33 #include <vos/mutex.hxx>
34 #include <com/sun/star/uno/Any.hxx>
35 #include <com/sun/star/uno/Reference.hxx>
36 #include <cppuhelper/weakref.hxx>
37 #include <com/sun/star/awt/Point.hpp>
38 #include <com/sun/star/awt/Rectangle.hpp>
39 #include <com/sun/star/lang/DisposedException.hpp>
40 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
41 #include <com/sun/star/accessibility/XAccessible.hpp>
42 #include <com/sun/star/accessibility/XAccessibleContext.hpp>
43 #include <com/sun/star/accessibility/XAccessibleComponent.hpp>
44 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
45 #include <comphelper/accessibleeventnotifier.hxx>
46 #include <unotools/accessiblestatesethelper.hxx>
47 #include <vcl/unohelp.hxx>
48 #include <vcl/svapp.hxx>
49 //add TEXT_SELECTION_CHANGED event
50 #ifndef _TEXTDATA_HXX
51 #include <svtools/textdata.hxx>
52 #endif
53 
54 #include <sfx2/viewfrm.hxx>
55 #include <sfx2/viewsh.hxx>
56 //------------------------------------------------------------------------
57 // Project-local header
58 //------------------------------------------------------------------------
59 #include "AccessibleTextEventQueue.hxx"
60 #include <svx/AccessibleTextHelper.hxx>
61 #include <svx/unoshape.hxx>
62 #include "editeng/unolingu.hxx"
63 #include <editeng/unotext.hxx>
64 
65 #include "editeng/unoedhlp.hxx"
66 #include "editeng/unopracc.hxx"
67 #include "editeng/AccessibleParaManager.hxx"
68 #include "editeng/AccessibleEditableTextPara.hxx"
69 #include <svx/svdmodel.hxx>
70 #include <svx/svdpntv.hxx>
71 #include "../table/cell.hxx"
72 #include "../table/accessiblecell.hxx"
73 #include <editeng/editdata.hxx>
74 #include <editeng/editeng.hxx>
75 #include <editeng/editview.hxx>
76 
77 using namespace ::com::sun::star;
78 using namespace ::com::sun::star::accessibility;
79 
80 namespace accessibility
81 {
82     Window* GetCurrentEditorWnd()
83     {
84         Window* pWin = NULL;
85         SfxViewFrame* pFrame = SfxViewFrame::Current();
86         if (pFrame)
87         {
88             const SfxViewShell * pViewShell = pFrame->GetViewShell();
89             if(pViewShell)
90             {
91                 pWin = pViewShell->GetWindow();
92             }
93         }
94         return pWin;
95     }
96 
97 //------------------------------------------------------------------------
98 // AccessibleTextHelper_Impl declaration
99 //------------------------------------------------------------------------
100 
101     DBG_NAME( AccessibleTextHelper_Impl )
102 
103     template < typename first_type, typename second_type >
104         ::std::pair< first_type, second_type > makeSortedPair( first_type   first,
105                                                                                  second_type    second  )
106     {
107         if( first > second )
108             return ::std::make_pair( second, first );
109         else
110             return ::std::make_pair( first, second );
111     }
112 
113     class AccessibleTextHelper_Impl : public SfxListener
114     {
115 
116     public:
117         typedef ::std::vector< sal_Int16 > VectorOfStates;
118 
119         // receive pointer to our frontend class and view window
120         AccessibleTextHelper_Impl();
121         ~AccessibleTextHelper_Impl();
122 
123         // XAccessibleContext child handling methods
124         sal_Int32 SAL_CALL getAccessibleChildCount() SAL_THROW((uno::RuntimeException));
125         uno::Reference< XAccessible > SAL_CALL getAccessibleChild( sal_Int32 i ) SAL_THROW((lang::IndexOutOfBoundsException, uno::RuntimeException));
126 
127         // XAccessibleEventBroadcaster child related methods
128         void SAL_CALL addEventListener( const uno::Reference< XAccessibleEventListener >& xListener ) SAL_THROW((uno::RuntimeException));
129         void SAL_CALL removeEventListener( const uno::Reference< XAccessibleEventListener >& xListener ) SAL_THROW((uno::RuntimeException));
130 
131         // XAccessibleComponent child related methods
132         uno::Reference< XAccessible > SAL_CALL getAccessibleAtPoint( const awt::Point& aPoint ) SAL_THROW((uno::RuntimeException));
133 
134         SvxEditSourceAdapter& GetEditSource() const SAL_THROW((uno::RuntimeException));
135         void SetEditSource( ::std::auto_ptr< SvxEditSource > pEditSource ) SAL_THROW((uno::RuntimeException));
136 
137         void SetEventSource( const uno::Reference< XAccessible >& rInterface )
138         {
139             DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
140             mxFrontEnd = rInterface;
141         }
142         uno::Reference< XAccessible > GetEventSource() const
143         {
144             DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
145             return mxFrontEnd;
146         }
147 
148         void SetOffset( const Point& );
149         Point GetOffset() const
150         {
151             DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
152             ::osl::MutexGuard aGuard( maMutex ); Point aPoint( maOffset );
153             return aPoint;
154         }
155 
156         void SetStartIndex( sal_Int32 nOffset );
157         sal_Int32 GetStartIndex() const
158         {
159             DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
160             // Strictly correct only with locked solar mutex, // but
161             // here we rely on the fact that sal_Int32 access is
162             // atomic
163             return mnStartIndex;
164         }
165 
166         void SetAdditionalChildStates( const VectorOfStates& rChildStates );
167         const VectorOfStates& GetAdditionalChildStates() const;
168 
169         sal_Bool IsSelected() const;
170 
171         void Dispose();
172 
173         // do NOT hold object mutex when calling this! Danger of deadlock
174         void FireEvent( const sal_Int16 nEventId, const uno::Any& rNewValue = uno::Any(), const uno::Any& rOldValue = uno::Any() ) const;
175         void FireEvent( const AccessibleEventObject& rEvent ) const;
176 
177         void SetFocus( sal_Bool bHaveFocus ) SAL_THROW((::com::sun::star::uno::RuntimeException));
178         sal_Bool HaveFocus() SAL_THROW((::com::sun::star::uno::RuntimeException));
179         void SetChildFocus( sal_Int32 nChild, sal_Bool bHaveFocus ) SAL_THROW((::com::sun::star::uno::RuntimeException));
180         void SetShapeFocus( sal_Bool bHaveFocus ) SAL_THROW((::com::sun::star::uno::RuntimeException));
181         void ChangeChildFocus( sal_Int32 nNewChild ) SAL_THROW((::com::sun::star::uno::RuntimeException));
182 
183 #ifdef DBG_UTIL
184         void CheckInvariants() const;
185 #endif
186 
187         // checks all children for visibility, throws away invisible ones
188         void UpdateVisibleChildren( bool bBroadcastEvents=true );
189 
190         // check all children for changes in position and size
191         void UpdateBoundRect();
192 
193         // calls SetSelection on the forwarder and updates maLastSelection
194         // cache.
195         void UpdateSelection();
196 
197     private:
198 
199         // Process event queue
200         void ProcessQueue();
201 
202         // syntactic sugar for FireEvent
203         void GotPropertyEvent( const uno::Any& rNewValue, const sal_Int16 nEventId ) const { FireEvent( nEventId, rNewValue ); }
204         void LostPropertyEvent( const uno::Any& rOldValue, const sal_Int16 nEventId ) const { FireEvent( nEventId, uno::Any(), rOldValue ); }
205 
206         // shutdown usage of current edit source on myself and the children.
207         void ShutdownEditSource() SAL_THROW((uno::RuntimeException));
208 
209         void ParagraphsMoved( sal_Int32 nFirst, sal_Int32 nMiddle, sal_Int32 nLast );
210 
211         virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint );
212 
213         int getNotifierClientId() const { return mnNotifierClientId; }
214 
215         // lock solar mutex before
216         SvxTextForwarder& GetTextForwarder() const SAL_THROW((uno::RuntimeException));
217         // lock solar mutex before
218         SvxViewForwarder& GetViewForwarder() const SAL_THROW((uno::RuntimeException));
219         // lock solar mutex before
220         SvxEditViewForwarder& GetEditViewForwarder( sal_Bool bCreate = sal_False ) const SAL_THROW((uno::RuntimeException));
221 
222         // are we in edit mode?
223         sal_Bool IsActive() const SAL_THROW((uno::RuntimeException));
224 
225         // our frontend class (the one implementing the actual
226         // interface). That's not necessarily the one containing the impl
227         // pointer!
228         uno::Reference< XAccessible > mxFrontEnd;
229 
230         // a wrapper for the text forwarders (guarded by solar mutex)
231         mutable SvxEditSourceAdapter maEditSource;
232 
233         // store last selection (to correctly report selection changes, guarded by solar mutex)
234         ESelection maLastSelection;
235 
236         // cache range of visible children (guarded by solar mutex)
237         sal_Int32 mnFirstVisibleChild;
238         sal_Int32 mnLastVisibleChild;
239 
240         // offset to add to all our children (unguarded, relying on
241         // the fact that sal_Int32 access is atomic)
242         sal_Int32 mnStartIndex;
243 
244         // the object handling our children (guarded by solar mutex)
245         ::accessibility::AccessibleParaManager maParaManager;
246 
247         // number of not-yet-closed event frames (BEGIN/END sequences) (guarded by solar mutex)
248         sal_Int32 maEventOpenFrames;
249 
250         // Queued events from Notify() (guarded by solar mutex)
251         AccessibleTextEventQueue maEventQueue;
252 
253         // spin lock to prevent notify in notify (guarded by solar mutex)
254         sal_Bool mbInNotify;
255 
256         // whether the object or it's children has the focus set (guarded by solar mutex)
257         sal_Bool mbGroupHasFocus;
258 
259         // whether we (this object) has the focus set (guarded by solar mutex)
260         sal_Bool mbThisHasFocus;
261 
262         mutable ::osl::Mutex maMutex;
263 
264         /// our current offset to the containing shape/cell (guarded by maMutex)
265         Point maOffset;
266 
267         /// client Id from AccessibleEventNotifier
268         int mnNotifierClientId;
269     };
270 
271     //------------------------------------------------------------------------
272     // AccessibleTextHelper_Impl implementation
273     //------------------------------------------------------------------------
274 
275     AccessibleTextHelper_Impl::AccessibleTextHelper_Impl() :
276         mxFrontEnd( NULL ),
277         maLastSelection( EE_PARA_NOT_FOUND,EE_PARA_NOT_FOUND,EE_PARA_NOT_FOUND,EE_PARA_NOT_FOUND ),
278         mnFirstVisibleChild( -1 ),
279         mnLastVisibleChild( -2 ),
280         mnStartIndex( 0 ),
281         maEventOpenFrames( 0 ),
282         mbInNotify( sal_False ),
283         mbGroupHasFocus( sal_False ),
284         mbThisHasFocus( sal_False ),
285         maOffset(0,0),
286         // well, that's strictly exception safe, though not really
287         // robust. We rely on the fact that this member is constructed
288         // last, and that the constructor body is empty, thus no
289         // chance for exceptions once the Id is fetched. Nevertheless,
290         // normally should employ RAII here...
291         mnNotifierClientId(::comphelper::AccessibleEventNotifier::registerClient())
292     {
293         DBG_CTOR( AccessibleTextHelper_Impl, NULL );
294 
295 #ifdef DBG_UTIL
296         OSL_TRACE( "AccessibleTextHelper_Impl received ID: %d", mnNotifierClientId );
297 #endif
298     }
299 
300     AccessibleTextHelper_Impl::~AccessibleTextHelper_Impl()
301     {
302         DBG_DTOR( AccessibleTextHelper_Impl, NULL );
303 
304         ::vos::OGuard aGuard( Application::GetSolarMutex() );
305 
306         try
307         {
308             // call Dispose here, too, since we've some resources not
309             // automatically freed otherwise
310             Dispose();
311         }
312         catch( const uno::Exception& ) {}
313     }
314 
315     SvxTextForwarder& AccessibleTextHelper_Impl::GetTextForwarder() const SAL_THROW((uno::RuntimeException))
316     {
317         DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
318 
319         if( !maEditSource.IsValid() )
320             throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Unknown edit source")), mxFrontEnd);
321 
322         SvxTextForwarder* pTextForwarder = maEditSource.GetTextForwarder();
323 
324         if( !pTextForwarder )
325             throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Unable to fetch text forwarder, model might be dead")), mxFrontEnd);
326 
327         if( pTextForwarder->IsValid() )
328             return *pTextForwarder;
329         else
330             throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Text forwarder is invalid, model might be dead")), mxFrontEnd);
331     }
332 
333     SvxViewForwarder& AccessibleTextHelper_Impl::GetViewForwarder() const SAL_THROW((uno::RuntimeException))
334     {
335         DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
336 
337         if( !maEditSource.IsValid() )
338             throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Unknown edit source")), mxFrontEnd);
339 
340         SvxViewForwarder* pViewForwarder = maEditSource.GetViewForwarder();
341 
342         if( !pViewForwarder )
343             throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Unable to fetch view forwarder, model might be dead")), mxFrontEnd);
344 
345         if( pViewForwarder->IsValid() )
346             return *pViewForwarder;
347         else
348             throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("View forwarder is invalid, model might be dead")), mxFrontEnd);
349     }
350 
351     SvxEditViewForwarder& AccessibleTextHelper_Impl::GetEditViewForwarder( sal_Bool bCreate ) const SAL_THROW((uno::RuntimeException))
352     {
353         DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
354 
355         if( !maEditSource.IsValid() )
356             throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Unknown edit source")), mxFrontEnd);
357 
358         SvxEditViewForwarder* pViewForwarder = maEditSource.GetEditViewForwarder( bCreate );
359 
360         if( !pViewForwarder )
361         {
362             if( bCreate )
363                 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Unable to fetch edit view forwarder, model might be dead")), mxFrontEnd);
364             else
365                 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("No edit view forwarder, object not in edit mode")), mxFrontEnd);
366         }
367 
368         if( pViewForwarder->IsValid() )
369             return *pViewForwarder;
370         else
371         {
372             if( bCreate )
373                 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("View forwarder is invalid, model might be dead")), mxFrontEnd);
374             else
375                 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("View forwarder is invalid, object not in edit mode")), mxFrontEnd);
376         }
377     }
378 
379     SvxEditSourceAdapter& AccessibleTextHelper_Impl::GetEditSource() const SAL_THROW((uno::RuntimeException))
380     {
381         DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
382 
383         if( maEditSource.IsValid() )
384             return maEditSource;
385         else
386             throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("AccessibleTextHelper_Impl::GetEditSource: no edit source")), mxFrontEnd );
387     }
388 
389     sal_Bool AccessibleTextHelper_Impl::IsSelected() const
390     {
391         DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
392 
393         sal_Bool bRet = sal_False;
394 
395         try
396         {
397             ESelection aSelection;
398             bRet = GetEditViewForwarder().GetSelection( aSelection );
399         }
400         catch( const uno::Exception& ) {}
401 
402         return bRet;
403     }
404 
405     // functor for sending child events (no stand-alone function, they are maybe not inlined)
406     class AccessibleTextHelper_OffsetChildIndex : public ::std::unary_function< ::accessibility::AccessibleEditableTextPara&, void >
407     {
408     public:
409         AccessibleTextHelper_OffsetChildIndex( sal_Int32 nDifference ) : mnDifference(nDifference) {}
410         void operator()( ::accessibility::AccessibleEditableTextPara& rPara )
411         {
412             rPara.SetIndexInParent( rPara.GetIndexInParent() + mnDifference );
413         }
414 
415     private:
416         const sal_Int32 mnDifference;
417     };
418 
419     void AccessibleTextHelper_Impl::SetStartIndex( sal_Int32 nOffset )
420     {
421         DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
422 
423         sal_Int32 nOldOffset( mnStartIndex );
424 
425         mnStartIndex = nOffset;
426 
427         if( nOldOffset != nOffset )
428         {
429             // update children
430             AccessibleTextHelper_OffsetChildIndex aFunctor( nOffset - nOldOffset );
431 
432             ::std::for_each( maParaManager.begin(), maParaManager.end(),
433                              AccessibleParaManager::WeakChildAdapter< AccessibleTextHelper_OffsetChildIndex > (aFunctor) );
434         }
435     }
436 
437     void AccessibleTextHelper_Impl::SetAdditionalChildStates( const VectorOfStates& rChildStates )
438     {
439         maParaManager.SetAdditionalChildStates( rChildStates );
440     }
441 
442     const AccessibleTextHelper_Impl::VectorOfStates& AccessibleTextHelper_Impl::GetAdditionalChildStates() const
443     {
444         return maParaManager.GetAdditionalChildStates();
445     }
446 
447     void AccessibleTextHelper_Impl::SetChildFocus( sal_Int32 nChild, sal_Bool bHaveFocus ) SAL_THROW((::com::sun::star::uno::RuntimeException))
448     {
449         DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
450 
451         if( bHaveFocus )
452         {
453             if( mbThisHasFocus )
454                 SetShapeFocus( sal_False );
455 
456             maParaManager.SetFocus( nChild );
457 
458             // we just received the focus, also send caret event then
459             UpdateSelection();
460 
461             DBG_TRACE1("AccessibleTextHelper_Impl::SetChildFocus(): Paragraph %d received focus", nChild );
462         }
463         else
464         {
465             maParaManager.SetFocus( -1 );
466 
467             DBG_TRACE1("AccessibleTextHelper_Impl::SetChildFocus(): Paragraph %d lost focus", nChild );
468 
469             if( mbGroupHasFocus )
470                 SetShapeFocus( sal_True );
471         }
472     }
473 
474     void AccessibleTextHelper_Impl::ChangeChildFocus( sal_Int32 nNewChild ) SAL_THROW((::com::sun::star::uno::RuntimeException))
475     {
476         DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
477 
478         if( mbThisHasFocus )
479             SetShapeFocus( sal_False );
480 
481         mbGroupHasFocus = sal_True;
482         maParaManager.SetFocus( nNewChild );
483 
484         DBG_TRACE1("AccessibleTextHelper_Impl::ChangeChildFocus(): Paragraph %d received focus", nNewChild );
485     }
486 
487     void AccessibleTextHelper_Impl::SetShapeFocus( sal_Bool bHaveFocus ) SAL_THROW((::com::sun::star::uno::RuntimeException))
488     {
489         DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
490 
491         sal_Bool bOldFocus( mbThisHasFocus );
492 
493         mbThisHasFocus = bHaveFocus;
494 
495         if( bOldFocus != bHaveFocus )
496         {
497             if( bHaveFocus )
498             {
499                 if( mxFrontEnd.is() )
500                 {
501                     AccessibleCell* pAccessibleCell = dynamic_cast< AccessibleCell* > ( mxFrontEnd.get() );
502                     if ( !pAccessibleCell )
503                         GotPropertyEvent( uno::makeAny(AccessibleStateType::FOCUSED), AccessibleEventId::STATE_CHANGED );
504                     else    // the focus event on cell should be fired on table directly
505                     {
506                         AccessibleTableShape* pAccTable = pAccessibleCell->GetParentTable();
507                         if (pAccTable)
508                             pAccTable->SetStateDirectly(AccessibleStateType::FOCUSED);
509                     }
510                 }
511                 DBG_TRACE("AccessibleTextHelper_Impl::SetShapeFocus(): Parent object received focus" );
512             }
513             else
514             {
515                 // The focus state should be reset directly on table.
516                 //LostPropertyEvent( uno::makeAny(AccessibleStateType::FOCUSED), AccessibleEventId::STATE_CHANGED );
517                 if( mxFrontEnd.is() )
518                 {
519                     AccessibleCell* pAccessibleCell = dynamic_cast< AccessibleCell* > ( mxFrontEnd.get() );
520                     if ( !pAccessibleCell )
521                             LostPropertyEvent( uno::makeAny(AccessibleStateType::FOCUSED), AccessibleEventId::STATE_CHANGED );
522                     else
523                     {
524                             AccessibleTableShape* pAccTable = pAccessibleCell->GetParentTable();
525                             if (pAccTable)
526                                 pAccTable->ResetStateDirectly(AccessibleStateType::FOCUSED);
527                     }
528                 }
529                 DBG_TRACE("AccessibleTextHelper_Impl::SetShapeFocus(): Parent object lost focus" );
530             }
531         }
532     }
533 
534     void AccessibleTextHelper_Impl::SetFocus( sal_Bool bHaveFocus ) SAL_THROW((::com::sun::star::uno::RuntimeException))
535     {
536         DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
537 
538         sal_Bool bOldFocus( mbGroupHasFocus );
539 
540         mbGroupHasFocus = bHaveFocus;
541 
542         if( IsActive() )
543         {
544             try
545             {
546                 // find the one with the cursor and get/set focus accordingly
547                 ESelection aSelection;
548                 if( GetEditViewForwarder().GetSelection( aSelection ) )
549                     SetChildFocus( aSelection.nEndPara, bHaveFocus );
550             }
551             catch( const uno::Exception& ) {}
552         }
553         else if( bOldFocus != bHaveFocus )
554         {
555             SetShapeFocus( bHaveFocus );
556         }
557 
558         DBG_TRACE2("AccessibleTextHelper_Impl::SetFocus: focus changed, Object %d, state: %s", this, bHaveFocus ? "focused" : "not focused");
559     }
560 
561     sal_Bool AccessibleTextHelper_Impl::HaveFocus() SAL_THROW((::com::sun::star::uno::RuntimeException))
562     {
563         DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
564 
565         // No locking of solar mutex here, since we rely on the fact
566         // that sal_Bool access is atomic
567         return mbThisHasFocus;
568     }
569 
570     sal_Bool AccessibleTextHelper_Impl::IsActive() const SAL_THROW((uno::RuntimeException))
571     {
572         DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
573 
574         try
575         {
576             SvxEditSource& rEditSource = GetEditSource();
577             SvxEditViewForwarder* pViewForwarder = rEditSource.GetEditViewForwarder();
578 
579             if( !pViewForwarder )
580                 return sal_False;
581 
582             if( mxFrontEnd.is() )
583             {
584                 AccessibleCell* pAccessibleCell = dynamic_cast< AccessibleCell* > ( mxFrontEnd.get() );
585                 if ( pAccessibleCell )
586                 {
587                     sdr::table::CellRef xCell = pAccessibleCell->getCellRef();
588                     if ( xCell.is() )
589                         return xCell->IsTextEditActive();
590                 }
591             }
592             if( pViewForwarder->IsValid() )
593                 return sal_True;
594             else
595                 return sal_False;
596         }
597         catch( const uno::RuntimeException& )
598         {
599             return sal_False;
600         }
601     }
602 
603     void AccessibleTextHelper_Impl::UpdateSelection()
604     {
605         DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
606 
607         try
608         {
609             ESelection aSelection;
610             if( GetEditViewForwarder().GetSelection( aSelection ) )
611             {
612                 if( !maLastSelection.IsEqual( aSelection ) &&
613                     aSelection.nEndPara < maParaManager.GetNum() )
614                 {
615                     // #103998# Not that important, changed from assertion to trace
616                     if( mbThisHasFocus )
617                     {
618                         DBG_TRACE("AccessibleTextHelper_Impl::UpdateSelection(): Parent has focus!");
619                     }
620 
621                     sal_uInt32 nMaxValidParaIndex( GetTextForwarder().GetParagraphCount() - 1 );
622 
623                     // notify all affected paragraphs (TODO: may be suboptimal,
624                     // since some paragraphs might stay selected)
625                     if( maLastSelection.nStartPara != EE_PARA_NOT_FOUND )
626                     {
627                         // Did the caret move from one paragraph to another?
628                         // #100530# no caret events if not focused.
629                         if( mbGroupHasFocus &&
630                             maLastSelection.nEndPara != aSelection.nEndPara )
631                         {
632                             if( maLastSelection.nEndPara < maParaManager.GetNum() )
633                             {
634                                 maParaManager.FireEvent( ::std::min( maLastSelection.nEndPara, nMaxValidParaIndex ),
635                                                          ::std::min( maLastSelection.nEndPara, nMaxValidParaIndex )+1,
636                                                          AccessibleEventId::CARET_CHANGED,
637                                                          uno::makeAny(static_cast<sal_Int32>(-1)),
638                                                          uno::makeAny(static_cast<sal_Int32>(maLastSelection.nEndPos)) );
639                             }
640 
641                             ChangeChildFocus( aSelection.nEndPara );
642 
643                             DBG_TRACE3("AccessibleTextHelper_Impl::UpdateSelection(): focus changed, Object: %d, Paragraph: %d, Last paragraph: %d",
644                                        this, aSelection.nEndPara, maLastSelection.nEndPara);
645                         }
646                     }
647 
648                     // #100530# no caret events if not focused.
649                     if( mbGroupHasFocus )
650                     {
651                         uno::Any aOldCursor;
652 
653                         // #i13705# The old cursor can only contain valid
654                         // values if it's the same paragraph!
655                         if( maLastSelection.nStartPara != EE_PARA_NOT_FOUND &&
656                             maLastSelection.nEndPara == aSelection.nEndPara )
657                         {
658                             aOldCursor <<= static_cast<sal_Int32>(maLastSelection.nEndPos);
659                         }
660                         else
661                         {
662                             aOldCursor <<= static_cast<sal_Int32>(-1);
663                         }
664 
665                         maParaManager.FireEvent( aSelection.nEndPara,
666                                                  aSelection.nEndPara+1,
667                                                  AccessibleEventId::CARET_CHANGED,
668                                                  uno::makeAny(static_cast<sal_Int32>(aSelection.nEndPos)),
669                                                  aOldCursor );
670                     }
671 
672                     DBG_TRACE5("AccessibleTextHelper_Impl::UpdateSelection(): caret changed, Object: %d, New pos: %d, Old pos: %d, New para: %d, Old para: %d",
673                                this, aSelection.nEndPos, maLastSelection.nEndPos, aSelection.nEndPara, maLastSelection.nEndPara);
674 
675                     // #108947# Sort new range before calling FireEvent
676                     ::std::pair< xub_StrLen, xub_StrLen > sortedSelection(
677                         makeSortedPair(::std::min( aSelection.nStartPara, nMaxValidParaIndex ),
678                                        ::std::min( aSelection.nEndPara, nMaxValidParaIndex ) ) );
679 
680                     // #108947# Sort last range before calling FireEvent
681                     ::std::pair< xub_StrLen, xub_StrLen > sortedLastSelection(
682                         makeSortedPair(::std::min( maLastSelection.nStartPara, nMaxValidParaIndex ),
683                                        ::std::min( maLastSelection.nEndPara, nMaxValidParaIndex ) ) );
684 
685                     // --> OD 2005-12-15 #i27299#
686                     // event TEXT_SELECTION_CHANGED has to be submitted.
687                     const sal_Int16 nTextSelChgEventId =
688                                     AccessibleEventId::TEXT_SELECTION_CHANGED;
689                     // <--
690                     // #107037# notify selection change
691                     if( maLastSelection.nStartPara == EE_PARA_NOT_FOUND )
692                     {
693                         // last selection is undefined
694                         // --> OD 2005-12-15 #i27299# - use method <ESelection::HasRange()>
695                         if ( aSelection.HasRange() )
696                         // <--
697                         {
698                             // selection was undefined, now is on
699                             maParaManager.FireEvent( sortedSelection.first,
700                                                      sortedSelection.second+1,
701                                                      nTextSelChgEventId );
702                         }
703                     }
704                     else
705                     {
706                         // last selection is valid
707                         // --> OD 2005-12-15 #i27299# - use method <ESelection::HasRange()>
708                         if ( maLastSelection.HasRange() &&
709                              !aSelection.HasRange() )
710                         // <--
711                         {
712                             // selection was on, now is empty
713                             maParaManager.FireEvent( sortedLastSelection.first,
714                                                      sortedLastSelection.second+1,
715                                                      nTextSelChgEventId );
716                         }
717                         // --> OD 2005-12-15 #i27299# - use method <ESelection::HasRange()>
718                         else if( !maLastSelection.HasRange() &&
719                                  aSelection.HasRange() )
720                         // <--
721                         {
722                             // selection was empty, now is on
723                             maParaManager.FireEvent( sortedSelection.first,
724                                                      sortedSelection.second+1,
725                                                      nTextSelChgEventId );
726                         }
727                         // --> OD 2005-12-15 #i27299#
728                         // - no event TEXT_SELECTION_CHANGED event, if new and
729                         //   last selection are empty.
730                         else if ( maLastSelection.HasRange() &&
731                                   aSelection.HasRange() )
732                         // <--
733                         {
734                             // --> OD 2005-12-16 #i27299#
735                             // - send event TEXT_SELECTION_CHANGED for difference
736                             //   between last and new selection.
737 //                            // selection was on, now is different: take union of ranges
738 //                            maParaManager.FireEvent( ::std::min(sortedSelection.first,
739 //                                                           sortedLastSelection.second),
740 //                                                     ::std::max(sortedSelection.first,
741 //                                                           sortedLastSelection.second)+1,
742 //                                                     nTextSelChgEventId );
743                             // use sorted last and new selection
744                             ESelection aTmpLastSel( maLastSelection );
745                             aTmpLastSel.Adjust();
746                             ESelection aTmpSel( aSelection );
747                             aTmpSel.Adjust();
748                             // first submit event for new and changed selection
749                             sal_uInt32 nPara = aTmpSel.nStartPara;
750                             for ( ; nPara <= aTmpSel.nEndPara; ++nPara )
751                             {
752                                 if ( nPara < aTmpLastSel.nStartPara ||
753                                      nPara > aTmpLastSel.nEndPara )
754                                 {
755                                     // new selection on paragraph <nPara>
756                                     maParaManager.FireEvent( nPara,
757                                                              nTextSelChgEventId );
758                                 }
759                                 else
760                                 {
761                                     // check for changed selection on paragraph <nPara>
762                                     const xub_StrLen nParaStartPos =
763                                             nPara == aTmpSel.nStartPara
764                                             ? aTmpSel.nStartPos : 0;
765                                     const xub_StrLen nParaEndPos =
766                                             nPara == aTmpSel.nEndPara
767                                             ? aTmpSel.nEndPos : STRING_LEN;
768                                     const xub_StrLen nLastParaStartPos =
769                                             nPara == aTmpLastSel.nStartPara
770                                             ? aTmpLastSel.nStartPos : 0;
771                                     const xub_StrLen nLastParaEndPos =
772                                             nPara == aTmpLastSel.nEndPara
773                                             ? aTmpLastSel.nEndPos : STRING_LEN;
774                                     if ( nParaStartPos != nLastParaStartPos ||
775                                          nParaEndPos != nLastParaEndPos )
776                                     {
777                                         maParaManager.FireEvent(
778                                                     nPara, nTextSelChgEventId );
779                                     }
780                                 }
781                             }
782                             // second submit event for 'old' selections
783                             nPara = aTmpLastSel.nStartPara;
784                             for ( ; nPara <= aTmpLastSel.nEndPara; ++nPara )
785                             {
786                                 if ( nPara < aTmpSel.nStartPara ||
787                                      nPara > aTmpSel.nEndPara )
788                                 {
789                                     maParaManager.FireEvent( nPara,
790                                                              nTextSelChgEventId );
791                                 }
792                             }
793                         }
794                     }
795 
796                     maLastSelection = aSelection;
797                 }
798             }
799         }
800         // no selection? no update actions
801         catch( const uno::RuntimeException& ) {}
802     }
803 
804     void AccessibleTextHelper_Impl::ShutdownEditSource() SAL_THROW((uno::RuntimeException))
805     {
806         DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
807 
808         // This should only be called with solar mutex locked, i.e. from the main office thread
809 
810         // This here is somewhat clumsy: As soon as our children have
811         // a NULL EditSource (maParaManager.SetEditSource()), they
812         // enter the disposed state and cannot be reanimated. Thus, it
813         // is unavoidable and a hard requirement to let go and create
814         // from scratch each and every child.
815 
816         // invalidate children
817         maParaManager.Dispose();
818         maParaManager.SetNum(0);
819 
820         // lost all children
821         if( mxFrontEnd.is() )
822             FireEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN);
823 
824         // quit listen on stale edit source
825         if( maEditSource.IsValid() )
826             EndListening( maEditSource.GetBroadcaster() );
827 
828         maEditSource.SetEditSource( ::std::auto_ptr< SvxEditSource >(NULL) );
829     }
830 
831     void AccessibleTextHelper_Impl::SetEditSource( ::std::auto_ptr< SvxEditSource > pEditSource ) SAL_THROW((uno::RuntimeException))
832     {
833         DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
834 
835         // This should only be called with solar mutex locked, i.e. from the main office thread
836 
837         // shutdown old edit source
838         ShutdownEditSource();
839 
840         // set new edit source
841         maEditSource.SetEditSource( pEditSource );
842 
843         // init child vector to the current child count
844         if( maEditSource.IsValid() )
845         {
846             maParaManager.SetNum( GetTextForwarder().GetParagraphCount() );
847 
848             // listen on new edit source
849             StartListening( maEditSource.GetBroadcaster() );
850 
851             UpdateVisibleChildren();
852         }
853     }
854 
855     void AccessibleTextHelper_Impl::SetOffset( const Point& rPoint )
856     {
857         DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
858 
859         // guard against non-atomic access to maOffset data structure
860         {
861             ::osl::MutexGuard aGuard( maMutex );
862             maOffset = rPoint;
863         }
864 
865         maParaManager.SetEEOffset( rPoint );
866 
867         // in all cases, check visibility afterwards.
868         UpdateVisibleChildren();
869         UpdateBoundRect();
870     }
871 
872     void AccessibleTextHelper_Impl::UpdateVisibleChildren( bool bBroadcastEvents )
873     {
874         DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
875 
876         try
877         {
878             SvxTextForwarder& rCacheTF = GetTextForwarder();
879             SvxViewForwarder& rCacheVF = GetViewForwarder();
880 
881             Rectangle aViewArea = rCacheVF.GetVisArea();
882 
883             if( IsActive() )
884             {
885                 // maybe the edit view scrolls, adapt aViewArea
886                 Rectangle aEditViewArea = GetEditViewForwarder().GetVisArea();
887                 aViewArea += aEditViewArea.TopLeft();
888 
889                 // now determine intersection
890                 aViewArea.Intersection( aEditViewArea );
891             }
892 
893             Rectangle aTmpBB, aParaBB;
894             sal_Bool bFirstChild = sal_True;
895             sal_Int32 nCurrPara;
896             sal_Int32 nParas=rCacheTF.GetParagraphCount();
897 
898             mnFirstVisibleChild = -1;
899             mnLastVisibleChild = -2;
900 
901             for( nCurrPara=0; nCurrPara<nParas; ++nCurrPara )
902             {
903                 DBG_ASSERT(nCurrPara >= 0 && nCurrPara <= USHRT_MAX,
904                            "AccessibleTextHelper_Impl::UpdateVisibleChildren: index value overflow");
905 
906                 aTmpBB = rCacheTF.GetParaBounds( nCurrPara );
907 
908                 // convert to screen coordinates
909                 aParaBB = ::accessibility::AccessibleEditableTextPara::LogicToPixel( aTmpBB, rCacheTF.GetMapMode(), rCacheVF );
910 
911                     // at least partially visible
912                     if( bFirstChild )
913                     {
914                         bFirstChild = sal_False;
915                         mnFirstVisibleChild = nCurrPara;
916                     }
917 
918                     mnLastVisibleChild = nCurrPara;
919 
920                     // child not yet created?
921                     ::accessibility::AccessibleParaManager::WeakChild aChild( maParaManager.GetChild(nCurrPara) );
922                     if( aChild.second.Width == 0 &&
923                         aChild.second.Height == 0 &&
924                         mxFrontEnd.is() &&
925                         bBroadcastEvents )
926                     {
927                         GotPropertyEvent( uno::makeAny( maParaManager.CreateChild( nCurrPara - mnFirstVisibleChild,
928                                                                                    mxFrontEnd, GetEditSource(), nCurrPara ).first ),
929                                           AccessibleEventId::CHILD );
930                     }
931             }
932         }
933         catch( const uno::Exception& )
934         {
935             DBG_ERROR("AccessibleTextHelper_Impl::UpdateVisibleChildren error while determining visible children");
936 
937             // something failed - currently no children
938             mnFirstVisibleChild = -1;
939             mnLastVisibleChild = -2;
940             maParaManager.SetNum(0);
941 
942             // lost all children
943             if( bBroadcastEvents )
944                 FireEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN);
945         }
946     }
947 
948     // functor for checking changes in paragraph bounding boxes (no stand-alone function, maybe not inlined)
949     class AccessibleTextHelper_UpdateChildBounds : public ::std::unary_function< const ::accessibility::AccessibleParaManager::WeakChild&,
950         ::accessibility::AccessibleParaManager::WeakChild >
951     {
952     public:
953         AccessibleTextHelper_UpdateChildBounds( AccessibleTextHelper_Impl& rImpl ) : mrImpl(rImpl) {}
954         ::accessibility::AccessibleParaManager::WeakChild operator()( const ::accessibility::AccessibleParaManager::WeakChild& rChild )
955         {
956             // retrieve hard reference from weak one
957             ::accessibility::AccessibleParaManager::WeakPara::HardRefType aHardRef( rChild.first.get() );
958 
959             if( aHardRef.is() )
960             {
961                 awt::Rectangle          aNewRect = aHardRef->getBounds();
962                 const awt::Rectangle&   aOldRect = rChild.second;
963 
964                 if( aNewRect.X != aOldRect.X ||
965                     aNewRect.Y != aOldRect.Y ||
966                     aNewRect.Width != aOldRect.Width ||
967                     aNewRect.Height != aOldRect.Height )
968                 {
969                     // visible data changed
970                     aHardRef->FireEvent( AccessibleEventId::BOUNDRECT_CHANGED );
971 
972                     // update internal bounds
973                     return ::accessibility::AccessibleParaManager::WeakChild( rChild.first, aNewRect );
974                 }
975             }
976 
977             // identity transform
978             return rChild;
979         }
980 
981     private:
982         AccessibleTextHelper_Impl&  mrImpl;
983     };
984 
985     void AccessibleTextHelper_Impl::UpdateBoundRect()
986     {
987         DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
988 
989         // send BOUNDRECT_CHANGED to affected children
990         AccessibleTextHelper_UpdateChildBounds aFunctor( *this );
991         ::std::transform( maParaManager.begin(), maParaManager.end(), maParaManager.begin(), aFunctor );
992     }
993 
994 #ifdef DBG_UTIL
995     void AccessibleTextHelper_Impl::CheckInvariants() const
996     {
997         if( mnFirstVisibleChild >= 0 &&
998             mnFirstVisibleChild > mnLastVisibleChild )
999         {
1000             DBG_ERROR( "AccessibleTextHelper: range invalid" );
1001         }
1002     }
1003 #endif
1004 
1005     // functor for sending child events (no stand-alone function, they are maybe not inlined)
1006     class AccessibleTextHelper_LostChildEvent : public ::std::unary_function< const ::accessibility::AccessibleParaManager::WeakChild&, void >
1007     {
1008     public:
1009         AccessibleTextHelper_LostChildEvent( AccessibleTextHelper_Impl& rImpl ) : mrImpl(rImpl) {}
1010         void operator()( const ::accessibility::AccessibleParaManager::WeakChild& rPara )
1011         {
1012             // retrieve hard reference from weak one
1013             ::accessibility::AccessibleParaManager::WeakPara::HardRefType aHardRef( rPara.first.get() );
1014 
1015             if( aHardRef.is() )
1016                 mrImpl.FireEvent(AccessibleEventId::CHILD, uno::Any(), uno::makeAny( aHardRef.getRef() ) );
1017         }
1018 
1019     private:
1020         AccessibleTextHelper_Impl&  mrImpl;
1021     };
1022 
1023     void AccessibleTextHelper_Impl::ParagraphsMoved( sal_Int32 nFirst, sal_Int32 nMiddle, sal_Int32 nLast )
1024     {
1025         DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
1026 
1027         const sal_Int32 nParas = GetTextForwarder().GetParagraphCount();
1028 
1029         /* rotate paragraphs
1030          * =================
1031          *
1032          * Three cases:
1033          *
1034          * 1.
1035          *   ... nParagraph ... nParam1 ... nParam2 ...
1036          *       |______________[xxxxxxxxxxx]
1037          *              becomes
1038          *       [xxxxxxxxxxx]|______________
1039          *
1040          * tail is 0
1041          *
1042          * 2.
1043          *   ... nParam1 ... nParagraph ... nParam2 ...
1044          *       [xxxxxxxxxxx|xxxxxxxxxxxxxx]____________
1045          *              becomes
1046          *       ____________[xxxxxxxxxxx|xxxxxxxxxxxxxx]
1047          *
1048          * tail is nParagraph - nParam1
1049          *
1050          * 3.
1051          *   ... nParam1 ... nParam2 ... nParagraph ...
1052          *       [xxxxxxxxxxx]___________|____________
1053          *              becomes
1054          *       ___________|____________[xxxxxxxxxxx]
1055          *
1056          * tail is nParam2 - nParam1
1057          */
1058 
1059         // sort nParagraph, nParam1 and nParam2 in ascending order, calc range
1060         if( nMiddle < nFirst )
1061         {
1062             ::std::swap(nFirst, nMiddle);
1063         }
1064         else if( nMiddle < nLast )
1065         {
1066             nLast = nLast + nMiddle - nFirst;
1067         }
1068         else
1069         {
1070             ::std::swap(nMiddle, nLast);
1071             nLast = nLast + nMiddle - nFirst;
1072         }
1073 
1074         if( nFirst < nParas && nMiddle < nParas && nLast < nParas )
1075         {
1076             // since we have no "paragraph index
1077             // changed" event on UAA, remove
1078             // [first,last] and insert again later (in
1079             // UpdateVisibleChildren)
1080 
1081             // maParaManager.Rotate( nFirst, nMiddle, nLast );
1082 
1083             // send CHILD_EVENT to affected children
1084             ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator begin = maParaManager.begin();
1085             ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator end = begin;
1086 
1087             ::std::advance( begin, nFirst );
1088             ::std::advance( end, nLast+1 );
1089 
1090             // TODO: maybe optimize here in the following way.  If the
1091             // number of removed children exceeds a certain threshold,
1092             // use INVALIDATE_CHILDREN
1093             AccessibleTextHelper_LostChildEvent aFunctor( *this );
1094 
1095             ::std::for_each( begin, end, aFunctor );
1096 
1097             maParaManager.Release(nFirst, nLast+1);
1098             // should be no need for UpdateBoundRect, since all affected children are cleared.
1099         }
1100     }
1101 
1102     // functor for sending child events (no stand-alone function, they are maybe not inlined)
1103     class AccessibleTextHelper_ChildrenTextChanged : public ::std::unary_function< ::accessibility::AccessibleEditableTextPara&, void >
1104     {
1105     public:
1106         void operator()( ::accessibility::AccessibleEditableTextPara& rPara )
1107         {
1108             rPara.TextChanged();
1109         }
1110     };
1111 
1112     /** functor processing queue events
1113 
1114         Reacts on TEXT_HINT_PARAINSERTED/REMOVED events and stores
1115         their content
1116      */
1117     class AccessibleTextHelper_QueueFunctor : public ::std::unary_function< const SfxHint*, void >
1118     {
1119     public:
1120         AccessibleTextHelper_QueueFunctor() :
1121             mnParasChanged( 0 ),
1122             mnParaIndex(-1),
1123             mnHintId(-1)
1124         {}
1125         void operator()( const SfxHint* pEvent )
1126         {
1127             if( pEvent &&
1128                 mnParasChanged != -1 )
1129             {
1130                 // determine hint type
1131                 const TextHint* pTextHint = PTR_CAST( TextHint, pEvent );
1132                 const SvxEditSourceHint* pEditSourceHint = PTR_CAST( SvxEditSourceHint, pEvent );
1133 
1134                 if( !pEditSourceHint && pTextHint &&
1135                     (pTextHint->GetId() == TEXT_HINT_PARAINSERTED ||
1136                      pTextHint->GetId() == TEXT_HINT_PARAREMOVED ) )
1137                 {
1138                     if( pTextHint->GetValue() == EE_PARA_ALL )
1139                     {
1140                         mnParasChanged = -1;
1141                     }
1142                     else
1143                     {
1144                         mnHintId = pTextHint->GetId();
1145                         mnParaIndex = pTextHint->GetValue();
1146                         ++mnParasChanged;
1147                     }
1148                 }
1149             }
1150         }
1151 
1152         /** Query number of paragraphs changed during queue processing.
1153 
1154             @return number of changed paragraphs, -1 for
1155             "every paragraph changed"
1156         */
1157         int GetNumberOfParasChanged() { return mnParasChanged; }
1158         /** Query index of last added/removed paragraph
1159 
1160             @return index of lastly added paragraphs, -1 for none
1161             added so far.
1162         */
1163         int GetParaIndex() { return mnParaIndex; }
1164         /** Query hint id of last interesting event
1165 
1166             @return hint id of last interesting event (REMOVED/INSERTED).
1167         */
1168         int GetHintId() { return mnHintId; }
1169 
1170     private:
1171         /** number of paragraphs changed during queue processing. -1 for
1172             "every paragraph changed"
1173         */
1174         int mnParasChanged;
1175         /// index of paragraph added/removed last
1176         int mnParaIndex;
1177         /// TextHint ID (removed/inserted) of last interesting event
1178         int mnHintId;
1179     };
1180 
1181     void AccessibleTextHelper_Impl::ProcessQueue()
1182     {
1183         DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
1184 
1185         // inspect queue for paragraph insert/remove events. If there
1186         // is exactly _one_ of those in the queue, and the number of
1187         // paragraphs has changed by exactly one, use that event to
1188         // determine a priori which paragraph was added/removed. This
1189         // is necessary, since I must sync right here with the
1190         // EditEngine state (number of paragraphs etc.), since I'm
1191         // potentially sending listener events right away.
1192         AccessibleTextHelper_QueueFunctor aFunctor;
1193         maEventQueue.ForEach( aFunctor );
1194 
1195         const sal_Int32 nNewParas( GetTextForwarder().GetParagraphCount() );
1196         const sal_Int32 nCurrParas( maParaManager.GetNum() );
1197 
1198         // whether every paragraph already is updated (no need to
1199         // repeat that later on, e.g. for PARA_MOVED events)
1200         bool            bEverythingUpdated( false );
1201 
1202         if( labs( nNewParas - nCurrParas ) == 1 &&
1203             aFunctor.GetNumberOfParasChanged() == 1 )
1204         {
1205             // #103483# Exactly one paragraph added/removed. This is
1206             // the normal case, optimize event handling here.
1207 
1208             if( aFunctor.GetHintId() == TEXT_HINT_PARAINSERTED )
1209             {
1210                 // update num of paras
1211                 maParaManager.SetNum( nNewParas );
1212 
1213                 // release everything from the insertion position until the end
1214                 maParaManager.Release(aFunctor.GetParaIndex(), nCurrParas);
1215 
1216                 // TODO: Clarify whether this behavior _really_ saves
1217                 // anybody anything!
1218                 // update children, _don't_ broadcast
1219                 UpdateVisibleChildren( false );
1220                 UpdateBoundRect();
1221 
1222                 // send insert event
1223                 // #109864# Enforce creation of this paragraph
1224                 try
1225                 {
1226                     GotPropertyEvent( uno::makeAny( getAccessibleChild( aFunctor.GetParaIndex() -
1227                                                                         mnFirstVisibleChild + GetStartIndex() ) ),
1228                                       AccessibleEventId::CHILD );
1229                 }
1230                 catch( const uno::Exception& )
1231                 {
1232                     DBG_ERROR("AccessibleTextHelper_Impl::ProcessQueue: could not create new paragraph");
1233                 }
1234             }
1235             else if( aFunctor.GetHintId() == TEXT_HINT_PARAREMOVED )
1236             {
1237                 ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator begin = maParaManager.begin();
1238                 ::std::advance( begin, aFunctor.GetParaIndex() );
1239                 ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator end = begin;
1240                 ::std::advance( end, 1 );
1241 
1242                 // #i61812# remember para to be removed for later notification
1243                 // AFTER the new state is applied (that after the para got removed)
1244                 ::uno::Reference< XAccessible > xPara;
1245                 ::accessibility::AccessibleParaManager::WeakPara::HardRefType aHardRef( begin->first.get() );
1246                 if( aHardRef.is() )
1247                     xPara = ::uno::Reference< XAccessible >( aHardRef.getRef(), ::uno::UNO_QUERY );
1248 
1249                 // release everything from the remove position until the end
1250                 maParaManager.Release(aFunctor.GetParaIndex(), nCurrParas);
1251 
1252                 // update num of paras
1253                 maParaManager.SetNum( nNewParas );
1254 
1255                 // TODO: Clarify whether this behavior _really_ saves
1256                 // anybody anything!
1257                 // update children, _don't_ broadcast
1258                 UpdateVisibleChildren( false );
1259                 UpdateBoundRect();
1260 
1261                 // #i61812# notification for removed para
1262                 if (xPara.is())
1263                     FireEvent(AccessibleEventId::CHILD, uno::Any(), uno::makeAny( xPara) );
1264             }
1265 #ifdef DBG_UTIL
1266             else
1267                 DBG_ERROR("AccessibleTextHelper_Impl::ProcessQueue() invalid hint id");
1268 #endif
1269         }
1270         else if( nNewParas != nCurrParas )
1271         {
1272             // release all paras
1273             maParaManager.Release(0, nCurrParas);
1274 
1275             // update num of paras
1276             maParaManager.SetNum( nNewParas );
1277 
1278             // #109864# create from scratch, don't broadcast
1279             UpdateVisibleChildren( false );
1280             UpdateBoundRect();
1281 
1282             // number of paragraphs somehow changed - but we have no
1283             // chance determining how. Thus, throw away everything and
1284             // create from scratch.
1285             // (child events should be broadcast after the changes are done...)
1286             FireEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN);
1287 
1288             // no need for further updates later on
1289             bEverythingUpdated = true;
1290         }
1291 
1292         while( !maEventQueue.IsEmpty() )
1293         {
1294             ::std::auto_ptr< SfxHint > pHint( maEventQueue.PopFront() );
1295             if( pHint.get() )
1296             {
1297                 const SfxHint& rHint = *(pHint.get());
1298 
1299                 // determine hint type
1300                 const SdrHint* pSdrHint = PTR_CAST( SdrHint, &rHint );
1301                 const SfxSimpleHint* pSimpleHint = PTR_CAST( SfxSimpleHint, &rHint );
1302                 const TextHint* pTextHint = PTR_CAST( TextHint, &rHint );
1303                 const SvxViewHint* pViewHint = PTR_CAST( SvxViewHint, &rHint );
1304                 const SvxEditSourceHint* pEditSourceHint = PTR_CAST( SvxEditSourceHint, &rHint );
1305 
1306                 try
1307                 {
1308                     const sal_Int32 nParas = GetTextForwarder().GetParagraphCount();
1309 
1310                     if( pEditSourceHint )
1311                     {
1312                         switch( pEditSourceHint->GetId() )
1313                         {
1314                             case EDITSOURCE_HINT_PARASMOVED:
1315                             {
1316                                 DBG_ASSERT( pEditSourceHint->GetStartValue() < GetTextForwarder().GetParagraphCount() &&
1317                                             pEditSourceHint->GetEndValue() < GetTextForwarder().GetParagraphCount(),
1318                                             "AccessibleTextHelper_Impl::NotifyHdl: Invalid notification");
1319 
1320                                 if( !bEverythingUpdated )
1321                                 {
1322                                     ParagraphsMoved(pEditSourceHint->GetStartValue(),
1323                                                     pEditSourceHint->GetValue(),
1324                                                     pEditSourceHint->GetEndValue());
1325 
1326                                     // in all cases, check visibility afterwards.
1327                                     UpdateVisibleChildren();
1328                                 }
1329                                 break;
1330                             }
1331 
1332                             case EDITSOURCE_HINT_SELECTIONCHANGED:
1333                                 // notify listeners
1334                                 try
1335                                 {
1336                                     UpdateSelection();
1337                                 }
1338                                 // maybe we're not in edit mode (this is not an error)
1339                                 catch( const uno::Exception& ) {}
1340                                 break;
1341                         }
1342                     }
1343                     else if( pTextHint )
1344                     {
1345                         switch( pTextHint->GetId() )
1346                         {
1347                             case TEXT_HINT_MODIFIED:
1348                             {
1349                                 // notify listeners
1350                                 sal_Int32 nPara( pTextHint->GetValue() );
1351 
1352                                 // #108900# Delegate change event to children
1353                                 AccessibleTextHelper_ChildrenTextChanged aNotifyChildrenFunctor;
1354 
1355                                 if( nPara == static_cast<sal_Int32>(EE_PARA_ALL) )
1356                                 {
1357                                     // #108900# Call every child
1358                                     ::std::for_each( maParaManager.begin(), maParaManager.end(),
1359                                                      AccessibleParaManager::WeakChildAdapter< AccessibleTextHelper_ChildrenTextChanged > (aNotifyChildrenFunctor) );
1360                                 }
1361                                 else
1362                                     if( nPara < nParas )
1363                                     {
1364                                         // #108900# Call child at index nPara
1365                                         ::std::for_each( maParaManager.begin()+nPara, maParaManager.begin()+nPara+1,
1366                                                          AccessibleParaManager::WeakChildAdapter< AccessibleTextHelper_ChildrenTextChanged > (aNotifyChildrenFunctor) );
1367                                     }
1368                                 break;
1369                             }
1370 
1371                             case TEXT_HINT_PARAINSERTED:
1372                                 // already happened above
1373                                 break;
1374 
1375                             case TEXT_HINT_PARAREMOVED:
1376                                 // already happened above
1377                                 break;
1378 
1379                             case TEXT_HINT_TEXTHEIGHTCHANGED:
1380                                 // visibility changed, done below
1381                                 break;
1382 
1383                             case TEXT_HINT_VIEWSCROLLED:
1384                                 // visibility changed, done below
1385                                 break;
1386                         }
1387 
1388                         // in all cases, check visibility afterwards.
1389                         UpdateVisibleChildren();
1390                         UpdateBoundRect();
1391                     }
1392                     else if( pViewHint )
1393                     {
1394                         switch( pViewHint->GetHintType() )
1395                         {
1396                             case SvxViewHint::SVX_HINT_VIEWCHANGED:
1397                                 // just check visibility
1398                                 UpdateVisibleChildren();
1399                                 UpdateBoundRect();
1400                                 break;
1401                         }
1402                     }
1403                     else if( pSdrHint )
1404                     {
1405                         switch( pSdrHint->GetKind() )
1406                         {
1407                             case HINT_BEGEDIT:
1408                             {
1409                                 if(!IsActive())
1410                                 {
1411                                     break;
1412                                 }
1413                                 // change children state
1414                                 maParaManager.SetActive();
1415 
1416                                 // per definition, edit mode text has the focus
1417                                 SetFocus( sal_True );
1418                                 break;
1419                             }
1420 
1421                             case HINT_ENDEDIT:
1422                             {
1423                                 // focused child now looses focus
1424                                 ESelection aSelection;
1425                                 if( GetEditViewForwarder().GetSelection( aSelection ) )
1426                                     SetChildFocus( aSelection.nEndPara, sal_False );
1427 
1428                                 // change children state
1429                                 maParaManager.SetActive( sal_False );
1430 
1431                                 maLastSelection = ESelection( EE_PARA_NOT_FOUND, EE_PARA_NOT_FOUND,
1432                                                               EE_PARA_NOT_FOUND, EE_PARA_NOT_FOUND);
1433                                 break;
1434                             }
1435                             default:
1436                                 break;
1437                         }
1438                     }
1439                     // it's VITAL to keep the SfxSimpleHint last! It's the base of some classes above!
1440                     else if( pSimpleHint )
1441                     {
1442                         switch( pSimpleHint->GetId() )
1443                         {
1444                             case SFX_HINT_DYING:
1445                                 // edit source is dying under us, become defunc then
1446                                 try
1447                                 {
1448                                     // make edit source inaccessible
1449                                     // Note: cannot destroy it here, since we're called from there!
1450                                     ShutdownEditSource();
1451                                 }
1452                                 catch( const uno::Exception& ) {}
1453 
1454                                 break;
1455                         }
1456                     }
1457                 }
1458                 catch( const uno::Exception& )
1459                 {
1460 #ifdef DBG_UTIL
1461                     OSL_TRACE("AccessibleTextHelper_Impl::ProcessQueue: Unhandled exception.");
1462 #endif
1463                 }
1464             }
1465         }
1466     }
1467 
1468     void AccessibleTextHelper_Impl::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
1469     {
1470         DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
1471 
1472         // precondition: solar mutex locked
1473         DBG_TESTSOLARMUTEX();
1474 
1475         // precondition: not in a recursion
1476         if( mbInNotify )
1477             return;
1478 
1479         mbInNotify = sal_True;
1480 
1481         // determine hint type
1482         const SdrHint* pSdrHint = PTR_CAST( SdrHint, &rHint );
1483         const SfxSimpleHint* pSimpleHint = PTR_CAST( SfxSimpleHint, &rHint );
1484         const TextHint* pTextHint = PTR_CAST( TextHint, &rHint );
1485         const SvxViewHint* pViewHint = PTR_CAST( SvxViewHint, &rHint );
1486         const SvxEditSourceHint* pEditSourceHint = PTR_CAST( SvxEditSourceHint, &rHint );
1487 
1488         try
1489         {
1490             // Process notification event
1491             if( pEditSourceHint )
1492             {
1493                 maEventQueue.Append( *pEditSourceHint );
1494                 // --> OD 2005-12-19 #i27299#
1495                 if( maEventOpenFrames == 0 )
1496                     ProcessQueue();
1497                 // <--
1498             }
1499             else if( pTextHint )
1500             {
1501                 switch( pTextHint->GetId() )
1502                 {
1503                     case TEXT_HINT_BLOCKNOTIFICATION_END:
1504                     case TEXT_HINT_INPUT_END:
1505                         --maEventOpenFrames;
1506 
1507                         if( maEventOpenFrames == 0 )
1508                         {
1509                             // #103483#
1510                             /* All information should have arrived
1511                              * now, process queue. As stated in the
1512                              * above bug, we can often avoid throwing
1513                              * away all paragraphs by looking forward
1514                              * in the event queue (searching for
1515                              * PARAINSERT/REMOVE events). Furthermore,
1516                              * processing the event queue only at the
1517                              * end of an interaction cycle, ensures
1518                              * that the EditEngine state and the
1519                              * AccessibleText state are the same
1520                              * (well, mostly. If there are _multiple_
1521                              * interaction cycles in the EE queues, it
1522                              * can still happen that EE state is
1523                              * different. That's so to say broken by
1524                              * design with that delayed EE event
1525                              * concept).
1526                              */
1527                             ProcessQueue();
1528                         }
1529                         break;
1530 
1531                     case TEXT_HINT_BLOCKNOTIFICATION_START:
1532                     case TEXT_HINT_INPUT_START:
1533                         ++maEventOpenFrames;
1534                         // --> OD 2005-12-19 #i27299# - no FALLTHROUGH
1535                         // reason: event will not be processes, thus appending
1536                         // the event isn't necessary.
1537                         break;
1538                         // <--
1539                     default:
1540                         maEventQueue.Append( *pTextHint );
1541                         // --> OD 2005-12-19 #i27299#
1542                         if( maEventOpenFrames == 0 )
1543                             ProcessQueue();
1544                         // <--
1545                         break;
1546                 }
1547             }
1548             else if( pViewHint )
1549             {
1550                 maEventQueue.Append( *pViewHint );
1551 
1552                 // process visibility right away, if not within an
1553                 // open EE notification frame. Otherwise, event
1554                 // processing would be delayed until next EE
1555                 // notification sequence.
1556                 if( maEventOpenFrames == 0 )
1557                     ProcessQueue();
1558             }
1559             else if( pSdrHint )
1560             {
1561                 maEventQueue.Append( *pSdrHint );
1562 
1563                 // process drawing layer events right away, if not
1564                 // within an open EE notification frame. Otherwise,
1565                 // event processing would be delayed until next EE
1566                 // notification sequence.
1567                 if( maEventOpenFrames == 0 )
1568                     ProcessQueue();
1569             }
1570             // it's VITAL to keep the SfxSimpleHint last! It's the base of some classes above!
1571             else if( pSimpleHint )
1572             {
1573                 // handle this event _at once_, because after that, objects are invalid
1574                 switch( pSimpleHint->GetId() )
1575                 {
1576                     case SFX_HINT_DYING:
1577                         // edit source is dying under us, become defunc then
1578                         maEventQueue.Clear();
1579                         try
1580                         {
1581                             // make edit source inaccessible
1582                             // Note: cannot destroy it here, since we're called from there!
1583                             ShutdownEditSource();
1584                         }
1585                         catch( const uno::Exception& ) {}
1586 
1587                         break;
1588                 }
1589             }
1590         }
1591         catch( const uno::Exception& )
1592         {
1593 #ifdef DBG_UTIL
1594             OSL_TRACE("AccessibleTextHelper_Impl::Notify: Unhandled exception.");
1595 #endif
1596             mbInNotify = sal_False;
1597         }
1598 
1599         mbInNotify = sal_False;
1600     }
1601 
1602     void AccessibleTextHelper_Impl::Dispose()
1603     {
1604         DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
1605 
1606         if( getNotifierClientId() != -1 )
1607         {
1608             try
1609             {
1610                 // #106234# Unregister from EventNotifier
1611                 ::comphelper::AccessibleEventNotifier::revokeClient( getNotifierClientId() );
1612 #ifdef DBG_UTIL
1613                 OSL_TRACE( "AccessibleTextHelper_Impl disposed ID: %d", mnNotifierClientId );
1614 #endif
1615             }
1616             catch( const uno::Exception& ) {}
1617 
1618             mnNotifierClientId = -1;
1619         }
1620 
1621         try
1622         {
1623             // dispose children
1624             maParaManager.Dispose();
1625         }
1626         catch( const uno::Exception& ) {}
1627 
1628         // quit listen on stale edit source
1629         if( maEditSource.IsValid() )
1630             EndListening( maEditSource.GetBroadcaster() );
1631 
1632         // clear references
1633         maEditSource.SetEditSource( ::std::auto_ptr< SvxEditSource >(NULL) );
1634         mxFrontEnd = NULL;
1635     }
1636 
1637     void AccessibleTextHelper_Impl::FireEvent( const sal_Int16 nEventId, const uno::Any& rNewValue, const uno::Any& rOldValue ) const
1638     {
1639         DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
1640 
1641         // -- object locked --
1642         ::osl::ClearableMutexGuard aGuard( maMutex );
1643 
1644         AccessibleEventObject aEvent;
1645 
1646         DBG_ASSERT(mxFrontEnd.is(), "AccessibleTextHelper::FireEvent: no event source set" );
1647 
1648         if( mxFrontEnd.is() )
1649             aEvent = AccessibleEventObject(mxFrontEnd->getAccessibleContext(), nEventId, rNewValue, rOldValue);
1650         else
1651             aEvent = AccessibleEventObject(uno::Reference< uno::XInterface >(), nEventId, rNewValue, rOldValue);
1652 
1653         // no locking necessary, FireEvent internally copies listeners
1654         // if someone removes/adds in between Further locking,
1655         // actually, might lead to deadlocks, since we're calling out
1656         // of this object
1657         aGuard.clear();
1658         // -- until here --
1659 
1660         FireEvent(aEvent);
1661     }
1662 
1663     void AccessibleTextHelper_Impl::FireEvent( const AccessibleEventObject& rEvent ) const
1664     {
1665         DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
1666 
1667         // #102261# Call global queue for focus events
1668         if( rEvent.EventId == AccessibleStateType::FOCUSED )
1669             vcl::unohelper::NotifyAccessibleStateEventGlobally( rEvent );
1670 
1671         // #106234# Delegate to EventNotifier
1672         ::comphelper::AccessibleEventNotifier::addEvent( getNotifierClientId(),
1673                                                          rEvent );
1674     }
1675 
1676     // XAccessibleContext
1677     sal_Int32 SAL_CALL AccessibleTextHelper_Impl::getAccessibleChildCount() SAL_THROW((uno::RuntimeException))
1678     {
1679         DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
1680 
1681         return mnLastVisibleChild - mnFirstVisibleChild + 1;
1682     }
1683 
1684     uno::Reference< XAccessible > SAL_CALL AccessibleTextHelper_Impl::getAccessibleChild( sal_Int32 i ) SAL_THROW((lang::IndexOutOfBoundsException, uno::RuntimeException))
1685     {
1686         DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
1687 
1688         i -= GetStartIndex();
1689 
1690         if( 0 > i || i >= getAccessibleChildCount() ||
1691             GetTextForwarder().GetParagraphCount() <= i )
1692         {
1693             throw lang::IndexOutOfBoundsException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Invalid child index")), mxFrontEnd);
1694         }
1695 
1696         DBG_ASSERT(mxFrontEnd.is(), "AccessibleTextHelper_Impl::UpdateVisibleChildren: no frontend set");
1697 
1698         if( mxFrontEnd.is() )
1699             return maParaManager.CreateChild( i, mxFrontEnd, GetEditSource(), mnFirstVisibleChild + i ).first;
1700         else
1701             return NULL;
1702     }
1703 
1704     void SAL_CALL AccessibleTextHelper_Impl::addEventListener( const uno::Reference< XAccessibleEventListener >& xListener ) SAL_THROW((uno::RuntimeException))
1705     {
1706         DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
1707 
1708         if( getNotifierClientId() != -1 )
1709             ::comphelper::AccessibleEventNotifier::addEventListener( getNotifierClientId(), xListener );
1710     }
1711 
1712     void SAL_CALL AccessibleTextHelper_Impl::removeEventListener( const uno::Reference< XAccessibleEventListener >& xListener ) SAL_THROW((uno::RuntimeException))
1713     {
1714         DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
1715 
1716         if( getNotifierClientId() != -1 )
1717             ::comphelper::AccessibleEventNotifier::removeEventListener( getNotifierClientId(), xListener );
1718     }
1719 
1720     uno::Reference< XAccessible > SAL_CALL AccessibleTextHelper_Impl::getAccessibleAtPoint( const awt::Point& _aPoint ) SAL_THROW((uno::RuntimeException))
1721     {
1722         DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
1723 
1724         // make given position relative
1725         if( !mxFrontEnd.is() )
1726             throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("AccessibleTextHelper_Impl::getAccessibleAt: frontend invalid")), mxFrontEnd );
1727 
1728         uno::Reference< XAccessibleContext > xFrontEndContext = mxFrontEnd->getAccessibleContext();
1729 
1730         if( !xFrontEndContext.is() )
1731             throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("AccessibleTextHelper_Impl::getAccessibleAt: frontend invalid")), mxFrontEnd );
1732 
1733         uno::Reference< XAccessibleComponent > xFrontEndComponent( xFrontEndContext, uno::UNO_QUERY );
1734 
1735         if( !xFrontEndComponent.is() )
1736             throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("AccessibleTextHelper_Impl::getAccessibleAt: frontend is no XAccessibleComponent")),
1737                                         mxFrontEnd );
1738 
1739         // #103862# No longer need to make given position relative
1740         Point aPoint( _aPoint.X, _aPoint.Y );
1741 
1742         // respect EditEngine offset to surrounding shape/cell
1743         aPoint -= GetOffset();
1744 
1745         // convert to EditEngine coordinate system
1746         SvxTextForwarder& rCacheTF = GetTextForwarder();
1747         Point aLogPoint( GetViewForwarder().PixelToLogic( aPoint, rCacheTF.GetMapMode() ) );
1748 
1749         // iterate over all visible children (including those not yet created)
1750         sal_Int32 nChild;
1751         for( nChild=mnFirstVisibleChild; nChild <= mnLastVisibleChild; ++nChild )
1752         {
1753             DBG_ASSERT(nChild >= 0 && nChild <= USHRT_MAX,
1754                        "AccessibleTextHelper_Impl::getAccessibleAt: index value overflow");
1755 
1756             Rectangle aParaBounds( rCacheTF.GetParaBounds( nChild ) );
1757 
1758             if( aParaBounds.IsInside( aLogPoint ) )
1759                 return getAccessibleChild( nChild - mnFirstVisibleChild + GetStartIndex() );
1760         }
1761 
1762         // found none
1763         return NULL;
1764     }
1765 
1766     //------------------------------------------------------------------------
1767     // AccessibleTextHelper implementation (simply forwards to impl)
1768     //------------------------------------------------------------------------
1769 
1770     AccessibleTextHelper::AccessibleTextHelper( ::std::auto_ptr< SvxEditSource > pEditSource ) :
1771         mpImpl( new AccessibleTextHelper_Impl() )
1772     {
1773         ::vos::OGuard aGuard( Application::GetSolarMutex() );
1774 
1775         SetEditSource( pEditSource );
1776     }
1777 
1778     AccessibleTextHelper::~AccessibleTextHelper()
1779     {
1780     }
1781 
1782     const SvxEditSource& AccessibleTextHelper::GetEditSource() const SAL_THROW((uno::RuntimeException))
1783     {
1784 #ifdef DBG_UTIL
1785         mpImpl->CheckInvariants();
1786 
1787         const SvxEditSource& aEditSource = mpImpl->GetEditSource();
1788 
1789         mpImpl->CheckInvariants();
1790 
1791         return aEditSource;
1792 #else
1793         return mpImpl->GetEditSource();
1794 #endif
1795     }
1796 
1797     void AccessibleTextHelper::SetEditSource( ::std::auto_ptr< SvxEditSource > pEditSource ) SAL_THROW((uno::RuntimeException))
1798     {
1799 #ifdef DBG_UTIL
1800         // precondition: solar mutex locked
1801         DBG_TESTSOLARMUTEX();
1802 
1803         mpImpl->CheckInvariants();
1804 #endif
1805 
1806         mpImpl->SetEditSource( pEditSource );
1807 
1808 #ifdef DBG_UTIL
1809         mpImpl->CheckInvariants();
1810 #endif
1811     }
1812 
1813     void AccessibleTextHelper::SetEventSource( const uno::Reference< XAccessible >& rInterface )
1814     {
1815 #ifdef DBG_UTIL
1816         mpImpl->CheckInvariants();
1817 #endif
1818 
1819         mpImpl->SetEventSource( rInterface );
1820 
1821 #ifdef DBG_UTIL
1822         mpImpl->CheckInvariants();
1823 #endif
1824     }
1825 
1826     uno::Reference< XAccessible > AccessibleTextHelper::GetEventSource() const
1827     {
1828 #ifdef DBG_UTIL
1829         mpImpl->CheckInvariants();
1830 
1831         uno::Reference< XAccessible > xRet( mpImpl->GetEventSource() );
1832 
1833         mpImpl->CheckInvariants();
1834 
1835         return xRet;
1836 #else
1837         return mpImpl->GetEventSource();
1838 #endif
1839     }
1840 
1841     void AccessibleTextHelper::SetFocus( sal_Bool bHaveFocus ) SAL_THROW((::com::sun::star::uno::RuntimeException))
1842     {
1843 #ifdef DBG_UTIL
1844         // precondition: solar mutex locked
1845         DBG_TESTSOLARMUTEX();
1846 
1847         mpImpl->CheckInvariants();
1848 #endif
1849 
1850         mpImpl->SetFocus( bHaveFocus );
1851 
1852 #ifdef DBG_UTIL
1853         mpImpl->CheckInvariants();
1854 #endif
1855     }
1856 
1857     sal_Bool AccessibleTextHelper::HaveFocus() SAL_THROW((::com::sun::star::uno::RuntimeException))
1858     {
1859 #ifdef DBG_UTIL
1860         mpImpl->CheckInvariants();
1861 
1862         sal_Bool bRet( mpImpl->HaveFocus() );
1863 
1864         mpImpl->CheckInvariants();
1865 
1866         return bRet;
1867 #else
1868         return mpImpl->HaveFocus();
1869 #endif
1870     }
1871 
1872     void AccessibleTextHelper::FireEvent( const sal_Int16 nEventId, const uno::Any& rNewValue, const uno::Any& rOldValue ) const
1873     {
1874 #ifdef DBG_UTIL
1875         mpImpl->CheckInvariants();
1876 #endif
1877 
1878         mpImpl->FireEvent( nEventId, rNewValue, rOldValue );
1879 
1880 #ifdef DBG_UTIL
1881         mpImpl->CheckInvariants();
1882 #endif
1883     }
1884 
1885     void AccessibleTextHelper::FireEvent( const AccessibleEventObject& rEvent ) const
1886     {
1887 #ifdef DBG_UTIL
1888         mpImpl->CheckInvariants();
1889 #endif
1890 
1891         mpImpl->FireEvent( rEvent );
1892 
1893 #ifdef DBG_UTIL
1894         mpImpl->CheckInvariants();
1895 #endif
1896     }
1897 
1898     void AccessibleTextHelper::SetOffset( const Point& rPoint )
1899     {
1900 #ifdef DBG_UTIL
1901         // precondition: solar mutex locked
1902         DBG_TESTSOLARMUTEX();
1903 
1904         mpImpl->CheckInvariants();
1905 #endif
1906 
1907         mpImpl->SetOffset( rPoint );
1908 
1909 #ifdef DBG_UTIL
1910         mpImpl->CheckInvariants();
1911 #endif
1912     }
1913 
1914     Point AccessibleTextHelper::GetOffset() const
1915     {
1916 #ifdef DBG_UTIL
1917         mpImpl->CheckInvariants();
1918 
1919         Point aPoint( mpImpl->GetOffset() );
1920 
1921         mpImpl->CheckInvariants();
1922 
1923         return aPoint;
1924 #else
1925         return mpImpl->GetOffset();
1926 #endif
1927     }
1928 
1929     void AccessibleTextHelper::SetStartIndex( sal_Int32 nOffset )
1930     {
1931 #ifdef DBG_UTIL
1932         // precondition: solar mutex locked
1933         DBG_TESTSOLARMUTEX();
1934 
1935         mpImpl->CheckInvariants();
1936 #endif
1937 
1938         mpImpl->SetStartIndex( nOffset );
1939 
1940 #ifdef DBG_UTIL
1941         mpImpl->CheckInvariants();
1942 #endif
1943     }
1944 
1945     sal_Int32 AccessibleTextHelper::GetStartIndex() const
1946     {
1947 #ifdef DBG_UTIL
1948         mpImpl->CheckInvariants();
1949 
1950         sal_Int32 nOffset = mpImpl->GetStartIndex();
1951 
1952         mpImpl->CheckInvariants();
1953 
1954         return nOffset;
1955 #else
1956         return mpImpl->GetStartIndex();
1957 #endif
1958     }
1959 
1960     void AccessibleTextHelper::SetAdditionalChildStates( const VectorOfStates& rChildStates )
1961     {
1962         mpImpl->SetAdditionalChildStates( rChildStates );
1963     }
1964 
1965     const AccessibleTextHelper::VectorOfStates& AccessibleTextHelper::GetAdditionalChildStates() const
1966     {
1967         return mpImpl->GetAdditionalChildStates();
1968     }
1969 
1970     void AccessibleTextHelper::UpdateChildren() SAL_THROW((::com::sun::star::uno::RuntimeException))
1971     {
1972 #ifdef DBG_UTIL
1973         // precondition: solar mutex locked
1974         DBG_TESTSOLARMUTEX();
1975 
1976         mpImpl->CheckInvariants();
1977 #endif
1978 
1979         mpImpl->UpdateVisibleChildren();
1980         mpImpl->UpdateBoundRect();
1981 
1982         mpImpl->UpdateSelection();
1983 
1984 #ifdef DBG_UTIL
1985         mpImpl->CheckInvariants();
1986 #endif
1987     }
1988 
1989     void AccessibleTextHelper::Dispose()
1990     {
1991         // As Dispose calls ShutdownEditSource, which in turn
1992         // deregisters as listener on the edit source, have to lock
1993         // here
1994         ::vos::OGuard aGuard( Application::GetSolarMutex() );
1995 
1996 #ifdef DBG_UTIL
1997         mpImpl->CheckInvariants();
1998 #endif
1999 
2000         mpImpl->Dispose();
2001 
2002 #ifdef DBG_UTIL
2003         mpImpl->CheckInvariants();
2004 #endif
2005     }
2006 
2007     sal_Bool AccessibleTextHelper::IsSelected() const
2008     {
2009         ::vos::OGuard aGuard( Application::GetSolarMutex() );
2010 
2011 #ifdef DBG_UTIL
2012         mpImpl->CheckInvariants();
2013 
2014         sal_Bool aRet = mpImpl->IsSelected();
2015 
2016         mpImpl->CheckInvariants();
2017 
2018         return aRet;
2019 #else
2020         return mpImpl->IsSelected();
2021 #endif
2022     }
2023 
2024     // XAccessibleContext
2025     sal_Int32 AccessibleTextHelper::GetChildCount() SAL_THROW((uno::RuntimeException))
2026     {
2027         ::vos::OGuard aGuard( Application::GetSolarMutex() );
2028 
2029 #ifdef DBG_UTIL
2030         mpImpl->CheckInvariants();
2031 
2032         sal_Int32 nRet = mpImpl->getAccessibleChildCount();
2033 
2034         mpImpl->CheckInvariants();
2035 
2036         return nRet;
2037 #else
2038         return mpImpl->getAccessibleChildCount();
2039 #endif
2040     }
2041 
2042     uno::Reference< XAccessible > AccessibleTextHelper::GetChild( sal_Int32 i ) SAL_THROW((lang::IndexOutOfBoundsException, uno::RuntimeException))
2043     {
2044         ::vos::OGuard aGuard( Application::GetSolarMutex() );
2045 
2046 #ifdef DBG_UTIL
2047         mpImpl->CheckInvariants();
2048 
2049         uno::Reference< XAccessible > xRet = mpImpl->getAccessibleChild( i );
2050 
2051         mpImpl->CheckInvariants();
2052 
2053         return xRet;
2054 #else
2055         return mpImpl->getAccessibleChild( i );
2056 #endif
2057     }
2058 
2059     void AccessibleTextHelper::AddEventListener( const uno::Reference< XAccessibleEventListener >& xListener ) SAL_THROW((uno::RuntimeException))
2060     {
2061 #ifdef DBG_UTIL
2062         mpImpl->CheckInvariants();
2063 
2064         mpImpl->addEventListener( xListener );
2065 
2066         mpImpl->CheckInvariants();
2067 #else
2068         mpImpl->addEventListener( xListener );
2069 #endif
2070     }
2071 
2072     void AccessibleTextHelper::RemoveEventListener( const uno::Reference< XAccessibleEventListener >& xListener ) SAL_THROW((uno::RuntimeException))
2073     {
2074 #ifdef DBG_UTIL
2075         mpImpl->CheckInvariants();
2076 
2077         mpImpl->removeEventListener( xListener );
2078 
2079         mpImpl->CheckInvariants();
2080 #else
2081         mpImpl->removeEventListener( xListener );
2082 #endif
2083     }
2084 
2085     // XAccessibleComponent
2086     uno::Reference< XAccessible > AccessibleTextHelper::GetAt( const awt::Point& aPoint ) SAL_THROW((uno::RuntimeException))
2087     {
2088         ::vos::OGuard aGuard( Application::GetSolarMutex() );
2089 
2090 #ifdef DBG_UTIL
2091         mpImpl->CheckInvariants();
2092 
2093         uno::Reference< XAccessible > xChild = mpImpl->getAccessibleAtPoint( aPoint );
2094 
2095         mpImpl->CheckInvariants();
2096 
2097         return xChild;
2098 #else
2099         return mpImpl->getAccessibleAtPoint( aPoint );
2100 #endif
2101     }
2102 
2103 } // end of namespace accessibility
2104 
2105 /* vim: set noet sw=4 ts=4: */
2106