Version 4.3.0.0.beta1, tag libreoffice-4.3.0.0.beta1
[LibreOffice.git] / svx / source / accessibility / AccessibleTextHelper.cxx
blobad3ac459dcf8bafce98d97ebcb7fb701a0c22e89
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 .
23 // Global header
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/svapp.hxx>
46 #include <vcl/textdata.hxx>
47 #include <vcl/unohelp.hxx>
48 #include <sfx2/viewfrm.hxx>
49 #include <sfx2/viewsh.hxx>
53 // Project-local header
56 #include "AccessibleTextEventQueue.hxx"
57 #include <svx/AccessibleTextHelper.hxx>
58 #include <svx/unoshape.hxx>
59 #include "editeng/unolingu.hxx"
60 #include <editeng/unotext.hxx>
62 #include "editeng/unoedhlp.hxx"
63 #include "editeng/unopracc.hxx"
64 #include "editeng/AccessibleParaManager.hxx"
65 #include "editeng/AccessibleEditableTextPara.hxx"
66 #include <svx/svdmodel.hxx>
67 #include <svx/svdpntv.hxx>
68 #include "../table/cell.hxx"
69 #include "../table/accessiblecell.hxx"
70 #include <editeng/editdata.hxx>
71 #include <editeng/editeng.hxx>
72 #include <editeng/editview.hxx>
74 using namespace ::com::sun::star;
75 using namespace ::com::sun::star::accessibility;
77 namespace accessibility
82 // AccessibleTextHelper_Impl declaration
87 template < typename first_type, typename second_type >
88 ::std::pair< first_type, second_type > makeSortedPair( first_type first,
89 second_type second )
91 if( first > second )
92 return ::std::make_pair( second, first );
93 else
94 return ::std::make_pair( first, second );
97 class AccessibleTextHelper_Impl : public SfxListener
100 public:
101 typedef ::std::vector< sal_Int16 > VectorOfStates;
103 // receive pointer to our frontend class and view window
104 AccessibleTextHelper_Impl();
105 virtual ~AccessibleTextHelper_Impl();
107 // XAccessibleContext child handling methods
108 sal_Int32 SAL_CALL getAccessibleChildCount() SAL_THROW((uno::RuntimeException));
109 uno::Reference< XAccessible > SAL_CALL getAccessibleChild( sal_Int32 i ) SAL_THROW((lang::IndexOutOfBoundsException, uno::RuntimeException));
111 // XAccessibleEventBroadcaster child related methods
112 void SAL_CALL addAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener ) SAL_THROW((uno::RuntimeException));
113 void SAL_CALL removeAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener ) SAL_THROW((uno::RuntimeException));
115 // XAccessibleComponent child related methods
116 uno::Reference< XAccessible > SAL_CALL getAccessibleAtPoint( const awt::Point& aPoint ) SAL_THROW((uno::RuntimeException));
118 SvxEditSourceAdapter& GetEditSource() const SAL_THROW((uno::RuntimeException));
119 SAL_WNODEPRECATED_DECLARATIONS_PUSH
120 void SetEditSource( ::std::auto_ptr< SvxEditSource > pEditSource ) SAL_THROW((uno::RuntimeException));
121 SAL_WNODEPRECATED_DECLARATIONS_POP
123 void SetEventSource( const uno::Reference< XAccessible >& rInterface )
125 mxFrontEnd = rInterface;
127 uno::Reference< XAccessible > GetEventSource() const
129 return mxFrontEnd;
132 void SetOffset( const Point& );
133 Point GetOffset() const
135 ::osl::MutexGuard aGuard( maMutex ); Point aPoint( maOffset );
136 return aPoint;
139 void SetStartIndex( sal_Int32 nOffset );
140 sal_Int32 GetStartIndex() const
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 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( bool bHaveFocus ) SAL_THROW((::com::sun::star::uno::RuntimeException));
159 bool HaveFocus() SAL_THROW((::com::sun::star::uno::RuntimeException));
160 void SetChildFocus( sal_Int32 nChild, bool bHaveFocus ) SAL_THROW((::com::sun::star::uno::RuntimeException));
161 void SetShapeFocus( 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 ) SAL_OVERRIDE;
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( bool bCreate = false ) const SAL_THROW((uno::RuntimeException));
203 // are we in edit mode?
204 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 its children has the focus set (guarded by solar mutex)
238 bool mbGroupHasFocus;
240 // whether we (this object) has the focus set (guarded by solar mutex)
241 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;
254 // AccessibleTextHelper_Impl implementation
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( false ),
267 mbThisHasFocus( 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())
277 #ifdef DBG_UTIL
278 OSL_TRACE( "AccessibleTextHelper_Impl received ID: %d", mnNotifierClientId );
279 #endif
282 AccessibleTextHelper_Impl::~AccessibleTextHelper_Impl()
285 SolarMutexGuard aGuard;
289 // call Dispose here, too, since we've some resources not
290 // automatically freed otherwise
291 Dispose();
293 catch( const uno::Exception& ) {}
296 SvxTextForwarder& AccessibleTextHelper_Impl::GetTextForwarder() const SAL_THROW((uno::RuntimeException))
299 if( !maEditSource.IsValid() )
300 throw uno::RuntimeException("Unknown edit source", mxFrontEnd);
302 SvxTextForwarder* pTextForwarder = maEditSource.GetTextForwarder();
304 if( !pTextForwarder )
305 throw uno::RuntimeException("Unable to fetch text forwarder, model might be dead", mxFrontEnd);
307 if( pTextForwarder->IsValid() )
308 return *pTextForwarder;
309 else
310 throw uno::RuntimeException("Text forwarder is invalid, model might be dead", mxFrontEnd);
313 SvxViewForwarder& AccessibleTextHelper_Impl::GetViewForwarder() const SAL_THROW((uno::RuntimeException))
316 if( !maEditSource.IsValid() )
317 throw uno::RuntimeException("Unknown edit source", mxFrontEnd);
319 SvxViewForwarder* pViewForwarder = maEditSource.GetViewForwarder();
321 if( !pViewForwarder )
322 throw uno::RuntimeException("Unable to fetch view forwarder, model might be dead", mxFrontEnd);
324 if( pViewForwarder->IsValid() )
325 return *pViewForwarder;
326 else
327 throw uno::RuntimeException("View forwarder is invalid, model might be dead", mxFrontEnd);
330 SvxEditViewForwarder& AccessibleTextHelper_Impl::GetEditViewForwarder( bool bCreate ) const SAL_THROW((uno::RuntimeException))
333 if( !maEditSource.IsValid() )
334 throw uno::RuntimeException("Unknown edit source", mxFrontEnd);
336 SvxEditViewForwarder* pViewForwarder = maEditSource.GetEditViewForwarder( bCreate );
338 if( !pViewForwarder )
340 if( bCreate )
341 throw uno::RuntimeException("Unable to fetch edit view forwarder, model might be dead", mxFrontEnd);
342 else
343 throw uno::RuntimeException("No edit view forwarder, object not in edit mode", mxFrontEnd);
346 if( pViewForwarder->IsValid() )
347 return *pViewForwarder;
348 else
350 if( bCreate )
351 throw uno::RuntimeException("View forwarder is invalid, model might be dead", mxFrontEnd);
352 else
353 throw uno::RuntimeException("View forwarder is invalid, object not in edit mode", mxFrontEnd);
357 SvxEditSourceAdapter& AccessibleTextHelper_Impl::GetEditSource() const SAL_THROW((uno::RuntimeException))
360 if( maEditSource.IsValid() )
361 return maEditSource;
362 else
363 throw uno::RuntimeException("AccessibleTextHelper_Impl::GetEditSource: no edit source", mxFrontEnd );
366 bool AccessibleTextHelper_Impl::IsSelected() const
369 bool bRet = false;
373 ESelection aSelection;
374 bRet = GetEditViewForwarder().GetSelection( aSelection );
376 catch( const uno::Exception& ) {}
378 return bRet;
381 // functor for sending child events (no stand-alone function, they are maybe not inlined)
382 class AccessibleTextHelper_OffsetChildIndex : public ::std::unary_function< ::accessibility::AccessibleEditableTextPara&, void >
384 public:
385 AccessibleTextHelper_OffsetChildIndex( sal_Int32 nDifference ) : mnDifference(nDifference) {}
386 void operator()( ::accessibility::AccessibleEditableTextPara& rPara )
388 rPara.SetIndexInParent( rPara.GetIndexInParent() + mnDifference );
391 private:
392 const sal_Int32 mnDifference;
395 void AccessibleTextHelper_Impl::SetStartIndex( sal_Int32 nOffset )
398 sal_Int32 nOldOffset( mnStartIndex );
400 mnStartIndex = nOffset;
402 if( nOldOffset != nOffset )
404 // update children
405 AccessibleTextHelper_OffsetChildIndex aFunctor( nOffset - nOldOffset );
407 ::std::for_each( maParaManager.begin(), maParaManager.end(),
408 AccessibleParaManager::WeakChildAdapter< AccessibleTextHelper_OffsetChildIndex > (aFunctor) );
412 void AccessibleTextHelper_Impl::SetAdditionalChildStates( const VectorOfStates& rChildStates )
414 maParaManager.SetAdditionalChildStates( rChildStates );
417 void AccessibleTextHelper_Impl::SetChildFocus( sal_Int32 nChild, bool bHaveFocus ) SAL_THROW((::com::sun::star::uno::RuntimeException))
420 if( bHaveFocus )
422 if( mbThisHasFocus )
423 SetShapeFocus( false );
425 maParaManager.SetFocus( nChild );
427 // we just received the focus, also send caret event then
428 UpdateSelection();
430 OSL_TRACE("AccessibleTextHelper_Impl::SetChildFocus(): Paragraph %d received focus", nChild );
432 else
434 maParaManager.SetFocus( -1 );
436 OSL_TRACE("AccessibleTextHelper_Impl::SetChildFocus(): Paragraph %d lost focus", nChild );
438 if( mbGroupHasFocus )
439 SetShapeFocus( true );
443 void AccessibleTextHelper_Impl::ChangeChildFocus( sal_Int32 nNewChild ) SAL_THROW((::com::sun::star::uno::RuntimeException))
446 if( mbThisHasFocus )
447 SetShapeFocus( false );
449 mbGroupHasFocus = true;
450 maParaManager.SetFocus( nNewChild );
452 OSL_TRACE("AccessibleTextHelper_Impl::ChangeChildFocus(): Paragraph %d received focus", nNewChild );
455 void AccessibleTextHelper_Impl::SetShapeFocus( bool bHaveFocus ) SAL_THROW((::com::sun::star::uno::RuntimeException))
458 bool bOldFocus( mbThisHasFocus );
460 mbThisHasFocus = bHaveFocus;
462 if( bOldFocus != bHaveFocus )
464 if( bHaveFocus )
466 if( mxFrontEnd.is() )
468 AccessibleCell* pAccessibleCell = dynamic_cast< AccessibleCell* > ( mxFrontEnd.get() );
469 if ( !pAccessibleCell )
470 GotPropertyEvent( uno::makeAny(AccessibleStateType::FOCUSED), AccessibleEventId::STATE_CHANGED );
471 else // the focus event on cell should be fired on table directly
473 AccessibleTableShape* pAccTable = pAccessibleCell->GetParentTable();
474 if (pAccTable)
475 pAccTable->SetStateDirectly(AccessibleStateType::FOCUSED);
478 OSL_TRACE("AccessibleTextHelper_Impl::SetShapeFocus(): Parent object received focus" );
480 else
482 // The focus state should be reset directly on table.
483 //LostPropertyEvent( uno::makeAny(AccessibleStateType::FOCUSED), AccessibleEventId::STATE_CHANGED );
484 if( mxFrontEnd.is() )
486 AccessibleCell* pAccessibleCell = dynamic_cast< AccessibleCell* > ( mxFrontEnd.get() );
487 if ( !pAccessibleCell )
488 LostPropertyEvent( uno::makeAny(AccessibleStateType::FOCUSED), AccessibleEventId::STATE_CHANGED );
489 else
491 AccessibleTableShape* pAccTable = pAccessibleCell->GetParentTable();
492 if (pAccTable)
493 pAccTable->ResetStateDirectly(AccessibleStateType::FOCUSED);
496 OSL_TRACE("AccessibleTextHelper_Impl::SetShapeFocus(): Parent object lost focus" );
501 void AccessibleTextHelper_Impl::SetFocus( bool bHaveFocus ) SAL_THROW((::com::sun::star::uno::RuntimeException))
504 bool bOldFocus( mbGroupHasFocus );
506 mbGroupHasFocus = bHaveFocus;
508 if( IsActive() )
512 // find the one with the cursor and get/set focus accordingly
513 ESelection aSelection;
514 if( GetEditViewForwarder().GetSelection( aSelection ) )
515 SetChildFocus( aSelection.nEndPara, bHaveFocus );
517 catch( const uno::Exception& ) {}
519 else if( bOldFocus != bHaveFocus )
521 SetShapeFocus( bHaveFocus );
524 OSL_TRACE("AccessibleTextHelper_Impl::SetFocus: focus changed, Object %p, state: %s", this, bHaveFocus ? "focused" : "not focused");
527 bool AccessibleTextHelper_Impl::HaveFocus() SAL_THROW((::com::sun::star::uno::RuntimeException))
530 // No locking of solar mutex here, since we rely on the fact
531 // that sal_Bool access is atomic
532 return mbThisHasFocus;
535 bool AccessibleTextHelper_Impl::IsActive() const SAL_THROW((uno::RuntimeException))
540 SvxEditSource& rEditSource = GetEditSource();
541 SvxEditViewForwarder* pViewForwarder = rEditSource.GetEditViewForwarder();
543 if( !pViewForwarder )
544 return false;
546 if( mxFrontEnd.is() )
548 AccessibleCell* pAccessibleCell = dynamic_cast< AccessibleCell* > ( mxFrontEnd.get() );
549 if ( pAccessibleCell )
551 sdr::table::CellRef xCell = pAccessibleCell->getCellRef();
552 if ( xCell.is() )
553 return xCell->IsTextEditActive();
556 if( pViewForwarder->IsValid() )
557 return true;
558 else
559 return false;
561 catch( const uno::RuntimeException& )
563 return false;
567 void AccessibleTextHelper_Impl::UpdateSelection()
572 ESelection aSelection;
573 if( GetEditViewForwarder().GetSelection( aSelection ) )
575 if( !maLastSelection.IsEqual( aSelection ) &&
576 aSelection.nEndPara < maParaManager.GetNum() )
578 // #103998# Not that important, changed from assertion to trace
579 if( mbThisHasFocus )
581 OSL_TRACE("AccessibleTextHelper_Impl::UpdateSelection(): Parent has focus!");
584 sal_Int32 nMaxValidParaIndex( GetTextForwarder().GetParagraphCount() - 1 );
586 // notify all affected paragraphs (TODO: may be suboptimal,
587 // since some paragraphs might stay selected)
588 if( maLastSelection.nStartPara != EE_PARA_NOT_FOUND )
590 // Did the caret move from one paragraph to another?
591 // #100530# no caret events if not focused.
592 if( mbGroupHasFocus &&
593 maLastSelection.nEndPara != aSelection.nEndPara )
595 if( maLastSelection.nEndPara < maParaManager.GetNum() )
597 maParaManager.FireEvent( ::std::min( maLastSelection.nEndPara, nMaxValidParaIndex ),
598 ::std::min( maLastSelection.nEndPara, nMaxValidParaIndex )+1,
599 AccessibleEventId::CARET_CHANGED,
600 uno::makeAny(static_cast<sal_Int32>(-1)),
601 uno::makeAny(static_cast<sal_Int32>(maLastSelection.nEndPos)) );
604 ChangeChildFocus( aSelection.nEndPara );
606 OSL_TRACE("AccessibleTextHelper_Impl::UpdateSelection(): focus changed, Object: %p, Paragraph: %d, Last paragraph: %d",
607 this, aSelection.nEndPara, maLastSelection.nEndPara);
611 // #100530# no caret events if not focused.
612 if( mbGroupHasFocus )
614 uno::Any aOldCursor;
616 // #i13705# The old cursor can only contain valid
617 // values if it's the same paragraph!
618 if( maLastSelection.nStartPara != EE_PARA_NOT_FOUND &&
619 maLastSelection.nEndPara == aSelection.nEndPara )
621 aOldCursor <<= static_cast<sal_Int32>(maLastSelection.nEndPos);
623 else
625 aOldCursor <<= static_cast<sal_Int32>(-1);
628 maParaManager.FireEvent( aSelection.nEndPara,
629 aSelection.nEndPara+1,
630 AccessibleEventId::CARET_CHANGED,
631 uno::makeAny(static_cast<sal_Int32>(aSelection.nEndPos)),
632 aOldCursor );
635 OSL_TRACE("AccessibleTextHelper_Impl::UpdateSelection(): caret changed, Object: %p, New pos: %d, Old pos: %d, New para: %d, Old para: %d",
636 this, aSelection.nEndPos, maLastSelection.nEndPos, aSelection.nEndPara, maLastSelection.nEndPara);
638 // #108947# Sort new range before calling FireEvent
639 ::std::pair<sal_Int32, sal_Int32> sortedSelection(
640 makeSortedPair(::std::min( aSelection.nStartPara, nMaxValidParaIndex ),
641 ::std::min( aSelection.nEndPara, nMaxValidParaIndex ) ) );
643 // #108947# Sort last range before calling FireEvent
644 ::std::pair<sal_Int32, sal_Int32> sortedLastSelection(
645 makeSortedPair(::std::min( maLastSelection.nStartPara, nMaxValidParaIndex ),
646 ::std::min( maLastSelection.nEndPara, nMaxValidParaIndex ) ) );
648 // event TEXT_SELECTION_CHANGED has to be submitted. (#i27299#)
649 const sal_Int16 nTextSelChgEventId =
650 AccessibleEventId::TEXT_SELECTION_CHANGED;
651 // #107037# notify selection change
652 if( maLastSelection.nStartPara == EE_PARA_NOT_FOUND )
654 // last selection is undefined
655 // use method <ESelection::HasRange()> (#i27299#)
656 if ( aSelection.HasRange() )
658 // selection was undefined, now is on
659 maParaManager.FireEvent( sortedSelection.first,
660 sortedSelection.second+1,
661 nTextSelChgEventId );
664 else
666 // last selection is valid
667 // use method <ESelection::HasRange()> (#i27299#)
668 if ( maLastSelection.HasRange() &&
669 !aSelection.HasRange() )
671 // selection was on, now is empty
672 maParaManager.FireEvent( sortedLastSelection.first,
673 sortedLastSelection.second+1,
674 nTextSelChgEventId );
676 // use method <ESelection::HasRange()> (#i27299#)
677 else if( !maLastSelection.HasRange() &&
678 aSelection.HasRange() )
680 // selection was empty, now is on
681 maParaManager.FireEvent( sortedSelection.first,
682 sortedSelection.second+1,
683 nTextSelChgEventId );
685 // no event TEXT_SELECTION_CHANGED event, if new and
686 // last selection are empty. (#i27299#)
687 else if ( maLastSelection.HasRange() &&
688 aSelection.HasRange() )
690 // use sorted last and new selection
691 ESelection aTmpLastSel( maLastSelection );
692 aTmpLastSel.Adjust();
693 ESelection aTmpSel( aSelection );
694 aTmpSel.Adjust();
695 // first submit event for new and changed selection
696 sal_Int32 nPara = aTmpSel.nStartPara;
697 for ( ; nPara <= aTmpSel.nEndPara; ++nPara )
699 if ( nPara < aTmpLastSel.nStartPara ||
700 nPara > aTmpLastSel.nEndPara )
702 // new selection on paragraph <nPara>
703 maParaManager.FireEvent( nPara,
704 nTextSelChgEventId );
706 else
708 // check for changed selection on paragraph <nPara>
709 const sal_Int32 nParaStartPos =
710 nPara == aTmpSel.nStartPara
711 ? aTmpSel.nStartPos : 0;
712 const sal_Int32 nParaEndPos =
713 nPara == aTmpSel.nEndPara
714 ? aTmpSel.nEndPos : -1;
715 const sal_Int32 nLastParaStartPos =
716 nPara == aTmpLastSel.nStartPara
717 ? aTmpLastSel.nStartPos : 0;
718 const sal_Int32 nLastParaEndPos =
719 nPara == aTmpLastSel.nEndPara
720 ? aTmpLastSel.nEndPos : -1;
721 if ( nParaStartPos != nLastParaStartPos ||
722 nParaEndPos != nLastParaEndPos )
724 maParaManager.FireEvent(
725 nPara, nTextSelChgEventId );
729 // second submit event for 'old' selections
730 nPara = aTmpLastSel.nStartPara;
731 for ( ; nPara <= aTmpLastSel.nEndPara; ++nPara )
733 if ( nPara < aTmpSel.nStartPara ||
734 nPara > aTmpSel.nEndPara )
736 maParaManager.FireEvent( nPara,
737 nTextSelChgEventId );
743 maLastSelection = aSelection;
747 // no selection? no update actions
748 catch( const uno::RuntimeException& ) {}
751 void AccessibleTextHelper_Impl::ShutdownEditSource() SAL_THROW((uno::RuntimeException))
754 // This should only be called with solar mutex locked, i.e. from the main office thread
756 // This here is somewhat clumsy: As soon as our children have
757 // a NULL EditSource (maParaManager.SetEditSource()), they
758 // enter the disposed state and cannot be reanimated. Thus, it
759 // is unavoidable and a hard requirement to let go and create
760 // from scratch each and every child.
762 // invalidate children
763 maParaManager.Dispose();
764 maParaManager.SetNum(0);
766 // lost all children
767 if( mxFrontEnd.is() )
768 FireEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN);
770 // quit listen on stale edit source
771 if( maEditSource.IsValid() )
772 EndListening( maEditSource.GetBroadcaster() );
774 SAL_WNODEPRECATED_DECLARATIONS_PUSH
775 maEditSource.SetEditSource( ::std::auto_ptr< SvxEditSource >(NULL) );
776 SAL_WNODEPRECATED_DECLARATIONS_POP
779 SAL_WNODEPRECATED_DECLARATIONS_PUSH
780 void AccessibleTextHelper_Impl::SetEditSource( ::std::auto_ptr< SvxEditSource > pEditSource ) SAL_THROW((uno::RuntimeException))
783 // This should only be called with solar mutex locked, i.e. from the main office thread
785 // shutdown old edit source
786 ShutdownEditSource();
788 // set new edit source
789 maEditSource.SetEditSource( pEditSource );
791 // init child vector to the current child count
792 if( maEditSource.IsValid() )
794 maParaManager.SetNum( GetTextForwarder().GetParagraphCount() );
796 // listen on new edit source
797 StartListening( maEditSource.GetBroadcaster() );
799 UpdateVisibleChildren();
802 SAL_WNODEPRECATED_DECLARATIONS_POP
804 void AccessibleTextHelper_Impl::SetOffset( const Point& rPoint )
807 // guard against non-atomic access to maOffset data structure
809 ::osl::MutexGuard aGuard( maMutex );
810 maOffset = rPoint;
813 maParaManager.SetEEOffset( rPoint );
815 // in all cases, check visibility afterwards.
816 UpdateVisibleChildren();
817 UpdateBoundRect();
820 void AccessibleTextHelper_Impl::UpdateVisibleChildren( bool bBroadcastEvents )
825 SvxTextForwarder& rCacheTF = GetTextForwarder();
826 SvxViewForwarder& rCacheVF = GetViewForwarder();
828 Rectangle aViewArea = rCacheVF.GetVisArea();
830 if( IsActive() )
832 // maybe the edit view scrolls, adapt aViewArea
833 Rectangle aEditViewArea = GetEditViewForwarder().GetVisArea();
834 aViewArea += aEditViewArea.TopLeft();
836 // now determine intersection
837 aViewArea.Intersection( aEditViewArea );
840 Rectangle aTmpBB, aParaBB;
841 bool bFirstChild = true;
842 sal_Int32 nCurrPara;
843 sal_Int32 nParas=rCacheTF.GetParagraphCount();
845 mnFirstVisibleChild = -1;
846 mnLastVisibleChild = -2;
848 for( nCurrPara=0; nCurrPara<nParas; ++nCurrPara )
850 DBG_ASSERT(nCurrPara >= 0 && nCurrPara <= USHRT_MAX,
851 "AccessibleTextHelper_Impl::UpdateVisibleChildren: index value overflow");
853 aTmpBB = rCacheTF.GetParaBounds( nCurrPara );
855 // convert to screen coordinates
856 aParaBB = ::accessibility::AccessibleEditableTextPara::LogicToPixel( aTmpBB, rCacheTF.GetMapMode(), rCacheVF );
857 // at least partially visible
858 if( bFirstChild )
860 bFirstChild = false;
861 mnFirstVisibleChild = nCurrPara;
864 mnLastVisibleChild = nCurrPara;
866 // child not yet created?
867 ::accessibility::AccessibleParaManager::WeakChild aChild( maParaManager.GetChild(nCurrPara) );
868 if( aChild.second.Width == 0 &&
869 aChild.second.Height == 0 &&
870 mxFrontEnd.is() &&
871 bBroadcastEvents )
873 GotPropertyEvent( uno::makeAny( maParaManager.CreateChild( nCurrPara - mnFirstVisibleChild,
874 mxFrontEnd, GetEditSource(), nCurrPara ).first ),
875 AccessibleEventId::CHILD );
879 catch( const uno::Exception& )
881 OSL_FAIL("AccessibleTextHelper_Impl::UpdateVisibleChildren error while determining visible children");
883 // something failed - currently no children
884 mnFirstVisibleChild = -1;
885 mnLastVisibleChild = -2;
886 maParaManager.SetNum(0);
888 // lost all children
889 if( bBroadcastEvents )
890 FireEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN);
894 // functor for checking changes in paragraph bounding boxes (no stand-alone function, maybe not inlined)
895 class AccessibleTextHelper_UpdateChildBounds : public ::std::unary_function< const ::accessibility::AccessibleParaManager::WeakChild&,
896 ::accessibility::AccessibleParaManager::WeakChild >
898 public:
899 AccessibleTextHelper_UpdateChildBounds( AccessibleTextHelper_Impl& rImpl ) : mrImpl(rImpl) {}
900 ::accessibility::AccessibleParaManager::WeakChild operator()( const ::accessibility::AccessibleParaManager::WeakChild& rChild )
902 // retrieve hard reference from weak one
903 ::accessibility::AccessibleParaManager::WeakPara::HardRefType aHardRef( rChild.first.get() );
905 if( aHardRef.is() )
907 awt::Rectangle aNewRect = aHardRef->getBounds();
908 const awt::Rectangle& aOldRect = rChild.second;
910 if( aNewRect.X != aOldRect.X ||
911 aNewRect.Y != aOldRect.Y ||
912 aNewRect.Width != aOldRect.Width ||
913 aNewRect.Height != aOldRect.Height )
915 // visible data changed
916 aHardRef->FireEvent( AccessibleEventId::BOUNDRECT_CHANGED );
918 // update internal bounds
919 return ::accessibility::AccessibleParaManager::WeakChild( rChild.first, aNewRect );
923 // identity transform
924 return rChild;
927 private:
928 AccessibleTextHelper_Impl& mrImpl;
931 void AccessibleTextHelper_Impl::UpdateBoundRect()
934 // send BOUNDRECT_CHANGED to affected children
935 AccessibleTextHelper_UpdateChildBounds aFunctor( *this );
936 ::std::transform( maParaManager.begin(), maParaManager.end(), maParaManager.begin(), aFunctor );
939 #ifdef DBG_UTIL
940 void AccessibleTextHelper_Impl::CheckInvariants() const
942 if( mnFirstVisibleChild >= 0 &&
943 mnFirstVisibleChild > mnLastVisibleChild )
945 OSL_FAIL( "AccessibleTextHelper: range invalid" );
948 #endif
950 // functor for sending child events (no stand-alone function, they are maybe not inlined)
951 class AccessibleTextHelper_LostChildEvent : public ::std::unary_function< const ::accessibility::AccessibleParaManager::WeakChild&, void >
953 public:
954 AccessibleTextHelper_LostChildEvent( AccessibleTextHelper_Impl& rImpl ) : mrImpl(rImpl) {}
955 void operator()( const ::accessibility::AccessibleParaManager::WeakChild& rPara )
957 // retrieve hard reference from weak one
958 ::accessibility::AccessibleParaManager::WeakPara::HardRefType aHardRef( rPara.first.get() );
960 if( aHardRef.is() )
961 mrImpl.FireEvent(AccessibleEventId::CHILD, uno::Any(), uno::makeAny( aHardRef.getRef() ) );
964 private:
965 AccessibleTextHelper_Impl& mrImpl;
968 void AccessibleTextHelper_Impl::ParagraphsMoved( sal_Int32 nFirst, sal_Int32 nMiddle, sal_Int32 nLast )
971 const sal_Int32 nParas = GetTextForwarder().GetParagraphCount();
973 /* rotate paragraphs
974 * =================
976 * Three cases:
978 * 1.
979 * ... nParagraph ... nParam1 ... nParam2 ...
980 * |______________[xxxxxxxxxxx]
981 * becomes
982 * [xxxxxxxxxxx]|______________
984 * tail is 0
986 * 2.
987 * ... nParam1 ... nParagraph ... nParam2 ...
988 * [xxxxxxxxxxx|xxxxxxxxxxxxxx]____________
989 * becomes
990 * ____________[xxxxxxxxxxx|xxxxxxxxxxxxxx]
992 * tail is nParagraph - nParam1
994 * 3.
995 * ... nParam1 ... nParam2 ... nParagraph ...
996 * [xxxxxxxxxxx]___________|____________
997 * becomes
998 * ___________|____________[xxxxxxxxxxx]
1000 * tail is nParam2 - nParam1
1003 // sort nParagraph, nParam1 and nParam2 in ascending order, calc range
1004 if( nMiddle < nFirst )
1006 ::std::swap(nFirst, nMiddle);
1008 else if( nMiddle < nLast )
1010 nLast = nLast + nMiddle - nFirst;
1012 else
1014 ::std::swap(nMiddle, nLast);
1015 nLast = nLast + nMiddle - nFirst;
1018 if( nFirst < nParas && nMiddle < nParas && nLast < nParas )
1020 // since we have no "paragraph index
1021 // changed" event on UAA, remove
1022 // [first,last] and insert again later (in
1023 // UpdateVisibleChildren)
1025 // maParaManager.Rotate( nFirst, nMiddle, nLast );
1027 // send CHILD_EVENT to affected children
1028 ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator begin = maParaManager.begin();
1029 ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator end = begin;
1031 ::std::advance( begin, nFirst );
1032 ::std::advance( end, nLast+1 );
1034 // TODO: maybe optimize here in the following way. If the
1035 // number of removed children exceeds a certain threshold,
1036 // use INVALIDATE_CHILDREN
1037 AccessibleTextHelper_LostChildEvent aFunctor( *this );
1039 ::std::for_each( begin, end, aFunctor );
1041 maParaManager.Release(nFirst, nLast+1);
1042 // should be no need for UpdateBoundRect, since all affected children are cleared.
1046 // functor for sending child events (no stand-alone function, they are maybe not inlined)
1047 class AccessibleTextHelper_ChildrenTextChanged : public ::std::unary_function< ::accessibility::AccessibleEditableTextPara&, void >
1049 public:
1050 void operator()( ::accessibility::AccessibleEditableTextPara& rPara )
1052 rPara.TextChanged();
1056 /** functor processing queue events
1058 Reacts on TEXT_HINT_PARAINSERTED/REMOVED events and stores
1059 their content
1061 class AccessibleTextHelper_QueueFunctor : public ::std::unary_function< const SfxHint*, void >
1063 public:
1064 AccessibleTextHelper_QueueFunctor() :
1065 mnParasChanged( 0 ),
1066 mnParaIndex(-1),
1067 mnHintId(-1)
1069 void operator()( const SfxHint* pEvent )
1071 if( pEvent &&
1072 mnParasChanged != -1 )
1074 // determine hint type
1075 const TextHint* pTextHint = PTR_CAST( TextHint, pEvent );
1076 const SvxEditSourceHint* pEditSourceHint = PTR_CAST( SvxEditSourceHint, pEvent );
1078 if( !pEditSourceHint && pTextHint &&
1079 (pTextHint->GetId() == TEXT_HINT_PARAINSERTED ||
1080 pTextHint->GetId() == TEXT_HINT_PARAREMOVED ) )
1082 if( pTextHint->GetValue() == EE_PARA_ALL )
1084 mnParasChanged = -1;
1086 else
1088 mnHintId = pTextHint->GetId();
1089 mnParaIndex = pTextHint->GetValue();
1090 ++mnParasChanged;
1096 /** Query number of paragraphs changed during queue processing.
1098 @return number of changed paragraphs, -1 for
1099 "every paragraph changed"
1101 sal_Int32 GetNumberOfParasChanged() { return mnParasChanged; }
1102 /** Query index of last added/removed paragraph
1104 @return index of lastly added paragraphs, -1 for none
1105 added so far.
1107 sal_Int32 GetParaIndex() { return mnParaIndex; }
1108 /** Query hint id of last interesting event
1110 @return hint id of last interesting event (REMOVED/INSERTED).
1112 int GetHintId() { return mnHintId; }
1114 private:
1115 /** number of paragraphs changed during queue processing. -1 for
1116 "every paragraph changed"
1118 sal_Int32 mnParasChanged;
1119 /// index of paragraph added/removed last
1120 sal_Int32 mnParaIndex;
1121 /// TextHint ID (removed/inserted) of last interesting event
1122 int mnHintId;
1125 void AccessibleTextHelper_Impl::ProcessQueue()
1128 // inspect queue for paragraph insert/remove events. If there
1129 // is exactly _one_ of those in the queue, and the number of
1130 // paragraphs has changed by exactly one, use that event to
1131 // determine a priori which paragraph was added/removed. This
1132 // is necessary, since I must sync right here with the
1133 // EditEngine state (number of paragraphs etc.), since I'm
1134 // potentially sending listener events right away.
1135 AccessibleTextHelper_QueueFunctor aFunctor;
1136 maEventQueue.ForEach( aFunctor );
1138 const sal_Int32 nNewParas( GetTextForwarder().GetParagraphCount() );
1139 const sal_Int32 nCurrParas( maParaManager.GetNum() );
1141 // whether every paragraph already is updated (no need to
1142 // repeat that later on, e.g. for PARA_MOVED events)
1143 bool bEverythingUpdated( false );
1145 if( labs( nNewParas - nCurrParas ) == 1 &&
1146 aFunctor.GetNumberOfParasChanged() == 1 )
1148 // #103483# Exactly one paragraph added/removed. This is
1149 // the normal case, optimize event handling here.
1151 if( aFunctor.GetHintId() == TEXT_HINT_PARAINSERTED )
1153 // update num of paras
1154 maParaManager.SetNum( nNewParas );
1156 // release everything from the insertion position until the end
1157 maParaManager.Release(aFunctor.GetParaIndex(), nCurrParas);
1159 // TODO: Clarify whether this behaviour _really_ saves
1160 // anybody anything!
1161 // update children, _don't_ broadcast
1162 UpdateVisibleChildren( false );
1163 UpdateBoundRect();
1165 // send insert event
1166 // #109864# Enforce creation of this paragraph
1169 GotPropertyEvent( uno::makeAny( getAccessibleChild( aFunctor.GetParaIndex() -
1170 mnFirstVisibleChild + GetStartIndex() ) ),
1171 AccessibleEventId::CHILD );
1173 catch( const uno::Exception& )
1175 OSL_FAIL("AccessibleTextHelper_Impl::ProcessQueue: could not create new paragraph");
1178 else if( aFunctor.GetHintId() == TEXT_HINT_PARAREMOVED )
1180 ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator begin = maParaManager.begin();
1181 ::std::advance( begin, aFunctor.GetParaIndex() );
1182 ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator end = begin;
1183 ::std::advance( end, 1 );
1185 // #i61812# remember para to be removed for later notification
1186 // AFTER the new state is applied (that after the para got removed)
1187 ::uno::Reference< XAccessible > xPara;
1188 ::accessibility::AccessibleParaManager::WeakPara::HardRefType aHardRef( begin->first.get() );
1189 if( aHardRef.is() )
1190 xPara = ::uno::Reference< XAccessible >( aHardRef.getRef(), ::uno::UNO_QUERY );
1192 // release everything from the remove position until the end
1193 maParaManager.Release(aFunctor.GetParaIndex(), nCurrParas);
1195 // update num of paras
1196 maParaManager.SetNum( nNewParas );
1198 // TODO: Clarify whether this behaviour _really_ saves
1199 // anybody anything!
1200 // update children, _don't_ broadcast
1201 UpdateVisibleChildren( false );
1202 UpdateBoundRect();
1204 // #i61812# notification for removed para
1205 if (xPara.is())
1206 FireEvent(AccessibleEventId::CHILD, uno::Any(), uno::makeAny( xPara) );
1208 #ifdef DBG_UTIL
1209 else
1210 OSL_FAIL("AccessibleTextHelper_Impl::ProcessQueue() invalid hint id");
1211 #endif
1213 else if( nNewParas != nCurrParas )
1215 // release all paras
1216 maParaManager.Release(0, nCurrParas);
1218 // update num of paras
1219 maParaManager.SetNum( nNewParas );
1221 // #109864# create from scratch, don't broadcast
1222 UpdateVisibleChildren( false );
1223 UpdateBoundRect();
1225 // number of paragraphs somehow changed - but we have no
1226 // chance determining how. Thus, throw away everything and
1227 // create from scratch.
1228 // (child events should be broadcast after the changes are done...)
1229 FireEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN);
1231 // no need for further updates later on
1232 bEverythingUpdated = true;
1235 while( !maEventQueue.IsEmpty() )
1237 SAL_WNODEPRECATED_DECLARATIONS_PUSH
1238 ::std::auto_ptr< SfxHint > pHint( maEventQueue.PopFront() );
1239 SAL_WNODEPRECATED_DECLARATIONS_POP
1240 if( pHint.get() )
1242 const SfxHint& rHint = *(pHint.get());
1244 // determine hint type
1245 const SdrHint* pSdrHint = PTR_CAST( SdrHint, &rHint );
1246 const SfxSimpleHint* pSimpleHint = PTR_CAST( SfxSimpleHint, &rHint );
1247 const TextHint* pTextHint = PTR_CAST( TextHint, &rHint );
1248 const SvxViewHint* pViewHint = PTR_CAST( SvxViewHint, &rHint );
1249 const SvxEditSourceHint* pEditSourceHint = PTR_CAST( SvxEditSourceHint, &rHint );
1253 const sal_Int32 nParas = GetTextForwarder().GetParagraphCount();
1255 if( pEditSourceHint )
1257 switch( pEditSourceHint->GetId() )
1259 case EDITSOURCE_HINT_PARASMOVED:
1261 DBG_ASSERT( pEditSourceHint->GetStartValue() < GetTextForwarder().GetParagraphCount() &&
1262 pEditSourceHint->GetEndValue() < GetTextForwarder().GetParagraphCount(),
1263 "AccessibleTextHelper_Impl::NotifyHdl: Invalid notification");
1265 if( !bEverythingUpdated )
1267 ParagraphsMoved(pEditSourceHint->GetStartValue(),
1268 pEditSourceHint->GetValue(),
1269 pEditSourceHint->GetEndValue());
1271 // in all cases, check visibility afterwards.
1272 UpdateVisibleChildren();
1274 break;
1277 case EDITSOURCE_HINT_SELECTIONCHANGED:
1278 // notify listeners
1281 UpdateSelection();
1283 // maybe we're not in edit mode (this is not an error)
1284 catch( const uno::Exception& ) {}
1285 break;
1288 else if( pTextHint )
1290 switch( pTextHint->GetId() )
1292 case TEXT_HINT_MODIFIED:
1294 // notify listeners
1295 sal_Int32 nPara( pTextHint->GetValue() );
1297 // #108900# Delegate change event to children
1298 AccessibleTextHelper_ChildrenTextChanged aNotifyChildrenFunctor;
1300 if( nPara == EE_PARA_ALL )
1302 // #108900# Call every child
1303 ::std::for_each( maParaManager.begin(), maParaManager.end(),
1304 AccessibleParaManager::WeakChildAdapter< AccessibleTextHelper_ChildrenTextChanged > (aNotifyChildrenFunctor) );
1306 else
1307 if( nPara < nParas )
1309 // #108900# Call child at index nPara
1310 ::std::for_each( maParaManager.begin()+nPara, maParaManager.begin()+nPara+1,
1311 AccessibleParaManager::WeakChildAdapter< AccessibleTextHelper_ChildrenTextChanged > (aNotifyChildrenFunctor) );
1313 break;
1316 case TEXT_HINT_PARAINSERTED:
1317 // already happened above
1318 break;
1320 case TEXT_HINT_PARAREMOVED:
1321 // already happened above
1322 break;
1324 case TEXT_HINT_TEXTHEIGHTCHANGED:
1325 // visibility changed, done below
1326 break;
1328 case TEXT_HINT_VIEWSCROLLED:
1329 // visibility changed, done below
1330 break;
1333 // in all cases, check visibility afterwards.
1334 UpdateVisibleChildren();
1335 UpdateBoundRect();
1337 else if( pViewHint )
1339 switch( pViewHint->GetHintType() )
1341 case SvxViewHint::SVX_HINT_VIEWCHANGED:
1342 // just check visibility
1343 UpdateVisibleChildren();
1344 UpdateBoundRect();
1345 break;
1348 else if( pSdrHint )
1350 switch( pSdrHint->GetKind() )
1352 case HINT_BEGEDIT:
1354 if(!IsActive())
1356 break;
1358 // change children state
1359 maParaManager.SetActive();
1361 // per definition, edit mode text has the focus
1362 SetFocus( true );
1363 break;
1366 case HINT_ENDEDIT:
1368 // focused child now loses focus
1369 ESelection aSelection;
1370 if( GetEditViewForwarder().GetSelection( aSelection ) )
1371 SetChildFocus( aSelection.nEndPara, false );
1373 // change children state
1374 maParaManager.SetActive( false );
1376 maLastSelection = ESelection( EE_PARA_NOT_FOUND, EE_INDEX_NOT_FOUND,
1377 EE_PARA_NOT_FOUND, EE_INDEX_NOT_FOUND);
1378 break;
1380 default:
1381 break;
1384 // it's VITAL to keep the SfxSimpleHint last! It's the base of some classes above!
1385 else if( pSimpleHint )
1387 switch( pSimpleHint->GetId() )
1389 case SFX_HINT_DYING:
1390 // edit source is dying under us, become defunc then
1393 // make edit source inaccessible
1394 // Note: cannot destroy it here, since we're called from there!
1395 ShutdownEditSource();
1397 catch( const uno::Exception& ) {}
1399 break;
1403 catch( const uno::Exception& )
1405 #ifdef DBG_UTIL
1406 OSL_TRACE("AccessibleTextHelper_Impl::ProcessQueue: Unhandled exception.");
1407 #endif
1413 void AccessibleTextHelper_Impl::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
1416 // precondition: solar mutex locked
1417 DBG_TESTSOLARMUTEX();
1419 // precondition: not in a recursion
1420 if( mbInNotify )
1421 return;
1423 mbInNotify = true;
1425 // determine hint type
1426 const SdrHint* pSdrHint = PTR_CAST( SdrHint, &rHint );
1427 const SfxSimpleHint* pSimpleHint = PTR_CAST( SfxSimpleHint, &rHint );
1428 const TextHint* pTextHint = PTR_CAST( TextHint, &rHint );
1429 const SvxViewHint* pViewHint = PTR_CAST( SvxViewHint, &rHint );
1430 const SvxEditSourceHint* pEditSourceHint = PTR_CAST( SvxEditSourceHint, &rHint );
1434 // Process notification event
1435 if( pEditSourceHint )
1437 maEventQueue.Append( *pEditSourceHint );
1438 // EditEngine should emit TEXT_SELECTION_CHANGED events (#i27299#)
1439 if( maEventOpenFrames == 0 )
1440 ProcessQueue();
1442 else if( pTextHint )
1444 switch( pTextHint->GetId() )
1446 case TEXT_HINT_BLOCKNOTIFICATION_END:
1447 case TEXT_HINT_INPUT_END:
1448 --maEventOpenFrames;
1450 if( maEventOpenFrames == 0 )
1452 // #103483#
1453 /* All information should have arrived
1454 * now, process queue. As stated in the
1455 * above bug, we can often avoid throwing
1456 * away all paragraphs by looking forward
1457 * in the event queue (searching for
1458 * PARAINSERT/REMOVE events). Furthermore,
1459 * processing the event queue only at the
1460 * end of an interaction cycle, ensures
1461 * that the EditEngine state and the
1462 * AccessibleText state are the same
1463 * (well, mostly. If there are _multiple_
1464 * interaction cycles in the EE queues, it
1465 * can still happen that EE state is
1466 * different. That's so to say broken by
1467 * design with that delayed EE event
1468 * concept).
1470 ProcessQueue();
1472 break;
1474 case TEXT_HINT_BLOCKNOTIFICATION_START:
1475 case TEXT_HINT_INPUT_START:
1476 ++maEventOpenFrames;
1477 // no FALLTHROUGH reason: event will not be processed,
1478 // thus appending the event isn't necessary. (#i27299#)
1479 break;
1480 default:
1481 maEventQueue.Append( *pTextHint );
1482 // EditEngine should emit TEXT_SELECTION_CHANGED events (#i27299#)
1483 if( maEventOpenFrames == 0 )
1484 ProcessQueue();
1485 break;
1488 else if( pViewHint )
1490 maEventQueue.Append( *pViewHint );
1492 // process visibility right away, if not within an
1493 // open EE notification frame. Otherwise, event
1494 // processing would be delayed until next EE
1495 // notification sequence.
1496 if( maEventOpenFrames == 0 )
1497 ProcessQueue();
1499 else if( pSdrHint )
1501 maEventQueue.Append( *pSdrHint );
1503 // process drawing layer events right away, if not
1504 // within an open EE notification frame. Otherwise,
1505 // event processing would be delayed until next EE
1506 // notification sequence.
1507 if( maEventOpenFrames == 0 )
1508 ProcessQueue();
1510 // it's VITAL to keep the SfxSimpleHint last! It's the base of some classes above!
1511 else if( pSimpleHint )
1513 // handle this event _at once_, because after that, objects are invalid
1514 switch( pSimpleHint->GetId() )
1516 case SFX_HINT_DYING:
1517 // edit source is dying under us, become defunc then
1518 maEventQueue.Clear();
1521 // make edit source inaccessible
1522 // Note: cannot destroy it here, since we're called from there!
1523 ShutdownEditSource();
1525 catch( const uno::Exception& ) {}
1527 break;
1531 catch( const uno::Exception& )
1533 #ifdef DBG_UTIL
1534 OSL_TRACE("AccessibleTextHelper_Impl::Notify: Unhandled exception.");
1535 #endif
1536 mbInNotify = false;
1539 mbInNotify = false;
1542 void AccessibleTextHelper_Impl::Dispose()
1545 if( getNotifierClientId() != -1 )
1549 // #106234# Unregister from EventNotifier
1550 ::comphelper::AccessibleEventNotifier::revokeClient( getNotifierClientId() );
1551 #ifdef DBG_UTIL
1552 OSL_TRACE( "AccessibleTextHelper_Impl disposed ID: %d", mnNotifierClientId );
1553 #endif
1555 catch( const uno::Exception& ) {}
1557 mnNotifierClientId = -1;
1562 // dispose children
1563 maParaManager.Dispose();
1565 catch( const uno::Exception& ) {}
1567 // quit listen on stale edit source
1568 if( maEditSource.IsValid() )
1569 EndListening( maEditSource.GetBroadcaster() );
1571 // clear references
1572 SAL_WNODEPRECATED_DECLARATIONS_PUSH
1573 maEditSource.SetEditSource( ::std::auto_ptr< SvxEditSource >(NULL) );
1574 SAL_WNODEPRECATED_DECLARATIONS_POP
1575 mxFrontEnd = NULL;
1578 void AccessibleTextHelper_Impl::FireEvent( const sal_Int16 nEventId, const uno::Any& rNewValue, const uno::Any& rOldValue ) const
1581 // -- object locked --
1582 ::osl::ClearableMutexGuard aGuard( maMutex );
1584 AccessibleEventObject aEvent;
1586 DBG_ASSERT(mxFrontEnd.is(), "AccessibleTextHelper::FireEvent: no event source set" );
1588 if( mxFrontEnd.is() )
1589 aEvent = AccessibleEventObject(mxFrontEnd->getAccessibleContext(), nEventId, rNewValue, rOldValue);
1590 else
1591 aEvent = AccessibleEventObject(uno::Reference< uno::XInterface >(), nEventId, rNewValue, rOldValue);
1593 // no locking necessary, FireEvent internally copies listeners
1594 // if someone removes/adds in between Further locking,
1595 // actually, might lead to deadlocks, since we're calling out
1596 // of this object
1597 aGuard.clear();
1598 // -- until here --
1600 FireEvent(aEvent);
1603 void AccessibleTextHelper_Impl::FireEvent( const AccessibleEventObject& rEvent ) const
1606 // #102261# Call global queue for focus events
1607 if( rEvent.EventId == AccessibleStateType::FOCUSED )
1608 vcl::unohelper::NotifyAccessibleStateEventGlobally( rEvent );
1610 // #106234# Delegate to EventNotifier
1611 ::comphelper::AccessibleEventNotifier::addEvent( getNotifierClientId(),
1612 rEvent );
1615 // XAccessibleContext
1616 sal_Int32 SAL_CALL AccessibleTextHelper_Impl::getAccessibleChildCount() SAL_THROW((uno::RuntimeException))
1619 return mnLastVisibleChild - mnFirstVisibleChild + 1;
1622 uno::Reference< XAccessible > SAL_CALL AccessibleTextHelper_Impl::getAccessibleChild( sal_Int32 i ) SAL_THROW((lang::IndexOutOfBoundsException, uno::RuntimeException))
1625 i -= GetStartIndex();
1627 if( 0 > i || i >= getAccessibleChildCount() ||
1628 GetTextForwarder().GetParagraphCount() <= i )
1630 throw lang::IndexOutOfBoundsException("Invalid child index", mxFrontEnd);
1633 DBG_ASSERT(mxFrontEnd.is(), "AccessibleTextHelper_Impl::UpdateVisibleChildren: no frontend set");
1635 if( mxFrontEnd.is() )
1636 return maParaManager.CreateChild( i, mxFrontEnd, GetEditSource(), mnFirstVisibleChild + i ).first;
1637 else
1638 return NULL;
1641 void SAL_CALL AccessibleTextHelper_Impl::addAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener ) SAL_THROW((uno::RuntimeException))
1644 if( getNotifierClientId() != -1 )
1645 ::comphelper::AccessibleEventNotifier::addEventListener( getNotifierClientId(), xListener );
1648 void SAL_CALL AccessibleTextHelper_Impl::removeAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener ) SAL_THROW((uno::RuntimeException))
1651 if( getNotifierClientId() != -1 )
1652 ::comphelper::AccessibleEventNotifier::removeEventListener( getNotifierClientId(), xListener );
1655 uno::Reference< XAccessible > SAL_CALL AccessibleTextHelper_Impl::getAccessibleAtPoint( const awt::Point& _aPoint ) SAL_THROW((uno::RuntimeException))
1658 // make given position relative
1659 if( !mxFrontEnd.is() )
1660 throw uno::RuntimeException("AccessibleTextHelper_Impl::getAccessibleAt: frontend invalid", mxFrontEnd );
1662 uno::Reference< XAccessibleContext > xFrontEndContext = mxFrontEnd->getAccessibleContext();
1664 if( !xFrontEndContext.is() )
1665 throw uno::RuntimeException("AccessibleTextHelper_Impl::getAccessibleAt: frontend invalid", mxFrontEnd );
1667 uno::Reference< XAccessibleComponent > xFrontEndComponent( xFrontEndContext, uno::UNO_QUERY );
1669 if( !xFrontEndComponent.is() )
1670 throw uno::RuntimeException("AccessibleTextHelper_Impl::getAccessibleAt: frontend is no XAccessibleComponent", mxFrontEnd );
1672 // #103862# No longer need to make given position relative
1673 Point aPoint( _aPoint.X, _aPoint.Y );
1675 // respect EditEngine offset to surrounding shape/cell
1676 aPoint -= GetOffset();
1678 // convert to EditEngine coordinate system
1679 SvxTextForwarder& rCacheTF = GetTextForwarder();
1680 Point aLogPoint( GetViewForwarder().PixelToLogic( aPoint, rCacheTF.GetMapMode() ) );
1682 // iterate over all visible children (including those not yet created)
1683 sal_Int32 nChild;
1684 for( nChild=mnFirstVisibleChild; nChild <= mnLastVisibleChild; ++nChild )
1686 DBG_ASSERT(nChild >= 0 && nChild <= USHRT_MAX,
1687 "AccessibleTextHelper_Impl::getAccessibleAt: index value overflow");
1689 Rectangle aParaBounds( rCacheTF.GetParaBounds( nChild ) );
1691 if( aParaBounds.IsInside( aLogPoint ) )
1692 return getAccessibleChild( nChild - mnFirstVisibleChild + GetStartIndex() );
1695 // found none
1696 return NULL;
1701 // AccessibleTextHelper implementation (simply forwards to impl)
1705 SAL_WNODEPRECATED_DECLARATIONS_PUSH
1706 AccessibleTextHelper::AccessibleTextHelper( ::std::auto_ptr< SvxEditSource > pEditSource ) :
1707 mpImpl( new AccessibleTextHelper_Impl() )
1709 SolarMutexGuard aGuard;
1711 SetEditSource( pEditSource );
1713 SAL_WNODEPRECATED_DECLARATIONS_PUSH
1715 AccessibleTextHelper::~AccessibleTextHelper()
1719 const SvxEditSource& AccessibleTextHelper::GetEditSource() const SAL_THROW((uno::RuntimeException))
1721 #ifdef DBG_UTIL
1722 mpImpl->CheckInvariants();
1724 const SvxEditSource& aEditSource = mpImpl->GetEditSource();
1726 mpImpl->CheckInvariants();
1728 return aEditSource;
1729 #else
1730 return mpImpl->GetEditSource();
1731 #endif
1734 SAL_WNODEPRECATED_DECLARATIONS_PUSH
1735 void AccessibleTextHelper::SetEditSource( ::std::auto_ptr< SvxEditSource > pEditSource ) SAL_THROW((uno::RuntimeException))
1737 #ifdef DBG_UTIL
1738 // precondition: solar mutex locked
1739 DBG_TESTSOLARMUTEX();
1741 mpImpl->CheckInvariants();
1742 #endif
1744 mpImpl->SetEditSource( pEditSource );
1746 #ifdef DBG_UTIL
1747 mpImpl->CheckInvariants();
1748 #endif
1750 SAL_WNODEPRECATED_DECLARATIONS_POP
1752 void AccessibleTextHelper::SetEventSource( const uno::Reference< XAccessible >& rInterface )
1754 #ifdef DBG_UTIL
1755 mpImpl->CheckInvariants();
1756 #endif
1758 mpImpl->SetEventSource( rInterface );
1760 #ifdef DBG_UTIL
1761 mpImpl->CheckInvariants();
1762 #endif
1765 uno::Reference< XAccessible > AccessibleTextHelper::GetEventSource() const
1767 #ifdef DBG_UTIL
1768 mpImpl->CheckInvariants();
1770 uno::Reference< XAccessible > xRet( mpImpl->GetEventSource() );
1772 mpImpl->CheckInvariants();
1774 return xRet;
1775 #else
1776 return mpImpl->GetEventSource();
1777 #endif
1780 void AccessibleTextHelper::SetFocus( bool bHaveFocus ) SAL_THROW((::com::sun::star::uno::RuntimeException))
1782 #ifdef DBG_UTIL
1783 // precondition: solar mutex locked
1784 DBG_TESTSOLARMUTEX();
1786 mpImpl->CheckInvariants();
1787 #endif
1789 mpImpl->SetFocus( bHaveFocus );
1791 #ifdef DBG_UTIL
1792 mpImpl->CheckInvariants();
1793 #endif
1796 bool AccessibleTextHelper::HaveFocus() SAL_THROW((::com::sun::star::uno::RuntimeException))
1798 #ifdef DBG_UTIL
1799 mpImpl->CheckInvariants();
1801 bool bRet( mpImpl->HaveFocus() );
1803 mpImpl->CheckInvariants();
1805 return bRet;
1806 #else
1807 return mpImpl->HaveFocus();
1808 #endif
1811 void AccessibleTextHelper::FireEvent( const sal_Int16 nEventId, const uno::Any& rNewValue, const uno::Any& rOldValue ) const
1813 #ifdef DBG_UTIL
1814 mpImpl->CheckInvariants();
1815 #endif
1817 mpImpl->FireEvent( nEventId, rNewValue, rOldValue );
1819 #ifdef DBG_UTIL
1820 mpImpl->CheckInvariants();
1821 #endif
1824 void AccessibleTextHelper::FireEvent( const AccessibleEventObject& rEvent ) const
1826 #ifdef DBG_UTIL
1827 mpImpl->CheckInvariants();
1828 #endif
1830 mpImpl->FireEvent( rEvent );
1832 #ifdef DBG_UTIL
1833 mpImpl->CheckInvariants();
1834 #endif
1837 void AccessibleTextHelper::SetOffset( const Point& rPoint )
1839 #ifdef DBG_UTIL
1840 // precondition: solar mutex locked
1841 DBG_TESTSOLARMUTEX();
1843 mpImpl->CheckInvariants();
1844 #endif
1846 mpImpl->SetOffset( rPoint );
1848 #ifdef DBG_UTIL
1849 mpImpl->CheckInvariants();
1850 #endif
1853 Point AccessibleTextHelper::GetOffset() const
1855 #ifdef DBG_UTIL
1856 mpImpl->CheckInvariants();
1858 Point aPoint( mpImpl->GetOffset() );
1860 mpImpl->CheckInvariants();
1862 return aPoint;
1863 #else
1864 return mpImpl->GetOffset();
1865 #endif
1868 void AccessibleTextHelper::SetStartIndex( sal_Int32 nOffset )
1870 #ifdef DBG_UTIL
1871 // precondition: solar mutex locked
1872 DBG_TESTSOLARMUTEX();
1874 mpImpl->CheckInvariants();
1875 #endif
1877 mpImpl->SetStartIndex( nOffset );
1879 #ifdef DBG_UTIL
1880 mpImpl->CheckInvariants();
1881 #endif
1884 sal_Int32 AccessibleTextHelper::GetStartIndex() const
1886 #ifdef DBG_UTIL
1887 mpImpl->CheckInvariants();
1889 sal_Int32 nOffset = mpImpl->GetStartIndex();
1891 mpImpl->CheckInvariants();
1893 return nOffset;
1894 #else
1895 return mpImpl->GetStartIndex();
1896 #endif
1899 void AccessibleTextHelper::SetAdditionalChildStates( const VectorOfStates& rChildStates )
1901 mpImpl->SetAdditionalChildStates( rChildStates );
1904 void AccessibleTextHelper::UpdateChildren() SAL_THROW((::com::sun::star::uno::RuntimeException))
1906 #ifdef DBG_UTIL
1907 // precondition: solar mutex locked
1908 DBG_TESTSOLARMUTEX();
1910 mpImpl->CheckInvariants();
1911 #endif
1913 mpImpl->UpdateVisibleChildren();
1914 mpImpl->UpdateBoundRect();
1916 mpImpl->UpdateSelection();
1918 #ifdef DBG_UTIL
1919 mpImpl->CheckInvariants();
1920 #endif
1923 void AccessibleTextHelper::Dispose()
1925 // As Dispose calls ShutdownEditSource, which in turn
1926 // deregisters as listener on the edit source, have to lock
1927 // here
1928 SolarMutexGuard aGuard;
1930 #ifdef DBG_UTIL
1931 mpImpl->CheckInvariants();
1932 #endif
1934 mpImpl->Dispose();
1936 #ifdef DBG_UTIL
1937 mpImpl->CheckInvariants();
1938 #endif
1941 bool AccessibleTextHelper::IsSelected() const
1943 SolarMutexGuard aGuard;
1945 #ifdef DBG_UTIL
1946 mpImpl->CheckInvariants();
1948 bool aRet = mpImpl->IsSelected();
1950 mpImpl->CheckInvariants();
1952 return aRet;
1953 #else
1954 return mpImpl->IsSelected();
1955 #endif
1958 // XAccessibleContext
1959 sal_Int32 AccessibleTextHelper::GetChildCount() SAL_THROW((uno::RuntimeException))
1961 SolarMutexGuard aGuard;
1963 #ifdef DBG_UTIL
1964 mpImpl->CheckInvariants();
1966 sal_Int32 nRet = mpImpl->getAccessibleChildCount();
1968 mpImpl->CheckInvariants();
1970 return nRet;
1971 #else
1972 return mpImpl->getAccessibleChildCount();
1973 #endif
1976 uno::Reference< XAccessible > AccessibleTextHelper::GetChild( sal_Int32 i ) SAL_THROW((lang::IndexOutOfBoundsException, uno::RuntimeException))
1978 SolarMutexGuard aGuard;
1980 #ifdef DBG_UTIL
1981 mpImpl->CheckInvariants();
1983 uno::Reference< XAccessible > xRet = mpImpl->getAccessibleChild( i );
1985 mpImpl->CheckInvariants();
1987 return xRet;
1988 #else
1989 return mpImpl->getAccessibleChild( i );
1990 #endif
1993 void AccessibleTextHelper::AddEventListener( const uno::Reference< XAccessibleEventListener >& xListener ) SAL_THROW((uno::RuntimeException))
1995 #ifdef DBG_UTIL
1996 mpImpl->CheckInvariants();
1998 mpImpl->addAccessibleEventListener( xListener );
2000 mpImpl->CheckInvariants();
2001 #else
2002 mpImpl->addAccessibleEventListener( xListener );
2003 #endif
2006 void AccessibleTextHelper::RemoveEventListener( const uno::Reference< XAccessibleEventListener >& xListener ) SAL_THROW((uno::RuntimeException))
2008 #ifdef DBG_UTIL
2009 mpImpl->CheckInvariants();
2011 mpImpl->removeAccessibleEventListener( xListener );
2013 mpImpl->CheckInvariants();
2014 #else
2015 mpImpl->removeAccessibleEventListener( xListener );
2016 #endif
2019 // XAccessibleComponent
2020 uno::Reference< XAccessible > AccessibleTextHelper::GetAt( const awt::Point& aPoint ) SAL_THROW((uno::RuntimeException))
2022 SolarMutexGuard aGuard;
2024 #ifdef DBG_UTIL
2025 mpImpl->CheckInvariants();
2027 uno::Reference< XAccessible > xChild = mpImpl->getAccessibleAtPoint( aPoint );
2029 mpImpl->CheckInvariants();
2031 return xChild;
2032 #else
2033 return mpImpl->getAccessibleAtPoint( aPoint );
2034 #endif
2037 } // end of namespace accessibility
2041 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */