tdf#130857 qt weld: Support mail merge "Server Auth" dialog
[LibreOffice.git] / svx / source / accessibility / AccessibleTextHelper.cxx
blob2f3e0c26505efd574999c2ec4e6519bd115055e6
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 .
20 #include <sal/config.h>
22 #include <cstdlib>
23 #include <memory>
24 #include <mutex>
25 #include <utility>
26 #include <algorithm>
27 #include <sal/log.hxx>
28 #include <com/sun/star/uno/Any.hxx>
29 #include <com/sun/star/uno/Reference.hxx>
30 #include <com/sun/star/awt/Point.hpp>
31 #include <com/sun/star/awt/Rectangle.hpp>
32 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
33 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
34 #include <com/sun/star/accessibility/XAccessible.hpp>
35 #include <com/sun/star/accessibility/XAccessibleContext.hpp>
36 #include <com/sun/star/accessibility/XAccessibleComponent.hpp>
37 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
38 #include <comphelper/accessibleeventnotifier.hxx>
39 #include <vcl/svapp.hxx>
40 #include <vcl/textdata.hxx>
43 // Project-local header
46 #include "AccessibleTextEventQueue.hxx"
47 #include <svx/AccessibleTextHelper.hxx>
49 #include <editeng/unoedhlp.hxx>
50 #include <editeng/unoedprx.hxx>
51 #include <editeng/AccessibleParaManager.hxx>
52 #include <editeng/AccessibleEditableTextPara.hxx>
53 #include <svx/svdmodel.hxx>
54 #include <svx/svdpntv.hxx>
55 #include <cell.hxx>
56 #include "../table/accessiblecell.hxx"
57 #include <editeng/editdata.hxx>
58 #include <tools/debug.hxx>
59 #include <comphelper/diagnose_ex.hxx>
61 using namespace ::com::sun::star;
62 using namespace ::com::sun::star::accessibility;
64 namespace accessibility
67 // AccessibleTextHelper_Impl declaration
69 template < typename first_type, typename second_type >
70 static ::std::pair< first_type, second_type > makeSortedPair( first_type first,
71 second_type second )
73 if( first > second )
74 return ::std::make_pair( second, first );
75 else
76 return ::std::make_pair( first, second );
79 class AccessibleTextHelper_Impl : public SfxListener
81 public:
82 typedef ::std::vector< sal_Int16 > VectorOfStates;
84 // receive pointer to our frontend class and view window
85 AccessibleTextHelper_Impl();
86 virtual ~AccessibleTextHelper_Impl() override;
88 // XAccessibleContext child handling methods
89 sal_Int64 getAccessibleChildCount() const;
90 uno::Reference< XAccessible > getAccessibleChild( sal_Int64 i );
92 // XAccessibleEventBroadcaster child related methods
93 void addAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener );
94 void removeAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener );
96 // XAccessibleComponent child related methods
97 uno::Reference< XAccessible > getAccessibleAtPoint( const awt::Point& aPoint );
99 SvxEditSourceAdapter& GetEditSource() const;
101 void SetEditSource( ::std::unique_ptr< SvxEditSource > && pEditSource );
103 void SetEventSource( const uno::Reference< XAccessible >& rInterface )
105 mxFrontEnd = rInterface;
108 void SetOffset( const Point& );
109 Point GetOffset() const
111 std::scoped_lock aGuard( maMutex ); Point aPoint( maOffset );
112 return aPoint;
115 void SetStartIndex( sal_Int32 nOffset );
116 sal_Int32 GetStartIndex() const
118 // Strictly correct only with locked solar mutex, // but
119 // here we rely on the fact that sal_Int32 access is
120 // atomic
121 return mnStartIndex;
124 void SetAdditionalChildStates( sal_Int64 nChildStates );
126 void Dispose();
128 // do NOT hold object mutex when calling this! Danger of deadlock
129 void FireEvent( const sal_Int16 nEventId, const uno::Any& rNewValue = uno::Any(), const uno::Any& rOldValue = uno::Any() ) const;
130 void FireEvent( const AccessibleEventObject& rEvent ) const;
132 void SetFocus( bool bHaveFocus );
133 bool HaveFocus() const
135 // No locking of solar mutex here, since we rely on the fact
136 // that sal_Bool access is atomic
137 return mbThisHasFocus;
139 void SetChildFocus( sal_Int32 nChild, bool bHaveFocus );
140 void SetShapeFocus( bool bHaveFocus );
141 void ChangeChildFocus( sal_Int32 nNewChild );
143 #ifdef DBG_UTIL
144 void CheckInvariants() const;
145 #endif
147 // checks all children for visibility, throws away invisible ones
148 void UpdateVisibleChildren( bool bBroadcastEvents=true );
150 // check all children for changes in position and size
151 void UpdateBoundRect();
153 // calls SetSelection on the forwarder and updates maLastSelection
154 // cache.
155 void UpdateSelection();
157 private:
159 // Process event queue
160 void ProcessQueue();
162 // shutdown usage of current edit source on myself and the children.
163 void ShutdownEditSource();
165 void ParagraphsMoved( sal_Int32 nFirst, sal_Int32 nMiddle, sal_Int32 nLast );
167 virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
169 comphelper::AccessibleEventNotifier::TClientId getNotifierClientId() const { return mnNotifierClientId; }
171 // lock solar mutex before
172 SvxTextForwarder& GetTextForwarder() const;
173 // lock solar mutex before
174 SvxViewForwarder& GetViewForwarder() const;
175 // lock solar mutex before
176 SvxEditViewForwarder& GetEditViewForwarder() const;
178 // are we in edit mode?
179 bool IsActive() const;
181 // our frontend class (the one implementing the actual
182 // interface). That's not necessarily the one containing the impl
183 // pointer!
184 uno::Reference< XAccessible > mxFrontEnd;
186 // a wrapper for the text forwarders (guarded by solar mutex)
187 mutable SvxEditSourceAdapter maEditSource;
189 // store last selection (to correctly report selection changes, guarded by solar mutex)
190 ESelection maLastSelection;
192 // cache range of visible children (guarded by solar mutex)
193 sal_Int32 mnFirstVisibleChild;
194 sal_Int32 mnLastVisibleChild;
196 // offset to add to all our children (unguarded, relying on
197 // the fact that sal_Int32 access is atomic)
198 sal_Int32 mnStartIndex;
200 // the object handling our children (guarded by solar mutex)
201 ::accessibility::AccessibleParaManager maParaManager;
203 // Queued events from Notify() (guarded by solar mutex)
204 AccessibleTextEventQueue maEventQueue;
206 // spin lock to prevent notify in notify (guarded by solar mutex)
207 bool mbInNotify;
209 // whether the object or its children has the focus set (guarded by solar mutex)
210 bool mbGroupHasFocus;
212 // whether we (this object) has the focus set (guarded by solar mutex)
213 bool mbThisHasFocus;
215 mutable std::mutex maMutex;
217 /// our current offset to the containing shape/cell (guarded by maMutex)
218 Point maOffset;
220 /// client Id from AccessibleEventNotifier
221 comphelper::AccessibleEventNotifier::TClientId mnNotifierClientId;
222 static constexpr comphelper::AccessibleEventNotifier::TClientId snNotifierClientRevoked
223 = std::numeric_limits<comphelper::AccessibleEventNotifier::TClientId>::max();
226 AccessibleTextHelper_Impl::AccessibleTextHelper_Impl() :
227 maLastSelection( ESelection::AtEnd() ),
228 mnFirstVisibleChild( -1 ),
229 mnLastVisibleChild( -2 ),
230 mnStartIndex( 0 ),
231 mbInNotify( false ),
232 mbGroupHasFocus( false ),
233 mbThisHasFocus( false ),
234 maOffset(0,0),
235 // well, that's strictly exception safe, though not really
236 // robust. We rely on the fact that this member is constructed
237 // last, and that the constructor body is empty, thus no
238 // chance for exceptions once the Id is fetched. Nevertheless,
239 // normally should employ RAII here...
240 mnNotifierClientId(::comphelper::AccessibleEventNotifier::registerClient())
242 SAL_INFO("svx", "received ID: " << mnNotifierClientId );
245 AccessibleTextHelper_Impl::~AccessibleTextHelper_Impl()
247 SolarMutexGuard aGuard;
251 // call Dispose here, too, since we've some resources not
252 // automatically freed otherwise
253 Dispose();
255 catch( const uno::Exception& ) {}
258 SvxTextForwarder& AccessibleTextHelper_Impl::GetTextForwarder() const
260 if( !maEditSource.IsValid() )
261 throw uno::RuntimeException(u"Unknown edit source"_ustr, mxFrontEnd);
263 SvxTextForwarder* pTextForwarder = maEditSource.GetTextForwarder();
265 if( !pTextForwarder )
266 throw uno::RuntimeException(u"Unable to fetch text forwarder, model might be dead"_ustr, mxFrontEnd);
268 if( !pTextForwarder->IsValid() )
269 throw uno::RuntimeException(u"Text forwarder is invalid, model might be dead"_ustr, mxFrontEnd);
271 return *pTextForwarder;
274 SvxViewForwarder& AccessibleTextHelper_Impl::GetViewForwarder() const
276 if( !maEditSource.IsValid() )
277 throw uno::RuntimeException(u"Unknown edit source"_ustr, mxFrontEnd);
279 SvxViewForwarder* pViewForwarder = maEditSource.GetViewForwarder();
281 if( !pViewForwarder )
282 throw uno::RuntimeException(u"Unable to fetch view forwarder, model might be dead"_ustr, mxFrontEnd);
284 if( !pViewForwarder->IsValid() )
285 throw uno::RuntimeException(u"View forwarder is invalid, model might be dead"_ustr, mxFrontEnd);
287 return *pViewForwarder;
290 SvxEditViewForwarder& AccessibleTextHelper_Impl::GetEditViewForwarder() const
292 if( !maEditSource.IsValid() )
293 throw uno::RuntimeException(u"Unknown edit source"_ustr, mxFrontEnd);
295 SvxEditViewForwarder* pViewForwarder = maEditSource.GetEditViewForwarder();
297 if( !pViewForwarder )
299 throw uno::RuntimeException(u"No edit view forwarder, object not in edit mode"_ustr, mxFrontEnd);
302 if( !pViewForwarder->IsValid() )
304 throw uno::RuntimeException(u"View forwarder is invalid, object not in edit mode"_ustr, mxFrontEnd);
307 return *pViewForwarder;
310 SvxEditSourceAdapter& AccessibleTextHelper_Impl::GetEditSource() const
312 if( !maEditSource.IsValid() )
313 throw uno::RuntimeException(u"AccessibleTextHelper_Impl::GetEditSource: no edit source"_ustr, mxFrontEnd );
314 return maEditSource;
317 namespace {
319 // functor for sending child events (no stand-alone function, they are maybe not inlined)
320 class AccessibleTextHelper_OffsetChildIndex
322 public:
323 explicit AccessibleTextHelper_OffsetChildIndex( sal_Int32 nDifference ) : mnDifference(nDifference) {}
324 void operator()( ::accessibility::AccessibleEditableTextPara& rPara )
326 rPara.SetIndexInParent( rPara.GetIndexInParent() + mnDifference );
329 private:
330 const sal_Int32 mnDifference;
335 void AccessibleTextHelper_Impl::SetStartIndex( sal_Int32 nOffset )
337 sal_Int32 nOldOffset( mnStartIndex );
339 mnStartIndex = nOffset;
341 if( nOldOffset != nOffset )
343 // update children
344 AccessibleTextHelper_OffsetChildIndex aFunctor( nOffset - nOldOffset );
346 ::std::for_each( maParaManager.begin(), maParaManager.end(),
347 AccessibleParaManager::WeakChildAdapter< AccessibleTextHelper_OffsetChildIndex > (aFunctor) );
351 void AccessibleTextHelper_Impl::SetAdditionalChildStates( sal_Int64 nChildStates )
353 maParaManager.SetAdditionalChildStates( nChildStates );
356 void AccessibleTextHelper_Impl::SetChildFocus( sal_Int32 nChild, bool bHaveFocus )
358 if( bHaveFocus )
360 if( mbThisHasFocus )
361 SetShapeFocus( false );
363 maParaManager.SetFocus( nChild );
365 // we just received the focus, also send caret event then
366 UpdateSelection();
368 SAL_INFO("svx", "Paragraph " << nChild << " received focus");
370 else
372 maParaManager.SetFocus( -1 );
374 SAL_INFO("svx", "Paragraph " << nChild << " lost focus");
376 if( mbGroupHasFocus )
377 SetShapeFocus( true );
381 void AccessibleTextHelper_Impl::ChangeChildFocus( sal_Int32 nNewChild )
383 if( mbThisHasFocus )
384 SetShapeFocus( false );
386 mbGroupHasFocus = true;
387 maParaManager.SetFocus( nNewChild );
389 SAL_INFO("svx", "Paragraph " << nNewChild << " received focus");
392 void AccessibleTextHelper_Impl::SetShapeFocus( bool bHaveFocus )
394 bool bOldFocus( mbThisHasFocus );
396 mbThisHasFocus = bHaveFocus;
398 if( bOldFocus == bHaveFocus )
399 return;
401 if( bHaveFocus )
403 if( mxFrontEnd.is() )
405 AccessibleCell* pAccessibleCell = dynamic_cast< AccessibleCell* > ( mxFrontEnd.get() );
406 if ( !pAccessibleCell )
407 FireEvent(AccessibleEventId::STATE_CHANGED, uno::Any(AccessibleStateType::FOCUSED));
408 else // the focus event on cell should be fired on table directly
410 AccessibleTableShape* pAccTable = pAccessibleCell->GetParentTable();
411 if (pAccTable)
412 pAccTable->SetStateDirectly(AccessibleStateType::FOCUSED);
415 SAL_INFO("svx", "Parent object received focus" );
417 else
419 // The focus state should be reset directly on table.
420 //LostPropertyEvent( uno::makeAny(AccessibleStateType::FOCUSED), AccessibleEventId::STATE_CHANGED );
421 if( mxFrontEnd.is() )
423 AccessibleCell* pAccessibleCell = dynamic_cast< AccessibleCell* > ( mxFrontEnd.get() );
424 if ( !pAccessibleCell )
425 FireEvent( AccessibleEventId::STATE_CHANGED, uno::Any(), uno::Any(AccessibleStateType::FOCUSED) );
426 else
428 AccessibleTableShape* pAccTable = pAccessibleCell->GetParentTable();
429 if (pAccTable)
430 pAccTable->ResetStateDirectly(AccessibleStateType::FOCUSED);
433 SAL_INFO("svx", "Parent object lost focus" );
437 void AccessibleTextHelper_Impl::SetFocus( bool bHaveFocus )
439 bool bOldFocus( mbGroupHasFocus );
441 mbGroupHasFocus = bHaveFocus;
443 if( IsActive() )
447 // find the one with the cursor and get/set focus accordingly
448 ESelection aSelection;
449 if( GetEditViewForwarder().GetSelection( aSelection ) )
450 SetChildFocus(aSelection.end.nPara, bHaveFocus);
452 catch( const uno::Exception& ) {}
454 else if( bOldFocus != bHaveFocus )
456 SetShapeFocus( bHaveFocus );
459 SAL_INFO("svx", "focus changed, Object " << this << ", state: " << (bHaveFocus ? "focused" : "not focused") );
462 bool AccessibleTextHelper_Impl::IsActive() const
466 SvxEditSource& rEditSource = GetEditSource();
467 SvxEditViewForwarder* pViewForwarder = rEditSource.GetEditViewForwarder();
469 if( !pViewForwarder )
470 return false;
472 if( mxFrontEnd.is() )
474 AccessibleCell* pAccessibleCell = dynamic_cast< AccessibleCell* > ( mxFrontEnd.get() );
475 if ( pAccessibleCell )
477 sdr::table::CellRef xCell = pAccessibleCell->getCellRef();
478 if ( xCell.is() )
479 return xCell->IsActiveCell();
482 return pViewForwarder->IsValid();
484 catch( const uno::RuntimeException& )
486 return false;
490 void AccessibleTextHelper_Impl::UpdateSelection()
494 ESelection aSelection;
495 if( GetEditViewForwarder().GetSelection( aSelection ) )
497 if( maLastSelection != aSelection &&
498 aSelection.end.nPara < maParaManager.GetNum() )
500 // #103998# Not that important, changed from assertion to trace
501 if( mbThisHasFocus )
503 SAL_INFO("svx", "Parent has focus!");
506 sal_Int32 nMaxValidParaIndex( GetTextForwarder().GetParagraphCount() - 1 );
508 // notify all affected paragraphs (TODO: may be suboptimal,
509 // since some paragraphs might stay selected)
510 if (maLastSelection.start.nPara != EE_PARA_MAX)
512 // Did the caret move from one paragraph to another?
513 // #100530# no caret events if not focused.
514 if( mbGroupHasFocus &&
515 maLastSelection.end.nPara != aSelection.end.nPara )
517 if (maLastSelection.end.nPara < maParaManager.GetNum())
519 maParaManager.FireEvent( ::std::min( maLastSelection.end.nPara, nMaxValidParaIndex ),
520 ::std::min( maLastSelection.end.nPara, nMaxValidParaIndex )+1,
521 AccessibleEventId::CARET_CHANGED,
522 uno::Any(static_cast<sal_Int32>(-1)),
523 uno::Any(maLastSelection.end.nIndex) );
526 ChangeChildFocus(aSelection.end.nPara);
528 SAL_INFO(
529 "svx",
530 "focus changed, Object: " << this
531 << ", Paragraph: " << aSelection.end.nPara
532 << ", Last paragraph: "
533 << maLastSelection.end.nPara);
537 // #100530# no caret events if not focused.
538 if( mbGroupHasFocus )
540 uno::Any aOldCursor;
542 // #i13705# The old cursor can only contain valid
543 // values if it's the same paragraph!
544 if( maLastSelection.start.nPara != EE_PARA_MAX &&
545 maLastSelection.end.nPara == aSelection.end.nPara )
547 aOldCursor <<= maLastSelection.end.nIndex;
549 else
551 aOldCursor <<= static_cast<sal_Int32>(-1);
554 maParaManager.FireEvent( aSelection.end.nPara,
555 aSelection.end.nPara+1,
556 AccessibleEventId::CARET_CHANGED,
557 uno::Any(aSelection.end.nIndex),
558 aOldCursor );
561 SAL_INFO(
562 "svx",
563 "caret changed, Object: " << this << ", New pos: "
564 << aSelection.end.nIndex << ", Old pos: "
565 << maLastSelection.end.nIndex << ", New para: "
566 << aSelection.end.nPara << ", Old para: "
567 << maLastSelection.end.nPara);
569 // #108947# Sort new range before calling FireEvent
570 ::std::pair<sal_Int32, sal_Int32> sortedSelection(
571 makeSortedPair(::std::min( aSelection.start.nPara, nMaxValidParaIndex ),
572 ::std::min( aSelection.end.nPara, nMaxValidParaIndex ) ) );
574 // #108947# Sort last range before calling FireEvent
575 ::std::pair<sal_Int32, sal_Int32> sortedLastSelection(
576 makeSortedPair(::std::min( maLastSelection.start.nPara, nMaxValidParaIndex ),
577 ::std::min( maLastSelection.end.nPara, nMaxValidParaIndex ) ) );
579 // event TEXT_SELECTION_CHANGED has to be submitted. (#i27299#)
580 const sal_Int16 nTextSelChgEventId =
581 AccessibleEventId::TEXT_SELECTION_CHANGED;
582 // #107037# notify selection change
583 if (maLastSelection.start.nPara == EE_PARA_MAX)
585 // last selection is undefined
586 // use method <ESelection::HasRange()> (#i27299#)
587 if ( aSelection.HasRange() )
589 // selection was undefined, now is on
590 maParaManager.FireEvent( sortedSelection.first,
591 sortedSelection.second+1,
592 nTextSelChgEventId );
595 else
597 // last selection is valid
598 // use method <ESelection::HasRange()> (#i27299#)
599 if ( maLastSelection.HasRange() &&
600 !aSelection.HasRange() )
602 // selection was on, now is empty
603 maParaManager.FireEvent( sortedLastSelection.first,
604 sortedLastSelection.second+1,
605 nTextSelChgEventId );
607 // use method <ESelection::HasRange()> (#i27299#)
608 else if( !maLastSelection.HasRange() &&
609 aSelection.HasRange() )
611 // selection was empty, now is on
612 maParaManager.FireEvent( sortedSelection.first,
613 sortedSelection.second+1,
614 nTextSelChgEventId );
616 // no event TEXT_SELECTION_CHANGED event, if new and
617 // last selection are empty. (#i27299#)
618 else if ( maLastSelection.HasRange() &&
619 aSelection.HasRange() )
621 // use sorted last and new selection
622 ESelection aTmpLastSel( maLastSelection );
623 aTmpLastSel.Adjust();
624 ESelection aTmpSel( aSelection );
625 aTmpSel.Adjust();
626 // first submit event for new and changed selection
627 sal_Int32 nPara = aTmpSel.start.nPara;
628 for ( ; nPara <= aTmpSel.end.nPara; ++nPara )
630 if ( nPara < aTmpLastSel.start.nPara ||
631 nPara > aTmpLastSel.end.nPara )
633 // new selection on paragraph <nPara>
634 maParaManager.FireEvent( nPara,
635 nTextSelChgEventId );
637 else
639 // check for changed selection on paragraph <nPara>
640 const sal_Int32 nParaStartPos =
641 nPara == aTmpSel.start.nPara
642 ? aTmpSel.start.nIndex : 0;
643 const sal_Int32 nParaEndPos =
644 nPara == aTmpSel.end.nPara
645 ? aTmpSel.end.nIndex : -1;
646 const sal_Int32 nLastParaStartPos =
647 nPara == aTmpLastSel.start.nPara
648 ? aTmpLastSel.start.nIndex : 0;
649 const sal_Int32 nLastParaEndPos =
650 nPara == aTmpLastSel.end.nPara
651 ? aTmpLastSel.end.nIndex : -1;
652 if ( nParaStartPos != nLastParaStartPos ||
653 nParaEndPos != nLastParaEndPos )
655 maParaManager.FireEvent(
656 nPara, nTextSelChgEventId );
660 // second submit event for 'old' selections
661 nPara = aTmpLastSel.start.nPara;
662 for ( ; nPara <= aTmpLastSel.end.nPara; ++nPara )
664 if ( nPara < aTmpSel.start.nPara ||
665 nPara > aTmpSel.end.nPara )
667 maParaManager.FireEvent( nPara,
668 nTextSelChgEventId );
674 maLastSelection = aSelection;
678 // no selection? no update actions
679 catch( const uno::RuntimeException& ) {}
682 void AccessibleTextHelper_Impl::ShutdownEditSource()
684 // This should only be called with solar mutex locked, i.e. from the main office thread
686 // This here is somewhat clumsy: As soon as our children have
687 // a NULL EditSource (maParaManager.SetEditSource()), they
688 // enter the disposed state and cannot be reanimated. Thus, it
689 // is unavoidable and a hard requirement to let go and create
690 // from scratch each and every child.
692 // invalidate children
693 maParaManager.Dispose();
694 maParaManager.SetNum(0);
696 // lost all children
697 if( mxFrontEnd.is() )
698 FireEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN);
700 // quit listen on stale edit source
701 if( maEditSource.IsValid() )
702 EndListening( maEditSource.GetBroadcaster() );
704 maEditSource.SetEditSource( ::std::unique_ptr< SvxEditSource >() );
707 void AccessibleTextHelper_Impl::SetEditSource( ::std::unique_ptr< SvxEditSource > && pEditSource )
709 // This should only be called with solar mutex locked, i.e. from the main office thread
711 // shutdown old edit source
712 ShutdownEditSource();
714 // set new edit source
715 maEditSource.SetEditSource( std::move(pEditSource) );
717 // init child vector to the current child count
718 if( maEditSource.IsValid() )
720 maParaManager.SetNum( GetTextForwarder().GetParagraphCount() );
722 // listen on new edit source
723 StartListening( maEditSource.GetBroadcaster() );
725 UpdateVisibleChildren();
729 void AccessibleTextHelper_Impl::SetOffset( const Point& rPoint )
731 // guard against non-atomic access to maOffset data structure
733 std::scoped_lock aGuard( maMutex );
734 maOffset = rPoint;
737 maParaManager.SetEEOffset( rPoint );
739 // in all cases, check visibility afterwards.
740 UpdateVisibleChildren();
741 UpdateBoundRect();
744 void AccessibleTextHelper_Impl::UpdateVisibleChildren( bool bBroadcastEvents )
748 SvxTextForwarder& rCacheTF = GetTextForwarder();
749 sal_Int32 nParas=rCacheTF.GetParagraphCount();
751 // GetTextForwarder might have replaced everything, update
752 // paragraph count in case it's outdated
753 maParaManager.SetNum( nParas );
755 mnFirstVisibleChild = -1;
756 mnLastVisibleChild = -2;
758 for( sal_Int32 nCurrPara=0; nCurrPara<nParas; ++nCurrPara )
760 if (nCurrPara == 0)
761 mnFirstVisibleChild = nCurrPara;
762 mnLastVisibleChild = nCurrPara;
763 if (mxFrontEnd.is() && bBroadcastEvents)
765 // child not yet created?
766 if (!maParaManager.HasCreatedChild(nCurrPara))
768 FireEvent(AccessibleEventId::CHILD, uno::Any(maParaManager.CreateChild(nCurrPara - mnFirstVisibleChild,
769 mxFrontEnd, GetEditSource(), nCurrPara).first));
774 catch( const uno::Exception& )
776 OSL_FAIL("AccessibleTextHelper_Impl::UpdateVisibleChildren error while determining visible children");
778 // something failed - currently no children
779 mnFirstVisibleChild = -1;
780 mnLastVisibleChild = -2;
781 maParaManager.SetNum(0);
783 // lost all children
784 if( bBroadcastEvents )
785 FireEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN);
789 void AccessibleTextHelper_Impl::UpdateBoundRect()
791 // send BOUNDRECT_CHANGED to affected children
792 for(auto it = maParaManager.begin(); it != maParaManager.end(); ++it)
794 ::accessibility::AccessibleParaManager::WeakChild& rChild = *it;
795 // retrieve hard reference from weak one
796 auto aHardRef( rChild.first.get() );
798 if( aHardRef.is() )
800 awt::Rectangle aNewRect = aHardRef->getBounds();
801 const awt::Rectangle& aOldRect = rChild.second;
803 if( aNewRect.X != aOldRect.X ||
804 aNewRect.Y != aOldRect.Y ||
805 aNewRect.Width != aOldRect.Width ||
806 aNewRect.Height != aOldRect.Height )
808 // visible data changed
809 aHardRef->FireEvent( AccessibleEventId::BOUNDRECT_CHANGED );
811 // update internal bounds
812 rChild = ::accessibility::AccessibleParaManager::WeakChild( rChild.first, aNewRect );
818 #ifdef DBG_UTIL
819 void AccessibleTextHelper_Impl::CheckInvariants() const
821 if( mnFirstVisibleChild >= 0 &&
822 mnFirstVisibleChild > mnLastVisibleChild )
824 OSL_FAIL( "AccessibleTextHelper: range invalid" );
827 #endif
829 namespace {
831 // functor for sending child events (no stand-alone function, they are maybe not inlined)
832 class AccessibleTextHelper_LostChildEvent
834 public:
835 explicit AccessibleTextHelper_LostChildEvent( AccessibleTextHelper_Impl& rImpl ) : mrImpl(rImpl) {}
836 void operator()( const ::accessibility::AccessibleParaManager::WeakChild& rPara )
838 // retrieve hard reference from weak one
839 auto aHardRef( rPara.first.get() );
841 if( aHardRef.is() )
842 mrImpl.FireEvent(AccessibleEventId::CHILD, uno::Any(), uno::Any(css::uno::Reference<css::accessibility::XAccessible>(aHardRef)) );
845 private:
846 AccessibleTextHelper_Impl& mrImpl;
851 void AccessibleTextHelper_Impl::ParagraphsMoved( sal_Int32 nFirst, sal_Int32 nMiddle, sal_Int32 nLast )
853 const sal_Int32 nParas = GetTextForwarder().GetParagraphCount();
855 /* rotate paragraphs
856 * =================
858 * Three cases:
860 * 1.
861 * ... nParagraph ... nParam1 ... nParam2 ...
862 * |______________[xxxxxxxxxxx]
863 * becomes
864 * [xxxxxxxxxxx]|______________
866 * tail is 0
868 * 2.
869 * ... nParam1 ... nParagraph ... nParam2 ...
870 * [xxxxxxxxxxx|xxxxxxxxxxxxxx]____________
871 * becomes
872 * ____________[xxxxxxxxxxx|xxxxxxxxxxxxxx]
874 * tail is nParagraph - nParam1
876 * 3.
877 * ... nParam1 ... nParam2 ... nParagraph ...
878 * [xxxxxxxxxxx]___________|____________
879 * becomes
880 * ___________|____________[xxxxxxxxxxx]
882 * tail is nParam2 - nParam1
885 // sort nParagraph, nParam1 and nParam2 in ascending order, calc range
886 if( nMiddle < nFirst )
888 ::std::swap(nFirst, nMiddle);
890 else if( nMiddle < nLast )
892 nLast = nLast + nMiddle - nFirst;
894 else
896 ::std::swap(nMiddle, nLast);
897 nLast = nLast + nMiddle - nFirst;
900 if( !(nFirst < nParas && nMiddle < nParas && nLast < nParas) )
901 return;
903 // since we have no "paragraph index
904 // changed" event on UAA, remove
905 // [first,last] and insert again later (in
906 // UpdateVisibleChildren)
908 // maParaManager.Rotate( nFirst, nMiddle, nLast );
910 // send CHILD_EVENT to affected children
911 ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator begin = maParaManager.begin();
912 ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator end = begin;
914 ::std::advance( begin, nFirst );
915 ::std::advance( end, nLast+1 );
917 // TODO: maybe optimize here in the following way. If the
918 // number of removed children exceeds a certain threshold,
919 // use InvalidateFlags::Children
920 AccessibleTextHelper_LostChildEvent aFunctor( *this );
922 ::std::for_each( begin, end, aFunctor );
924 maParaManager.Release(nFirst, nLast+1);
925 // should be no need for UpdateBoundRect, since all affected children are cleared.
928 namespace {
930 // functor for sending child events (no stand-alone function, they are maybe not inlined)
931 class AccessibleTextHelper_ChildrenTextChanged
933 public:
934 void operator()( ::accessibility::AccessibleEditableTextPara& rPara )
936 rPara.TextChanged();
940 /** functor processing queue events
942 Reacts on SfxHintId::TextParaInserted/REMOVED events and stores
943 their content
945 class AccessibleTextHelper_QueueFunctor
947 public:
948 AccessibleTextHelper_QueueFunctor() :
949 mnParasChanged( 0 ),
950 mnParaIndex(-1),
951 mnHintId(SfxHintId::NONE)
953 void operator()( const SfxHint* pEvent )
955 if( !pEvent || mnParasChanged == -1 )
956 return;
958 // determine hint type
959 const TextHint* pTextHint = dynamic_cast<const TextHint*>( pEvent );
960 const SvxEditSourceHint* pEditSourceHint = dynamic_cast<const SvxEditSourceHint*>( pEvent );
962 if( !(!pEditSourceHint && pTextHint &&
963 (pTextHint->GetId() == SfxHintId::TextParaInserted ||
964 pTextHint->GetId() == SfxHintId::TextParaRemoved )) )
965 return;
967 if (pTextHint->GetValue() == EE_PARA_MAX)
969 mnParasChanged = -1;
971 else
973 mnHintId = pTextHint->GetId();
974 mnParaIndex = pTextHint->GetValue();
975 ++mnParasChanged;
979 /** Query number of paragraphs changed during queue processing.
981 @return number of changed paragraphs, -1 for
982 "every paragraph changed"
984 sal_Int32 GetNumberOfParasChanged() const { return mnParasChanged; }
985 /** Query index of last added/removed paragraph
987 @return index of lastly added paragraphs, -1 for none
988 added so far.
990 sal_Int32 GetParaIndex() const { return mnParaIndex; }
991 /** Query hint id of last interesting event
993 @return hint id of last interesting event (REMOVED/INSERTED).
995 SfxHintId GetHintId() const { return mnHintId; }
997 private:
998 /** number of paragraphs changed during queue processing. -1 for
999 "every paragraph changed"
1001 sal_Int32 mnParasChanged;
1002 /// index of paragraph added/removed last
1003 sal_Int32 mnParaIndex;
1004 /// TextHint ID (removed/inserted) of last interesting event
1005 SfxHintId mnHintId;
1010 void AccessibleTextHelper_Impl::ProcessQueue()
1012 // inspect queue for paragraph insert/remove events. If there
1013 // is exactly _one_ of those in the queue, and the number of
1014 // paragraphs has changed by exactly one, use that event to
1015 // determine a priori which paragraph was added/removed. This
1016 // is necessary, since I must sync right here with the
1017 // EditEngine state (number of paragraphs etc.), since I'm
1018 // potentially sending listener events right away.
1019 AccessibleTextHelper_QueueFunctor aFunctor;
1020 maEventQueue.ForEach( aFunctor );
1022 const sal_Int32 nNewParas( GetTextForwarder().GetParagraphCount() );
1023 const sal_Int32 nCurrParas( maParaManager.GetNum() );
1025 // whether every paragraph already is updated (no need to
1026 // repeat that later on, e.g. for PARA_MOVED events)
1027 bool bEverythingUpdated( false );
1029 if( std::abs( nNewParas - nCurrParas ) == 1 &&
1030 aFunctor.GetNumberOfParasChanged() == 1 )
1032 // #103483# Exactly one paragraph added/removed. This is
1033 // the normal case, optimize event handling here.
1035 if( aFunctor.GetHintId() == SfxHintId::TextParaInserted )
1037 // update num of paras
1038 maParaManager.SetNum( nNewParas );
1040 // release everything from the insertion position until the end
1041 maParaManager.Release(aFunctor.GetParaIndex(), nCurrParas);
1043 // TODO: Clarify whether this behaviour _really_ saves
1044 // anybody anything!
1045 // update children, _don't_ broadcast
1046 UpdateVisibleChildren( false );
1047 UpdateBoundRect();
1049 // send insert event
1050 // #109864# Enforce creation of this paragraph
1053 FireEvent(AccessibleEventId::CHILD, uno::Any(getAccessibleChild(aFunctor.GetParaIndex() -
1054 mnFirstVisibleChild + GetStartIndex())));
1056 catch( const uno::Exception& )
1058 OSL_FAIL("AccessibleTextHelper_Impl::ProcessQueue: could not create new paragraph");
1061 else if( aFunctor.GetHintId() == SfxHintId::TextParaRemoved )
1063 ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator begin = maParaManager.begin();
1064 ::std::advance( begin, aFunctor.GetParaIndex() );
1065 ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator end = begin;
1066 ::std::advance( end, 1 );
1068 // #i61812# remember para to be removed for later notification
1069 // AFTER the new state is applied (that after the para got removed)
1070 ::uno::Reference< XAccessible > xPara(begin->first.get());
1072 // release everything from the remove position until the end
1073 maParaManager.Release(aFunctor.GetParaIndex(), nCurrParas);
1075 // update num of paras
1076 maParaManager.SetNum( nNewParas );
1078 // TODO: Clarify whether this behaviour _really_ saves
1079 // anybody anything!
1080 // update children, _don't_ broadcast
1081 UpdateVisibleChildren( false );
1082 UpdateBoundRect();
1084 // #i61812# notification for removed para
1085 if (xPara.is())
1086 FireEvent(AccessibleEventId::CHILD, uno::Any(), uno::Any( xPara) );
1088 #ifdef DBG_UTIL
1089 else
1090 OSL_FAIL("AccessibleTextHelper_Impl::ProcessQueue() invalid hint id");
1091 #endif
1093 else if( nNewParas != nCurrParas )
1095 // release all paras
1096 maParaManager.Release(0, nCurrParas);
1098 // update num of paras
1099 maParaManager.SetNum( nNewParas );
1101 // #109864# create from scratch, don't broadcast
1102 UpdateVisibleChildren( false );
1103 UpdateBoundRect();
1105 // number of paragraphs somehow changed - but we have no
1106 // chance determining how. Thus, throw away everything and
1107 // create from scratch.
1108 // (child events should be broadcast after the changes are done...)
1109 FireEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN);
1111 // no need for further updates later on
1112 bEverythingUpdated = true;
1115 bool bUpdatedBoundRectAndVisibleChildren(false);
1117 while( !maEventQueue.IsEmpty() )
1119 ::std::unique_ptr< SfxHint > pHint( maEventQueue.PopFront() );
1120 if (pHint)
1122 const SfxHint& rHint = *pHint;
1124 // Note, if you add events here, you need to update the AccessibleTextEventQueue::Append
1125 // code, because only the events we process here, are actually queued there.
1130 if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint)
1132 const SdrHint* pSdrHint = static_cast< const SdrHint* >( &rHint );
1134 switch( pSdrHint->GetKind() )
1136 case SdrHintKind::BeginEdit:
1138 if(!IsActive())
1140 break;
1142 // change children state
1143 maParaManager.SetActive();
1145 // per definition, edit mode text has the focus
1146 SetFocus( true );
1147 break;
1150 case SdrHintKind::EndEdit:
1152 // focused child now loses focus
1153 ESelection aSelection;
1154 if( GetEditViewForwarder().GetSelection( aSelection ) )
1155 SetChildFocus(aSelection.end.nPara, false);
1157 // change children state
1158 maParaManager.SetActive( false );
1160 maLastSelection = ESelection::AtEnd();
1161 break;
1163 default:
1164 break;
1167 else if( const SvxEditSourceHint* pEditSourceHint = dynamic_cast<const SvxEditSourceHint*>( &rHint ) )
1169 switch( pEditSourceHint->GetId() )
1171 case SfxHintId::EditSourceParasMoved:
1173 DBG_ASSERT( pEditSourceHint->GetStartValue() < GetTextForwarder().GetParagraphCount() &&
1174 pEditSourceHint->GetEndValue() < GetTextForwarder().GetParagraphCount(),
1175 "AccessibleTextHelper_Impl::NotifyHdl: Invalid notification");
1177 if( !bEverythingUpdated )
1179 ParagraphsMoved(pEditSourceHint->GetStartValue(),
1180 pEditSourceHint->GetValue(),
1181 pEditSourceHint->GetEndValue());
1183 // in all cases, check visibility afterwards.
1184 UpdateVisibleChildren();
1186 break;
1189 case SfxHintId::EditSourceSelectionChanged:
1190 // notify listeners
1193 UpdateSelection();
1195 // maybe we're not in edit mode (this is not an error)
1196 catch( const uno::Exception& ) {}
1197 break;
1198 default: break;
1201 else if( const TextHint* pTextHint = dynamic_cast<const TextHint*>( &rHint ) )
1203 const sal_Int32 nParas = GetTextForwarder().GetParagraphCount();
1205 switch( pTextHint->GetId() )
1207 case SfxHintId::TextModified:
1209 // notify listeners
1210 sal_Int32 nPara( pTextHint->GetValue() );
1212 // #108900# Delegate change event to children
1213 AccessibleTextHelper_ChildrenTextChanged aNotifyChildrenFunctor;
1215 if (nPara == EE_PARA_MAX)
1217 // #108900# Call every child
1218 ::std::for_each( maParaManager.begin(), maParaManager.end(),
1219 AccessibleParaManager::WeakChildAdapter< AccessibleTextHelper_ChildrenTextChanged > (aNotifyChildrenFunctor) );
1221 else
1222 if( nPara < nParas )
1224 // #108900# Call child at index nPara
1225 ::std::for_each( maParaManager.begin()+nPara, maParaManager.begin()+nPara+1,
1226 AccessibleParaManager::WeakChildAdapter< AccessibleTextHelper_ChildrenTextChanged > (aNotifyChildrenFunctor) );
1228 break;
1231 case SfxHintId::TextParaInserted:
1232 // already happened above
1233 break;
1235 case SfxHintId::TextParaRemoved:
1236 // already happened above
1237 break;
1239 case SfxHintId::TextHeightChanged:
1240 // visibility changed, done below
1241 break;
1243 case SfxHintId::TextViewScrolled:
1244 // visibility changed, done below
1245 break;
1246 default: break;
1249 // in all cases, check visibility afterwards.
1250 if (!bUpdatedBoundRectAndVisibleChildren)
1252 UpdateVisibleChildren();
1253 UpdateBoundRect();
1254 bUpdatedBoundRectAndVisibleChildren = true;
1257 else if (rHint.GetId() == SfxHintId::SvxViewChanged)
1259 // just check visibility
1260 if (!bUpdatedBoundRectAndVisibleChildren)
1262 UpdateVisibleChildren();
1263 UpdateBoundRect();
1264 bUpdatedBoundRectAndVisibleChildren = true;
1267 // it's VITAL to keep the SfxSimpleHint last! It's the base of some classes above!
1268 else if( rHint.GetId() == SfxHintId::Dying)
1270 // edit source is dying under us, become defunc then
1273 // make edit source inaccessible
1274 // Note: cannot destroy it here, since we're called from there!
1275 ShutdownEditSource();
1277 catch( const uno::Exception& ) {}
1280 catch( const uno::Exception& )
1282 DBG_UNHANDLED_EXCEPTION("svx");
1288 void AccessibleTextHelper_Impl::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
1290 // precondition: solar mutex locked
1291 DBG_TESTSOLARMUTEX();
1293 // precondition: not in a recursion
1294 if( mbInNotify )
1295 return;
1297 mbInNotify = true;
1301 // Process notification event, arranged in order of likelihood of
1302 // occurrence to avoid unnecessary dynamic_cast. Note that
1303 // SvxEditSourceHint is derived from TextHint, so has to be checked
1304 // before that.
1305 if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint)
1307 const SdrHint* pSdrHint = static_cast< const SdrHint* >( &rHint );
1308 // process drawing layer events right away, if not
1309 // within an open EE notification frame. Otherwise,
1310 // event processing would be delayed until next EE
1311 // notification sequence.
1312 maEventQueue.Append( *pSdrHint );
1314 else if (rHint.GetId() == SfxHintId::SvxViewChanged)
1316 const SvxViewChangedHint* pViewHint = static_cast<const SvxViewChangedHint*>(&rHint);
1317 // process visibility right away, if not within an
1318 // open EE notification frame. Otherwise, event
1319 // processing would be delayed until next EE
1320 // notification sequence.
1321 maEventQueue.Append( *pViewHint );
1323 else if( const SvxEditSourceHint* pEditSourceHint = dynamic_cast<const SvxEditSourceHint*>( &rHint ) )
1325 // EditEngine should emit TEXT_SELECTION_CHANGED events (#i27299#)
1326 maEventQueue.Append( *pEditSourceHint );
1328 else if( const TextHint* pTextHint = dynamic_cast<const TextHint*>( &rHint ) )
1330 // EditEngine should emit TEXT_SELECTION_CHANGED events (#i27299#)
1331 if(pTextHint->GetId() == SfxHintId::TextProcessNotifications)
1332 ProcessQueue();
1333 else
1334 maEventQueue.Append( *pTextHint );
1336 // it's VITAL to keep the SfxHint last! It's the base of the classes above!
1337 else if( rHint.GetId() == SfxHintId::Dying )
1339 // handle this event _at once_, because after that, objects are invalid
1340 // edit source is dying under us, become defunc then
1341 maEventQueue.Clear();
1344 // make edit source inaccessible
1345 // Note: cannot destroy it here, since we're called from there!
1346 ShutdownEditSource();
1348 catch( const uno::Exception& ) {}
1351 catch( const uno::Exception& )
1353 DBG_UNHANDLED_EXCEPTION("svx");
1354 mbInNotify = false;
1357 mbInNotify = false;
1360 void AccessibleTextHelper_Impl::Dispose()
1362 if( getNotifierClientId() != snNotifierClientRevoked)
1366 // #106234# Unregister from EventNotifier
1367 ::comphelper::AccessibleEventNotifier::revokeClient( getNotifierClientId() );
1368 SAL_INFO("svx", "disposed ID: " << mnNotifierClientId );
1370 catch( const uno::Exception& ) {}
1372 mnNotifierClientId = snNotifierClientRevoked;
1377 // dispose children
1378 maParaManager.Dispose();
1380 catch( const uno::Exception& ) {}
1382 // quit listen on stale edit source
1383 if( maEditSource.IsValid() )
1384 EndListening( maEditSource.GetBroadcaster() );
1386 // clear references
1387 maEditSource.SetEditSource( ::std::unique_ptr< SvxEditSource >() );
1388 mxFrontEnd = nullptr;
1391 void AccessibleTextHelper_Impl::FireEvent( const sal_Int16 nEventId, const uno::Any& rNewValue, const uno::Any& rOldValue ) const
1393 // -- object locked --
1394 AccessibleEventObject aEvent;
1396 std::scoped_lock aGuard(maMutex);
1398 DBG_ASSERT(mxFrontEnd.is(), "AccessibleTextHelper::FireEvent: no event source set");
1400 if (mxFrontEnd.is())
1401 aEvent = AccessibleEventObject(mxFrontEnd->getAccessibleContext(), nEventId,
1402 rNewValue, rOldValue, -1);
1403 else
1404 aEvent = AccessibleEventObject(uno::Reference<uno::XInterface>(), nEventId,
1405 rNewValue, rOldValue, -1);
1407 // no locking necessary, FireEvent internally copies listeners
1408 // if someone removes/adds in between Further locking,
1409 // actually, might lead to deadlocks, since we're calling out
1410 // of this object
1412 // -- until here --
1414 FireEvent(aEvent);
1417 void AccessibleTextHelper_Impl::FireEvent( const AccessibleEventObject& rEvent ) const
1419 // #106234# Delegate to EventNotifier
1420 if (getNotifierClientId() != snNotifierClientRevoked)
1421 ::comphelper::AccessibleEventNotifier::addEvent( getNotifierClientId(), rEvent );
1424 // XAccessibleContext
1425 sal_Int64 AccessibleTextHelper_Impl::getAccessibleChildCount() const
1427 return mnLastVisibleChild - mnFirstVisibleChild + 1;
1430 uno::Reference< XAccessible > AccessibleTextHelper_Impl::getAccessibleChild( sal_Int64 i )
1432 i -= GetStartIndex();
1434 if( 0 > i || i >= getAccessibleChildCount() ||
1435 GetTextForwarder().GetParagraphCount() <= i )
1437 throw lang::IndexOutOfBoundsException(u"Invalid child index"_ustr, mxFrontEnd);
1440 DBG_ASSERT(mxFrontEnd.is(), "AccessibleTextHelper_Impl::UpdateVisibleChildren: no frontend set");
1442 if( mxFrontEnd.is() )
1443 return maParaManager.CreateChild( i, mxFrontEnd, GetEditSource(), mnFirstVisibleChild + i ).first;
1444 else
1445 return nullptr;
1448 void AccessibleTextHelper_Impl::addAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener )
1450 if( getNotifierClientId() != snNotifierClientRevoked )
1451 ::comphelper::AccessibleEventNotifier::addEventListener( getNotifierClientId(), xListener );
1454 void AccessibleTextHelper_Impl::removeAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener )
1456 if( getNotifierClientId() == snNotifierClientRevoked )
1457 return;
1459 const sal_Int32 nListenerCount = ::comphelper::AccessibleEventNotifier::removeEventListener( getNotifierClientId(), xListener );
1460 if ( !nListenerCount )
1462 // no listeners anymore
1463 // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client),
1464 // and at least to us not firing any events anymore, in case somebody calls
1465 // NotifyAccessibleEvent, again
1466 ::comphelper::AccessibleEventNotifier::TClientId nId( getNotifierClientId() );
1467 mnNotifierClientId = snNotifierClientRevoked;
1468 ::comphelper::AccessibleEventNotifier::revokeClient( nId );
1472 uno::Reference< XAccessible > AccessibleTextHelper_Impl::getAccessibleAtPoint( const awt::Point& _aPoint )
1474 // make given position relative
1475 if( !mxFrontEnd.is() )
1476 throw uno::RuntimeException(u"AccessibleTextHelper_Impl::getAccessibleAt: frontend invalid"_ustr, mxFrontEnd );
1478 uno::Reference< XAccessibleContext > xFrontEndContext = mxFrontEnd->getAccessibleContext();
1480 if( !xFrontEndContext.is() )
1481 throw uno::RuntimeException(u"AccessibleTextHelper_Impl::getAccessibleAt: frontend invalid"_ustr, mxFrontEnd );
1483 uno::Reference< XAccessibleComponent > xFrontEndComponent( xFrontEndContext, uno::UNO_QUERY_THROW );
1485 // #103862# No longer need to make given position relative
1486 Point aPoint( _aPoint.X, _aPoint.Y );
1488 // respect EditEngine offset to surrounding shape/cell
1489 aPoint -= GetOffset();
1491 // convert to EditEngine coordinate system
1492 SvxTextForwarder& rCacheTF = GetTextForwarder();
1493 Point aLogPoint( GetViewForwarder().PixelToLogic( aPoint, rCacheTF.GetMapMode() ) );
1495 // iterate over all visible children (including those not yet created)
1496 sal_Int64 nChild;
1497 for( nChild=mnFirstVisibleChild; nChild <= mnLastVisibleChild; ++nChild )
1499 DBG_ASSERT(nChild >= 0,
1500 "AccessibleTextHelper_Impl::getAccessibleAt: index value overflow");
1502 tools::Rectangle aParaBounds( rCacheTF.GetParaBounds( nChild ) );
1504 if( aParaBounds.Contains( aLogPoint ) )
1505 return getAccessibleChild( nChild - mnFirstVisibleChild + GetStartIndex() );
1508 // found none
1509 return nullptr;
1513 // AccessibleTextHelper implementation (simply forwards to impl)
1515 AccessibleTextHelper::AccessibleTextHelper( ::std::unique_ptr< SvxEditSource > && pEditSource ) :
1516 mpImpl( new AccessibleTextHelper_Impl() )
1518 SolarMutexGuard aGuard;
1520 SetEditSource( std::move(pEditSource) );
1523 AccessibleTextHelper::~AccessibleTextHelper()
1527 const SvxEditSource& AccessibleTextHelper::GetEditSource() const
1529 #ifdef DBG_UTIL
1530 mpImpl->CheckInvariants();
1532 const SvxEditSource& aEditSource = mpImpl->GetEditSource();
1534 mpImpl->CheckInvariants();
1536 return aEditSource;
1537 #else
1538 return mpImpl->GetEditSource();
1539 #endif
1542 void AccessibleTextHelper::SetEditSource( ::std::unique_ptr< SvxEditSource > && pEditSource )
1544 #ifdef DBG_UTIL
1545 // precondition: solar mutex locked
1546 DBG_TESTSOLARMUTEX();
1548 mpImpl->CheckInvariants();
1549 #endif
1551 mpImpl->SetEditSource( std::move(pEditSource) );
1553 #ifdef DBG_UTIL
1554 mpImpl->CheckInvariants();
1555 #endif
1558 void AccessibleTextHelper::SetEventSource( const uno::Reference< XAccessible >& rInterface )
1560 #ifdef DBG_UTIL
1561 mpImpl->CheckInvariants();
1562 #endif
1564 mpImpl->SetEventSource( rInterface );
1566 #ifdef DBG_UTIL
1567 mpImpl->CheckInvariants();
1568 #endif
1571 void AccessibleTextHelper::SetFocus( bool bHaveFocus )
1573 #ifdef DBG_UTIL
1574 // precondition: solar mutex locked
1575 DBG_TESTSOLARMUTEX();
1577 mpImpl->CheckInvariants();
1578 #endif
1580 mpImpl->SetFocus( bHaveFocus );
1582 #ifdef DBG_UTIL
1583 mpImpl->CheckInvariants();
1584 #endif
1587 bool AccessibleTextHelper::HaveFocus()
1589 #ifdef DBG_UTIL
1590 mpImpl->CheckInvariants();
1592 bool bRet( mpImpl->HaveFocus() );
1594 mpImpl->CheckInvariants();
1596 return bRet;
1597 #else
1598 return mpImpl->HaveFocus();
1599 #endif
1602 void AccessibleTextHelper::SetOffset( const Point& rPoint )
1604 #ifdef DBG_UTIL
1605 // precondition: solar mutex locked
1606 DBG_TESTSOLARMUTEX();
1608 mpImpl->CheckInvariants();
1609 #endif
1611 mpImpl->SetOffset( rPoint );
1613 #ifdef DBG_UTIL
1614 mpImpl->CheckInvariants();
1615 #endif
1618 void AccessibleTextHelper::SetStartIndex( sal_Int32 nOffset )
1620 #ifdef DBG_UTIL
1621 // precondition: solar mutex locked
1622 DBG_TESTSOLARMUTEX();
1624 mpImpl->CheckInvariants();
1625 #endif
1627 mpImpl->SetStartIndex( nOffset );
1629 #ifdef DBG_UTIL
1630 mpImpl->CheckInvariants();
1631 #endif
1634 sal_Int32 AccessibleTextHelper::GetStartIndex() const
1636 #ifdef DBG_UTIL
1637 mpImpl->CheckInvariants();
1639 sal_Int32 nOffset = mpImpl->GetStartIndex();
1641 mpImpl->CheckInvariants();
1643 return nOffset;
1644 #else
1645 return mpImpl->GetStartIndex();
1646 #endif
1649 void AccessibleTextHelper::SetAdditionalChildStates( sal_Int64 nChildStates )
1651 mpImpl->SetAdditionalChildStates( nChildStates );
1654 void AccessibleTextHelper::UpdateChildren()
1656 #ifdef DBG_UTIL
1657 // precondition: solar mutex locked
1658 DBG_TESTSOLARMUTEX();
1660 mpImpl->CheckInvariants();
1661 #endif
1663 mpImpl->UpdateVisibleChildren();
1664 mpImpl->UpdateBoundRect();
1666 mpImpl->UpdateSelection();
1668 #ifdef DBG_UTIL
1669 mpImpl->CheckInvariants();
1670 #endif
1673 void AccessibleTextHelper::UpdateSelection()
1675 #ifdef DBG_UTIL
1676 // precondition: solar mutex locked
1677 DBG_TESTSOLARMUTEX();
1678 mpImpl->CheckInvariants();
1679 #endif
1681 mpImpl->UpdateSelection();
1683 #ifdef DBG_UTIL
1684 mpImpl->CheckInvariants();
1685 #endif
1688 void AccessibleTextHelper::Dispose()
1690 // As Dispose calls ShutdownEditSource, which in turn
1691 // deregisters as listener on the edit source, have to lock
1692 // here
1693 SolarMutexGuard aGuard;
1695 #ifdef DBG_UTIL
1696 mpImpl->CheckInvariants();
1697 #endif
1699 mpImpl->Dispose();
1701 #ifdef DBG_UTIL
1702 mpImpl->CheckInvariants();
1703 #endif
1706 // XAccessibleContext
1707 sal_Int64 AccessibleTextHelper::GetChildCount() const
1709 SolarMutexGuard aGuard;
1711 #ifdef DBG_UTIL
1712 mpImpl->CheckInvariants();
1714 sal_Int64 nRet = mpImpl->getAccessibleChildCount();
1716 mpImpl->CheckInvariants();
1718 return nRet;
1719 #else
1720 return mpImpl->getAccessibleChildCount();
1721 #endif
1724 uno::Reference< XAccessible > AccessibleTextHelper::GetChild( sal_Int64 i )
1726 SolarMutexGuard aGuard;
1728 #ifdef DBG_UTIL
1729 mpImpl->CheckInvariants();
1731 uno::Reference< XAccessible > xRet = mpImpl->getAccessibleChild( i );
1733 mpImpl->CheckInvariants();
1735 return xRet;
1736 #else
1737 return mpImpl->getAccessibleChild( i );
1738 #endif
1741 void AccessibleTextHelper::AddEventListener( const uno::Reference< XAccessibleEventListener >& xListener )
1743 #ifdef DBG_UTIL
1744 mpImpl->CheckInvariants();
1746 mpImpl->addAccessibleEventListener( xListener );
1748 mpImpl->CheckInvariants();
1749 #else
1750 mpImpl->addAccessibleEventListener( xListener );
1751 #endif
1754 void AccessibleTextHelper::RemoveEventListener( const uno::Reference< XAccessibleEventListener >& xListener )
1756 #ifdef DBG_UTIL
1757 mpImpl->CheckInvariants();
1759 mpImpl->removeAccessibleEventListener( xListener );
1761 mpImpl->CheckInvariants();
1762 #else
1763 mpImpl->removeAccessibleEventListener( xListener );
1764 #endif
1767 // XAccessibleComponent
1768 uno::Reference< XAccessible > AccessibleTextHelper::GetAt( const awt::Point& aPoint )
1770 SolarMutexGuard aGuard;
1772 #ifdef DBG_UTIL
1773 mpImpl->CheckInvariants();
1775 uno::Reference< XAccessible > xChild = mpImpl->getAccessibleAtPoint( aPoint );
1777 mpImpl->CheckInvariants();
1779 return xChild;
1780 #else
1781 return mpImpl->getAccessibleAtPoint( aPoint );
1782 #endif
1785 } // end of namespace accessibility
1788 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */