update credits
[LibreOffice.git] / svx / source / accessibility / AccessibleTextHelper.cxx
blob4d3b5a2ec80c64b4e246e76372f43dc64604090f
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 //------------------------------------------------------------------------
23 // Global header
25 //------------------------------------------------------------------------
27 #include <limits.h>
28 #include <memory>
29 #include <algorithm>
30 #include <deque>
31 #include <osl/mutex.hxx>
32 #include <com/sun/star/uno/Any.hxx>
33 #include <com/sun/star/uno/Reference.hxx>
34 #include <cppuhelper/weakref.hxx>
35 #include <com/sun/star/awt/Point.hpp>
36 #include <com/sun/star/awt/Rectangle.hpp>
37 #include <com/sun/star/lang/DisposedException.hpp>
38 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
39 #include <com/sun/star/accessibility/XAccessible.hpp>
40 #include <com/sun/star/accessibility/XAccessibleContext.hpp>
41 #include <com/sun/star/accessibility/XAccessibleComponent.hpp>
42 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
43 #include <comphelper/accessibleeventnotifier.hxx>
44 #include <unotools/accessiblestatesethelper.hxx>
45 #include <vcl/unohelp.hxx>
46 #include <vcl/svapp.hxx>
48 //------------------------------------------------------------------------
50 // Project-local header
52 //------------------------------------------------------------------------
53 #include "AccessibleTextEventQueue.hxx"
54 #include <svx/AccessibleTextHelper.hxx>
55 #include <svx/unoshape.hxx>
56 #include "editeng/unolingu.hxx"
57 #include <editeng/unotext.hxx>
59 #include "editeng/unoedhlp.hxx"
60 #include "editeng/unopracc.hxx"
61 #include "editeng/AccessibleParaManager.hxx"
62 #include "editeng/AccessibleEditableTextPara.hxx"
63 #include <svx/svdmodel.hxx>
64 #include <svx/svdpntv.hxx>
65 #include <editeng/editdata.hxx>
66 #include <editeng/editeng.hxx>
67 #include <editeng/editview.hxx>
69 using namespace ::com::sun::star;
70 using namespace ::com::sun::star::accessibility;
72 namespace accessibility
75 //------------------------------------------------------------------------
77 // AccessibleTextHelper_Impl declaration
79 //------------------------------------------------------------------------
81 DBG_NAME( AccessibleTextHelper_Impl )
83 template < typename first_type, typename second_type >
84 ::std::pair< first_type, second_type > makeSortedPair( first_type first,
85 second_type second )
87 if( first > second )
88 return ::std::make_pair( second, first );
89 else
90 return ::std::make_pair( first, second );
93 class AccessibleTextHelper_Impl : public SfxListener
96 public:
97 typedef ::std::vector< sal_Int16 > VectorOfStates;
99 // receive pointer to our frontend class and view window
100 AccessibleTextHelper_Impl();
101 ~AccessibleTextHelper_Impl();
103 // XAccessibleContext child handling methods
104 sal_Int32 SAL_CALL getAccessibleChildCount() SAL_THROW((uno::RuntimeException));
105 uno::Reference< XAccessible > SAL_CALL getAccessibleChild( sal_Int32 i ) SAL_THROW((lang::IndexOutOfBoundsException, uno::RuntimeException));
107 // XAccessibleEventBroadcaster child related methods
108 void SAL_CALL addAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener ) SAL_THROW((uno::RuntimeException));
109 void SAL_CALL removeAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener ) SAL_THROW((uno::RuntimeException));
111 // XAccessibleComponent child related methods
112 uno::Reference< XAccessible > SAL_CALL getAccessibleAtPoint( const awt::Point& aPoint ) SAL_THROW((uno::RuntimeException));
114 SvxEditSourceAdapter& GetEditSource() const SAL_THROW((uno::RuntimeException));
115 SAL_WNODEPRECATED_DECLARATIONS_PUSH
116 void SetEditSource( ::std::auto_ptr< SvxEditSource > pEditSource ) SAL_THROW((uno::RuntimeException));
117 SAL_WNODEPRECATED_DECLARATIONS_POP
119 void SetEventSource( const uno::Reference< XAccessible >& rInterface )
121 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
122 mxFrontEnd = rInterface;
124 uno::Reference< XAccessible > GetEventSource() const
126 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
127 return mxFrontEnd;
130 void SetOffset( const Point& );
131 Point GetOffset() const
133 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
134 ::osl::MutexGuard aGuard( maMutex ); Point aPoint( maOffset );
135 return aPoint;
138 void SetStartIndex( sal_Int32 nOffset );
139 sal_Int32 GetStartIndex() const
141 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
142 // Strictly correct only with locked solar mutex, // but
143 // here we rely on the fact that sal_Int32 access is
144 // atomic
145 return mnStartIndex;
148 void SetAdditionalChildStates( const VectorOfStates& rChildStates );
150 sal_Bool IsSelected() const;
152 void Dispose();
154 // do NOT hold object mutex when calling this! Danger of deadlock
155 void FireEvent( const sal_Int16 nEventId, const uno::Any& rNewValue = uno::Any(), const uno::Any& rOldValue = uno::Any() ) const;
156 void FireEvent( const AccessibleEventObject& rEvent ) const;
158 void SetFocus( sal_Bool bHaveFocus ) SAL_THROW((::com::sun::star::uno::RuntimeException));
159 sal_Bool HaveFocus() SAL_THROW((::com::sun::star::uno::RuntimeException));
160 void SetChildFocus( sal_Int32 nChild, sal_Bool bHaveFocus ) SAL_THROW((::com::sun::star::uno::RuntimeException));
161 void SetShapeFocus( sal_Bool bHaveFocus ) SAL_THROW((::com::sun::star::uno::RuntimeException));
162 void ChangeChildFocus( sal_Int32 nNewChild ) SAL_THROW((::com::sun::star::uno::RuntimeException));
164 #ifdef DBG_UTIL
165 void CheckInvariants() const;
166 #endif
168 // checks all children for visibility, throws away invisible ones
169 void UpdateVisibleChildren( bool bBroadcastEvents=true );
171 // check all children for changes in position and size
172 void UpdateBoundRect();
174 // calls SetSelection on the forwarder and updates maLastSelection
175 // cache.
176 void UpdateSelection();
178 private:
180 // Process event queue
181 void ProcessQueue();
183 // syntactic sugar for FireEvent
184 void GotPropertyEvent( const uno::Any& rNewValue, const sal_Int16 nEventId ) const { FireEvent( nEventId, rNewValue ); }
185 void LostPropertyEvent( const uno::Any& rOldValue, const sal_Int16 nEventId ) const { FireEvent( nEventId, uno::Any(), rOldValue ); }
187 // shutdown usage of current edit source on myself and the children.
188 void ShutdownEditSource() SAL_THROW((uno::RuntimeException));
190 void ParagraphsMoved( sal_Int32 nFirst, sal_Int32 nMiddle, sal_Int32 nLast );
192 virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint );
194 int getNotifierClientId() const { return mnNotifierClientId; }
196 // lock solar mutex before
197 SvxTextForwarder& GetTextForwarder() const SAL_THROW((uno::RuntimeException));
198 // lock solar mutex before
199 SvxViewForwarder& GetViewForwarder() const SAL_THROW((uno::RuntimeException));
200 // lock solar mutex before
201 SvxEditViewForwarder& GetEditViewForwarder( sal_Bool bCreate = sal_False ) const SAL_THROW((uno::RuntimeException));
203 // are we in edit mode?
204 sal_Bool IsActive() const SAL_THROW((uno::RuntimeException));
206 // our frontend class (the one implementing the actual
207 // interface). That's not necessarily the one containing the impl
208 // pointer!
209 uno::Reference< XAccessible > mxFrontEnd;
211 // a wrapper for the text forwarders (guarded by solar mutex)
212 mutable SvxEditSourceAdapter maEditSource;
214 // store last selection (to correctly report selection changes, guarded by solar mutex)
215 ESelection maLastSelection;
217 // cache range of visible children (guarded by solar mutex)
218 sal_Int32 mnFirstVisibleChild;
219 sal_Int32 mnLastVisibleChild;
221 // offset to add to all our children (unguarded, relying on
222 // the fact that sal_Int32 access is atomic)
223 sal_Int32 mnStartIndex;
225 // the object handling our children (guarded by solar mutex)
226 ::accessibility::AccessibleParaManager maParaManager;
228 // number of not-yet-closed event frames (BEGIN/END sequences) (guarded by solar mutex)
229 sal_Int32 maEventOpenFrames;
231 // Queued events from Notify() (guarded by solar mutex)
232 AccessibleTextEventQueue maEventQueue;
234 // spin lock to prevent notify in notify (guarded by solar mutex)
235 bool mbInNotify;
237 // whether the object or it's children has the focus set (guarded by solar mutex)
238 sal_Bool mbGroupHasFocus;
240 // whether we (this object) has the focus set (guarded by solar mutex)
241 sal_Bool mbThisHasFocus;
243 mutable ::osl::Mutex maMutex;
245 /// our current offset to the containing shape/cell (guarded by maMutex)
246 Point maOffset;
248 /// client Id from AccessibleEventNotifier
249 int mnNotifierClientId;
252 //------------------------------------------------------------------------
254 // AccessibleTextHelper_Impl implementation
256 //------------------------------------------------------------------------
258 AccessibleTextHelper_Impl::AccessibleTextHelper_Impl() :
259 mxFrontEnd( NULL ),
260 maLastSelection( EE_PARA_NOT_FOUND,EE_INDEX_NOT_FOUND,EE_PARA_NOT_FOUND,EE_INDEX_NOT_FOUND ),
261 mnFirstVisibleChild( -1 ),
262 mnLastVisibleChild( -2 ),
263 mnStartIndex( 0 ),
264 maEventOpenFrames( 0 ),
265 mbInNotify( false ),
266 mbGroupHasFocus( sal_False ),
267 mbThisHasFocus( sal_False ),
268 maOffset(0,0),
269 // well, that's strictly exception safe, though not really
270 // robust. We rely on the fact that this member is constructed
271 // last, and that the constructor body is empty, thus no
272 // chance for exceptions once the Id is fetched. Nevertheless,
273 // normally should employ RAII here...
274 mnNotifierClientId(::comphelper::AccessibleEventNotifier::registerClient())
276 DBG_CTOR( AccessibleTextHelper_Impl, NULL );
278 #ifdef DBG_UTIL
279 OSL_TRACE( "AccessibleTextHelper_Impl received ID: %d", mnNotifierClientId );
280 #endif
283 AccessibleTextHelper_Impl::~AccessibleTextHelper_Impl()
285 DBG_DTOR( AccessibleTextHelper_Impl, NULL );
287 SolarMutexGuard aGuard;
291 // call Dispose here, too, since we've some resources not
292 // automatically freed otherwise
293 Dispose();
295 catch( const uno::Exception& ) {}
298 SvxTextForwarder& AccessibleTextHelper_Impl::GetTextForwarder() const SAL_THROW((uno::RuntimeException))
300 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
302 if( !maEditSource.IsValid() )
303 throw uno::RuntimeException("Unknown edit source", mxFrontEnd);
305 SvxTextForwarder* pTextForwarder = maEditSource.GetTextForwarder();
307 if( !pTextForwarder )
308 throw uno::RuntimeException("Unable to fetch text forwarder, model might be dead", mxFrontEnd);
310 if( pTextForwarder->IsValid() )
311 return *pTextForwarder;
312 else
313 throw uno::RuntimeException("Text forwarder is invalid, model might be dead", mxFrontEnd);
316 SvxViewForwarder& AccessibleTextHelper_Impl::GetViewForwarder() const SAL_THROW((uno::RuntimeException))
318 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
320 if( !maEditSource.IsValid() )
321 throw uno::RuntimeException("Unknown edit source", mxFrontEnd);
323 SvxViewForwarder* pViewForwarder = maEditSource.GetViewForwarder();
325 if( !pViewForwarder )
326 throw uno::RuntimeException("Unable to fetch view forwarder, model might be dead", mxFrontEnd);
328 if( pViewForwarder->IsValid() )
329 return *pViewForwarder;
330 else
331 throw uno::RuntimeException("View forwarder is invalid, model might be dead", mxFrontEnd);
334 SvxEditViewForwarder& AccessibleTextHelper_Impl::GetEditViewForwarder( sal_Bool bCreate ) const SAL_THROW((uno::RuntimeException))
336 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
338 if( !maEditSource.IsValid() )
339 throw uno::RuntimeException("Unknown edit source", mxFrontEnd);
341 SvxEditViewForwarder* pViewForwarder = maEditSource.GetEditViewForwarder( bCreate );
343 if( !pViewForwarder )
345 if( bCreate )
346 throw uno::RuntimeException("Unable to fetch edit view forwarder, model might be dead", mxFrontEnd);
347 else
348 throw uno::RuntimeException("No edit view forwarder, object not in edit mode", mxFrontEnd);
351 if( pViewForwarder->IsValid() )
352 return *pViewForwarder;
353 else
355 if( bCreate )
356 throw uno::RuntimeException("View forwarder is invalid, model might be dead", mxFrontEnd);
357 else
358 throw uno::RuntimeException("View forwarder is invalid, object not in edit mode", mxFrontEnd);
362 SvxEditSourceAdapter& AccessibleTextHelper_Impl::GetEditSource() const SAL_THROW((uno::RuntimeException))
364 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
366 if( maEditSource.IsValid() )
367 return maEditSource;
368 else
369 throw uno::RuntimeException("AccessibleTextHelper_Impl::GetEditSource: no edit source", mxFrontEnd );
372 sal_Bool AccessibleTextHelper_Impl::IsSelected() const
374 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
376 sal_Bool bRet = sal_False;
380 ESelection aSelection;
381 bRet = GetEditViewForwarder().GetSelection( aSelection );
383 catch( const uno::Exception& ) {}
385 return bRet;
388 // functor for sending child events (no stand-alone function, they are maybe not inlined)
389 class AccessibleTextHelper_OffsetChildIndex : public ::std::unary_function< ::accessibility::AccessibleEditableTextPara&, void >
391 public:
392 AccessibleTextHelper_OffsetChildIndex( sal_Int32 nDifference ) : mnDifference(nDifference) {}
393 void operator()( ::accessibility::AccessibleEditableTextPara& rPara )
395 rPara.SetIndexInParent( rPara.GetIndexInParent() + mnDifference );
398 private:
399 const sal_Int32 mnDifference;
402 void AccessibleTextHelper_Impl::SetStartIndex( sal_Int32 nOffset )
404 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
406 sal_Int32 nOldOffset( mnStartIndex );
408 mnStartIndex = nOffset;
410 if( nOldOffset != nOffset )
412 // update children
413 AccessibleTextHelper_OffsetChildIndex aFunctor( nOffset - nOldOffset );
415 ::std::for_each( maParaManager.begin(), maParaManager.end(),
416 AccessibleParaManager::WeakChildAdapter< AccessibleTextHelper_OffsetChildIndex > (aFunctor) );
420 void AccessibleTextHelper_Impl::SetAdditionalChildStates( const VectorOfStates& rChildStates )
422 maParaManager.SetAdditionalChildStates( rChildStates );
425 void AccessibleTextHelper_Impl::SetChildFocus( sal_Int32 nChild, sal_Bool bHaveFocus ) SAL_THROW((::com::sun::star::uno::RuntimeException))
427 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
429 if( bHaveFocus )
431 if( mbThisHasFocus )
432 SetShapeFocus( sal_False );
434 maParaManager.SetFocus( nChild );
436 // we just received the focus, also send caret event then
437 UpdateSelection();
439 OSL_TRACE("AccessibleTextHelper_Impl::SetChildFocus(): Paragraph %d received focus", nChild );
441 else
443 maParaManager.SetFocus( -1 );
445 OSL_TRACE("AccessibleTextHelper_Impl::SetChildFocus(): Paragraph %d lost focus", nChild );
447 if( mbGroupHasFocus )
448 SetShapeFocus( sal_True );
452 void AccessibleTextHelper_Impl::ChangeChildFocus( sal_Int32 nNewChild ) SAL_THROW((::com::sun::star::uno::RuntimeException))
454 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
456 if( mbThisHasFocus )
457 SetShapeFocus( sal_False );
459 mbGroupHasFocus = sal_True;
460 maParaManager.SetFocus( nNewChild );
462 OSL_TRACE("AccessibleTextHelper_Impl::ChangeChildFocus(): Paragraph %d received focus", nNewChild );
465 void AccessibleTextHelper_Impl::SetShapeFocus( sal_Bool bHaveFocus ) SAL_THROW((::com::sun::star::uno::RuntimeException))
467 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
469 sal_Bool bOldFocus( mbThisHasFocus );
471 mbThisHasFocus = bHaveFocus;
473 if( bOldFocus != bHaveFocus )
475 if( bHaveFocus )
477 GotPropertyEvent( uno::makeAny(AccessibleStateType::FOCUSED), AccessibleEventId::STATE_CHANGED );
478 OSL_TRACE("AccessibleTextHelper_Impl::SetShapeFocus(): Parent object received focus" );
480 else
482 LostPropertyEvent( uno::makeAny(AccessibleStateType::FOCUSED), AccessibleEventId::STATE_CHANGED );
483 OSL_TRACE("AccessibleTextHelper_Impl::SetShapeFocus(): Parent object lost focus" );
488 void AccessibleTextHelper_Impl::SetFocus( sal_Bool bHaveFocus ) SAL_THROW((::com::sun::star::uno::RuntimeException))
490 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
492 sal_Bool bOldFocus( mbGroupHasFocus );
494 mbGroupHasFocus = bHaveFocus;
496 if( IsActive() )
500 // find the one with the cursor and get/set focus accordingly
501 ESelection aSelection;
502 if( GetEditViewForwarder().GetSelection( aSelection ) )
503 SetChildFocus( aSelection.nEndPara, bHaveFocus );
505 catch( const uno::Exception& ) {}
507 else if( bOldFocus != bHaveFocus )
509 SetShapeFocus( bHaveFocus );
512 OSL_TRACE("AccessibleTextHelper_Impl::SetFocus: focus changed, Object %d, state: %s", this, bHaveFocus ? "focused" : "not focused");
515 sal_Bool AccessibleTextHelper_Impl::HaveFocus() SAL_THROW((::com::sun::star::uno::RuntimeException))
517 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
519 // No locking of solar mutex here, since we rely on the fact
520 // that sal_Bool access is atomic
521 return mbThisHasFocus;
524 sal_Bool AccessibleTextHelper_Impl::IsActive() const SAL_THROW((uno::RuntimeException))
526 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
530 SvxEditSource& rEditSource = GetEditSource();
531 SvxEditViewForwarder* pViewForwarder = rEditSource.GetEditViewForwarder();
533 if( !pViewForwarder )
534 return sal_False;
536 if( pViewForwarder->IsValid() )
537 return sal_True;
538 else
539 return sal_False;
541 catch( const uno::RuntimeException& )
543 return sal_False;
547 void AccessibleTextHelper_Impl::UpdateSelection()
549 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
553 ESelection aSelection;
554 if( GetEditViewForwarder().GetSelection( aSelection ) )
556 if( !maLastSelection.IsEqual( aSelection ) &&
557 aSelection.nEndPara < maParaManager.GetNum() )
559 // #103998# Not that important, changed from assertion to trace
560 if( mbThisHasFocus )
562 OSL_TRACE("AccessibleTextHelper_Impl::UpdateSelection(): Parent has focus!");
565 sal_Int32 nMaxValidParaIndex( GetTextForwarder().GetParagraphCount() - 1 );
567 // notify all affected paragraphs (TODO: may be suboptimal,
568 // since some paragraphs might stay selected)
569 if( maLastSelection.nStartPara != EE_PARA_NOT_FOUND )
571 // Did the caret move from one paragraph to another?
572 // #100530# no caret events if not focused.
573 if( mbGroupHasFocus &&
574 maLastSelection.nEndPara != aSelection.nEndPara )
576 if( maLastSelection.nEndPara < maParaManager.GetNum() )
578 maParaManager.FireEvent( ::std::min( maLastSelection.nEndPara, nMaxValidParaIndex ),
579 ::std::min( maLastSelection.nEndPara, nMaxValidParaIndex )+1,
580 AccessibleEventId::CARET_CHANGED,
581 uno::makeAny(static_cast<sal_Int32>(-1)),
582 uno::makeAny(static_cast<sal_Int32>(maLastSelection.nEndPos)) );
585 ChangeChildFocus( aSelection.nEndPara );
587 OSL_TRACE("AccessibleTextHelper_Impl::UpdateSelection(): focus changed, Object: %d, Paragraph: %d, Last paragraph: %d",
588 this, aSelection.nEndPara, maLastSelection.nEndPara);
592 // #100530# no caret events if not focused.
593 if( mbGroupHasFocus )
595 uno::Any aOldCursor;
597 // #i13705# The old cursor can only contain valid
598 // values if it's the same paragraph!
599 if( maLastSelection.nStartPara != EE_PARA_NOT_FOUND &&
600 maLastSelection.nEndPara == aSelection.nEndPara )
602 aOldCursor <<= static_cast<sal_Int32>(maLastSelection.nEndPos);
604 else
606 aOldCursor <<= static_cast<sal_Int32>(-1);
609 maParaManager.FireEvent( aSelection.nEndPara,
610 aSelection.nEndPara+1,
611 AccessibleEventId::CARET_CHANGED,
612 uno::makeAny(static_cast<sal_Int32>(aSelection.nEndPos)),
613 aOldCursor );
616 OSL_TRACE("AccessibleTextHelper_Impl::UpdateSelection(): caret changed, Object: %d, New pos: %d, Old pos: %d, New para: %d, Old para: %d",
617 this, aSelection.nEndPos, maLastSelection.nEndPos, aSelection.nEndPara, maLastSelection.nEndPara);
619 // #108947# Sort new range before calling FireEvent
620 ::std::pair< xub_StrLen, xub_StrLen > sortedSelection(
621 makeSortedPair(::std::min( aSelection.nStartPara, nMaxValidParaIndex ),
622 ::std::min( aSelection.nEndPara, nMaxValidParaIndex ) ) );
624 // #108947# Sort last range before calling FireEvent
625 ::std::pair< xub_StrLen, xub_StrLen > sortedLastSelection(
626 makeSortedPair(::std::min( maLastSelection.nStartPara, nMaxValidParaIndex ),
627 ::std::min( maLastSelection.nEndPara, nMaxValidParaIndex ) ) );
629 // event TEXT_SELECTION_CHANGED has to be submitted. (#i27299#)
630 const sal_Int16 nTextSelChgEventId =
631 AccessibleEventId::TEXT_SELECTION_CHANGED;
632 // #107037# notify selection change
633 if( maLastSelection.nStartPara == EE_PARA_NOT_FOUND )
635 // last selection is undefined
636 // use method <ESelection::HasRange()> (#i27299#)
637 if ( aSelection.HasRange() )
639 // selection was undefined, now is on
640 maParaManager.FireEvent( sortedSelection.first,
641 sortedSelection.second+1,
642 nTextSelChgEventId );
645 else
647 // last selection is valid
648 // use method <ESelection::HasRange()> (#i27299#)
649 if ( maLastSelection.HasRange() &&
650 !aSelection.HasRange() )
652 // selection was on, now is empty
653 maParaManager.FireEvent( sortedLastSelection.first,
654 sortedLastSelection.second+1,
655 nTextSelChgEventId );
657 // use method <ESelection::HasRange()> (#i27299#)
658 else if( !maLastSelection.HasRange() &&
659 aSelection.HasRange() )
661 // selection was empty, now is on
662 maParaManager.FireEvent( sortedSelection.first,
663 sortedSelection.second+1,
664 nTextSelChgEventId );
666 // no event TEXT_SELECTION_CHANGED event, if new and
667 // last selection are empty. (#i27299#)
668 else if ( maLastSelection.HasRange() &&
669 aSelection.HasRange() )
671 // use sorted last and new selection
672 ESelection aTmpLastSel( maLastSelection );
673 aTmpLastSel.Adjust();
674 ESelection aTmpSel( aSelection );
675 aTmpSel.Adjust();
676 // first submit event for new and changed selection
677 sal_Int32 nPara = aTmpSel.nStartPara;
678 for ( ; nPara <= aTmpSel.nEndPara; ++nPara )
680 if ( nPara < aTmpLastSel.nStartPara ||
681 nPara > aTmpLastSel.nEndPara )
683 // new selection on paragraph <nPara>
684 maParaManager.FireEvent( nPara,
685 nTextSelChgEventId );
687 else
689 // check for changed selection on paragraph <nPara>
690 const xub_StrLen nParaStartPos =
691 nPara == aTmpSel.nStartPara
692 ? aTmpSel.nStartPos : 0;
693 const xub_StrLen nParaEndPos =
694 nPara == aTmpSel.nEndPara
695 ? aTmpSel.nEndPos : STRING_LEN;
696 const xub_StrLen nLastParaStartPos =
697 nPara == aTmpLastSel.nStartPara
698 ? aTmpLastSel.nStartPos : 0;
699 const xub_StrLen nLastParaEndPos =
700 nPara == aTmpLastSel.nEndPara
701 ? aTmpLastSel.nEndPos : STRING_LEN;
702 if ( nParaStartPos != nLastParaStartPos ||
703 nParaEndPos != nLastParaEndPos )
705 maParaManager.FireEvent(
706 nPara, nTextSelChgEventId );
710 // second submit event for 'old' selections
711 nPara = aTmpLastSel.nStartPara;
712 for ( ; nPara <= aTmpLastSel.nEndPara; ++nPara )
714 if ( nPara < aTmpSel.nStartPara ||
715 nPara > aTmpSel.nEndPara )
717 maParaManager.FireEvent( nPara,
718 nTextSelChgEventId );
724 maLastSelection = aSelection;
728 // no selection? no update actions
729 catch( const uno::RuntimeException& ) {}
732 void AccessibleTextHelper_Impl::ShutdownEditSource() SAL_THROW((uno::RuntimeException))
734 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
736 // This should only be called with solar mutex locked, i.e. from the main office thread
738 // This here is somewhat clumsy: As soon as our children have
739 // a NULL EditSource (maParaManager.SetEditSource()), they
740 // enter the disposed state and cannot be reanimated. Thus, it
741 // is unavoidable and a hard requirement to let go and create
742 // from scratch each and every child.
744 // invalidate children
745 maParaManager.Dispose();
746 maParaManager.SetNum(0);
748 // lost all children
749 if( mxFrontEnd.is() )
750 FireEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN);
752 // quit listen on stale edit source
753 if( maEditSource.IsValid() )
754 EndListening( maEditSource.GetBroadcaster() );
756 SAL_WNODEPRECATED_DECLARATIONS_PUSH
757 maEditSource.SetEditSource( ::std::auto_ptr< SvxEditSource >(NULL) );
758 SAL_WNODEPRECATED_DECLARATIONS_POP
761 SAL_WNODEPRECATED_DECLARATIONS_PUSH
762 void AccessibleTextHelper_Impl::SetEditSource( ::std::auto_ptr< SvxEditSource > pEditSource ) SAL_THROW((uno::RuntimeException))
764 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
766 // This should only be called with solar mutex locked, i.e. from the main office thread
768 // shutdown old edit source
769 ShutdownEditSource();
771 // set new edit source
772 maEditSource.SetEditSource( pEditSource );
774 // init child vector to the current child count
775 if( maEditSource.IsValid() )
777 maParaManager.SetNum( GetTextForwarder().GetParagraphCount() );
779 // listen on new edit source
780 StartListening( maEditSource.GetBroadcaster() );
782 UpdateVisibleChildren();
785 SAL_WNODEPRECATED_DECLARATIONS_POP
787 void AccessibleTextHelper_Impl::SetOffset( const Point& rPoint )
789 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
791 // guard against non-atomic access to maOffset data structure
793 ::osl::MutexGuard aGuard( maMutex );
794 maOffset = rPoint;
797 maParaManager.SetEEOffset( rPoint );
799 // in all cases, check visibility afterwards.
800 UpdateVisibleChildren();
801 UpdateBoundRect();
804 void AccessibleTextHelper_Impl::UpdateVisibleChildren( bool bBroadcastEvents )
806 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
810 SvxTextForwarder& rCacheTF = GetTextForwarder();
811 SvxViewForwarder& rCacheVF = GetViewForwarder();
813 Rectangle aViewArea = rCacheVF.GetVisArea();
815 if( IsActive() )
817 // maybe the edit view scrolls, adapt aViewArea
818 Rectangle aEditViewArea = GetEditViewForwarder().GetVisArea();
819 aViewArea += aEditViewArea.TopLeft();
821 // now determine intersection
822 aViewArea.Intersection( aEditViewArea );
825 Rectangle aTmpBB, aParaBB;
826 sal_Bool bFirstChild = sal_True;
827 sal_Int32 nCurrPara;
828 sal_Int32 nParas=rCacheTF.GetParagraphCount();
830 mnFirstVisibleChild = -1;
831 mnLastVisibleChild = -2;
833 for( nCurrPara=0; nCurrPara<nParas; ++nCurrPara )
835 DBG_ASSERT(nCurrPara >= 0 && nCurrPara <= USHRT_MAX,
836 "AccessibleTextHelper_Impl::UpdateVisibleChildren: index value overflow");
838 aTmpBB = rCacheTF.GetParaBounds( nCurrPara );
840 // convert to screen coordinates
841 aParaBB = ::accessibility::AccessibleEditableTextPara::LogicToPixel( aTmpBB, rCacheTF.GetMapMode(), rCacheVF );
843 if( aParaBB.IsOver( aViewArea ) )
845 // at least partially visible
846 if( bFirstChild )
848 bFirstChild = sal_False;
849 mnFirstVisibleChild = nCurrPara;
852 mnLastVisibleChild = nCurrPara;
854 // child not yet created?
855 ::accessibility::AccessibleParaManager::WeakChild aChild( maParaManager.GetChild(nCurrPara) );
856 if( aChild.second.Width == 0 &&
857 aChild.second.Height == 0 &&
858 mxFrontEnd.is() &&
859 bBroadcastEvents )
861 GotPropertyEvent( uno::makeAny( maParaManager.CreateChild( nCurrPara - mnFirstVisibleChild,
862 mxFrontEnd, GetEditSource(), nCurrPara ).first ),
863 AccessibleEventId::CHILD );
866 else
868 // not or no longer visible
869 if( maParaManager.IsReferencable( nCurrPara ) )
871 if( bBroadcastEvents )
872 LostPropertyEvent( uno::makeAny( maParaManager.GetChild( nCurrPara ).first.get().getRef() ),
873 AccessibleEventId::CHILD );
875 // clear reference
876 maParaManager.Release( nCurrPara );
881 catch( const uno::Exception& )
883 OSL_FAIL("AccessibleTextHelper_Impl::UpdateVisibleChildren error while determining visible children");
885 // something failed - currently no children
886 mnFirstVisibleChild = -1;
887 mnLastVisibleChild = -2;
888 maParaManager.SetNum(0);
890 // lost all children
891 if( bBroadcastEvents )
892 FireEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN);
896 // functor for checking changes in paragraph bounding boxes (no stand-alone function, maybe not inlined)
897 class AccessibleTextHelper_UpdateChildBounds : public ::std::unary_function< const ::accessibility::AccessibleParaManager::WeakChild&,
898 ::accessibility::AccessibleParaManager::WeakChild >
900 public:
901 AccessibleTextHelper_UpdateChildBounds( AccessibleTextHelper_Impl& rImpl ) : mrImpl(rImpl) {}
902 ::accessibility::AccessibleParaManager::WeakChild operator()( const ::accessibility::AccessibleParaManager::WeakChild& rChild )
904 // retrieve hard reference from weak one
905 ::accessibility::AccessibleParaManager::WeakPara::HardRefType aHardRef( rChild.first.get() );
907 if( aHardRef.is() )
909 awt::Rectangle aNewRect = aHardRef->getBounds();
910 const awt::Rectangle& aOldRect = rChild.second;
912 if( aNewRect.X != aOldRect.X ||
913 aNewRect.Y != aOldRect.Y ||
914 aNewRect.Width != aOldRect.Width ||
915 aNewRect.Height != aOldRect.Height )
917 // visible data changed
918 aHardRef->FireEvent( AccessibleEventId::BOUNDRECT_CHANGED );
920 // update internal bounds
921 return ::accessibility::AccessibleParaManager::WeakChild( rChild.first, aNewRect );
925 // identity transform
926 return rChild;
929 private:
930 AccessibleTextHelper_Impl& mrImpl;
933 void AccessibleTextHelper_Impl::UpdateBoundRect()
935 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
937 // send BOUNDRECT_CHANGED to affected children
938 AccessibleTextHelper_UpdateChildBounds aFunctor( *this );
939 ::std::transform( maParaManager.begin(), maParaManager.end(), maParaManager.begin(), aFunctor );
942 #ifdef DBG_UTIL
943 void AccessibleTextHelper_Impl::CheckInvariants() const
945 if( mnFirstVisibleChild >= 0 &&
946 mnFirstVisibleChild > mnLastVisibleChild )
948 OSL_FAIL( "AccessibleTextHelper: range invalid" );
951 #endif
953 // functor for sending child events (no stand-alone function, they are maybe not inlined)
954 class AccessibleTextHelper_LostChildEvent : public ::std::unary_function< const ::accessibility::AccessibleParaManager::WeakChild&, void >
956 public:
957 AccessibleTextHelper_LostChildEvent( AccessibleTextHelper_Impl& rImpl ) : mrImpl(rImpl) {}
958 void operator()( const ::accessibility::AccessibleParaManager::WeakChild& rPara )
960 // retrieve hard reference from weak one
961 ::accessibility::AccessibleParaManager::WeakPara::HardRefType aHardRef( rPara.first.get() );
963 if( aHardRef.is() )
964 mrImpl.FireEvent(AccessibleEventId::CHILD, uno::Any(), uno::makeAny( aHardRef.getRef() ) );
967 private:
968 AccessibleTextHelper_Impl& mrImpl;
971 void AccessibleTextHelper_Impl::ParagraphsMoved( sal_Int32 nFirst, sal_Int32 nMiddle, sal_Int32 nLast )
973 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
975 const sal_Int32 nParas = GetTextForwarder().GetParagraphCount();
977 /* rotate paragraphs
978 * =================
980 * Three cases:
982 * 1.
983 * ... nParagraph ... nParam1 ... nParam2 ...
984 * |______________[xxxxxxxxxxx]
985 * becomes
986 * [xxxxxxxxxxx]|______________
988 * tail is 0
990 * 2.
991 * ... nParam1 ... nParagraph ... nParam2 ...
992 * [xxxxxxxxxxx|xxxxxxxxxxxxxx]____________
993 * becomes
994 * ____________[xxxxxxxxxxx|xxxxxxxxxxxxxx]
996 * tail is nParagraph - nParam1
998 * 3.
999 * ... nParam1 ... nParam2 ... nParagraph ...
1000 * [xxxxxxxxxxx]___________|____________
1001 * becomes
1002 * ___________|____________[xxxxxxxxxxx]
1004 * tail is nParam2 - nParam1
1007 // sort nParagraph, nParam1 and nParam2 in ascending order, calc range
1008 if( nMiddle < nFirst )
1010 ::std::swap(nFirst, nMiddle);
1012 else if( nMiddle < nLast )
1014 nLast = nLast + nMiddle - nFirst;
1016 else
1018 ::std::swap(nMiddle, nLast);
1019 nLast = nLast + nMiddle - nFirst;
1022 if( nFirst < nParas && nMiddle < nParas && nLast < nParas )
1024 // since we have no "paragraph index
1025 // changed" event on UAA, remove
1026 // [first,last] and insert again later (in
1027 // UpdateVisibleChildren)
1029 // maParaManager.Rotate( nFirst, nMiddle, nLast );
1031 // send CHILD_EVENT to affected children
1032 ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator begin = maParaManager.begin();
1033 ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator end = begin;
1035 ::std::advance( begin, nFirst );
1036 ::std::advance( end, nLast+1 );
1038 // TODO: maybe optimize here in the following way. If the
1039 // number of removed children exceeds a certain threshold,
1040 // use INVALIDATE_CHILDREN
1041 AccessibleTextHelper_LostChildEvent aFunctor( *this );
1043 ::std::for_each( begin, end, aFunctor );
1045 maParaManager.Release(nFirst, nLast+1);
1046 // should be no need for UpdateBoundRect, since all affected children are cleared.
1050 // functor for sending child events (no stand-alone function, they are maybe not inlined)
1051 class AccessibleTextHelper_ChildrenTextChanged : public ::std::unary_function< ::accessibility::AccessibleEditableTextPara&, void >
1053 public:
1054 void operator()( ::accessibility::AccessibleEditableTextPara& rPara )
1056 rPara.TextChanged();
1060 /** functor processing queue events
1062 Reacts on TEXT_HINT_PARAINSERTED/REMOVED events and stores
1063 their content
1065 class AccessibleTextHelper_QueueFunctor : public ::std::unary_function< const SfxHint*, void >
1067 public:
1068 AccessibleTextHelper_QueueFunctor() :
1069 mnParasChanged( 0 ),
1070 mnParaIndex(-1),
1071 mnHintId(-1)
1073 void operator()( const SfxHint* pEvent )
1075 if( pEvent &&
1076 mnParasChanged != -1 )
1078 // determine hint type
1079 const TextHint* pTextHint = PTR_CAST( TextHint, pEvent );
1080 const SvxEditSourceHint* pEditSourceHint = PTR_CAST( SvxEditSourceHint, pEvent );
1082 if( !pEditSourceHint && pTextHint &&
1083 (pTextHint->GetId() == TEXT_HINT_PARAINSERTED ||
1084 pTextHint->GetId() == TEXT_HINT_PARAREMOVED ) )
1086 if( pTextHint->GetValue() == EE_PARA_ALL )
1088 mnParasChanged = -1;
1090 else
1092 mnHintId = pTextHint->GetId();
1093 mnParaIndex = pTextHint->GetValue();
1094 ++mnParasChanged;
1100 /** Query number of paragraphs changed during queue processing.
1102 @return number of changed paragraphs, -1 for
1103 "every paragraph changed"
1105 sal_Int32 GetNumberOfParasChanged() { return mnParasChanged; }
1106 /** Query index of last added/removed paragraph
1108 @return index of lastly added paragraphs, -1 for none
1109 added so far.
1111 sal_Int32 GetParaIndex() { return mnParaIndex; }
1112 /** Query hint id of last interesting event
1114 @return hint id of last interesting event (REMOVED/INSERTED).
1116 int GetHintId() { return mnHintId; }
1118 private:
1119 /** number of paragraphs changed during queue processing. -1 for
1120 "every paragraph changed"
1122 sal_Int32 mnParasChanged;
1123 /// index of paragraph added/removed last
1124 sal_Int32 mnParaIndex;
1125 /// TextHint ID (removed/inserted) of last interesting event
1126 int mnHintId;
1129 void AccessibleTextHelper_Impl::ProcessQueue()
1131 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
1133 // inspect queue for paragraph insert/remove events. If there
1134 // is exactly _one_ of those in the queue, and the number of
1135 // paragraphs has changed by exactly one, use that event to
1136 // determine a priori which paragraph was added/removed. This
1137 // is necessary, since I must sync right here with the
1138 // EditEngine state (number of paragraphs etc.), since I'm
1139 // potentially sending listener events right away.
1140 AccessibleTextHelper_QueueFunctor aFunctor;
1141 maEventQueue.ForEach( aFunctor );
1143 const sal_Int32 nNewParas( GetTextForwarder().GetParagraphCount() );
1144 const sal_Int32 nCurrParas( maParaManager.GetNum() );
1146 // whether every paragraph already is updated (no need to
1147 // repeat that later on, e.g. for PARA_MOVED events)
1148 bool bEverythingUpdated( false );
1150 if( labs( nNewParas - nCurrParas ) == 1 &&
1151 aFunctor.GetNumberOfParasChanged() == 1 )
1153 // #103483# Exactly one paragraph added/removed. This is
1154 // the normal case, optimize event handling here.
1156 if( aFunctor.GetHintId() == TEXT_HINT_PARAINSERTED )
1158 // update num of paras
1159 maParaManager.SetNum( nNewParas );
1161 // release everything from the insertion position until the end
1162 maParaManager.Release(aFunctor.GetParaIndex(), nCurrParas);
1164 // TODO: Clarify whether this behaviour _really_ saves
1165 // anybody anything!
1166 // update children, _don't_ broadcast
1167 UpdateVisibleChildren( false );
1168 UpdateBoundRect();
1170 // send insert event
1171 // #109864# Enforce creation of this paragraph
1174 GotPropertyEvent( uno::makeAny( getAccessibleChild( aFunctor.GetParaIndex() -
1175 mnFirstVisibleChild + GetStartIndex() ) ),
1176 AccessibleEventId::CHILD );
1178 catch( const uno::Exception& )
1180 OSL_FAIL("AccessibleTextHelper_Impl::ProcessQueue: could not create new paragraph");
1183 else if( aFunctor.GetHintId() == TEXT_HINT_PARAREMOVED )
1185 ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator begin = maParaManager.begin();
1186 ::std::advance( begin, aFunctor.GetParaIndex() );
1187 ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator end = begin;
1188 ::std::advance( end, 1 );
1190 // #i61812# remember para to be removed for later notification
1191 // AFTER the new state is applied (that after the para got removed)
1192 ::uno::Reference< XAccessible > xPara;
1193 ::accessibility::AccessibleParaManager::WeakPara::HardRefType aHardRef( begin->first.get() );
1194 if( aHardRef.is() )
1195 xPara = ::uno::Reference< XAccessible >( aHardRef.getRef(), ::uno::UNO_QUERY );
1197 // release everything from the remove position until the end
1198 maParaManager.Release(aFunctor.GetParaIndex(), nCurrParas);
1200 // update num of paras
1201 maParaManager.SetNum( nNewParas );
1203 // TODO: Clarify whether this behaviour _really_ saves
1204 // anybody anything!
1205 // update children, _don't_ broadcast
1206 UpdateVisibleChildren( false );
1207 UpdateBoundRect();
1209 // #i61812# notification for removed para
1210 if (xPara.is())
1211 FireEvent(AccessibleEventId::CHILD, uno::Any(), uno::makeAny( xPara) );
1213 #ifdef DBG_UTIL
1214 else
1215 OSL_FAIL("AccessibleTextHelper_Impl::ProcessQueue() invalid hint id");
1216 #endif
1218 else if( nNewParas != nCurrParas )
1220 // release all paras
1221 maParaManager.Release(0, nCurrParas);
1223 // update num of paras
1224 maParaManager.SetNum( nNewParas );
1226 // #109864# create from scratch, don't broadcast
1227 UpdateVisibleChildren( false );
1228 UpdateBoundRect();
1230 // number of paragraphs somehow changed - but we have no
1231 // chance determining how. Thus, throw away everything and
1232 // create from scratch.
1233 // (child events should be broadcast after the changes are done...)
1234 FireEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN);
1236 // no need for further updates later on
1237 bEverythingUpdated = true;
1240 while( !maEventQueue.IsEmpty() )
1242 SAL_WNODEPRECATED_DECLARATIONS_PUSH
1243 ::std::auto_ptr< SfxHint > pHint( maEventQueue.PopFront() );
1244 SAL_WNODEPRECATED_DECLARATIONS_POP
1245 if( pHint.get() )
1247 const SfxHint& rHint = *(pHint.get());
1249 // determine hint type
1250 const SdrHint* pSdrHint = PTR_CAST( SdrHint, &rHint );
1251 const SfxSimpleHint* pSimpleHint = PTR_CAST( SfxSimpleHint, &rHint );
1252 const TextHint* pTextHint = PTR_CAST( TextHint, &rHint );
1253 const SvxViewHint* pViewHint = PTR_CAST( SvxViewHint, &rHint );
1254 const SvxEditSourceHint* pEditSourceHint = PTR_CAST( SvxEditSourceHint, &rHint );
1258 const sal_Int32 nParas = GetTextForwarder().GetParagraphCount();
1260 if( pEditSourceHint )
1262 switch( pEditSourceHint->GetId() )
1264 case EDITSOURCE_HINT_PARASMOVED:
1266 DBG_ASSERT( pEditSourceHint->GetStartValue() < GetTextForwarder().GetParagraphCount() &&
1267 pEditSourceHint->GetEndValue() < GetTextForwarder().GetParagraphCount(),
1268 "AccessibleTextHelper_Impl::NotifyHdl: Invalid notification");
1270 if( !bEverythingUpdated )
1272 ParagraphsMoved(pEditSourceHint->GetStartValue(),
1273 pEditSourceHint->GetValue(),
1274 pEditSourceHint->GetEndValue());
1276 // in all cases, check visibility afterwards.
1277 UpdateVisibleChildren();
1279 break;
1282 case EDITSOURCE_HINT_SELECTIONCHANGED:
1283 // notify listeners
1286 UpdateSelection();
1288 // maybe we're not in edit mode (this is not an error)
1289 catch( const uno::Exception& ) {}
1290 break;
1293 else if( pTextHint )
1295 switch( pTextHint->GetId() )
1297 case TEXT_HINT_MODIFIED:
1299 // notify listeners
1300 sal_Int32 nPara( pTextHint->GetValue() );
1302 // #108900# Delegate change event to children
1303 AccessibleTextHelper_ChildrenTextChanged aNotifyChildrenFunctor;
1305 if( nPara == EE_PARA_ALL )
1307 // #108900# Call every child
1308 ::std::for_each( maParaManager.begin(), maParaManager.end(),
1309 AccessibleParaManager::WeakChildAdapter< AccessibleTextHelper_ChildrenTextChanged > (aNotifyChildrenFunctor) );
1311 else
1312 if( nPara < nParas )
1314 // #108900# Call child at index nPara
1315 ::std::for_each( maParaManager.begin()+nPara, maParaManager.begin()+nPara+1,
1316 AccessibleParaManager::WeakChildAdapter< AccessibleTextHelper_ChildrenTextChanged > (aNotifyChildrenFunctor) );
1318 break;
1321 case TEXT_HINT_PARAINSERTED:
1322 // already happened above
1323 break;
1325 case TEXT_HINT_PARAREMOVED:
1326 // already happened above
1327 break;
1329 case TEXT_HINT_TEXTHEIGHTCHANGED:
1330 // visibility changed, done below
1331 break;
1333 case TEXT_HINT_VIEWSCROLLED:
1334 // visibility changed, done below
1335 break;
1338 // in all cases, check visibility afterwards.
1339 UpdateVisibleChildren();
1340 UpdateBoundRect();
1342 else if( pViewHint )
1344 switch( pViewHint->GetHintType() )
1346 case SvxViewHint::SVX_HINT_VIEWCHANGED:
1347 // just check visibility
1348 UpdateVisibleChildren();
1349 UpdateBoundRect();
1350 break;
1353 else if( pSdrHint )
1355 switch( pSdrHint->GetKind() )
1357 case HINT_BEGEDIT:
1359 // change children state
1360 maParaManager.SetActive();
1362 // per definition, edit mode text has the focus
1363 SetFocus( sal_True );
1364 break;
1367 case HINT_ENDEDIT:
1369 // focused child now looses focus
1370 ESelection aSelection;
1371 if( GetEditViewForwarder().GetSelection( aSelection ) )
1372 SetChildFocus( aSelection.nEndPara, sal_False );
1374 // change children state
1375 maParaManager.SetActive( sal_False );
1377 maLastSelection = ESelection( EE_PARA_NOT_FOUND, EE_INDEX_NOT_FOUND,
1378 EE_PARA_NOT_FOUND, EE_INDEX_NOT_FOUND);
1379 break;
1381 default:
1382 break;
1385 // it's VITAL to keep the SfxSimpleHint last! It's the base of some classes above!
1386 else if( pSimpleHint )
1388 switch( pSimpleHint->GetId() )
1390 case SFX_HINT_DYING:
1391 // edit source is dying under us, become defunc then
1394 // make edit source inaccessible
1395 // Note: cannot destroy it here, since we're called from there!
1396 ShutdownEditSource();
1398 catch( const uno::Exception& ) {}
1400 break;
1404 catch( const uno::Exception& )
1406 #ifdef DBG_UTIL
1407 OSL_TRACE("AccessibleTextHelper_Impl::ProcessQueue: Unhandled exception.");
1408 #endif
1414 void AccessibleTextHelper_Impl::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
1416 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
1418 // precondition: solar mutex locked
1419 DBG_TESTSOLARMUTEX();
1421 // precondition: not in a recursion
1422 if( mbInNotify )
1423 return;
1425 mbInNotify = true;
1427 // determine hint type
1428 const SdrHint* pSdrHint = PTR_CAST( SdrHint, &rHint );
1429 const SfxSimpleHint* pSimpleHint = PTR_CAST( SfxSimpleHint, &rHint );
1430 const TextHint* pTextHint = PTR_CAST( TextHint, &rHint );
1431 const SvxViewHint* pViewHint = PTR_CAST( SvxViewHint, &rHint );
1432 const SvxEditSourceHint* pEditSourceHint = PTR_CAST( SvxEditSourceHint, &rHint );
1436 // Process notification event
1437 if( pEditSourceHint )
1439 maEventQueue.Append( *pEditSourceHint );
1440 // EditEngine should emit TEXT_SELECTION_CHANGED events (#i27299#)
1441 if( maEventOpenFrames == 0 )
1442 ProcessQueue();
1444 else if( pTextHint )
1446 switch( pTextHint->GetId() )
1448 case TEXT_HINT_BLOCKNOTIFICATION_END:
1449 case TEXT_HINT_INPUT_END:
1450 --maEventOpenFrames;
1452 if( maEventOpenFrames == 0 )
1454 // #103483#
1455 /* All information should have arrived
1456 * now, process queue. As stated in the
1457 * above bug, we can often avoid throwing
1458 * away all paragraphs by looking forward
1459 * in the event queue (searching for
1460 * PARAINSERT/REMOVE events). Furthermore,
1461 * processing the event queue only at the
1462 * end of an interaction cycle, ensures
1463 * that the EditEngine state and the
1464 * AccessibleText state are the same
1465 * (well, mostly. If there are _multiple_
1466 * interaction cycles in the EE queues, it
1467 * can still happen that EE state is
1468 * different. That's so to say broken by
1469 * design with that delayed EE event
1470 * concept).
1472 ProcessQueue();
1474 break;
1476 case TEXT_HINT_BLOCKNOTIFICATION_START:
1477 case TEXT_HINT_INPUT_START:
1478 ++maEventOpenFrames;
1479 // no FALLTHROUGH reason: event will not be processed,
1480 // thus appending the event isn't necessary. (#i27299#)
1481 break;
1482 default:
1483 maEventQueue.Append( *pTextHint );
1484 // EditEngine should emit TEXT_SELECTION_CHANGED events (#i27299#)
1485 if( maEventOpenFrames == 0 )
1486 ProcessQueue();
1487 break;
1490 else if( pViewHint )
1492 maEventQueue.Append( *pViewHint );
1494 // process visibility right away, if not within an
1495 // open EE notification frame. Otherwise, event
1496 // processing would be delayed until next EE
1497 // notification sequence.
1498 if( maEventOpenFrames == 0 )
1499 ProcessQueue();
1501 else if( pSdrHint )
1503 maEventQueue.Append( *pSdrHint );
1505 // process drawing layer events right away, if not
1506 // within an open EE notification frame. Otherwise,
1507 // event processing would be delayed until next EE
1508 // notification sequence.
1509 if( maEventOpenFrames == 0 )
1510 ProcessQueue();
1512 // it's VITAL to keep the SfxSimpleHint last! It's the base of some classes above!
1513 else if( pSimpleHint )
1515 // handle this event _at once_, because after that, objects are invalid
1516 switch( pSimpleHint->GetId() )
1518 case SFX_HINT_DYING:
1519 // edit source is dying under us, become defunc then
1520 maEventQueue.Clear();
1523 // make edit source inaccessible
1524 // Note: cannot destroy it here, since we're called from there!
1525 ShutdownEditSource();
1527 catch( const uno::Exception& ) {}
1529 break;
1533 catch( const uno::Exception& )
1535 #ifdef DBG_UTIL
1536 OSL_TRACE("AccessibleTextHelper_Impl::Notify: Unhandled exception.");
1537 #endif
1538 mbInNotify = false;
1541 mbInNotify = false;
1544 void AccessibleTextHelper_Impl::Dispose()
1546 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
1548 if( getNotifierClientId() != -1 )
1552 // #106234# Unregister from EventNotifier
1553 ::comphelper::AccessibleEventNotifier::revokeClient( getNotifierClientId() );
1554 #ifdef DBG_UTIL
1555 OSL_TRACE( "AccessibleTextHelper_Impl disposed ID: %d", mnNotifierClientId );
1556 #endif
1558 catch( const uno::Exception& ) {}
1560 mnNotifierClientId = -1;
1565 // dispose children
1566 maParaManager.Dispose();
1568 catch( const uno::Exception& ) {}
1570 // quit listen on stale edit source
1571 if( maEditSource.IsValid() )
1572 EndListening( maEditSource.GetBroadcaster() );
1574 // clear references
1575 SAL_WNODEPRECATED_DECLARATIONS_PUSH
1576 maEditSource.SetEditSource( ::std::auto_ptr< SvxEditSource >(NULL) );
1577 SAL_WNODEPRECATED_DECLARATIONS_POP
1578 mxFrontEnd = NULL;
1581 void AccessibleTextHelper_Impl::FireEvent( const sal_Int16 nEventId, const uno::Any& rNewValue, const uno::Any& rOldValue ) const
1583 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
1585 // -- object locked --
1586 ::osl::ClearableMutexGuard aGuard( maMutex );
1588 AccessibleEventObject aEvent;
1590 DBG_ASSERT(mxFrontEnd.is(), "AccessibleTextHelper::FireEvent: no event source set" );
1592 if( mxFrontEnd.is() )
1593 aEvent = AccessibleEventObject(mxFrontEnd->getAccessibleContext(), nEventId, rNewValue, rOldValue);
1594 else
1595 aEvent = AccessibleEventObject(uno::Reference< uno::XInterface >(), nEventId, rNewValue, rOldValue);
1597 // no locking necessary, FireEvent internally copies listeners
1598 // if someone removes/adds in between Further locking,
1599 // actually, might lead to deadlocks, since we're calling out
1600 // of this object
1601 aGuard.clear();
1602 // -- until here --
1604 FireEvent(aEvent);
1607 void AccessibleTextHelper_Impl::FireEvent( const AccessibleEventObject& rEvent ) const
1609 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
1611 // #102261# Call global queue for focus events
1612 if( rEvent.EventId == AccessibleStateType::FOCUSED )
1613 vcl::unohelper::NotifyAccessibleStateEventGlobally( rEvent );
1615 // #106234# Delegate to EventNotifier
1616 ::comphelper::AccessibleEventNotifier::addEvent( getNotifierClientId(),
1617 rEvent );
1620 // XAccessibleContext
1621 sal_Int32 SAL_CALL AccessibleTextHelper_Impl::getAccessibleChildCount() SAL_THROW((uno::RuntimeException))
1623 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
1625 return mnLastVisibleChild - mnFirstVisibleChild + 1;
1628 uno::Reference< XAccessible > SAL_CALL AccessibleTextHelper_Impl::getAccessibleChild( sal_Int32 i ) SAL_THROW((lang::IndexOutOfBoundsException, uno::RuntimeException))
1630 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
1632 i -= GetStartIndex();
1634 if( 0 > i || i >= getAccessibleChildCount() ||
1635 GetTextForwarder().GetParagraphCount() <= i )
1637 throw lang::IndexOutOfBoundsException("Invalid child index", mxFrontEnd);
1640 DBG_ASSERT(mxFrontEnd.is(), "AccessibleTextHelper_Impl::UpdateVisibleChildren: no frontend set");
1642 if( mxFrontEnd.is() )
1643 return maParaManager.CreateChild( i, mxFrontEnd, GetEditSource(), mnFirstVisibleChild + i ).first;
1644 else
1645 return NULL;
1648 void SAL_CALL AccessibleTextHelper_Impl::addAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener ) SAL_THROW((uno::RuntimeException))
1650 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
1652 if( getNotifierClientId() != -1 )
1653 ::comphelper::AccessibleEventNotifier::addEventListener( getNotifierClientId(), xListener );
1656 void SAL_CALL AccessibleTextHelper_Impl::removeAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener ) SAL_THROW((uno::RuntimeException))
1658 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
1660 if( getNotifierClientId() != -1 )
1661 ::comphelper::AccessibleEventNotifier::removeEventListener( getNotifierClientId(), xListener );
1664 uno::Reference< XAccessible > SAL_CALL AccessibleTextHelper_Impl::getAccessibleAtPoint( const awt::Point& _aPoint ) SAL_THROW((uno::RuntimeException))
1666 DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL );
1668 // make given position relative
1669 if( !mxFrontEnd.is() )
1670 throw uno::RuntimeException("AccessibleTextHelper_Impl::getAccessibleAt: frontend invalid", mxFrontEnd );
1672 uno::Reference< XAccessibleContext > xFrontEndContext = mxFrontEnd->getAccessibleContext();
1674 if( !xFrontEndContext.is() )
1675 throw uno::RuntimeException("AccessibleTextHelper_Impl::getAccessibleAt: frontend invalid", mxFrontEnd );
1677 uno::Reference< XAccessibleComponent > xFrontEndComponent( xFrontEndContext, uno::UNO_QUERY );
1679 if( !xFrontEndComponent.is() )
1680 throw uno::RuntimeException("AccessibleTextHelper_Impl::getAccessibleAt: frontend is no XAccessibleComponent", mxFrontEnd );
1682 // #103862# No longer need to make given position relative
1683 Point aPoint( _aPoint.X, _aPoint.Y );
1685 // respect EditEngine offset to surrounding shape/cell
1686 aPoint -= GetOffset();
1688 // convert to EditEngine coordinate system
1689 SvxTextForwarder& rCacheTF = GetTextForwarder();
1690 Point aLogPoint( GetViewForwarder().PixelToLogic( aPoint, rCacheTF.GetMapMode() ) );
1692 // iterate over all visible children (including those not yet created)
1693 sal_Int32 nChild;
1694 for( nChild=mnFirstVisibleChild; nChild <= mnLastVisibleChild; ++nChild )
1696 DBG_ASSERT(nChild >= 0 && nChild <= USHRT_MAX,
1697 "AccessibleTextHelper_Impl::getAccessibleAt: index value overflow");
1699 Rectangle aParaBounds( rCacheTF.GetParaBounds( nChild ) );
1701 if( aParaBounds.IsInside( aLogPoint ) )
1702 return getAccessibleChild( nChild - mnFirstVisibleChild + GetStartIndex() );
1705 // found none
1706 return NULL;
1709 //------------------------------------------------------------------------
1711 // AccessibleTextHelper implementation (simply forwards to impl)
1713 //------------------------------------------------------------------------
1715 SAL_WNODEPRECATED_DECLARATIONS_PUSH
1716 AccessibleTextHelper::AccessibleTextHelper( ::std::auto_ptr< SvxEditSource > pEditSource ) :
1717 mpImpl( new AccessibleTextHelper_Impl() )
1719 SolarMutexGuard aGuard;
1721 SetEditSource( pEditSource );
1723 SAL_WNODEPRECATED_DECLARATIONS_PUSH
1725 AccessibleTextHelper::~AccessibleTextHelper()
1729 const SvxEditSource& AccessibleTextHelper::GetEditSource() const SAL_THROW((uno::RuntimeException))
1731 #ifdef DBG_UTIL
1732 mpImpl->CheckInvariants();
1734 const SvxEditSource& aEditSource = mpImpl->GetEditSource();
1736 mpImpl->CheckInvariants();
1738 return aEditSource;
1739 #else
1740 return mpImpl->GetEditSource();
1741 #endif
1744 SAL_WNODEPRECATED_DECLARATIONS_PUSH
1745 void AccessibleTextHelper::SetEditSource( ::std::auto_ptr< SvxEditSource > pEditSource ) SAL_THROW((uno::RuntimeException))
1747 #ifdef DBG_UTIL
1748 // precondition: solar mutex locked
1749 DBG_TESTSOLARMUTEX();
1751 mpImpl->CheckInvariants();
1752 #endif
1754 mpImpl->SetEditSource( pEditSource );
1756 #ifdef DBG_UTIL
1757 mpImpl->CheckInvariants();
1758 #endif
1760 SAL_WNODEPRECATED_DECLARATIONS_POP
1762 void AccessibleTextHelper::SetEventSource( const uno::Reference< XAccessible >& rInterface )
1764 #ifdef DBG_UTIL
1765 mpImpl->CheckInvariants();
1766 #endif
1768 mpImpl->SetEventSource( rInterface );
1770 #ifdef DBG_UTIL
1771 mpImpl->CheckInvariants();
1772 #endif
1775 uno::Reference< XAccessible > AccessibleTextHelper::GetEventSource() const
1777 #ifdef DBG_UTIL
1778 mpImpl->CheckInvariants();
1780 uno::Reference< XAccessible > xRet( mpImpl->GetEventSource() );
1782 mpImpl->CheckInvariants();
1784 return xRet;
1785 #else
1786 return mpImpl->GetEventSource();
1787 #endif
1790 void AccessibleTextHelper::SetFocus( sal_Bool bHaveFocus ) SAL_THROW((::com::sun::star::uno::RuntimeException))
1792 #ifdef DBG_UTIL
1793 // precondition: solar mutex locked
1794 DBG_TESTSOLARMUTEX();
1796 mpImpl->CheckInvariants();
1797 #endif
1799 mpImpl->SetFocus( bHaveFocus );
1801 #ifdef DBG_UTIL
1802 mpImpl->CheckInvariants();
1803 #endif
1806 sal_Bool AccessibleTextHelper::HaveFocus() SAL_THROW((::com::sun::star::uno::RuntimeException))
1808 #ifdef DBG_UTIL
1809 mpImpl->CheckInvariants();
1811 sal_Bool bRet( mpImpl->HaveFocus() );
1813 mpImpl->CheckInvariants();
1815 return bRet;
1816 #else
1817 return mpImpl->HaveFocus();
1818 #endif
1821 void AccessibleTextHelper::FireEvent( const sal_Int16 nEventId, const uno::Any& rNewValue, const uno::Any& rOldValue ) const
1823 #ifdef DBG_UTIL
1824 mpImpl->CheckInvariants();
1825 #endif
1827 mpImpl->FireEvent( nEventId, rNewValue, rOldValue );
1829 #ifdef DBG_UTIL
1830 mpImpl->CheckInvariants();
1831 #endif
1834 void AccessibleTextHelper::FireEvent( const AccessibleEventObject& rEvent ) const
1836 #ifdef DBG_UTIL
1837 mpImpl->CheckInvariants();
1838 #endif
1840 mpImpl->FireEvent( rEvent );
1842 #ifdef DBG_UTIL
1843 mpImpl->CheckInvariants();
1844 #endif
1847 void AccessibleTextHelper::SetOffset( const Point& rPoint )
1849 #ifdef DBG_UTIL
1850 // precondition: solar mutex locked
1851 DBG_TESTSOLARMUTEX();
1853 mpImpl->CheckInvariants();
1854 #endif
1856 mpImpl->SetOffset( rPoint );
1858 #ifdef DBG_UTIL
1859 mpImpl->CheckInvariants();
1860 #endif
1863 Point AccessibleTextHelper::GetOffset() const
1865 #ifdef DBG_UTIL
1866 mpImpl->CheckInvariants();
1868 Point aPoint( mpImpl->GetOffset() );
1870 mpImpl->CheckInvariants();
1872 return aPoint;
1873 #else
1874 return mpImpl->GetOffset();
1875 #endif
1878 void AccessibleTextHelper::SetStartIndex( sal_Int32 nOffset )
1880 #ifdef DBG_UTIL
1881 // precondition: solar mutex locked
1882 DBG_TESTSOLARMUTEX();
1884 mpImpl->CheckInvariants();
1885 #endif
1887 mpImpl->SetStartIndex( nOffset );
1889 #ifdef DBG_UTIL
1890 mpImpl->CheckInvariants();
1891 #endif
1894 sal_Int32 AccessibleTextHelper::GetStartIndex() const
1896 #ifdef DBG_UTIL
1897 mpImpl->CheckInvariants();
1899 sal_Int32 nOffset = mpImpl->GetStartIndex();
1901 mpImpl->CheckInvariants();
1903 return nOffset;
1904 #else
1905 return mpImpl->GetStartIndex();
1906 #endif
1909 void AccessibleTextHelper::SetAdditionalChildStates( const VectorOfStates& rChildStates )
1911 mpImpl->SetAdditionalChildStates( rChildStates );
1914 void AccessibleTextHelper::UpdateChildren() SAL_THROW((::com::sun::star::uno::RuntimeException))
1916 #ifdef DBG_UTIL
1917 // precondition: solar mutex locked
1918 DBG_TESTSOLARMUTEX();
1920 mpImpl->CheckInvariants();
1921 #endif
1923 mpImpl->UpdateVisibleChildren();
1924 mpImpl->UpdateBoundRect();
1926 mpImpl->UpdateSelection();
1928 #ifdef DBG_UTIL
1929 mpImpl->CheckInvariants();
1930 #endif
1933 void AccessibleTextHelper::Dispose()
1935 // As Dispose calls ShutdownEditSource, which in turn
1936 // deregisters as listener on the edit source, have to lock
1937 // here
1938 SolarMutexGuard aGuard;
1940 #ifdef DBG_UTIL
1941 mpImpl->CheckInvariants();
1942 #endif
1944 mpImpl->Dispose();
1946 #ifdef DBG_UTIL
1947 mpImpl->CheckInvariants();
1948 #endif
1951 sal_Bool AccessibleTextHelper::IsSelected() const
1953 SolarMutexGuard aGuard;
1955 #ifdef DBG_UTIL
1956 mpImpl->CheckInvariants();
1958 sal_Bool aRet = mpImpl->IsSelected();
1960 mpImpl->CheckInvariants();
1962 return aRet;
1963 #else
1964 return mpImpl->IsSelected();
1965 #endif
1968 // XAccessibleContext
1969 sal_Int32 AccessibleTextHelper::GetChildCount() SAL_THROW((uno::RuntimeException))
1971 SolarMutexGuard aGuard;
1973 #ifdef DBG_UTIL
1974 mpImpl->CheckInvariants();
1976 sal_Int32 nRet = mpImpl->getAccessibleChildCount();
1978 mpImpl->CheckInvariants();
1980 return nRet;
1981 #else
1982 return mpImpl->getAccessibleChildCount();
1983 #endif
1986 uno::Reference< XAccessible > AccessibleTextHelper::GetChild( sal_Int32 i ) SAL_THROW((lang::IndexOutOfBoundsException, uno::RuntimeException))
1988 SolarMutexGuard aGuard;
1990 #ifdef DBG_UTIL
1991 mpImpl->CheckInvariants();
1993 uno::Reference< XAccessible > xRet = mpImpl->getAccessibleChild( i );
1995 mpImpl->CheckInvariants();
1997 return xRet;
1998 #else
1999 return mpImpl->getAccessibleChild( i );
2000 #endif
2003 void AccessibleTextHelper::AddEventListener( const uno::Reference< XAccessibleEventListener >& xListener ) SAL_THROW((uno::RuntimeException))
2005 #ifdef DBG_UTIL
2006 mpImpl->CheckInvariants();
2008 mpImpl->addAccessibleEventListener( xListener );
2010 mpImpl->CheckInvariants();
2011 #else
2012 mpImpl->addAccessibleEventListener( xListener );
2013 #endif
2016 void AccessibleTextHelper::RemoveEventListener( const uno::Reference< XAccessibleEventListener >& xListener ) SAL_THROW((uno::RuntimeException))
2018 #ifdef DBG_UTIL
2019 mpImpl->CheckInvariants();
2021 mpImpl->removeAccessibleEventListener( xListener );
2023 mpImpl->CheckInvariants();
2024 #else
2025 mpImpl->removeAccessibleEventListener( xListener );
2026 #endif
2029 // XAccessibleComponent
2030 uno::Reference< XAccessible > AccessibleTextHelper::GetAt( const awt::Point& aPoint ) SAL_THROW((uno::RuntimeException))
2032 SolarMutexGuard aGuard;
2034 #ifdef DBG_UTIL
2035 mpImpl->CheckInvariants();
2037 uno::Reference< XAccessible > xChild = mpImpl->getAccessibleAtPoint( aPoint );
2039 mpImpl->CheckInvariants();
2041 return xChild;
2042 #else
2043 return mpImpl->getAccessibleAtPoint( aPoint );
2044 #endif
2047 } // end of namespace accessibility
2049 //------------------------------------------------------------------------
2051 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */