1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
28 #include <osl/mutex.hxx>
29 #include <sal/log.hxx>
30 #include <com/sun/star/uno/Any.hxx>
31 #include <com/sun/star/uno/Reference.hxx>
32 #include <cppuhelper/weakref.hxx>
33 #include <com/sun/star/awt/Point.hpp>
34 #include <com/sun/star/awt/Rectangle.hpp>
35 #include <com/sun/star/lang/DisposedException.hpp>
36 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
37 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
38 #include <com/sun/star/accessibility/XAccessible.hpp>
39 #include <com/sun/star/accessibility/XAccessibleContext.hpp>
40 #include <com/sun/star/accessibility/XAccessibleComponent.hpp>
41 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
42 #include <comphelper/accessibleeventnotifier.hxx>
43 #include <unotools/accessiblestatesethelper.hxx>
44 #include <vcl/svapp.hxx>
45 #include <vcl/textdata.hxx>
46 #include <vcl/unohelp.hxx>
47 #include <sfx2/viewfrm.hxx>
48 #include <sfx2/viewsh.hxx>
51 // Project-local header
54 #include "AccessibleTextEventQueue.hxx"
55 #include <svx/AccessibleTextHelper.hxx>
56 #include <svx/unoshape.hxx>
57 #include <editeng/unolingu.hxx>
58 #include <editeng/unotext.hxx>
60 #include <editeng/unoedhlp.hxx>
61 #include <editeng/unopracc.hxx>
62 #include <editeng/unoedprx.hxx>
63 #include <editeng/AccessibleParaManager.hxx>
64 #include <editeng/AccessibleEditableTextPara.hxx>
65 #include <svx/svdmodel.hxx>
66 #include <svx/svdpntv.hxx>
68 #include "../table/accessiblecell.hxx"
69 #include <editeng/editdata.hxx>
70 #include <editeng/editeng.hxx>
71 #include <editeng/editview.hxx>
72 #include <tools/debug.hxx>
73 #include <tools/diagnose_ex.h>
75 using namespace ::com::sun::star
;
76 using namespace ::com::sun::star::accessibility
;
78 namespace accessibility
81 // AccessibleTextHelper_Impl declaration
83 template < typename first_type
, typename second_type
>
84 static ::std::pair
< first_type
, second_type
> makeSortedPair( first_type first
,
88 return ::std::make_pair( second
, first
);
90 return ::std::make_pair( first
, second
);
93 class AccessibleTextHelper_Impl
: public SfxListener
96 typedef ::std::vector
< sal_Int16
> VectorOfStates
;
98 // receive pointer to our frontend class and view window
99 AccessibleTextHelper_Impl();
100 virtual ~AccessibleTextHelper_Impl() override
;
102 // XAccessibleContext child handling methods
103 sal_Int32
getAccessibleChildCount() const;
104 uno::Reference
< XAccessible
> getAccessibleChild( sal_Int32 i
);
106 // XAccessibleEventBroadcaster child related methods
107 void addAccessibleEventListener( const uno::Reference
< XAccessibleEventListener
>& xListener
);
108 void removeAccessibleEventListener( const uno::Reference
< XAccessibleEventListener
>& xListener
);
110 // XAccessibleComponent child related methods
111 uno::Reference
< XAccessible
> getAccessibleAtPoint( const awt::Point
& aPoint
);
113 SvxEditSourceAdapter
& GetEditSource() const;
115 void SetEditSource( ::std::unique_ptr
< SvxEditSource
> && pEditSource
);
117 void SetEventSource( const uno::Reference
< XAccessible
>& rInterface
)
119 mxFrontEnd
= rInterface
;
122 void SetOffset( const Point
& );
123 Point
GetOffset() const
125 ::osl::MutexGuard
aGuard( maMutex
); Point
aPoint( maOffset
);
129 void SetStartIndex( sal_Int32 nOffset
);
130 sal_Int32
GetStartIndex() const
132 // Strictly correct only with locked solar mutex, // but
133 // here we rely on the fact that sal_Int32 access is
138 void SetAdditionalChildStates( const VectorOfStates
& rChildStates
);
142 // do NOT hold object mutex when calling this! Danger of deadlock
143 void FireEvent( const sal_Int16 nEventId
, const uno::Any
& rNewValue
= uno::Any(), const uno::Any
& rOldValue
= uno::Any() ) const;
144 void FireEvent( const AccessibleEventObject
& rEvent
) const;
146 void SetFocus( bool bHaveFocus
);
149 // No locking of solar mutex here, since we rely on the fact
150 // that sal_Bool access is atomic
151 return mbThisHasFocus
;
153 void SetChildFocus( sal_Int32 nChild
, bool bHaveFocus
);
154 void SetShapeFocus( bool bHaveFocus
);
155 void ChangeChildFocus( sal_Int32 nNewChild
);
158 void CheckInvariants() const;
161 // checks all children for visibility, throws away invisible ones
162 void UpdateVisibleChildren( bool bBroadcastEvents
=true );
164 // check all children for changes in position and size
165 void UpdateBoundRect();
167 // calls SetSelection on the forwarder and updates maLastSelection
169 void UpdateSelection();
173 // Process event queue
176 // syntactic sugar for FireEvent
177 void GotPropertyEvent( const uno::Any
& rNewValue
, const sal_Int16 nEventId
) const { FireEvent( nEventId
, rNewValue
); }
179 // shutdown usage of current edit source on myself and the children.
180 void ShutdownEditSource();
182 void ParagraphsMoved( sal_Int32 nFirst
, sal_Int32 nMiddle
, sal_Int32 nLast
);
184 virtual void Notify( SfxBroadcaster
& rBC
, const SfxHint
& rHint
) override
;
186 int getNotifierClientId() const { return mnNotifierClientId
; }
188 // lock solar mutex before
189 SvxTextForwarder
& GetTextForwarder() const;
190 // lock solar mutex before
191 SvxViewForwarder
& GetViewForwarder() const;
192 // lock solar mutex before
193 SvxEditViewForwarder
& GetEditViewForwarder() const;
195 // are we in edit mode?
196 bool IsActive() const;
198 // our frontend class (the one implementing the actual
199 // interface). That's not necessarily the one containing the impl
201 uno::Reference
< XAccessible
> mxFrontEnd
;
203 // a wrapper for the text forwarders (guarded by solar mutex)
204 mutable SvxEditSourceAdapter maEditSource
;
206 // store last selection (to correctly report selection changes, guarded by solar mutex)
207 ESelection maLastSelection
;
209 // cache range of visible children (guarded by solar mutex)
210 sal_Int32 mnFirstVisibleChild
;
211 sal_Int32 mnLastVisibleChild
;
213 // offset to add to all our children (unguarded, relying on
214 // the fact that sal_Int32 access is atomic)
215 sal_Int32 mnStartIndex
;
217 // the object handling our children (guarded by solar mutex)
218 ::accessibility::AccessibleParaManager maParaManager
;
220 // Queued events from Notify() (guarded by solar mutex)
221 AccessibleTextEventQueue maEventQueue
;
223 // spin lock to prevent notify in notify (guarded by solar mutex)
226 // whether the object or its children has the focus set (guarded by solar mutex)
227 bool mbGroupHasFocus
;
229 // whether we (this object) has the focus set (guarded by solar mutex)
232 mutable ::osl::Mutex maMutex
;
234 /// our current offset to the containing shape/cell (guarded by maMutex)
237 /// client Id from AccessibleEventNotifier
238 int mnNotifierClientId
;
241 AccessibleTextHelper_Impl::AccessibleTextHelper_Impl() :
242 maLastSelection( EE_PARA_NOT_FOUND
,EE_INDEX_NOT_FOUND
,EE_PARA_NOT_FOUND
,EE_INDEX_NOT_FOUND
),
243 mnFirstVisibleChild( -1 ),
244 mnLastVisibleChild( -2 ),
247 mbGroupHasFocus( false ),
248 mbThisHasFocus( false ),
250 // well, that's strictly exception safe, though not really
251 // robust. We rely on the fact that this member is constructed
252 // last, and that the constructor body is empty, thus no
253 // chance for exceptions once the Id is fetched. Nevertheless,
254 // normally should employ RAII here...
255 mnNotifierClientId(::comphelper::AccessibleEventNotifier::registerClient())
257 SAL_INFO("svx", "received ID: " << mnNotifierClientId
);
260 AccessibleTextHelper_Impl::~AccessibleTextHelper_Impl()
262 SolarMutexGuard aGuard
;
266 // call Dispose here, too, since we've some resources not
267 // automatically freed otherwise
270 catch( const uno::Exception
& ) {}
273 SvxTextForwarder
& AccessibleTextHelper_Impl::GetTextForwarder() const
275 if( !maEditSource
.IsValid() )
276 throw uno::RuntimeException("Unknown edit source", mxFrontEnd
);
278 SvxTextForwarder
* pTextForwarder
= maEditSource
.GetTextForwarder();
280 if( !pTextForwarder
)
281 throw uno::RuntimeException("Unable to fetch text forwarder, model might be dead", mxFrontEnd
);
283 if( !pTextForwarder
->IsValid() )
284 throw uno::RuntimeException("Text forwarder is invalid, model might be dead", mxFrontEnd
);
286 return *pTextForwarder
;
289 SvxViewForwarder
& AccessibleTextHelper_Impl::GetViewForwarder() const
291 if( !maEditSource
.IsValid() )
292 throw uno::RuntimeException("Unknown edit source", mxFrontEnd
);
294 SvxViewForwarder
* pViewForwarder
= maEditSource
.GetViewForwarder();
296 if( !pViewForwarder
)
297 throw uno::RuntimeException("Unable to fetch view forwarder, model might be dead", mxFrontEnd
);
299 if( !pViewForwarder
->IsValid() )
300 throw uno::RuntimeException("View forwarder is invalid, model might be dead", mxFrontEnd
);
302 return *pViewForwarder
;
305 SvxEditViewForwarder
& AccessibleTextHelper_Impl::GetEditViewForwarder() const
307 if( !maEditSource
.IsValid() )
308 throw uno::RuntimeException("Unknown edit source", mxFrontEnd
);
310 SvxEditViewForwarder
* pViewForwarder
= maEditSource
.GetEditViewForwarder();
312 if( !pViewForwarder
)
314 throw uno::RuntimeException("No edit view forwarder, object not in edit mode", mxFrontEnd
);
317 if( !pViewForwarder
->IsValid() )
319 throw uno::RuntimeException("View forwarder is invalid, object not in edit mode", mxFrontEnd
);
322 return *pViewForwarder
;
325 SvxEditSourceAdapter
& AccessibleTextHelper_Impl::GetEditSource() const
327 if( !maEditSource
.IsValid() )
328 throw uno::RuntimeException("AccessibleTextHelper_Impl::GetEditSource: no edit source", mxFrontEnd
);
332 // functor for sending child events (no stand-alone function, they are maybe not inlined)
333 class AccessibleTextHelper_OffsetChildIndex
336 explicit AccessibleTextHelper_OffsetChildIndex( sal_Int32 nDifference
) : mnDifference(nDifference
) {}
337 void operator()( ::accessibility::AccessibleEditableTextPara
& rPara
)
339 rPara
.SetIndexInParent( rPara
.GetIndexInParent() + mnDifference
);
343 const sal_Int32 mnDifference
;
346 void AccessibleTextHelper_Impl::SetStartIndex( sal_Int32 nOffset
)
348 sal_Int32
nOldOffset( mnStartIndex
);
350 mnStartIndex
= nOffset
;
352 if( nOldOffset
!= nOffset
)
355 AccessibleTextHelper_OffsetChildIndex
aFunctor( nOffset
- nOldOffset
);
357 ::std::for_each( maParaManager
.begin(), maParaManager
.end(),
358 AccessibleParaManager::WeakChildAdapter
< AccessibleTextHelper_OffsetChildIndex
> (aFunctor
) );
362 void AccessibleTextHelper_Impl::SetAdditionalChildStates( const VectorOfStates
& rChildStates
)
364 maParaManager
.SetAdditionalChildStates( rChildStates
);
367 void AccessibleTextHelper_Impl::SetChildFocus( sal_Int32 nChild
, bool bHaveFocus
)
372 SetShapeFocus( false );
374 maParaManager
.SetFocus( nChild
);
376 // we just received the focus, also send caret event then
379 SAL_INFO("svx", "Paragraph " << nChild
<< " received focus");
383 maParaManager
.SetFocus( -1 );
385 SAL_INFO("svx", "Paragraph " << nChild
<< " lost focus");
387 if( mbGroupHasFocus
)
388 SetShapeFocus( true );
392 void AccessibleTextHelper_Impl::ChangeChildFocus( sal_Int32 nNewChild
)
395 SetShapeFocus( false );
397 mbGroupHasFocus
= true;
398 maParaManager
.SetFocus( nNewChild
);
400 SAL_INFO("svx", "Paragraph " << nNewChild
<< " received focus");
403 void AccessibleTextHelper_Impl::SetShapeFocus( bool bHaveFocus
)
405 bool bOldFocus( mbThisHasFocus
);
407 mbThisHasFocus
= bHaveFocus
;
409 if( bOldFocus
!= bHaveFocus
)
413 if( mxFrontEnd
.is() )
415 AccessibleCell
* pAccessibleCell
= dynamic_cast< AccessibleCell
* > ( mxFrontEnd
.get() );
416 if ( !pAccessibleCell
)
417 GotPropertyEvent( uno::makeAny(AccessibleStateType::FOCUSED
), AccessibleEventId::STATE_CHANGED
);
418 else // the focus event on cell should be fired on table directly
420 AccessibleTableShape
* pAccTable
= pAccessibleCell
->GetParentTable();
422 pAccTable
->SetStateDirectly(AccessibleStateType::FOCUSED
);
425 SAL_INFO("svx", "Parent object received focus" );
429 // The focus state should be reset directly on table.
430 //LostPropertyEvent( uno::makeAny(AccessibleStateType::FOCUSED), AccessibleEventId::STATE_CHANGED );
431 if( mxFrontEnd
.is() )
433 AccessibleCell
* pAccessibleCell
= dynamic_cast< AccessibleCell
* > ( mxFrontEnd
.get() );
434 if ( !pAccessibleCell
)
435 FireEvent( AccessibleEventId::STATE_CHANGED
, uno::Any(), uno::makeAny(AccessibleStateType::FOCUSED
) );
438 AccessibleTableShape
* pAccTable
= pAccessibleCell
->GetParentTable();
440 pAccTable
->ResetStateDirectly(AccessibleStateType::FOCUSED
);
443 SAL_INFO("svx", "Parent object lost focus" );
448 void AccessibleTextHelper_Impl::SetFocus( bool bHaveFocus
)
450 bool bOldFocus( mbGroupHasFocus
);
452 mbGroupHasFocus
= bHaveFocus
;
458 // find the one with the cursor and get/set focus accordingly
459 ESelection aSelection
;
460 if( GetEditViewForwarder().GetSelection( aSelection
) )
461 SetChildFocus( aSelection
.nEndPara
, bHaveFocus
);
463 catch( const uno::Exception
& ) {}
465 else if( bOldFocus
!= bHaveFocus
)
467 SetShapeFocus( bHaveFocus
);
470 SAL_INFO("svx", "focus changed, Object " << this << ", state: " << (bHaveFocus
? "focused" : "not focused") );
473 bool AccessibleTextHelper_Impl::IsActive() const
477 SvxEditSource
& rEditSource
= GetEditSource();
478 SvxEditViewForwarder
* pViewForwarder
= rEditSource
.GetEditViewForwarder();
480 if( !pViewForwarder
)
483 if( mxFrontEnd
.is() )
485 AccessibleCell
* pAccessibleCell
= dynamic_cast< AccessibleCell
* > ( mxFrontEnd
.get() );
486 if ( pAccessibleCell
)
488 sdr::table::CellRef xCell
= pAccessibleCell
->getCellRef();
490 return xCell
->IsActiveCell();
493 return pViewForwarder
->IsValid();
495 catch( const uno::RuntimeException
& )
501 void AccessibleTextHelper_Impl::UpdateSelection()
505 ESelection aSelection
;
506 if( GetEditViewForwarder().GetSelection( aSelection
) )
508 if( maLastSelection
!= aSelection
&&
509 aSelection
.nEndPara
< maParaManager
.GetNum() )
511 // #103998# Not that important, changed from assertion to trace
514 SAL_INFO("svx", "Parent has focus!");
517 sal_Int32
nMaxValidParaIndex( GetTextForwarder().GetParagraphCount() - 1 );
519 // notify all affected paragraphs (TODO: may be suboptimal,
520 // since some paragraphs might stay selected)
521 if( maLastSelection
.nStartPara
!= EE_PARA_NOT_FOUND
)
523 // Did the caret move from one paragraph to another?
524 // #100530# no caret events if not focused.
525 if( mbGroupHasFocus
&&
526 maLastSelection
.nEndPara
!= aSelection
.nEndPara
)
528 if( maLastSelection
.nEndPara
< maParaManager
.GetNum() )
530 maParaManager
.FireEvent( ::std::min( maLastSelection
.nEndPara
, nMaxValidParaIndex
),
531 ::std::min( maLastSelection
.nEndPara
, nMaxValidParaIndex
)+1,
532 AccessibleEventId::CARET_CHANGED
,
533 uno::makeAny(static_cast<sal_Int32
>(-1)),
534 uno::makeAny(maLastSelection
.nEndPos
) );
537 ChangeChildFocus( aSelection
.nEndPara
);
541 "focus changed, Object: " << this
542 << ", Paragraph: " << aSelection
.nEndPara
543 << ", Last paragraph: "
544 << maLastSelection
.nEndPara
);
548 // #100530# no caret events if not focused.
549 if( mbGroupHasFocus
)
553 // #i13705# The old cursor can only contain valid
554 // values if it's the same paragraph!
555 if( maLastSelection
.nStartPara
!= EE_PARA_NOT_FOUND
&&
556 maLastSelection
.nEndPara
== aSelection
.nEndPara
)
558 aOldCursor
<<= maLastSelection
.nEndPos
;
562 aOldCursor
<<= static_cast<sal_Int32
>(-1);
565 maParaManager
.FireEvent( aSelection
.nEndPara
,
566 aSelection
.nEndPara
+1,
567 AccessibleEventId::CARET_CHANGED
,
568 uno::makeAny(aSelection
.nEndPos
),
574 "caret changed, Object: " << this << ", New pos: "
575 << aSelection
.nEndPos
<< ", Old pos: "
576 << maLastSelection
.nEndPos
<< ", New para: "
577 << aSelection
.nEndPara
<< ", Old para: "
578 << maLastSelection
.nEndPara
);
580 // #108947# Sort new range before calling FireEvent
581 ::std::pair
<sal_Int32
, sal_Int32
> sortedSelection(
582 makeSortedPair(::std::min( aSelection
.nStartPara
, nMaxValidParaIndex
),
583 ::std::min( aSelection
.nEndPara
, nMaxValidParaIndex
) ) );
585 // #108947# Sort last range before calling FireEvent
586 ::std::pair
<sal_Int32
, sal_Int32
> sortedLastSelection(
587 makeSortedPair(::std::min( maLastSelection
.nStartPara
, nMaxValidParaIndex
),
588 ::std::min( maLastSelection
.nEndPara
, nMaxValidParaIndex
) ) );
590 // event TEXT_SELECTION_CHANGED has to be submitted. (#i27299#)
591 const sal_Int16 nTextSelChgEventId
=
592 AccessibleEventId::TEXT_SELECTION_CHANGED
;
593 // #107037# notify selection change
594 if( maLastSelection
.nStartPara
== EE_PARA_NOT_FOUND
)
596 // last selection is undefined
597 // use method <ESelection::HasRange()> (#i27299#)
598 if ( aSelection
.HasRange() )
600 // selection was undefined, now is on
601 maParaManager
.FireEvent( sortedSelection
.first
,
602 sortedSelection
.second
+1,
603 nTextSelChgEventId
);
608 // last selection is valid
609 // use method <ESelection::HasRange()> (#i27299#)
610 if ( maLastSelection
.HasRange() &&
611 !aSelection
.HasRange() )
613 // selection was on, now is empty
614 maParaManager
.FireEvent( sortedLastSelection
.first
,
615 sortedLastSelection
.second
+1,
616 nTextSelChgEventId
);
618 // use method <ESelection::HasRange()> (#i27299#)
619 else if( !maLastSelection
.HasRange() &&
620 aSelection
.HasRange() )
622 // selection was empty, now is on
623 maParaManager
.FireEvent( sortedSelection
.first
,
624 sortedSelection
.second
+1,
625 nTextSelChgEventId
);
627 // no event TEXT_SELECTION_CHANGED event, if new and
628 // last selection are empty. (#i27299#)
629 else if ( maLastSelection
.HasRange() &&
630 aSelection
.HasRange() )
632 // use sorted last and new selection
633 ESelection
aTmpLastSel( maLastSelection
);
634 aTmpLastSel
.Adjust();
635 ESelection
aTmpSel( aSelection
);
637 // first submit event for new and changed selection
638 sal_Int32 nPara
= aTmpSel
.nStartPara
;
639 for ( ; nPara
<= aTmpSel
.nEndPara
; ++nPara
)
641 if ( nPara
< aTmpLastSel
.nStartPara
||
642 nPara
> aTmpLastSel
.nEndPara
)
644 // new selection on paragraph <nPara>
645 maParaManager
.FireEvent( nPara
,
646 nTextSelChgEventId
);
650 // check for changed selection on paragraph <nPara>
651 const sal_Int32 nParaStartPos
=
652 nPara
== aTmpSel
.nStartPara
653 ? aTmpSel
.nStartPos
: 0;
654 const sal_Int32 nParaEndPos
=
655 nPara
== aTmpSel
.nEndPara
656 ? aTmpSel
.nEndPos
: -1;
657 const sal_Int32 nLastParaStartPos
=
658 nPara
== aTmpLastSel
.nStartPara
659 ? aTmpLastSel
.nStartPos
: 0;
660 const sal_Int32 nLastParaEndPos
=
661 nPara
== aTmpLastSel
.nEndPara
662 ? aTmpLastSel
.nEndPos
: -1;
663 if ( nParaStartPos
!= nLastParaStartPos
||
664 nParaEndPos
!= nLastParaEndPos
)
666 maParaManager
.FireEvent(
667 nPara
, nTextSelChgEventId
);
671 // second submit event for 'old' selections
672 nPara
= aTmpLastSel
.nStartPara
;
673 for ( ; nPara
<= aTmpLastSel
.nEndPara
; ++nPara
)
675 if ( nPara
< aTmpSel
.nStartPara
||
676 nPara
> aTmpSel
.nEndPara
)
678 maParaManager
.FireEvent( nPara
,
679 nTextSelChgEventId
);
685 maLastSelection
= aSelection
;
689 // no selection? no update actions
690 catch( const uno::RuntimeException
& ) {}
693 void AccessibleTextHelper_Impl::ShutdownEditSource()
695 // This should only be called with solar mutex locked, i.e. from the main office thread
697 // This here is somewhat clumsy: As soon as our children have
698 // a NULL EditSource (maParaManager.SetEditSource()), they
699 // enter the disposed state and cannot be reanimated. Thus, it
700 // is unavoidable and a hard requirement to let go and create
701 // from scratch each and every child.
703 // invalidate children
704 maParaManager
.Dispose();
705 maParaManager
.SetNum(0);
708 if( mxFrontEnd
.is() )
709 FireEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN
);
711 // quit listen on stale edit source
712 if( maEditSource
.IsValid() )
713 EndListening( maEditSource
.GetBroadcaster() );
715 maEditSource
.SetEditSource( ::std::unique_ptr
< SvxEditSource
>() );
718 void AccessibleTextHelper_Impl::SetEditSource( ::std::unique_ptr
< SvxEditSource
> && pEditSource
)
720 // This should only be called with solar mutex locked, i.e. from the main office thread
722 // shutdown old edit source
723 ShutdownEditSource();
725 // set new edit source
726 maEditSource
.SetEditSource( std::move(pEditSource
) );
728 // init child vector to the current child count
729 if( maEditSource
.IsValid() )
731 maParaManager
.SetNum( GetTextForwarder().GetParagraphCount() );
733 // listen on new edit source
734 StartListening( maEditSource
.GetBroadcaster() );
736 UpdateVisibleChildren();
740 void AccessibleTextHelper_Impl::SetOffset( const Point
& rPoint
)
742 // guard against non-atomic access to maOffset data structure
744 ::osl::MutexGuard
aGuard( maMutex
);
748 maParaManager
.SetEEOffset( rPoint
);
750 // in all cases, check visibility afterwards.
751 UpdateVisibleChildren();
755 void AccessibleTextHelper_Impl::UpdateVisibleChildren( bool bBroadcastEvents
)
759 SvxTextForwarder
& rCacheTF
= GetTextForwarder();
760 sal_Int32 nParas
=rCacheTF
.GetParagraphCount();
762 mnFirstVisibleChild
= -1;
763 mnLastVisibleChild
= -2;
765 for( sal_Int32 nCurrPara
=0; nCurrPara
<nParas
; ++nCurrPara
)
768 mnFirstVisibleChild
= nCurrPara
;
769 mnLastVisibleChild
= nCurrPara
;
770 if (mxFrontEnd
.is() && bBroadcastEvents
)
772 // child not yet created?
773 ::accessibility::AccessibleParaManager::WeakChild
aChild( maParaManager
.GetChild(nCurrPara
) );
774 if( aChild
.second
.Width
== 0 &&
775 aChild
.second
.Height
== 0 )
777 GotPropertyEvent( uno::makeAny( maParaManager
.CreateChild( nCurrPara
- mnFirstVisibleChild
,
778 mxFrontEnd
, GetEditSource(), nCurrPara
).first
),
779 AccessibleEventId::CHILD
);
784 catch( const uno::Exception
& )
786 OSL_FAIL("AccessibleTextHelper_Impl::UpdateVisibleChildren error while determining visible children");
788 // something failed - currently no children
789 mnFirstVisibleChild
= -1;
790 mnLastVisibleChild
= -2;
791 maParaManager
.SetNum(0);
794 if( bBroadcastEvents
)
795 FireEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN
);
799 // functor for checking changes in paragraph bounding boxes (no stand-alone function, maybe not inlined)
800 class AccessibleTextHelper_UpdateChildBounds
803 explicit AccessibleTextHelper_UpdateChildBounds() {}
804 ::accessibility::AccessibleParaManager::WeakChild
operator()( const ::accessibility::AccessibleParaManager::WeakChild
& rChild
)
806 // retrieve hard reference from weak one
807 auto aHardRef( rChild
.first
.get() );
811 awt::Rectangle aNewRect
= aHardRef
->getBounds();
812 const awt::Rectangle
& aOldRect
= rChild
.second
;
814 if( aNewRect
.X
!= aOldRect
.X
||
815 aNewRect
.Y
!= aOldRect
.Y
||
816 aNewRect
.Width
!= aOldRect
.Width
||
817 aNewRect
.Height
!= aOldRect
.Height
)
819 // visible data changed
820 aHardRef
->FireEvent( AccessibleEventId::BOUNDRECT_CHANGED
);
822 // update internal bounds
823 return ::accessibility::AccessibleParaManager::WeakChild( rChild
.first
, aNewRect
);
827 // identity transform
832 void AccessibleTextHelper_Impl::UpdateBoundRect()
834 // send BOUNDRECT_CHANGED to affected children
835 AccessibleTextHelper_UpdateChildBounds aFunctor
;
836 ::std::transform( maParaManager
.begin(), maParaManager
.end(), maParaManager
.begin(), aFunctor
);
840 void AccessibleTextHelper_Impl::CheckInvariants() const
842 if( mnFirstVisibleChild
>= 0 &&
843 mnFirstVisibleChild
> mnLastVisibleChild
)
845 OSL_FAIL( "AccessibleTextHelper: range invalid" );
850 // functor for sending child events (no stand-alone function, they are maybe not inlined)
851 class AccessibleTextHelper_LostChildEvent
854 explicit AccessibleTextHelper_LostChildEvent( AccessibleTextHelper_Impl
& rImpl
) : mrImpl(rImpl
) {}
855 void operator()( const ::accessibility::AccessibleParaManager::WeakChild
& rPara
)
857 // retrieve hard reference from weak one
858 auto aHardRef( rPara
.first
.get() );
861 mrImpl
.FireEvent(AccessibleEventId::CHILD
, uno::Any(), uno::makeAny
<css::uno::Reference
<css::accessibility::XAccessible
>>(aHardRef
.get()) );
865 AccessibleTextHelper_Impl
& mrImpl
;
868 void AccessibleTextHelper_Impl::ParagraphsMoved( sal_Int32 nFirst
, sal_Int32 nMiddle
, sal_Int32 nLast
)
870 const sal_Int32 nParas
= GetTextForwarder().GetParagraphCount();
878 * ... nParagraph ... nParam1 ... nParam2 ...
879 * |______________[xxxxxxxxxxx]
881 * [xxxxxxxxxxx]|______________
886 * ... nParam1 ... nParagraph ... nParam2 ...
887 * [xxxxxxxxxxx|xxxxxxxxxxxxxx]____________
889 * ____________[xxxxxxxxxxx|xxxxxxxxxxxxxx]
891 * tail is nParagraph - nParam1
894 * ... nParam1 ... nParam2 ... nParagraph ...
895 * [xxxxxxxxxxx]___________|____________
897 * ___________|____________[xxxxxxxxxxx]
899 * tail is nParam2 - nParam1
902 // sort nParagraph, nParam1 and nParam2 in ascending order, calc range
903 if( nMiddle
< nFirst
)
905 ::std::swap(nFirst
, nMiddle
);
907 else if( nMiddle
< nLast
)
909 nLast
= nLast
+ nMiddle
- nFirst
;
913 ::std::swap(nMiddle
, nLast
);
914 nLast
= nLast
+ nMiddle
- nFirst
;
917 if( nFirst
< nParas
&& nMiddle
< nParas
&& nLast
< nParas
)
919 // since we have no "paragraph index
920 // changed" event on UAA, remove
921 // [first,last] and insert again later (in
922 // UpdateVisibleChildren)
924 // maParaManager.Rotate( nFirst, nMiddle, nLast );
926 // send CHILD_EVENT to affected children
927 ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator begin
= maParaManager
.begin();
928 ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator end
= begin
;
930 ::std::advance( begin
, nFirst
);
931 ::std::advance( end
, nLast
+1 );
933 // TODO: maybe optimize here in the following way. If the
934 // number of removed children exceeds a certain threshold,
935 // use InvalidateFlags::Children
936 AccessibleTextHelper_LostChildEvent
aFunctor( *this );
938 ::std::for_each( begin
, end
, aFunctor
);
940 maParaManager
.Release(nFirst
, nLast
+1);
941 // should be no need for UpdateBoundRect, since all affected children are cleared.
945 // functor for sending child events (no stand-alone function, they are maybe not inlined)
946 class AccessibleTextHelper_ChildrenTextChanged
949 void operator()( ::accessibility::AccessibleEditableTextPara
& rPara
)
955 /** functor processing queue events
957 Reacts on SfxHintId::TextParaInserted/REMOVED events and stores
960 class AccessibleTextHelper_QueueFunctor
963 AccessibleTextHelper_QueueFunctor() :
966 mnHintId(SfxHintId::NONE
)
968 void operator()( const SfxHint
* pEvent
)
971 mnParasChanged
!= -1 )
973 // determine hint type
974 const TextHint
* pTextHint
= dynamic_cast<const TextHint
*>( pEvent
);
975 const SvxEditSourceHint
* pEditSourceHint
= dynamic_cast<const SvxEditSourceHint
*>( pEvent
);
977 if( !pEditSourceHint
&& pTextHint
&&
978 (pTextHint
->GetId() == SfxHintId::TextParaInserted
||
979 pTextHint
->GetId() == SfxHintId::TextParaRemoved
) )
981 if( pTextHint
->GetValue() == EE_PARA_ALL
)
987 mnHintId
= pTextHint
->GetId();
988 mnParaIndex
= pTextHint
->GetValue();
995 /** Query number of paragraphs changed during queue processing.
997 @return number of changed paragraphs, -1 for
998 "every paragraph changed"
1000 sal_Int32
GetNumberOfParasChanged() const { return mnParasChanged
; }
1001 /** Query index of last added/removed paragraph
1003 @return index of lastly added paragraphs, -1 for none
1006 sal_Int32
GetParaIndex() const { return mnParaIndex
; }
1007 /** Query hint id of last interesting event
1009 @return hint id of last interesting event (REMOVED/INSERTED).
1011 SfxHintId
GetHintId() const { return mnHintId
; }
1014 /** number of paragraphs changed during queue processing. -1 for
1015 "every paragraph changed"
1017 sal_Int32 mnParasChanged
;
1018 /// index of paragraph added/removed last
1019 sal_Int32 mnParaIndex
;
1020 /// TextHint ID (removed/inserted) of last interesting event
1024 void AccessibleTextHelper_Impl::ProcessQueue()
1026 // inspect queue for paragraph insert/remove events. If there
1027 // is exactly _one_ of those in the queue, and the number of
1028 // paragraphs has changed by exactly one, use that event to
1029 // determine a priori which paragraph was added/removed. This
1030 // is necessary, since I must sync right here with the
1031 // EditEngine state (number of paragraphs etc.), since I'm
1032 // potentially sending listener events right away.
1033 AccessibleTextHelper_QueueFunctor aFunctor
;
1034 maEventQueue
.ForEach( aFunctor
);
1036 const sal_Int32
nNewParas( GetTextForwarder().GetParagraphCount() );
1037 const sal_Int32
nCurrParas( maParaManager
.GetNum() );
1039 // whether every paragraph already is updated (no need to
1040 // repeat that later on, e.g. for PARA_MOVED events)
1041 bool bEverythingUpdated( false );
1043 if( labs( nNewParas
- nCurrParas
) == 1 &&
1044 aFunctor
.GetNumberOfParasChanged() == 1 )
1046 // #103483# Exactly one paragraph added/removed. This is
1047 // the normal case, optimize event handling here.
1049 if( aFunctor
.GetHintId() == SfxHintId::TextParaInserted
)
1051 // update num of paras
1052 maParaManager
.SetNum( nNewParas
);
1054 // release everything from the insertion position until the end
1055 maParaManager
.Release(aFunctor
.GetParaIndex(), nCurrParas
);
1057 // TODO: Clarify whether this behaviour _really_ saves
1058 // anybody anything!
1059 // update children, _don't_ broadcast
1060 UpdateVisibleChildren( false );
1063 // send insert event
1064 // #109864# Enforce creation of this paragraph
1067 GotPropertyEvent( uno::makeAny( getAccessibleChild( aFunctor
.GetParaIndex() -
1068 mnFirstVisibleChild
+ GetStartIndex() ) ),
1069 AccessibleEventId::CHILD
);
1071 catch( const uno::Exception
& )
1073 OSL_FAIL("AccessibleTextHelper_Impl::ProcessQueue: could not create new paragraph");
1076 else if( aFunctor
.GetHintId() == SfxHintId::TextParaRemoved
)
1078 ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator begin
= maParaManager
.begin();
1079 ::std::advance( begin
, aFunctor
.GetParaIndex() );
1080 ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator end
= begin
;
1081 ::std::advance( end
, 1 );
1083 // #i61812# remember para to be removed for later notification
1084 // AFTER the new state is applied (that after the para got removed)
1085 ::uno::Reference
< XAccessible
> xPara(begin
->first
.get().get());
1087 // release everything from the remove position until the end
1088 maParaManager
.Release(aFunctor
.GetParaIndex(), nCurrParas
);
1090 // update num of paras
1091 maParaManager
.SetNum( nNewParas
);
1093 // TODO: Clarify whether this behaviour _really_ saves
1094 // anybody anything!
1095 // update children, _don't_ broadcast
1096 UpdateVisibleChildren( false );
1099 // #i61812# notification for removed para
1101 FireEvent(AccessibleEventId::CHILD
, uno::Any(), uno::makeAny( xPara
) );
1105 OSL_FAIL("AccessibleTextHelper_Impl::ProcessQueue() invalid hint id");
1108 else if( nNewParas
!= nCurrParas
)
1110 // release all paras
1111 maParaManager
.Release(0, nCurrParas
);
1113 // update num of paras
1114 maParaManager
.SetNum( nNewParas
);
1116 // #109864# create from scratch, don't broadcast
1117 UpdateVisibleChildren( false );
1120 // number of paragraphs somehow changed - but we have no
1121 // chance determining how. Thus, throw away everything and
1122 // create from scratch.
1123 // (child events should be broadcast after the changes are done...)
1124 FireEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN
);
1126 // no need for further updates later on
1127 bEverythingUpdated
= true;
1130 while( !maEventQueue
.IsEmpty() )
1132 ::std::unique_ptr
< SfxHint
> pHint( maEventQueue
.PopFront() );
1135 const SfxHint
& rHint
= *pHint
;
1137 // Note, if you add events here, you need to update the AccessibleTextEventQueue::Append
1138 // code, because only the events we process here, are actually queued there.
1143 if (rHint
.GetId() == SfxHintId::ThisIsAnSdrHint
)
1145 const SdrHint
* pSdrHint
= static_cast< const SdrHint
* >( &rHint
);
1147 switch( pSdrHint
->GetKind() )
1149 case SdrHintKind::BeginEdit
:
1155 // change children state
1156 maParaManager
.SetActive();
1158 // per definition, edit mode text has the focus
1163 case SdrHintKind::EndEdit
:
1165 // focused child now loses focus
1166 ESelection aSelection
;
1167 if( GetEditViewForwarder().GetSelection( aSelection
) )
1168 SetChildFocus( aSelection
.nEndPara
, false );
1170 // change children state
1171 maParaManager
.SetActive( false );
1173 maLastSelection
= ESelection( EE_PARA_NOT_FOUND
, EE_INDEX_NOT_FOUND
,
1174 EE_PARA_NOT_FOUND
, EE_INDEX_NOT_FOUND
);
1181 else if( const SvxEditSourceHint
* pEditSourceHint
= dynamic_cast<const SvxEditSourceHint
*>( &rHint
) )
1183 switch( pEditSourceHint
->GetId() )
1185 case SfxHintId::EditSourceParasMoved
:
1187 DBG_ASSERT( pEditSourceHint
->GetStartValue() < GetTextForwarder().GetParagraphCount() &&
1188 pEditSourceHint
->GetEndValue() < GetTextForwarder().GetParagraphCount(),
1189 "AccessibleTextHelper_Impl::NotifyHdl: Invalid notification");
1191 if( !bEverythingUpdated
)
1193 ParagraphsMoved(pEditSourceHint
->GetStartValue(),
1194 pEditSourceHint
->GetValue(),
1195 pEditSourceHint
->GetEndValue());
1197 // in all cases, check visibility afterwards.
1198 UpdateVisibleChildren();
1203 case SfxHintId::EditSourceSelectionChanged
:
1209 // maybe we're not in edit mode (this is not an error)
1210 catch( const uno::Exception
& ) {}
1215 else if( const TextHint
* pTextHint
= dynamic_cast<const TextHint
*>( &rHint
) )
1217 const sal_Int32 nParas
= GetTextForwarder().GetParagraphCount();
1219 switch( pTextHint
->GetId() )
1221 case SfxHintId::TextModified
:
1224 sal_Int32
nPara( pTextHint
->GetValue() );
1226 // #108900# Delegate change event to children
1227 AccessibleTextHelper_ChildrenTextChanged aNotifyChildrenFunctor
;
1229 if( nPara
== EE_PARA_ALL
)
1231 // #108900# Call every child
1232 ::std::for_each( maParaManager
.begin(), maParaManager
.end(),
1233 AccessibleParaManager::WeakChildAdapter
< AccessibleTextHelper_ChildrenTextChanged
> (aNotifyChildrenFunctor
) );
1236 if( nPara
< nParas
)
1238 // #108900# Call child at index nPara
1239 ::std::for_each( maParaManager
.begin()+nPara
, maParaManager
.begin()+nPara
+1,
1240 AccessibleParaManager::WeakChildAdapter
< AccessibleTextHelper_ChildrenTextChanged
> (aNotifyChildrenFunctor
) );
1245 case SfxHintId::TextParaInserted
:
1246 // already happened above
1249 case SfxHintId::TextParaRemoved
:
1250 // already happened above
1253 case SfxHintId::TextHeightChanged
:
1254 // visibility changed, done below
1257 case SfxHintId::TextViewScrolled
:
1258 // visibility changed, done below
1263 // in all cases, check visibility afterwards.
1264 UpdateVisibleChildren();
1267 else if ( dynamic_cast<const SvxViewChangedHint
*>( &rHint
) )
1269 // just check visibility
1270 UpdateVisibleChildren();
1273 // it's VITAL to keep the SfxSimpleHint last! It's the base of some classes above!
1274 else if( rHint
.GetId() == SfxHintId::Dying
)
1276 // edit source is dying under us, become defunc then
1279 // make edit source inaccessible
1280 // Note: cannot destroy it here, since we're called from there!
1281 ShutdownEditSource();
1283 catch( const uno::Exception
& ) {}
1286 catch( const uno::Exception
& )
1288 DBG_UNHANDLED_EXCEPTION("svx");
1294 void AccessibleTextHelper_Impl::Notify( SfxBroadcaster
& /*rBC*/, const SfxHint
& rHint
)
1296 // precondition: solar mutex locked
1297 DBG_TESTSOLARMUTEX();
1299 // precondition: not in a recursion
1307 // Process notification event, arranged in order of likelihood of
1308 // occurrence to avoid unnecessary dynamic_cast. Note that
1309 // SvxEditSourceHint is derived from TextHint, so has to be checked
1311 if (rHint
.GetId() == SfxHintId::ThisIsAnSdrHint
)
1313 const SdrHint
* pSdrHint
= static_cast< const SdrHint
* >( &rHint
);
1314 // process drawing layer events right away, if not
1315 // within an open EE notification frame. Otherwise,
1316 // event processing would be delayed until next EE
1317 // notification sequence.
1318 maEventQueue
.Append( *pSdrHint
);
1320 else if( const SvxViewChangedHint
* pViewHint
= dynamic_cast<const SvxViewChangedHint
*>( &rHint
) )
1322 // process visibility right away, if not within an
1323 // open EE notification frame. Otherwise, event
1324 // processing would be delayed until next EE
1325 // notification sequence.
1326 maEventQueue
.Append( *pViewHint
);
1328 else if( const SvxEditSourceHint
* pEditSourceHint
= dynamic_cast<const SvxEditSourceHint
*>( &rHint
) )
1330 // EditEngine should emit TEXT_SELECTION_CHANGED events (#i27299#)
1331 maEventQueue
.Append( *pEditSourceHint
);
1333 else if( const TextHint
* pTextHint
= dynamic_cast<const TextHint
*>( &rHint
) )
1335 // EditEngine should emit TEXT_SELECTION_CHANGED events (#i27299#)
1336 if(pTextHint
->GetId() == SfxHintId::TextProcessNotifications
)
1339 maEventQueue
.Append( *pTextHint
);
1341 // it's VITAL to keep the SfxHint last! It's the base of the classes above!
1342 else if( rHint
.GetId() == SfxHintId::Dying
)
1344 // handle this event _at once_, because after that, objects are invalid
1345 // edit source is dying under us, become defunc then
1346 maEventQueue
.Clear();
1349 // make edit source inaccessible
1350 // Note: cannot destroy it here, since we're called from there!
1351 ShutdownEditSource();
1353 catch( const uno::Exception
& ) {}
1356 catch( const uno::Exception
& )
1358 DBG_UNHANDLED_EXCEPTION("svx");
1365 void AccessibleTextHelper_Impl::Dispose()
1367 if( getNotifierClientId() != -1 )
1371 // #106234# Unregister from EventNotifier
1372 ::comphelper::AccessibleEventNotifier::revokeClient( getNotifierClientId() );
1373 SAL_INFO("svx", "disposed ID: " << mnNotifierClientId
);
1375 catch( const uno::Exception
& ) {}
1377 mnNotifierClientId
= -1;
1383 maParaManager
.Dispose();
1385 catch( const uno::Exception
& ) {}
1387 // quit listen on stale edit source
1388 if( maEditSource
.IsValid() )
1389 EndListening( maEditSource
.GetBroadcaster() );
1392 maEditSource
.SetEditSource( ::std::unique_ptr
< SvxEditSource
>() );
1393 mxFrontEnd
= nullptr;
1396 void AccessibleTextHelper_Impl::FireEvent( const sal_Int16 nEventId
, const uno::Any
& rNewValue
, const uno::Any
& rOldValue
) const
1398 // -- object locked --
1399 AccessibleEventObject aEvent
;
1401 osl::MutexGuard
aGuard(maMutex
);
1403 DBG_ASSERT(mxFrontEnd
.is(), "AccessibleTextHelper::FireEvent: no event source set");
1405 if (mxFrontEnd
.is())
1406 aEvent
= AccessibleEventObject(mxFrontEnd
->getAccessibleContext(), nEventId
,
1407 rNewValue
, rOldValue
);
1409 aEvent
= AccessibleEventObject(uno::Reference
<uno::XInterface
>(), nEventId
,
1410 rNewValue
, rOldValue
);
1412 // no locking necessary, FireEvent internally copies listeners
1413 // if someone removes/adds in between Further locking,
1414 // actually, might lead to deadlocks, since we're calling out
1422 void AccessibleTextHelper_Impl::FireEvent( const AccessibleEventObject
& rEvent
) const
1424 // #102261# Call global queue for focus events
1425 if( rEvent
.EventId
== AccessibleStateType::FOCUSED
)
1426 vcl::unohelper::NotifyAccessibleStateEventGlobally( rEvent
);
1428 // #106234# Delegate to EventNotifier
1429 ::comphelper::AccessibleEventNotifier::addEvent( getNotifierClientId(),
1433 // XAccessibleContext
1434 sal_Int32
AccessibleTextHelper_Impl::getAccessibleChildCount() const
1436 return mnLastVisibleChild
- mnFirstVisibleChild
+ 1;
1439 uno::Reference
< XAccessible
> AccessibleTextHelper_Impl::getAccessibleChild( sal_Int32 i
)
1441 i
-= GetStartIndex();
1443 if( 0 > i
|| i
>= getAccessibleChildCount() ||
1444 GetTextForwarder().GetParagraphCount() <= i
)
1446 throw lang::IndexOutOfBoundsException("Invalid child index", mxFrontEnd
);
1449 DBG_ASSERT(mxFrontEnd
.is(), "AccessibleTextHelper_Impl::UpdateVisibleChildren: no frontend set");
1451 if( mxFrontEnd
.is() )
1452 return maParaManager
.CreateChild( i
, mxFrontEnd
, GetEditSource(), mnFirstVisibleChild
+ i
).first
;
1457 void AccessibleTextHelper_Impl::addAccessibleEventListener( const uno::Reference
< XAccessibleEventListener
>& xListener
)
1459 if( getNotifierClientId() != -1 )
1460 ::comphelper::AccessibleEventNotifier::addEventListener( getNotifierClientId(), xListener
);
1463 void AccessibleTextHelper_Impl::removeAccessibleEventListener( const uno::Reference
< XAccessibleEventListener
>& xListener
)
1465 if( getNotifierClientId() != -1 )
1467 const sal_Int32 nListenerCount
= ::comphelper::AccessibleEventNotifier::removeEventListener( getNotifierClientId(), xListener
);
1468 if ( !nListenerCount
)
1470 // no listeners anymore
1471 // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client),
1472 // and at least to us not firing any events anymore, in case somebody calls
1473 // NotifyAccessibleEvent, again
1474 ::comphelper::AccessibleEventNotifier::TClientId
nId( getNotifierClientId() );
1475 mnNotifierClientId
= -1;
1476 ::comphelper::AccessibleEventNotifier::revokeClient( nId
);
1481 uno::Reference
< XAccessible
> AccessibleTextHelper_Impl::getAccessibleAtPoint( const awt::Point
& _aPoint
)
1483 // make given position relative
1484 if( !mxFrontEnd
.is() )
1485 throw uno::RuntimeException("AccessibleTextHelper_Impl::getAccessibleAt: frontend invalid", mxFrontEnd
);
1487 uno::Reference
< XAccessibleContext
> xFrontEndContext
= mxFrontEnd
->getAccessibleContext();
1489 if( !xFrontEndContext
.is() )
1490 throw uno::RuntimeException("AccessibleTextHelper_Impl::getAccessibleAt: frontend invalid", mxFrontEnd
);
1492 uno::Reference
< XAccessibleComponent
> xFrontEndComponent( xFrontEndContext
, uno::UNO_QUERY_THROW
);
1494 // #103862# No longer need to make given position relative
1495 Point
aPoint( _aPoint
.X
, _aPoint
.Y
);
1497 // respect EditEngine offset to surrounding shape/cell
1498 aPoint
-= GetOffset();
1500 // convert to EditEngine coordinate system
1501 SvxTextForwarder
& rCacheTF
= GetTextForwarder();
1502 Point
aLogPoint( GetViewForwarder().PixelToLogic( aPoint
, rCacheTF
.GetMapMode() ) );
1504 // iterate over all visible children (including those not yet created)
1506 for( nChild
=mnFirstVisibleChild
; nChild
<= mnLastVisibleChild
; ++nChild
)
1508 DBG_ASSERT(nChild
>= 0,
1509 "AccessibleTextHelper_Impl::getAccessibleAt: index value overflow");
1511 tools::Rectangle
aParaBounds( rCacheTF
.GetParaBounds( nChild
) );
1513 if( aParaBounds
.IsInside( aLogPoint
) )
1514 return getAccessibleChild( nChild
- mnFirstVisibleChild
+ GetStartIndex() );
1522 // AccessibleTextHelper implementation (simply forwards to impl)
1524 AccessibleTextHelper::AccessibleTextHelper( ::std::unique_ptr
< SvxEditSource
> && pEditSource
) :
1525 mpImpl( new AccessibleTextHelper_Impl() )
1527 SolarMutexGuard aGuard
;
1529 SetEditSource( std::move(pEditSource
) );
1532 AccessibleTextHelper::~AccessibleTextHelper()
1536 const SvxEditSource
& AccessibleTextHelper::GetEditSource() const
1539 mpImpl
->CheckInvariants();
1541 const SvxEditSource
& aEditSource
= mpImpl
->GetEditSource();
1543 mpImpl
->CheckInvariants();
1547 return mpImpl
->GetEditSource();
1551 void AccessibleTextHelper::SetEditSource( ::std::unique_ptr
< SvxEditSource
> && pEditSource
)
1554 // precondition: solar mutex locked
1555 DBG_TESTSOLARMUTEX();
1557 mpImpl
->CheckInvariants();
1560 mpImpl
->SetEditSource( std::move(pEditSource
) );
1563 mpImpl
->CheckInvariants();
1567 void AccessibleTextHelper::SetEventSource( const uno::Reference
< XAccessible
>& rInterface
)
1570 mpImpl
->CheckInvariants();
1573 mpImpl
->SetEventSource( rInterface
);
1576 mpImpl
->CheckInvariants();
1580 void AccessibleTextHelper::SetFocus( bool bHaveFocus
)
1583 // precondition: solar mutex locked
1584 DBG_TESTSOLARMUTEX();
1586 mpImpl
->CheckInvariants();
1589 mpImpl
->SetFocus( bHaveFocus
);
1592 mpImpl
->CheckInvariants();
1596 bool AccessibleTextHelper::HaveFocus()
1599 mpImpl
->CheckInvariants();
1601 bool bRet( mpImpl
->HaveFocus() );
1603 mpImpl
->CheckInvariants();
1607 return mpImpl
->HaveFocus();
1611 void AccessibleTextHelper::SetOffset( const Point
& rPoint
)
1614 // precondition: solar mutex locked
1615 DBG_TESTSOLARMUTEX();
1617 mpImpl
->CheckInvariants();
1620 mpImpl
->SetOffset( rPoint
);
1623 mpImpl
->CheckInvariants();
1627 void AccessibleTextHelper::SetStartIndex( sal_Int32 nOffset
)
1630 // precondition: solar mutex locked
1631 DBG_TESTSOLARMUTEX();
1633 mpImpl
->CheckInvariants();
1636 mpImpl
->SetStartIndex( nOffset
);
1639 mpImpl
->CheckInvariants();
1643 sal_Int32
AccessibleTextHelper::GetStartIndex() const
1646 mpImpl
->CheckInvariants();
1648 sal_Int32 nOffset
= mpImpl
->GetStartIndex();
1650 mpImpl
->CheckInvariants();
1654 return mpImpl
->GetStartIndex();
1658 void AccessibleTextHelper::SetAdditionalChildStates( const VectorOfStates
& rChildStates
)
1660 mpImpl
->SetAdditionalChildStates( rChildStates
);
1663 void AccessibleTextHelper::UpdateChildren()
1666 // precondition: solar mutex locked
1667 DBG_TESTSOLARMUTEX();
1669 mpImpl
->CheckInvariants();
1672 mpImpl
->UpdateVisibleChildren();
1673 mpImpl
->UpdateBoundRect();
1675 mpImpl
->UpdateSelection();
1678 mpImpl
->CheckInvariants();
1682 void AccessibleTextHelper::Dispose()
1684 // As Dispose calls ShutdownEditSource, which in turn
1685 // deregisters as listener on the edit source, have to lock
1687 SolarMutexGuard aGuard
;
1690 mpImpl
->CheckInvariants();
1696 mpImpl
->CheckInvariants();
1700 // XAccessibleContext
1701 sal_Int32
AccessibleTextHelper::GetChildCount() const
1703 SolarMutexGuard aGuard
;
1706 mpImpl
->CheckInvariants();
1708 sal_Int32 nRet
= mpImpl
->getAccessibleChildCount();
1710 mpImpl
->CheckInvariants();
1714 return mpImpl
->getAccessibleChildCount();
1718 uno::Reference
< XAccessible
> AccessibleTextHelper::GetChild( sal_Int32 i
)
1720 SolarMutexGuard aGuard
;
1723 mpImpl
->CheckInvariants();
1725 uno::Reference
< XAccessible
> xRet
= mpImpl
->getAccessibleChild( i
);
1727 mpImpl
->CheckInvariants();
1731 return mpImpl
->getAccessibleChild( i
);
1735 void AccessibleTextHelper::AddEventListener( const uno::Reference
< XAccessibleEventListener
>& xListener
)
1738 mpImpl
->CheckInvariants();
1740 mpImpl
->addAccessibleEventListener( xListener
);
1742 mpImpl
->CheckInvariants();
1744 mpImpl
->addAccessibleEventListener( xListener
);
1748 void AccessibleTextHelper::RemoveEventListener( const uno::Reference
< XAccessibleEventListener
>& xListener
)
1751 mpImpl
->CheckInvariants();
1753 mpImpl
->removeAccessibleEventListener( xListener
);
1755 mpImpl
->CheckInvariants();
1757 mpImpl
->removeAccessibleEventListener( xListener
);
1761 // XAccessibleComponent
1762 uno::Reference
< XAccessible
> AccessibleTextHelper::GetAt( const awt::Point
& aPoint
)
1764 SolarMutexGuard aGuard
;
1767 mpImpl
->CheckInvariants();
1769 uno::Reference
< XAccessible
> xChild
= mpImpl
->getAccessibleAtPoint( aPoint
);
1771 mpImpl
->CheckInvariants();
1775 return mpImpl
->getAccessibleAtPoint( aPoint
);
1779 } // end of namespace accessibility
1782 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */