bump product version to 5.0.4.1
[LibreOffice.git] / svx / source / accessibility / AccessibleTextHelper.cxx
blob9a8c240e13d76ee9db45cf2321cae0c2e626cb51
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
23 // Global header
27 #include <limits.h>
28 #include <memory>
29 #include <utility>
30 #include <algorithm>
31 #include <deque>
32 #include <osl/mutex.hxx>
33 #include <com/sun/star/uno/Any.hxx>
34 #include <com/sun/star/uno/Reference.hxx>
35 #include <cppuhelper/weakref.hxx>
36 #include <com/sun/star/awt/Point.hpp>
37 #include <com/sun/star/awt/Rectangle.hpp>
38 #include <com/sun/star/lang/DisposedException.hpp>
39 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
40 #include <com/sun/star/accessibility/XAccessible.hpp>
41 #include <com/sun/star/accessibility/XAccessibleContext.hpp>
42 #include <com/sun/star/accessibility/XAccessibleComponent.hpp>
43 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
44 #include <comphelper/accessibleeventnotifier.hxx>
45 #include <unotools/accessiblestatesethelper.hxx>
46 #include <vcl/svapp.hxx>
47 #include <vcl/textdata.hxx>
48 #include <vcl/unohelp.hxx>
49 #include <sfx2/viewfrm.hxx>
50 #include <sfx2/viewsh.hxx>
54 // Project-local header
57 #include "AccessibleTextEventQueue.hxx"
58 #include <svx/AccessibleTextHelper.hxx>
59 #include <svx/unoshape.hxx>
60 #include "editeng/unolingu.hxx"
61 #include <editeng/unotext.hxx>
63 #include "editeng/unoedhlp.hxx"
64 #include "editeng/unopracc.hxx"
65 #include "editeng/AccessibleParaManager.hxx"
66 #include "editeng/AccessibleEditableTextPara.hxx"
67 #include <svx/svdmodel.hxx>
68 #include <svx/svdpntv.hxx>
69 #include "../table/cell.hxx"
70 #include "../table/accessiblecell.hxx"
71 #include <editeng/editdata.hxx>
72 #include <editeng/editeng.hxx>
73 #include <editeng/editview.hxx>
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 ::std::pair< first_type, second_type > makeSortedPair( first_type first,
85 second_type second )
87 if( first > second )
88 return ::std::make_pair( second, first );
89 else
90 return ::std::make_pair( first, second );
93 class AccessibleTextHelper_Impl : public SfxListener
95 public:
96 typedef ::std::vector< sal_Int16 > VectorOfStates;
98 // receive pointer to our frontend class and view window
99 AccessibleTextHelper_Impl();
100 virtual ~AccessibleTextHelper_Impl();
102 // XAccessibleContext child handling methods
103 sal_Int32 SAL_CALL getAccessibleChildCount();
104 uno::Reference< XAccessible > SAL_CALL getAccessibleChild( sal_Int32 i );
106 // XAccessibleEventBroadcaster child related methods
107 void SAL_CALL addAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener );
108 void SAL_CALL removeAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener );
110 // XAccessibleComponent child related methods
111 uno::Reference< XAccessible > SAL_CALL 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 );
126 return aPoint;
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
134 // atomic
135 return mnStartIndex;
138 void SetAdditionalChildStates( const VectorOfStates& rChildStates );
140 void Dispose();
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 );
147 bool HaveFocus()
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 );
157 #ifdef DBG_UTIL
158 void CheckInvariants() const;
159 #endif
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
168 // cache.
169 void UpdateSelection();
171 private:
173 // Process event queue
174 void ProcessQueue();
176 // syntactic sugar for FireEvent
177 void GotPropertyEvent( const uno::Any& rNewValue, const sal_Int16 nEventId ) const { FireEvent( nEventId, rNewValue ); }
178 void LostPropertyEvent( const uno::Any& rOldValue, const sal_Int16 nEventId ) const { FireEvent( nEventId, uno::Any(), rOldValue ); }
180 // shutdown usage of current edit source on myself and the children.
181 void ShutdownEditSource();
183 void ParagraphsMoved( sal_Int32 nFirst, sal_Int32 nMiddle, sal_Int32 nLast );
185 virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) SAL_OVERRIDE;
187 int getNotifierClientId() const { return mnNotifierClientId; }
189 // lock solar mutex before
190 SvxTextForwarder& GetTextForwarder() const;
191 // lock solar mutex before
192 SvxViewForwarder& GetViewForwarder() const;
193 // lock solar mutex before
194 SvxEditViewForwarder& GetEditViewForwarder( bool bCreate = false ) const;
196 // are we in edit mode?
197 bool IsActive() const;
199 // our frontend class (the one implementing the actual
200 // interface). That's not necessarily the one containing the impl
201 // pointer!
202 uno::Reference< XAccessible > mxFrontEnd;
204 // a wrapper for the text forwarders (guarded by solar mutex)
205 mutable SvxEditSourceAdapter maEditSource;
207 // store last selection (to correctly report selection changes, guarded by solar mutex)
208 ESelection maLastSelection;
210 // cache range of visible children (guarded by solar mutex)
211 sal_Int32 mnFirstVisibleChild;
212 sal_Int32 mnLastVisibleChild;
214 // offset to add to all our children (unguarded, relying on
215 // the fact that sal_Int32 access is atomic)
216 sal_Int32 mnStartIndex;
218 // the object handling our children (guarded by solar mutex)
219 ::accessibility::AccessibleParaManager maParaManager;
221 // number of not-yet-closed event frames (BEGIN/END sequences) (guarded by solar mutex)
222 sal_Int32 maEventOpenFrames;
224 // Queued events from Notify() (guarded by solar mutex)
225 AccessibleTextEventQueue maEventQueue;
227 // spin lock to prevent notify in notify (guarded by solar mutex)
228 bool mbInNotify;
230 // whether the object or its children has the focus set (guarded by solar mutex)
231 bool mbGroupHasFocus;
233 // whether we (this object) has the focus set (guarded by solar mutex)
234 bool mbThisHasFocus;
236 mutable ::osl::Mutex maMutex;
238 /// our current offset to the containing shape/cell (guarded by maMutex)
239 Point maOffset;
241 /// client Id from AccessibleEventNotifier
242 int mnNotifierClientId;
245 AccessibleTextHelper_Impl::AccessibleTextHelper_Impl() :
246 mxFrontEnd( NULL ),
247 maLastSelection( EE_PARA_NOT_FOUND,EE_INDEX_NOT_FOUND,EE_PARA_NOT_FOUND,EE_INDEX_NOT_FOUND ),
248 mnFirstVisibleChild( -1 ),
249 mnLastVisibleChild( -2 ),
250 mnStartIndex( 0 ),
251 maEventOpenFrames( 0 ),
252 mbInNotify( false ),
253 mbGroupHasFocus( false ),
254 mbThisHasFocus( false ),
255 maOffset(0,0),
256 // well, that's strictly exception safe, though not really
257 // robust. We rely on the fact that this member is constructed
258 // last, and that the constructor body is empty, thus no
259 // chance for exceptions once the Id is fetched. Nevertheless,
260 // normally should employ RAII here...
261 mnNotifierClientId(::comphelper::AccessibleEventNotifier::registerClient())
263 #ifdef DBG_UTIL
264 OSL_TRACE( "AccessibleTextHelper_Impl received ID: %d", mnNotifierClientId );
265 #endif
268 AccessibleTextHelper_Impl::~AccessibleTextHelper_Impl()
270 SolarMutexGuard aGuard;
274 // call Dispose here, too, since we've some resources not
275 // automatically freed otherwise
276 Dispose();
278 catch( const uno::Exception& ) {}
281 SvxTextForwarder& AccessibleTextHelper_Impl::GetTextForwarder() const
283 if( !maEditSource.IsValid() )
284 throw uno::RuntimeException("Unknown edit source", mxFrontEnd);
286 SvxTextForwarder* pTextForwarder = maEditSource.GetTextForwarder();
288 if( !pTextForwarder )
289 throw uno::RuntimeException("Unable to fetch text forwarder, model might be dead", mxFrontEnd);
291 if( pTextForwarder->IsValid() )
292 return *pTextForwarder;
293 else
294 throw uno::RuntimeException("Text forwarder is invalid, model might be dead", mxFrontEnd);
297 SvxViewForwarder& AccessibleTextHelper_Impl::GetViewForwarder() const
299 if( !maEditSource.IsValid() )
300 throw uno::RuntimeException("Unknown edit source", mxFrontEnd);
302 SvxViewForwarder* pViewForwarder = maEditSource.GetViewForwarder();
304 if( !pViewForwarder )
305 throw uno::RuntimeException("Unable to fetch view forwarder, model might be dead", mxFrontEnd);
307 if( pViewForwarder->IsValid() )
308 return *pViewForwarder;
309 else
310 throw uno::RuntimeException("View forwarder is invalid, model might be dead", mxFrontEnd);
313 SvxEditViewForwarder& AccessibleTextHelper_Impl::GetEditViewForwarder( bool bCreate ) const
315 if( !maEditSource.IsValid() )
316 throw uno::RuntimeException("Unknown edit source", mxFrontEnd);
318 SvxEditViewForwarder* pViewForwarder = maEditSource.GetEditViewForwarder( bCreate );
320 if( !pViewForwarder )
322 if( bCreate )
323 throw uno::RuntimeException("Unable to fetch edit view forwarder, model might be dead", mxFrontEnd);
324 else
325 throw uno::RuntimeException("No edit view forwarder, object not in edit mode", mxFrontEnd);
328 if( pViewForwarder->IsValid() )
329 return *pViewForwarder;
330 else
332 if( bCreate )
333 throw uno::RuntimeException("View forwarder is invalid, model might be dead", mxFrontEnd);
334 else
335 throw uno::RuntimeException("View forwarder is invalid, object not in edit mode", mxFrontEnd);
339 SvxEditSourceAdapter& AccessibleTextHelper_Impl::GetEditSource() const
341 if( maEditSource.IsValid() )
342 return maEditSource;
343 else
344 throw uno::RuntimeException("AccessibleTextHelper_Impl::GetEditSource: no edit source", mxFrontEnd );
347 // functor for sending child events (no stand-alone function, they are maybe not inlined)
348 class AccessibleTextHelper_OffsetChildIndex : public ::std::unary_function< ::accessibility::AccessibleEditableTextPara&, void >
350 public:
351 AccessibleTextHelper_OffsetChildIndex( sal_Int32 nDifference ) : mnDifference(nDifference) {}
352 void operator()( ::accessibility::AccessibleEditableTextPara& rPara )
354 rPara.SetIndexInParent( rPara.GetIndexInParent() + mnDifference );
357 private:
358 const sal_Int32 mnDifference;
361 void AccessibleTextHelper_Impl::SetStartIndex( sal_Int32 nOffset )
363 sal_Int32 nOldOffset( mnStartIndex );
365 mnStartIndex = nOffset;
367 if( nOldOffset != nOffset )
369 // update children
370 AccessibleTextHelper_OffsetChildIndex aFunctor( nOffset - nOldOffset );
372 ::std::for_each( maParaManager.begin(), maParaManager.end(),
373 AccessibleParaManager::WeakChildAdapter< AccessibleTextHelper_OffsetChildIndex > (aFunctor) );
377 void AccessibleTextHelper_Impl::SetAdditionalChildStates( const VectorOfStates& rChildStates )
379 maParaManager.SetAdditionalChildStates( rChildStates );
382 void AccessibleTextHelper_Impl::SetChildFocus( sal_Int32 nChild, bool bHaveFocus )
384 if( bHaveFocus )
386 if( mbThisHasFocus )
387 SetShapeFocus( false );
389 maParaManager.SetFocus( nChild );
391 // we just received the focus, also send caret event then
392 UpdateSelection();
394 SAL_INFO("svx", "Paragraph " << nChild << " received focus");
396 else
398 maParaManager.SetFocus( -1 );
400 SAL_INFO("svx", "Paragraph " << nChild << " lost focus");
402 if( mbGroupHasFocus )
403 SetShapeFocus( true );
407 void AccessibleTextHelper_Impl::ChangeChildFocus( sal_Int32 nNewChild )
409 if( mbThisHasFocus )
410 SetShapeFocus( false );
412 mbGroupHasFocus = true;
413 maParaManager.SetFocus( nNewChild );
415 SAL_INFO("svx", "Paragraph " << nNewChild << " received focus");
418 void AccessibleTextHelper_Impl::SetShapeFocus( bool bHaveFocus )
420 bool bOldFocus( mbThisHasFocus );
422 mbThisHasFocus = bHaveFocus;
424 if( bOldFocus != bHaveFocus )
426 if( bHaveFocus )
428 if( mxFrontEnd.is() )
430 AccessibleCell* pAccessibleCell = dynamic_cast< AccessibleCell* > ( mxFrontEnd.get() );
431 if ( !pAccessibleCell )
432 GotPropertyEvent( uno::makeAny(AccessibleStateType::FOCUSED), AccessibleEventId::STATE_CHANGED );
433 else // the focus event on cell should be fired on table directly
435 AccessibleTableShape* pAccTable = pAccessibleCell->GetParentTable();
436 if (pAccTable)
437 pAccTable->SetStateDirectly(AccessibleStateType::FOCUSED);
440 OSL_TRACE("AccessibleTextHelper_Impl::SetShapeFocus(): Parent object received focus" );
442 else
444 // The focus state should be reset directly on table.
445 //LostPropertyEvent( uno::makeAny(AccessibleStateType::FOCUSED), AccessibleEventId::STATE_CHANGED );
446 if( mxFrontEnd.is() )
448 AccessibleCell* pAccessibleCell = dynamic_cast< AccessibleCell* > ( mxFrontEnd.get() );
449 if ( !pAccessibleCell )
450 LostPropertyEvent( uno::makeAny(AccessibleStateType::FOCUSED), AccessibleEventId::STATE_CHANGED );
451 else
453 AccessibleTableShape* pAccTable = pAccessibleCell->GetParentTable();
454 if (pAccTable)
455 pAccTable->ResetStateDirectly(AccessibleStateType::FOCUSED);
458 OSL_TRACE("AccessibleTextHelper_Impl::SetShapeFocus(): Parent object lost focus" );
463 void AccessibleTextHelper_Impl::SetFocus( bool bHaveFocus )
465 bool bOldFocus( mbGroupHasFocus );
467 mbGroupHasFocus = bHaveFocus;
469 if( IsActive() )
473 // find the one with the cursor and get/set focus accordingly
474 ESelection aSelection;
475 if( GetEditViewForwarder().GetSelection( aSelection ) )
476 SetChildFocus( aSelection.nEndPara, bHaveFocus );
478 catch( const uno::Exception& ) {}
480 else if( bOldFocus != bHaveFocus )
482 SetShapeFocus( bHaveFocus );
485 OSL_TRACE("AccessibleTextHelper_Impl::SetFocus: focus changed, Object %p, state: %s", this, bHaveFocus ? "focused" : "not focused");
488 bool AccessibleTextHelper_Impl::IsActive() const
492 SvxEditSource& rEditSource = GetEditSource();
493 SvxEditViewForwarder* pViewForwarder = rEditSource.GetEditViewForwarder();
495 if( !pViewForwarder )
496 return false;
498 if( mxFrontEnd.is() )
500 AccessibleCell* pAccessibleCell = dynamic_cast< AccessibleCell* > ( mxFrontEnd.get() );
501 if ( pAccessibleCell )
503 sdr::table::CellRef xCell = pAccessibleCell->getCellRef();
504 if ( xCell.is() )
505 return xCell->IsTextEditActive();
508 if( pViewForwarder->IsValid() )
509 return true;
510 else
511 return false;
513 catch( const uno::RuntimeException& )
515 return false;
519 void AccessibleTextHelper_Impl::UpdateSelection()
523 ESelection aSelection;
524 if( GetEditViewForwarder().GetSelection( aSelection ) )
526 if( !maLastSelection.IsEqual( aSelection ) &&
527 aSelection.nEndPara < maParaManager.GetNum() )
529 // #103998# Not that important, changed from assertion to trace
530 if( mbThisHasFocus )
532 OSL_TRACE("AccessibleTextHelper_Impl::UpdateSelection(): Parent has focus!");
535 sal_Int32 nMaxValidParaIndex( GetTextForwarder().GetParagraphCount() - 1 );
537 // notify all affected paragraphs (TODO: may be suboptimal,
538 // since some paragraphs might stay selected)
539 if( maLastSelection.nStartPara != EE_PARA_NOT_FOUND )
541 // Did the caret move from one paragraph to another?
542 // #100530# no caret events if not focused.
543 if( mbGroupHasFocus &&
544 maLastSelection.nEndPara != aSelection.nEndPara )
546 if( maLastSelection.nEndPara < maParaManager.GetNum() )
548 maParaManager.FireEvent( ::std::min( maLastSelection.nEndPara, nMaxValidParaIndex ),
549 ::std::min( maLastSelection.nEndPara, nMaxValidParaIndex )+1,
550 AccessibleEventId::CARET_CHANGED,
551 uno::makeAny(static_cast<sal_Int32>(-1)),
552 uno::makeAny(static_cast<sal_Int32>(maLastSelection.nEndPos)) );
555 ChangeChildFocus( aSelection.nEndPara );
557 SAL_INFO(
558 "svx",
559 "focus changed, Object: " << this
560 << ", Paragraph: " << aSelection.nEndPara
561 << ", Last paragraph: "
562 << maLastSelection.nEndPara);
566 // #100530# no caret events if not focused.
567 if( mbGroupHasFocus )
569 uno::Any aOldCursor;
571 // #i13705# The old cursor can only contain valid
572 // values if it's the same paragraph!
573 if( maLastSelection.nStartPara != EE_PARA_NOT_FOUND &&
574 maLastSelection.nEndPara == aSelection.nEndPara )
576 aOldCursor <<= static_cast<sal_Int32>(maLastSelection.nEndPos);
578 else
580 aOldCursor <<= static_cast<sal_Int32>(-1);
583 maParaManager.FireEvent( aSelection.nEndPara,
584 aSelection.nEndPara+1,
585 AccessibleEventId::CARET_CHANGED,
586 uno::makeAny(static_cast<sal_Int32>(aSelection.nEndPos)),
587 aOldCursor );
590 SAL_INFO(
591 "svx",
592 "caret changed, Object: " << this << ", New pos: "
593 << aSelection.nEndPos << ", Old pos: "
594 << maLastSelection.nEndPos << ", New para: "
595 << aSelection.nEndPara << ", Old para: "
596 << maLastSelection.nEndPara);
598 // #108947# Sort new range before calling FireEvent
599 ::std::pair<sal_Int32, sal_Int32> sortedSelection(
600 makeSortedPair(::std::min( aSelection.nStartPara, nMaxValidParaIndex ),
601 ::std::min( aSelection.nEndPara, nMaxValidParaIndex ) ) );
603 // #108947# Sort last range before calling FireEvent
604 ::std::pair<sal_Int32, sal_Int32> sortedLastSelection(
605 makeSortedPair(::std::min( maLastSelection.nStartPara, nMaxValidParaIndex ),
606 ::std::min( maLastSelection.nEndPara, nMaxValidParaIndex ) ) );
608 // event TEXT_SELECTION_CHANGED has to be submitted. (#i27299#)
609 const sal_Int16 nTextSelChgEventId =
610 AccessibleEventId::TEXT_SELECTION_CHANGED;
611 // #107037# notify selection change
612 if( maLastSelection.nStartPara == EE_PARA_NOT_FOUND )
614 // last selection is undefined
615 // use method <ESelection::HasRange()> (#i27299#)
616 if ( aSelection.HasRange() )
618 // selection was undefined, now is on
619 maParaManager.FireEvent( sortedSelection.first,
620 sortedSelection.second+1,
621 nTextSelChgEventId );
624 else
626 // last selection is valid
627 // use method <ESelection::HasRange()> (#i27299#)
628 if ( maLastSelection.HasRange() &&
629 !aSelection.HasRange() )
631 // selection was on, now is empty
632 maParaManager.FireEvent( sortedLastSelection.first,
633 sortedLastSelection.second+1,
634 nTextSelChgEventId );
636 // use method <ESelection::HasRange()> (#i27299#)
637 else if( !maLastSelection.HasRange() &&
638 aSelection.HasRange() )
640 // selection was empty, now is on
641 maParaManager.FireEvent( sortedSelection.first,
642 sortedSelection.second+1,
643 nTextSelChgEventId );
645 // no event TEXT_SELECTION_CHANGED event, if new and
646 // last selection are empty. (#i27299#)
647 else if ( maLastSelection.HasRange() &&
648 aSelection.HasRange() )
650 // use sorted last and new selection
651 ESelection aTmpLastSel( maLastSelection );
652 aTmpLastSel.Adjust();
653 ESelection aTmpSel( aSelection );
654 aTmpSel.Adjust();
655 // first submit event for new and changed selection
656 sal_Int32 nPara = aTmpSel.nStartPara;
657 for ( ; nPara <= aTmpSel.nEndPara; ++nPara )
659 if ( nPara < aTmpLastSel.nStartPara ||
660 nPara > aTmpLastSel.nEndPara )
662 // new selection on paragraph <nPara>
663 maParaManager.FireEvent( nPara,
664 nTextSelChgEventId );
666 else
668 // check for changed selection on paragraph <nPara>
669 const sal_Int32 nParaStartPos =
670 nPara == aTmpSel.nStartPara
671 ? aTmpSel.nStartPos : 0;
672 const sal_Int32 nParaEndPos =
673 nPara == aTmpSel.nEndPara
674 ? aTmpSel.nEndPos : -1;
675 const sal_Int32 nLastParaStartPos =
676 nPara == aTmpLastSel.nStartPara
677 ? aTmpLastSel.nStartPos : 0;
678 const sal_Int32 nLastParaEndPos =
679 nPara == aTmpLastSel.nEndPara
680 ? aTmpLastSel.nEndPos : -1;
681 if ( nParaStartPos != nLastParaStartPos ||
682 nParaEndPos != nLastParaEndPos )
684 maParaManager.FireEvent(
685 nPara, nTextSelChgEventId );
689 // second submit event for 'old' selections
690 nPara = aTmpLastSel.nStartPara;
691 for ( ; nPara <= aTmpLastSel.nEndPara; ++nPara )
693 if ( nPara < aTmpSel.nStartPara ||
694 nPara > aTmpSel.nEndPara )
696 maParaManager.FireEvent( nPara,
697 nTextSelChgEventId );
703 maLastSelection = aSelection;
707 // no selection? no update actions
708 catch( const uno::RuntimeException& ) {}
711 void AccessibleTextHelper_Impl::ShutdownEditSource()
713 // This should only be called with solar mutex locked, i.e. from the main office thread
715 // This here is somewhat clumsy: As soon as our children have
716 // a NULL EditSource (maParaManager.SetEditSource()), they
717 // enter the disposed state and cannot be reanimated. Thus, it
718 // is unavoidable and a hard requirement to let go and create
719 // from scratch each and every child.
721 // invalidate children
722 maParaManager.Dispose();
723 maParaManager.SetNum(0);
725 // lost all children
726 if( mxFrontEnd.is() )
727 FireEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN);
729 // quit listen on stale edit source
730 if( maEditSource.IsValid() )
731 EndListening( maEditSource.GetBroadcaster() );
733 maEditSource.SetEditSource( ::std::unique_ptr< SvxEditSource >() );
736 void AccessibleTextHelper_Impl::SetEditSource( ::std::unique_ptr< SvxEditSource > && pEditSource )
738 // This should only be called with solar mutex locked, i.e. from the main office thread
740 // shutdown old edit source
741 ShutdownEditSource();
743 // set new edit source
744 maEditSource.SetEditSource( std::move(pEditSource) );
746 // init child vector to the current child count
747 if( maEditSource.IsValid() )
749 maParaManager.SetNum( GetTextForwarder().GetParagraphCount() );
751 // listen on new edit source
752 StartListening( maEditSource.GetBroadcaster() );
754 UpdateVisibleChildren();
758 void AccessibleTextHelper_Impl::SetOffset( const Point& rPoint )
760 // guard against non-atomic access to maOffset data structure
762 ::osl::MutexGuard aGuard( maMutex );
763 maOffset = rPoint;
766 maParaManager.SetEEOffset( rPoint );
768 // in all cases, check visibility afterwards.
769 UpdateVisibleChildren();
770 UpdateBoundRect();
773 void AccessibleTextHelper_Impl::UpdateVisibleChildren( bool bBroadcastEvents )
777 SvxTextForwarder& rCacheTF = GetTextForwarder();
778 SvxViewForwarder& rCacheVF = GetViewForwarder();
780 Rectangle aViewArea = rCacheVF.GetVisArea();
782 if( IsActive() )
784 // maybe the edit view scrolls, adapt aViewArea
785 Rectangle aEditViewArea = GetEditViewForwarder().GetVisArea();
786 aViewArea += aEditViewArea.TopLeft();
788 // now determine intersection
789 aViewArea.Intersection( aEditViewArea );
792 Rectangle aTmpBB, aParaBB;
793 bool bFirstChild = true;
794 sal_Int32 nCurrPara;
795 sal_Int32 nParas=rCacheTF.GetParagraphCount();
797 mnFirstVisibleChild = -1;
798 mnLastVisibleChild = -2;
800 for( nCurrPara=0; nCurrPara<nParas; ++nCurrPara )
802 DBG_ASSERT(nCurrPara >= 0 && nCurrPara <= USHRT_MAX,
803 "AccessibleTextHelper_Impl::UpdateVisibleChildren: index value overflow");
805 aTmpBB = rCacheTF.GetParaBounds( nCurrPara );
807 // convert to screen coordinates
808 aParaBB = ::accessibility::AccessibleEditableTextPara::LogicToPixel( aTmpBB, rCacheTF.GetMapMode(), rCacheVF );
809 // at least partially visible
810 if( bFirstChild )
812 bFirstChild = false;
813 mnFirstVisibleChild = nCurrPara;
816 mnLastVisibleChild = nCurrPara;
818 // child not yet created?
819 ::accessibility::AccessibleParaManager::WeakChild aChild( maParaManager.GetChild(nCurrPara) );
820 if( aChild.second.Width == 0 &&
821 aChild.second.Height == 0 &&
822 mxFrontEnd.is() &&
823 bBroadcastEvents )
825 GotPropertyEvent( uno::makeAny( maParaManager.CreateChild( nCurrPara - mnFirstVisibleChild,
826 mxFrontEnd, GetEditSource(), nCurrPara ).first ),
827 AccessibleEventId::CHILD );
831 catch( const uno::Exception& )
833 OSL_FAIL("AccessibleTextHelper_Impl::UpdateVisibleChildren error while determining visible children");
835 // something failed - currently no children
836 mnFirstVisibleChild = -1;
837 mnLastVisibleChild = -2;
838 maParaManager.SetNum(0);
840 // lost all children
841 if( bBroadcastEvents )
842 FireEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN);
846 // functor for checking changes in paragraph bounding boxes (no stand-alone function, maybe not inlined)
847 class AccessibleTextHelper_UpdateChildBounds : public ::std::unary_function< const ::accessibility::AccessibleParaManager::WeakChild&,
848 ::accessibility::AccessibleParaManager::WeakChild >
850 public:
851 AccessibleTextHelper_UpdateChildBounds( AccessibleTextHelper_Impl& rImpl ) : mrImpl(rImpl) {}
852 ::accessibility::AccessibleParaManager::WeakChild operator()( const ::accessibility::AccessibleParaManager::WeakChild& rChild )
854 // retrieve hard reference from weak one
855 ::accessibility::AccessibleParaManager::WeakPara::HardRefType aHardRef( rChild.first.get() );
857 if( aHardRef.is() )
859 awt::Rectangle aNewRect = aHardRef->getBounds();
860 const awt::Rectangle& aOldRect = rChild.second;
862 if( aNewRect.X != aOldRect.X ||
863 aNewRect.Y != aOldRect.Y ||
864 aNewRect.Width != aOldRect.Width ||
865 aNewRect.Height != aOldRect.Height )
867 // visible data changed
868 aHardRef->FireEvent( AccessibleEventId::BOUNDRECT_CHANGED );
870 // update internal bounds
871 return ::accessibility::AccessibleParaManager::WeakChild( rChild.first, aNewRect );
875 // identity transform
876 return rChild;
879 private:
880 AccessibleTextHelper_Impl& mrImpl;
883 void AccessibleTextHelper_Impl::UpdateBoundRect()
885 // send BOUNDRECT_CHANGED to affected children
886 AccessibleTextHelper_UpdateChildBounds aFunctor( *this );
887 ::std::transform( maParaManager.begin(), maParaManager.end(), maParaManager.begin(), aFunctor );
890 #ifdef DBG_UTIL
891 void AccessibleTextHelper_Impl::CheckInvariants() const
893 if( mnFirstVisibleChild >= 0 &&
894 mnFirstVisibleChild > mnLastVisibleChild )
896 OSL_FAIL( "AccessibleTextHelper: range invalid" );
899 #endif
901 // functor for sending child events (no stand-alone function, they are maybe not inlined)
902 class AccessibleTextHelper_LostChildEvent : public ::std::unary_function< const ::accessibility::AccessibleParaManager::WeakChild&, void >
904 public:
905 AccessibleTextHelper_LostChildEvent( AccessibleTextHelper_Impl& rImpl ) : mrImpl(rImpl) {}
906 void operator()( const ::accessibility::AccessibleParaManager::WeakChild& rPara )
908 // retrieve hard reference from weak one
909 ::accessibility::AccessibleParaManager::WeakPara::HardRefType aHardRef( rPara.first.get() );
911 if( aHardRef.is() )
912 mrImpl.FireEvent(AccessibleEventId::CHILD, uno::Any(), uno::makeAny( aHardRef.getRef() ) );
915 private:
916 AccessibleTextHelper_Impl& mrImpl;
919 void AccessibleTextHelper_Impl::ParagraphsMoved( sal_Int32 nFirst, sal_Int32 nMiddle, sal_Int32 nLast )
921 const sal_Int32 nParas = GetTextForwarder().GetParagraphCount();
923 /* rotate paragraphs
924 * =================
926 * Three cases:
928 * 1.
929 * ... nParagraph ... nParam1 ... nParam2 ...
930 * |______________[xxxxxxxxxxx]
931 * becomes
932 * [xxxxxxxxxxx]|______________
934 * tail is 0
936 * 2.
937 * ... nParam1 ... nParagraph ... nParam2 ...
938 * [xxxxxxxxxxx|xxxxxxxxxxxxxx]____________
939 * becomes
940 * ____________[xxxxxxxxxxx|xxxxxxxxxxxxxx]
942 * tail is nParagraph - nParam1
944 * 3.
945 * ... nParam1 ... nParam2 ... nParagraph ...
946 * [xxxxxxxxxxx]___________|____________
947 * becomes
948 * ___________|____________[xxxxxxxxxxx]
950 * tail is nParam2 - nParam1
953 // sort nParagraph, nParam1 and nParam2 in ascending order, calc range
954 if( nMiddle < nFirst )
956 ::std::swap(nFirst, nMiddle);
958 else if( nMiddle < nLast )
960 nLast = nLast + nMiddle - nFirst;
962 else
964 ::std::swap(nMiddle, nLast);
965 nLast = nLast + nMiddle - nFirst;
968 if( nFirst < nParas && nMiddle < nParas && nLast < nParas )
970 // since we have no "paragraph index
971 // changed" event on UAA, remove
972 // [first,last] and insert again later (in
973 // UpdateVisibleChildren)
975 // maParaManager.Rotate( nFirst, nMiddle, nLast );
977 // send CHILD_EVENT to affected children
978 ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator begin = maParaManager.begin();
979 ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator end = begin;
981 ::std::advance( begin, nFirst );
982 ::std::advance( end, nLast+1 );
984 // TODO: maybe optimize here in the following way. If the
985 // number of removed children exceeds a certain threshold,
986 // use INVALIDATE_CHILDREN
987 AccessibleTextHelper_LostChildEvent aFunctor( *this );
989 ::std::for_each( begin, end, aFunctor );
991 maParaManager.Release(nFirst, nLast+1);
992 // should be no need for UpdateBoundRect, since all affected children are cleared.
996 // functor for sending child events (no stand-alone function, they are maybe not inlined)
997 class AccessibleTextHelper_ChildrenTextChanged : public ::std::unary_function< ::accessibility::AccessibleEditableTextPara&, void >
999 public:
1000 void operator()( ::accessibility::AccessibleEditableTextPara& rPara )
1002 rPara.TextChanged();
1006 /** functor processing queue events
1008 Reacts on TEXT_HINT_PARAINSERTED/REMOVED events and stores
1009 their content
1011 class AccessibleTextHelper_QueueFunctor : public ::std::unary_function< const SfxHint*, void >
1013 public:
1014 AccessibleTextHelper_QueueFunctor() :
1015 mnParasChanged( 0 ),
1016 mnParaIndex(-1),
1017 mnHintId(-1)
1019 void operator()( const SfxHint* pEvent )
1021 if( pEvent &&
1022 mnParasChanged != -1 )
1024 // determine hint type
1025 const TextHint* pTextHint = dynamic_cast<const TextHint*>( pEvent );
1026 const SvxEditSourceHint* pEditSourceHint = dynamic_cast<const SvxEditSourceHint*>( pEvent );
1028 if( !pEditSourceHint && pTextHint &&
1029 (pTextHint->GetId() == TEXT_HINT_PARAINSERTED ||
1030 pTextHint->GetId() == TEXT_HINT_PARAREMOVED ) )
1032 if( pTextHint->GetValue() == EE_PARA_ALL )
1034 mnParasChanged = -1;
1036 else
1038 mnHintId = pTextHint->GetId();
1039 mnParaIndex = pTextHint->GetValue();
1040 ++mnParasChanged;
1046 /** Query number of paragraphs changed during queue processing.
1048 @return number of changed paragraphs, -1 for
1049 "every paragraph changed"
1051 sal_Int32 GetNumberOfParasChanged() { return mnParasChanged; }
1052 /** Query index of last added/removed paragraph
1054 @return index of lastly added paragraphs, -1 for none
1055 added so far.
1057 sal_Int32 GetParaIndex() { return mnParaIndex; }
1058 /** Query hint id of last interesting event
1060 @return hint id of last interesting event (REMOVED/INSERTED).
1062 int GetHintId() { return mnHintId; }
1064 private:
1065 /** number of paragraphs changed during queue processing. -1 for
1066 "every paragraph changed"
1068 sal_Int32 mnParasChanged;
1069 /// index of paragraph added/removed last
1070 sal_Int32 mnParaIndex;
1071 /// TextHint ID (removed/inserted) of last interesting event
1072 int mnHintId;
1075 void AccessibleTextHelper_Impl::ProcessQueue()
1077 // inspect queue for paragraph insert/remove events. If there
1078 // is exactly _one_ of those in the queue, and the number of
1079 // paragraphs has changed by exactly one, use that event to
1080 // determine a priori which paragraph was added/removed. This
1081 // is necessary, since I must sync right here with the
1082 // EditEngine state (number of paragraphs etc.), since I'm
1083 // potentially sending listener events right away.
1084 AccessibleTextHelper_QueueFunctor aFunctor;
1085 maEventQueue.ForEach( aFunctor );
1087 const sal_Int32 nNewParas( GetTextForwarder().GetParagraphCount() );
1088 const sal_Int32 nCurrParas( maParaManager.GetNum() );
1090 // whether every paragraph already is updated (no need to
1091 // repeat that later on, e.g. for PARA_MOVED events)
1092 bool bEverythingUpdated( false );
1094 if( labs( nNewParas - nCurrParas ) == 1 &&
1095 aFunctor.GetNumberOfParasChanged() == 1 )
1097 // #103483# Exactly one paragraph added/removed. This is
1098 // the normal case, optimize event handling here.
1100 if( aFunctor.GetHintId() == TEXT_HINT_PARAINSERTED )
1102 // update num of paras
1103 maParaManager.SetNum( nNewParas );
1105 // release everything from the insertion position until the end
1106 maParaManager.Release(aFunctor.GetParaIndex(), nCurrParas);
1108 // TODO: Clarify whether this behaviour _really_ saves
1109 // anybody anything!
1110 // update children, _don't_ broadcast
1111 UpdateVisibleChildren( false );
1112 UpdateBoundRect();
1114 // send insert event
1115 // #109864# Enforce creation of this paragraph
1118 GotPropertyEvent( uno::makeAny( getAccessibleChild( aFunctor.GetParaIndex() -
1119 mnFirstVisibleChild + GetStartIndex() ) ),
1120 AccessibleEventId::CHILD );
1122 catch( const uno::Exception& )
1124 OSL_FAIL("AccessibleTextHelper_Impl::ProcessQueue: could not create new paragraph");
1127 else if( aFunctor.GetHintId() == TEXT_HINT_PARAREMOVED )
1129 ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator begin = maParaManager.begin();
1130 ::std::advance( begin, aFunctor.GetParaIndex() );
1131 ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator end = begin;
1132 ::std::advance( end, 1 );
1134 // #i61812# remember para to be removed for later notification
1135 // AFTER the new state is applied (that after the para got removed)
1136 ::uno::Reference< XAccessible > xPara;
1137 ::accessibility::AccessibleParaManager::WeakPara::HardRefType aHardRef( begin->first.get() );
1138 if( aHardRef.is() )
1139 xPara = ::uno::Reference< XAccessible >( aHardRef.getRef(), ::uno::UNO_QUERY );
1141 // release everything from the remove position until the end
1142 maParaManager.Release(aFunctor.GetParaIndex(), nCurrParas);
1144 // update num of paras
1145 maParaManager.SetNum( nNewParas );
1147 // TODO: Clarify whether this behaviour _really_ saves
1148 // anybody anything!
1149 // update children, _don't_ broadcast
1150 UpdateVisibleChildren( false );
1151 UpdateBoundRect();
1153 // #i61812# notification for removed para
1154 if (xPara.is())
1155 FireEvent(AccessibleEventId::CHILD, uno::Any(), uno::makeAny( xPara) );
1157 #ifdef DBG_UTIL
1158 else
1159 OSL_FAIL("AccessibleTextHelper_Impl::ProcessQueue() invalid hint id");
1160 #endif
1162 else if( nNewParas != nCurrParas )
1164 // release all paras
1165 maParaManager.Release(0, nCurrParas);
1167 // update num of paras
1168 maParaManager.SetNum( nNewParas );
1170 // #109864# create from scratch, don't broadcast
1171 UpdateVisibleChildren( false );
1172 UpdateBoundRect();
1174 // number of paragraphs somehow changed - but we have no
1175 // chance determining how. Thus, throw away everything and
1176 // create from scratch.
1177 // (child events should be broadcast after the changes are done...)
1178 FireEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN);
1180 // no need for further updates later on
1181 bEverythingUpdated = true;
1184 while( !maEventQueue.IsEmpty() )
1186 ::std::unique_ptr< SfxHint > pHint( maEventQueue.PopFront() );
1187 if( pHint.get() )
1189 const SfxHint& rHint = *(pHint.get());
1191 // determine hint type
1192 const SdrHint* pSdrHint = dynamic_cast<const SdrHint*>( &rHint );
1193 const SfxSimpleHint* pSimpleHint = dynamic_cast<const SfxSimpleHint*>( &rHint );
1194 const TextHint* pTextHint = dynamic_cast<const TextHint*>( &rHint );
1195 const SvxViewHint* pViewHint = dynamic_cast<const SvxViewHint*>( &rHint );
1196 const SvxEditSourceHint* pEditSourceHint = dynamic_cast<const SvxEditSourceHint*>( &rHint );
1200 const sal_Int32 nParas = GetTextForwarder().GetParagraphCount();
1202 if( pEditSourceHint )
1204 switch( pEditSourceHint->GetId() )
1206 case EDITSOURCE_HINT_PARASMOVED:
1208 DBG_ASSERT( pEditSourceHint->GetStartValue() < GetTextForwarder().GetParagraphCount() &&
1209 pEditSourceHint->GetEndValue() < GetTextForwarder().GetParagraphCount(),
1210 "AccessibleTextHelper_Impl::NotifyHdl: Invalid notification");
1212 if( !bEverythingUpdated )
1214 ParagraphsMoved(pEditSourceHint->GetStartValue(),
1215 pEditSourceHint->GetValue(),
1216 pEditSourceHint->GetEndValue());
1218 // in all cases, check visibility afterwards.
1219 UpdateVisibleChildren();
1221 break;
1224 case EDITSOURCE_HINT_SELECTIONCHANGED:
1225 // notify listeners
1228 UpdateSelection();
1230 // maybe we're not in edit mode (this is not an error)
1231 catch( const uno::Exception& ) {}
1232 break;
1235 else if( pTextHint )
1237 switch( pTextHint->GetId() )
1239 case TEXT_HINT_MODIFIED:
1241 // notify listeners
1242 sal_Int32 nPara( pTextHint->GetValue() );
1244 // #108900# Delegate change event to children
1245 AccessibleTextHelper_ChildrenTextChanged aNotifyChildrenFunctor;
1247 if( nPara == EE_PARA_ALL )
1249 // #108900# Call every child
1250 ::std::for_each( maParaManager.begin(), maParaManager.end(),
1251 AccessibleParaManager::WeakChildAdapter< AccessibleTextHelper_ChildrenTextChanged > (aNotifyChildrenFunctor) );
1253 else
1254 if( nPara < nParas )
1256 // #108900# Call child at index nPara
1257 ::std::for_each( maParaManager.begin()+nPara, maParaManager.begin()+nPara+1,
1258 AccessibleParaManager::WeakChildAdapter< AccessibleTextHelper_ChildrenTextChanged > (aNotifyChildrenFunctor) );
1260 break;
1263 case TEXT_HINT_PARAINSERTED:
1264 // already happened above
1265 break;
1267 case TEXT_HINT_PARAREMOVED:
1268 // already happened above
1269 break;
1271 case TEXT_HINT_TEXTHEIGHTCHANGED:
1272 // visibility changed, done below
1273 break;
1275 case TEXT_HINT_VIEWSCROLLED:
1276 // visibility changed, done below
1277 break;
1280 // in all cases, check visibility afterwards.
1281 UpdateVisibleChildren();
1282 UpdateBoundRect();
1284 else if( pViewHint )
1286 switch( pViewHint->GetHintType() )
1288 case SvxViewHint::SVX_HINT_VIEWCHANGED:
1289 // just check visibility
1290 UpdateVisibleChildren();
1291 UpdateBoundRect();
1292 break;
1295 else if( pSdrHint )
1297 switch( pSdrHint->GetKind() )
1299 case HINT_BEGEDIT:
1301 if(!IsActive())
1303 break;
1305 // change children state
1306 maParaManager.SetActive();
1308 // per definition, edit mode text has the focus
1309 SetFocus( true );
1310 break;
1313 case HINT_ENDEDIT:
1315 // focused child now loses focus
1316 ESelection aSelection;
1317 if( GetEditViewForwarder().GetSelection( aSelection ) )
1318 SetChildFocus( aSelection.nEndPara, false );
1320 // change children state
1321 maParaManager.SetActive( false );
1323 maLastSelection = ESelection( EE_PARA_NOT_FOUND, EE_INDEX_NOT_FOUND,
1324 EE_PARA_NOT_FOUND, EE_INDEX_NOT_FOUND);
1325 break;
1327 default:
1328 break;
1331 // it's VITAL to keep the SfxSimpleHint last! It's the base of some classes above!
1332 else if( pSimpleHint )
1334 switch( pSimpleHint->GetId() )
1336 case SFX_HINT_DYING:
1337 // edit source is dying under us, become defunc then
1340 // make edit source inaccessible
1341 // Note: cannot destroy it here, since we're called from there!
1342 ShutdownEditSource();
1344 catch( const uno::Exception& ) {}
1346 break;
1350 catch( const uno::Exception& )
1352 #ifdef DBG_UTIL
1353 OSL_TRACE("AccessibleTextHelper_Impl::ProcessQueue: Unhandled exception.");
1354 #endif
1360 void AccessibleTextHelper_Impl::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
1362 // precondition: solar mutex locked
1363 DBG_TESTSOLARMUTEX();
1365 // precondition: not in a recursion
1366 if( mbInNotify )
1367 return;
1369 mbInNotify = true;
1371 // determine hint type
1372 const SdrHint* pSdrHint = dynamic_cast<const SdrHint*>( &rHint );
1373 const SfxSimpleHint* pSimpleHint = dynamic_cast<const SfxSimpleHint*>( &rHint );
1374 const TextHint* pTextHint = dynamic_cast<const TextHint*>( &rHint );
1375 const SvxViewHint* pViewHint = dynamic_cast<const SvxViewHint*>( &rHint );
1376 const SvxEditSourceHint* pEditSourceHint = dynamic_cast<const SvxEditSourceHint*>( &rHint );
1380 // Process notification event
1381 if( pEditSourceHint )
1383 maEventQueue.Append( *pEditSourceHint );
1384 // EditEngine should emit TEXT_SELECTION_CHANGED events (#i27299#)
1385 if( maEventOpenFrames == 0 )
1386 ProcessQueue();
1388 else if( pTextHint )
1390 switch( pTextHint->GetId() )
1392 case TEXT_HINT_BLOCKNOTIFICATION_END:
1393 case TEXT_HINT_INPUT_END:
1394 --maEventOpenFrames;
1396 if( maEventOpenFrames == 0 )
1398 // #103483#
1399 /* All information should have arrived
1400 * now, process queue. As stated in the
1401 * above bug, we can often avoid throwing
1402 * away all paragraphs by looking forward
1403 * in the event queue (searching for
1404 * PARAINSERT/REMOVE events). Furthermore,
1405 * processing the event queue only at the
1406 * end of an interaction cycle, ensures
1407 * that the EditEngine state and the
1408 * AccessibleText state are the same
1409 * (well, mostly. If there are _multiple_
1410 * interaction cycles in the EE queues, it
1411 * can still happen that EE state is
1412 * different. That's so to say broken by
1413 * design with that delayed EE event
1414 * concept).
1416 ProcessQueue();
1418 break;
1420 case TEXT_HINT_BLOCKNOTIFICATION_START:
1421 case TEXT_HINT_INPUT_START:
1422 ++maEventOpenFrames;
1423 // no FALLTHROUGH reason: event will not be processed,
1424 // thus appending the event isn't necessary. (#i27299#)
1425 break;
1426 default:
1427 maEventQueue.Append( *pTextHint );
1428 // EditEngine should emit TEXT_SELECTION_CHANGED events (#i27299#)
1429 if( maEventOpenFrames == 0 )
1430 ProcessQueue();
1431 break;
1434 else if( pViewHint )
1436 maEventQueue.Append( *pViewHint );
1438 // process visibility right away, if not within an
1439 // open EE notification frame. Otherwise, event
1440 // processing would be delayed until next EE
1441 // notification sequence.
1442 if( maEventOpenFrames == 0 )
1443 ProcessQueue();
1445 else if( pSdrHint )
1447 maEventQueue.Append( *pSdrHint );
1449 // process drawing layer events right away, if not
1450 // within an open EE notification frame. Otherwise,
1451 // event processing would be delayed until next EE
1452 // notification sequence.
1453 if( maEventOpenFrames == 0 )
1454 ProcessQueue();
1456 // it's VITAL to keep the SfxSimpleHint last! It's the base of some classes above!
1457 else if( pSimpleHint )
1459 // handle this event _at once_, because after that, objects are invalid
1460 switch( pSimpleHint->GetId() )
1462 case SFX_HINT_DYING:
1463 // edit source is dying under us, become defunc then
1464 maEventQueue.Clear();
1467 // make edit source inaccessible
1468 // Note: cannot destroy it here, since we're called from there!
1469 ShutdownEditSource();
1471 catch( const uno::Exception& ) {}
1473 break;
1477 catch( const uno::Exception& )
1479 #ifdef DBG_UTIL
1480 OSL_TRACE("AccessibleTextHelper_Impl::Notify: Unhandled exception.");
1481 #endif
1482 mbInNotify = false;
1485 mbInNotify = false;
1488 void AccessibleTextHelper_Impl::Dispose()
1490 if( getNotifierClientId() != -1 )
1494 // #106234# Unregister from EventNotifier
1495 ::comphelper::AccessibleEventNotifier::revokeClient( getNotifierClientId() );
1496 #ifdef DBG_UTIL
1497 OSL_TRACE( "AccessibleTextHelper_Impl disposed ID: %d", mnNotifierClientId );
1498 #endif
1500 catch( const uno::Exception& ) {}
1502 mnNotifierClientId = -1;
1507 // dispose children
1508 maParaManager.Dispose();
1510 catch( const uno::Exception& ) {}
1512 // quit listen on stale edit source
1513 if( maEditSource.IsValid() )
1514 EndListening( maEditSource.GetBroadcaster() );
1516 // clear references
1517 maEditSource.SetEditSource( ::std::unique_ptr< SvxEditSource >() );
1518 mxFrontEnd = NULL;
1521 void AccessibleTextHelper_Impl::FireEvent( const sal_Int16 nEventId, const uno::Any& rNewValue, const uno::Any& rOldValue ) const
1523 // -- object locked --
1524 ::osl::ClearableMutexGuard aGuard( maMutex );
1526 AccessibleEventObject aEvent;
1528 DBG_ASSERT(mxFrontEnd.is(), "AccessibleTextHelper::FireEvent: no event source set" );
1530 if( mxFrontEnd.is() )
1531 aEvent = AccessibleEventObject(mxFrontEnd->getAccessibleContext(), nEventId, rNewValue, rOldValue);
1532 else
1533 aEvent = AccessibleEventObject(uno::Reference< uno::XInterface >(), nEventId, rNewValue, rOldValue);
1535 // no locking necessary, FireEvent internally copies listeners
1536 // if someone removes/adds in between Further locking,
1537 // actually, might lead to deadlocks, since we're calling out
1538 // of this object
1539 aGuard.clear();
1540 // -- until here --
1542 FireEvent(aEvent);
1545 void AccessibleTextHelper_Impl::FireEvent( const AccessibleEventObject& rEvent ) const
1547 // #102261# Call global queue for focus events
1548 if( rEvent.EventId == AccessibleStateType::FOCUSED )
1549 vcl::unohelper::NotifyAccessibleStateEventGlobally( rEvent );
1551 // #106234# Delegate to EventNotifier
1552 ::comphelper::AccessibleEventNotifier::addEvent( getNotifierClientId(),
1553 rEvent );
1556 // XAccessibleContext
1557 sal_Int32 SAL_CALL AccessibleTextHelper_Impl::getAccessibleChildCount()
1559 return mnLastVisibleChild - mnFirstVisibleChild + 1;
1562 uno::Reference< XAccessible > SAL_CALL AccessibleTextHelper_Impl::getAccessibleChild( sal_Int32 i )
1564 i -= GetStartIndex();
1566 if( 0 > i || i >= getAccessibleChildCount() ||
1567 GetTextForwarder().GetParagraphCount() <= i )
1569 throw lang::IndexOutOfBoundsException("Invalid child index", mxFrontEnd);
1572 DBG_ASSERT(mxFrontEnd.is(), "AccessibleTextHelper_Impl::UpdateVisibleChildren: no frontend set");
1574 if( mxFrontEnd.is() )
1575 return maParaManager.CreateChild( i, mxFrontEnd, GetEditSource(), mnFirstVisibleChild + i ).first;
1576 else
1577 return NULL;
1580 void SAL_CALL AccessibleTextHelper_Impl::addAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener )
1582 if( getNotifierClientId() != -1 )
1583 ::comphelper::AccessibleEventNotifier::addEventListener( getNotifierClientId(), xListener );
1586 void SAL_CALL AccessibleTextHelper_Impl::removeAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener )
1588 if( getNotifierClientId() != -1 )
1590 const sal_Int32 nListenerCount = ::comphelper::AccessibleEventNotifier::removeEventListener( getNotifierClientId(), xListener );
1591 if ( !nListenerCount )
1593 // no listeners anymore
1594 // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client),
1595 // and at least to us not firing any events anymore, in case somebody calls
1596 // NotifyAccessibleEvent, again
1597 ::comphelper::AccessibleEventNotifier::TClientId nId( getNotifierClientId() );
1598 mnNotifierClientId = -1;
1599 ::comphelper::AccessibleEventNotifier::revokeClient( nId );
1604 uno::Reference< XAccessible > SAL_CALL AccessibleTextHelper_Impl::getAccessibleAtPoint( const awt::Point& _aPoint )
1606 // make given position relative
1607 if( !mxFrontEnd.is() )
1608 throw uno::RuntimeException("AccessibleTextHelper_Impl::getAccessibleAt: frontend invalid", mxFrontEnd );
1610 uno::Reference< XAccessibleContext > xFrontEndContext = mxFrontEnd->getAccessibleContext();
1612 if( !xFrontEndContext.is() )
1613 throw uno::RuntimeException("AccessibleTextHelper_Impl::getAccessibleAt: frontend invalid", mxFrontEnd );
1615 uno::Reference< XAccessibleComponent > xFrontEndComponent( xFrontEndContext, uno::UNO_QUERY );
1617 if( !xFrontEndComponent.is() )
1618 throw uno::RuntimeException("AccessibleTextHelper_Impl::getAccessibleAt: frontend is no XAccessibleComponent", mxFrontEnd );
1620 // #103862# No longer need to make given position relative
1621 Point aPoint( _aPoint.X, _aPoint.Y );
1623 // respect EditEngine offset to surrounding shape/cell
1624 aPoint -= GetOffset();
1626 // convert to EditEngine coordinate system
1627 SvxTextForwarder& rCacheTF = GetTextForwarder();
1628 Point aLogPoint( GetViewForwarder().PixelToLogic( aPoint, rCacheTF.GetMapMode() ) );
1630 // iterate over all visible children (including those not yet created)
1631 sal_Int32 nChild;
1632 for( nChild=mnFirstVisibleChild; nChild <= mnLastVisibleChild; ++nChild )
1634 DBG_ASSERT(nChild >= 0 && nChild <= USHRT_MAX,
1635 "AccessibleTextHelper_Impl::getAccessibleAt: index value overflow");
1637 Rectangle aParaBounds( rCacheTF.GetParaBounds( nChild ) );
1639 if( aParaBounds.IsInside( aLogPoint ) )
1640 return getAccessibleChild( nChild - mnFirstVisibleChild + GetStartIndex() );
1643 // found none
1644 return NULL;
1649 // AccessibleTextHelper implementation (simply forwards to impl)
1651 AccessibleTextHelper::AccessibleTextHelper( ::std::unique_ptr< SvxEditSource > && pEditSource ) :
1652 mpImpl( new AccessibleTextHelper_Impl() )
1654 SolarMutexGuard aGuard;
1656 SetEditSource( std::move(pEditSource) );
1659 AccessibleTextHelper::~AccessibleTextHelper()
1663 const SvxEditSource& AccessibleTextHelper::GetEditSource() const
1665 #ifdef DBG_UTIL
1666 mpImpl->CheckInvariants();
1668 const SvxEditSource& aEditSource = mpImpl->GetEditSource();
1670 mpImpl->CheckInvariants();
1672 return aEditSource;
1673 #else
1674 return mpImpl->GetEditSource();
1675 #endif
1678 void AccessibleTextHelper::SetEditSource( ::std::unique_ptr< SvxEditSource > && pEditSource )
1680 #ifdef DBG_UTIL
1681 // precondition: solar mutex locked
1682 DBG_TESTSOLARMUTEX();
1684 mpImpl->CheckInvariants();
1685 #endif
1687 mpImpl->SetEditSource( std::move(pEditSource) );
1689 #ifdef DBG_UTIL
1690 mpImpl->CheckInvariants();
1691 #endif
1694 void AccessibleTextHelper::SetEventSource( const uno::Reference< XAccessible >& rInterface )
1696 #ifdef DBG_UTIL
1697 mpImpl->CheckInvariants();
1698 #endif
1700 mpImpl->SetEventSource( rInterface );
1702 #ifdef DBG_UTIL
1703 mpImpl->CheckInvariants();
1704 #endif
1707 void AccessibleTextHelper::SetFocus( bool bHaveFocus )
1709 #ifdef DBG_UTIL
1710 // precondition: solar mutex locked
1711 DBG_TESTSOLARMUTEX();
1713 mpImpl->CheckInvariants();
1714 #endif
1716 mpImpl->SetFocus( bHaveFocus );
1718 #ifdef DBG_UTIL
1719 mpImpl->CheckInvariants();
1720 #endif
1723 bool AccessibleTextHelper::HaveFocus()
1725 #ifdef DBG_UTIL
1726 mpImpl->CheckInvariants();
1728 bool bRet( mpImpl->HaveFocus() );
1730 mpImpl->CheckInvariants();
1732 return bRet;
1733 #else
1734 return mpImpl->HaveFocus();
1735 #endif
1738 void AccessibleTextHelper::SetOffset( const Point& rPoint )
1740 #ifdef DBG_UTIL
1741 // precondition: solar mutex locked
1742 DBG_TESTSOLARMUTEX();
1744 mpImpl->CheckInvariants();
1745 #endif
1747 mpImpl->SetOffset( rPoint );
1749 #ifdef DBG_UTIL
1750 mpImpl->CheckInvariants();
1751 #endif
1754 void AccessibleTextHelper::SetStartIndex( sal_Int32 nOffset )
1756 #ifdef DBG_UTIL
1757 // precondition: solar mutex locked
1758 DBG_TESTSOLARMUTEX();
1760 mpImpl->CheckInvariants();
1761 #endif
1763 mpImpl->SetStartIndex( nOffset );
1765 #ifdef DBG_UTIL
1766 mpImpl->CheckInvariants();
1767 #endif
1770 sal_Int32 AccessibleTextHelper::GetStartIndex() const
1772 #ifdef DBG_UTIL
1773 mpImpl->CheckInvariants();
1775 sal_Int32 nOffset = mpImpl->GetStartIndex();
1777 mpImpl->CheckInvariants();
1779 return nOffset;
1780 #else
1781 return mpImpl->GetStartIndex();
1782 #endif
1785 void AccessibleTextHelper::SetAdditionalChildStates( const VectorOfStates& rChildStates )
1787 mpImpl->SetAdditionalChildStates( rChildStates );
1790 void AccessibleTextHelper::UpdateChildren()
1792 #ifdef DBG_UTIL
1793 // precondition: solar mutex locked
1794 DBG_TESTSOLARMUTEX();
1796 mpImpl->CheckInvariants();
1797 #endif
1799 mpImpl->UpdateVisibleChildren();
1800 mpImpl->UpdateBoundRect();
1802 mpImpl->UpdateSelection();
1804 #ifdef DBG_UTIL
1805 mpImpl->CheckInvariants();
1806 #endif
1809 void AccessibleTextHelper::Dispose()
1811 // As Dispose calls ShutdownEditSource, which in turn
1812 // deregisters as listener on the edit source, have to lock
1813 // here
1814 SolarMutexGuard aGuard;
1816 #ifdef DBG_UTIL
1817 mpImpl->CheckInvariants();
1818 #endif
1820 mpImpl->Dispose();
1822 #ifdef DBG_UTIL
1823 mpImpl->CheckInvariants();
1824 #endif
1827 // XAccessibleContext
1828 sal_Int32 AccessibleTextHelper::GetChildCount()
1830 SolarMutexGuard aGuard;
1832 #ifdef DBG_UTIL
1833 mpImpl->CheckInvariants();
1835 sal_Int32 nRet = mpImpl->getAccessibleChildCount();
1837 mpImpl->CheckInvariants();
1839 return nRet;
1840 #else
1841 return mpImpl->getAccessibleChildCount();
1842 #endif
1845 uno::Reference< XAccessible > AccessibleTextHelper::GetChild( sal_Int32 i )
1847 SolarMutexGuard aGuard;
1849 #ifdef DBG_UTIL
1850 mpImpl->CheckInvariants();
1852 uno::Reference< XAccessible > xRet = mpImpl->getAccessibleChild( i );
1854 mpImpl->CheckInvariants();
1856 return xRet;
1857 #else
1858 return mpImpl->getAccessibleChild( i );
1859 #endif
1862 void AccessibleTextHelper::AddEventListener( const uno::Reference< XAccessibleEventListener >& xListener )
1864 #ifdef DBG_UTIL
1865 mpImpl->CheckInvariants();
1867 mpImpl->addAccessibleEventListener( xListener );
1869 mpImpl->CheckInvariants();
1870 #else
1871 mpImpl->addAccessibleEventListener( xListener );
1872 #endif
1875 void AccessibleTextHelper::RemoveEventListener( const uno::Reference< XAccessibleEventListener >& xListener )
1877 #ifdef DBG_UTIL
1878 mpImpl->CheckInvariants();
1880 mpImpl->removeAccessibleEventListener( xListener );
1882 mpImpl->CheckInvariants();
1883 #else
1884 mpImpl->removeAccessibleEventListener( xListener );
1885 #endif
1888 // XAccessibleComponent
1889 uno::Reference< XAccessible > AccessibleTextHelper::GetAt( const awt::Point& aPoint )
1891 SolarMutexGuard aGuard;
1893 #ifdef DBG_UTIL
1894 mpImpl->CheckInvariants();
1896 uno::Reference< XAccessible > xChild = mpImpl->getAccessibleAtPoint( aPoint );
1898 mpImpl->CheckInvariants();
1900 return xChild;
1901 #else
1902 return mpImpl->getAccessibleAtPoint( aPoint );
1903 #endif
1906 } // end of namespace accessibility
1910 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */