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