1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: AccessibleTextHelper.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_svx.hxx"
34 //------------------------------------------------------------------------
38 //------------------------------------------------------------------------
44 #include <vos/mutex.hxx>
45 #include <com/sun/star/uno/Any.hxx>
46 #include <com/sun/star/uno/Reference.hxx>
47 #include <cppuhelper/weakref.hxx>
48 #include <com/sun/star/awt/Point.hpp>
49 #include <com/sun/star/awt/Rectangle.hpp>
50 #include <com/sun/star/lang/DisposedException.hpp>
51 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
52 #include <com/sun/star/accessibility/XAccessible.hpp>
53 #include <com/sun/star/accessibility/XAccessibleContext.hpp>
54 #include <com/sun/star/accessibility/XAccessibleComponent.hpp>
55 #include <com/sun/star/accessibility/AccessibleRole.hpp>
56 #include <com/sun/star/accessibility/AccessibleTextType.hpp>
57 #include <com/sun/star/accessibility/XAccessibleText.hpp>
58 #include <com/sun/star/accessibility/XAccessibleEditableText.hpp>
59 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
60 #include <comphelper/accessibleeventnotifier.hxx>
61 #include <unotools/accessiblestatesethelper.hxx>
62 #include <vcl/unohelp.hxx>
63 #include <vcl/svapp.hxx>
65 //------------------------------------------------------------------------
67 // Project-local header
69 //------------------------------------------------------------------------
70 #include "AccessibleTextEventQueue.hxx"
71 #include <svx/AccessibleTextHelper.hxx>
72 #include <svx/unoshape.hxx>
73 #include "unolingu.hxx"
74 #include <svx/unotext.hxx>
76 #include "unoedhlp.hxx"
77 #include "unopracc.hxx"
78 #include "AccessibleParaManager.hxx"
79 #include "AccessibleEditableTextPara.hxx"
80 #include <svx/svdmodel.hxx>
81 #include <svx/svdpntv.hxx>
82 #include <svx/editdata.hxx>
83 #include <svx/editeng.hxx>
84 #include <svx/editview.hxx>
86 using namespace ::com::sun::star
;
87 using namespace ::com::sun::star::accessibility
;
89 namespace accessibility
92 //------------------------------------------------------------------------
94 // AccessibleTextHelper_Impl declaration
96 //------------------------------------------------------------------------
98 DBG_NAME( AccessibleTextHelper_Impl
)
100 template < typename first_type
, typename second_type
>
101 ::std::pair
< first_type
, second_type
> makeSortedPair( first_type first
,
105 return ::std::make_pair( second
, first
);
107 return ::std::make_pair( first
, second
);
110 class AccessibleTextHelper_Impl
: public SfxListener
114 typedef ::std::vector
< sal_Int16
> VectorOfStates
;
116 // receive pointer to our frontend class and view window
117 AccessibleTextHelper_Impl();
118 ~AccessibleTextHelper_Impl();
120 // XAccessibleContext child handling methods
121 sal_Int32 SAL_CALL
getAccessibleChildCount() SAL_THROW((uno::RuntimeException
));
122 uno::Reference
< XAccessible
> SAL_CALL
getAccessibleChild( sal_Int32 i
) SAL_THROW((lang::IndexOutOfBoundsException
, uno::RuntimeException
));
124 // XAccessibleEventBroadcaster child related methods
125 void SAL_CALL
addEventListener( const uno::Reference
< XAccessibleEventListener
>& xListener
) SAL_THROW((uno::RuntimeException
));
126 void SAL_CALL
removeEventListener( const uno::Reference
< XAccessibleEventListener
>& xListener
) SAL_THROW((uno::RuntimeException
));
128 // XAccessibleComponent child related methods
129 uno::Reference
< XAccessible
> SAL_CALL
getAccessibleAtPoint( const awt::Point
& aPoint
) SAL_THROW((uno::RuntimeException
));
131 SvxEditSourceAdapter
& GetEditSource() const SAL_THROW((uno::RuntimeException
));
132 void SetEditSource( ::std::auto_ptr
< SvxEditSource
> pEditSource
) SAL_THROW((uno::RuntimeException
));
134 void SetEventSource( const uno::Reference
< XAccessible
>& rInterface
)
136 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
137 mxFrontEnd
= rInterface
;
139 uno::Reference
< XAccessible
> GetEventSource() const
141 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
145 void SetOffset( const Point
& );
146 Point
GetOffset() const
148 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
149 ::osl::MutexGuard
aGuard( maMutex
); Point
aPoint( maOffset
);
153 void SetStartIndex( sal_Int32 nOffset
);
154 sal_Int32
GetStartIndex() const
156 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
157 // Strictly correct only with locked solar mutex, // but
158 // here we rely on the fact that sal_Int32 access is
163 void SetAdditionalChildStates( const VectorOfStates
& rChildStates
);
164 const VectorOfStates
& GetAdditionalChildStates() const;
166 sal_Bool
IsSelected() const;
170 // do NOT hold object mutex when calling this! Danger of deadlock
171 void FireEvent( const sal_Int16 nEventId
, const uno::Any
& rNewValue
= uno::Any(), const uno::Any
& rOldValue
= uno::Any() ) const;
172 void FireEvent( const AccessibleEventObject
& rEvent
) const;
174 void SetFocus( sal_Bool bHaveFocus
) SAL_THROW((::com::sun::star::uno::RuntimeException
));
175 sal_Bool
HaveFocus() SAL_THROW((::com::sun::star::uno::RuntimeException
));
176 void SetChildFocus( sal_Int32 nChild
, sal_Bool bHaveFocus
) SAL_THROW((::com::sun::star::uno::RuntimeException
));
177 void SetShapeFocus( sal_Bool bHaveFocus
) SAL_THROW((::com::sun::star::uno::RuntimeException
));
178 void ChangeChildFocus( sal_Int32 nNewChild
) SAL_THROW((::com::sun::star::uno::RuntimeException
));
181 void CheckInvariants() const;
184 // checks all children for visibility, throws away invisible ones
185 void UpdateVisibleChildren( bool bBroadcastEvents
=true );
187 // check all children for changes in positÃon and size
188 void UpdateBoundRect();
190 // calls SetSelection on the forwarder and updates maLastSelection
192 void UpdateSelection();
196 // Process event queue
199 // syntactic sugar for FireEvent
200 void GotPropertyEvent( const uno::Any
& rNewValue
, const sal_Int16 nEventId
) const { FireEvent( nEventId
, rNewValue
); }
201 void LostPropertyEvent( const uno::Any
& rOldValue
, const sal_Int16 nEventId
) const { FireEvent( nEventId
, uno::Any(), rOldValue
); }
203 // shutdown usage of current edit source on myself and the children.
204 void ShutdownEditSource() SAL_THROW((uno::RuntimeException
));
206 void ParagraphsMoved( sal_Int32 nFirst
, sal_Int32 nMiddle
, sal_Int32 nLast
);
208 virtual void Notify( SfxBroadcaster
& rBC
, const SfxHint
& rHint
);
210 int getNotifierClientId() const { return mnNotifierClientId
; }
212 // lock solar mutex before
213 SvxTextForwarder
& GetTextForwarder() const SAL_THROW((uno::RuntimeException
));
214 // lock solar mutex before
215 SvxViewForwarder
& GetViewForwarder() const SAL_THROW((uno::RuntimeException
));
216 // lock solar mutex before
217 SvxEditViewForwarder
& GetEditViewForwarder( sal_Bool bCreate
= sal_False
) const SAL_THROW((uno::RuntimeException
));
219 // are we in edit mode?
220 sal_Bool
IsActive() const SAL_THROW((uno::RuntimeException
));
222 // our frontend class (the one implementing the actual
223 // interface). That's not necessarily the one containing the impl
225 uno::Reference
< XAccessible
> mxFrontEnd
;
227 // a wrapper for the text forwarders (guarded by solar mutex)
228 mutable SvxEditSourceAdapter maEditSource
;
230 // store last selection (to correctly report selection changes, guarded by solar mutex)
231 ESelection maLastSelection
;
233 // cache range of visible children (guarded by solar mutex)
234 sal_Int32 mnFirstVisibleChild
;
235 sal_Int32 mnLastVisibleChild
;
237 // offset to add to all our children (unguarded, relying on
238 // the fact that sal_Int32 access is atomic)
239 sal_Int32 mnStartIndex
;
241 // the object handling our children (guarded by solar mutex)
242 ::accessibility::AccessibleParaManager maParaManager
;
244 // number of not-yet-closed event frames (BEGIN/END sequences) (guarded by solar mutex)
245 sal_Int32 maEventOpenFrames
;
247 // Queued events from Notify() (guarded by solar mutex)
248 AccessibleTextEventQueue maEventQueue
;
250 // spin lock to prevent notify in notify (guarded by solar mutex)
253 // whether the object or it's children has the focus set (guarded by solar mutex)
254 sal_Bool mbGroupHasFocus
;
256 // whether we (this object) has the focus set (guarded by solar mutex)
257 sal_Bool mbThisHasFocus
;
259 mutable ::osl::Mutex maMutex
;
261 /// our current offset to the containing shape/cell (guarded by maMutex)
264 /// client Id from AccessibleEventNotifier
265 int mnNotifierClientId
;
268 //------------------------------------------------------------------------
270 // AccessibleTextHelper_Impl implementation
272 //------------------------------------------------------------------------
274 AccessibleTextHelper_Impl::AccessibleTextHelper_Impl() :
276 maLastSelection( EE_PARA_NOT_FOUND
,EE_PARA_NOT_FOUND
,EE_PARA_NOT_FOUND
,EE_PARA_NOT_FOUND
),
277 mnFirstVisibleChild( -1 ),
278 mnLastVisibleChild( -2 ),
280 maEventOpenFrames( 0 ),
281 mbInNotify( sal_False
),
282 mbGroupHasFocus( sal_False
),
283 mbThisHasFocus( sal_False
),
285 // well, that's strictly exception safe, though not really
286 // robust. We rely on the fact that this member is constructed
287 // last, and that the constructor body is empty, thus no
288 // chance for exceptions once the Id is fetched. Nevertheless,
289 // normally should employ RAII here...
290 mnNotifierClientId(::comphelper::AccessibleEventNotifier::registerClient())
292 DBG_CTOR( AccessibleTextHelper_Impl
, NULL
);
295 OSL_TRACE( "AccessibleTextHelper_Impl received ID: %d", mnNotifierClientId
);
299 AccessibleTextHelper_Impl::~AccessibleTextHelper_Impl()
301 DBG_DTOR( AccessibleTextHelper_Impl
, NULL
);
303 ::vos::OGuard
aGuard( Application::GetSolarMutex() );
307 // call Dispose here, too, since we've some resources not
308 // automatically freed otherwise
311 catch( const uno::Exception
& ) {}
314 SvxTextForwarder
& AccessibleTextHelper_Impl::GetTextForwarder() const SAL_THROW((uno::RuntimeException
))
316 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
318 if( !maEditSource
.IsValid() )
319 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Unknown edit source")), mxFrontEnd
);
321 SvxTextForwarder
* pTextForwarder
= maEditSource
.GetTextForwarder();
323 if( !pTextForwarder
)
324 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Unable to fetch text forwarder, model might be dead")), mxFrontEnd
);
326 if( pTextForwarder
->IsValid() )
327 return *pTextForwarder
;
329 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Text forwarder is invalid, model might be dead")), mxFrontEnd
);
332 SvxViewForwarder
& AccessibleTextHelper_Impl::GetViewForwarder() const SAL_THROW((uno::RuntimeException
))
334 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
336 if( !maEditSource
.IsValid() )
337 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Unknown edit source")), mxFrontEnd
);
339 SvxViewForwarder
* pViewForwarder
= maEditSource
.GetViewForwarder();
341 if( !pViewForwarder
)
342 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Unable to fetch view forwarder, model might be dead")), mxFrontEnd
);
344 if( pViewForwarder
->IsValid() )
345 return *pViewForwarder
;
347 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("View forwarder is invalid, model might be dead")), mxFrontEnd
);
350 SvxEditViewForwarder
& AccessibleTextHelper_Impl::GetEditViewForwarder( sal_Bool bCreate
) const SAL_THROW((uno::RuntimeException
))
352 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
354 if( !maEditSource
.IsValid() )
355 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Unknown edit source")), mxFrontEnd
);
357 SvxEditViewForwarder
* pViewForwarder
= maEditSource
.GetEditViewForwarder( bCreate
);
359 if( !pViewForwarder
)
362 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Unable to fetch edit view forwarder, model might be dead")), mxFrontEnd
);
364 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("No edit view forwarder, object not in edit mode")), mxFrontEnd
);
367 if( pViewForwarder
->IsValid() )
368 return *pViewForwarder
;
372 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("View forwarder is invalid, model might be dead")), mxFrontEnd
);
374 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("View forwarder is invalid, object not in edit mode")), mxFrontEnd
);
378 SvxEditSourceAdapter
& AccessibleTextHelper_Impl::GetEditSource() const SAL_THROW((uno::RuntimeException
))
380 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
382 if( maEditSource
.IsValid() )
385 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("AccessibleTextHelper_Impl::GetEditSource: no edit source")), mxFrontEnd
);
388 sal_Bool
AccessibleTextHelper_Impl::IsSelected() const
390 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
392 sal_Bool bRet
= sal_False
;
396 ESelection aSelection
;
397 bRet
= GetEditViewForwarder().GetSelection( aSelection
);
399 catch( const uno::Exception
& ) {}
404 // functor for sending child events (no stand-alone function, they are maybe not inlined)
405 class AccessibleTextHelper_OffsetChildIndex
: public ::std::unary_function
< ::accessibility::AccessibleEditableTextPara
&, void >
408 AccessibleTextHelper_OffsetChildIndex( sal_Int32 nDifference
) : mnDifference(nDifference
) {}
409 void operator()( ::accessibility::AccessibleEditableTextPara
& rPara
)
411 rPara
.SetIndexInParent( rPara
.GetIndexInParent() + mnDifference
);
415 const sal_Int32 mnDifference
;
418 void AccessibleTextHelper_Impl::SetStartIndex( sal_Int32 nOffset
)
420 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
422 sal_Int32
nOldOffset( mnStartIndex
);
424 mnStartIndex
= nOffset
;
426 if( nOldOffset
!= nOffset
)
429 AccessibleTextHelper_OffsetChildIndex
aFunctor( nOffset
- nOldOffset
);
431 ::std::for_each( maParaManager
.begin(), maParaManager
.end(),
432 AccessibleParaManager::WeakChildAdapter
< AccessibleTextHelper_OffsetChildIndex
> (aFunctor
) );
436 void AccessibleTextHelper_Impl::SetAdditionalChildStates( const VectorOfStates
& rChildStates
)
438 maParaManager
.SetAdditionalChildStates( rChildStates
);
441 const AccessibleTextHelper_Impl::VectorOfStates
& AccessibleTextHelper_Impl::GetAdditionalChildStates() const
443 return maParaManager
.GetAdditionalChildStates();
446 void AccessibleTextHelper_Impl::SetChildFocus( sal_Int32 nChild
, sal_Bool bHaveFocus
) SAL_THROW((::com::sun::star::uno::RuntimeException
))
448 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
453 SetShapeFocus( sal_False
);
455 maParaManager
.SetFocus( nChild
);
457 // we just received the focus, also send caret event then
460 DBG_TRACE1("AccessibleTextHelper_Impl::SetChildFocus(): Paragraph %d received focus", nChild
);
464 maParaManager
.SetFocus( -1 );
466 DBG_TRACE1("AccessibleTextHelper_Impl::SetChildFocus(): Paragraph %d lost focus", nChild
);
468 if( mbGroupHasFocus
)
469 SetShapeFocus( sal_True
);
473 void AccessibleTextHelper_Impl::ChangeChildFocus( sal_Int32 nNewChild
) SAL_THROW((::com::sun::star::uno::RuntimeException
))
475 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
478 SetShapeFocus( sal_False
);
480 mbGroupHasFocus
= sal_True
;
481 maParaManager
.SetFocus( nNewChild
);
483 DBG_TRACE1("AccessibleTextHelper_Impl::ChangeChildFocus(): Paragraph %d received focus", nNewChild
);
486 void AccessibleTextHelper_Impl::SetShapeFocus( sal_Bool bHaveFocus
) SAL_THROW((::com::sun::star::uno::RuntimeException
))
488 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
490 sal_Bool
bOldFocus( mbThisHasFocus
);
492 mbThisHasFocus
= bHaveFocus
;
494 if( bOldFocus
!= bHaveFocus
)
498 GotPropertyEvent( uno::makeAny(AccessibleStateType::FOCUSED
), AccessibleEventId::STATE_CHANGED
);
499 DBG_TRACE("AccessibleTextHelper_Impl::SetShapeFocus(): Parent object received focus" );
503 LostPropertyEvent( uno::makeAny(AccessibleStateType::FOCUSED
), AccessibleEventId::STATE_CHANGED
);
504 DBG_TRACE("AccessibleTextHelper_Impl::SetShapeFocus(): Parent object lost focus" );
509 void AccessibleTextHelper_Impl::SetFocus( sal_Bool bHaveFocus
) SAL_THROW((::com::sun::star::uno::RuntimeException
))
511 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
513 sal_Bool
bOldFocus( mbGroupHasFocus
);
515 mbGroupHasFocus
= bHaveFocus
;
521 // find the one with the cursor and get/set focus accordingly
522 ESelection aSelection
;
523 if( GetEditViewForwarder().GetSelection( aSelection
) )
524 SetChildFocus( aSelection
.nEndPara
, bHaveFocus
);
526 catch( const uno::Exception
& ) {}
528 else if( bOldFocus
!= bHaveFocus
)
530 SetShapeFocus( bHaveFocus
);
533 DBG_TRACE2("AccessibleTextHelper_Impl::SetFocus: focus changed, Object %d, state: %s", this, bHaveFocus
? "focused" : "not focused");
536 sal_Bool
AccessibleTextHelper_Impl::HaveFocus() SAL_THROW((::com::sun::star::uno::RuntimeException
))
538 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
540 // No locking of solar mutex here, since we rely on the fact
541 // that sal_Bool access is atomic
542 return mbThisHasFocus
;
545 sal_Bool
AccessibleTextHelper_Impl::IsActive() const SAL_THROW((uno::RuntimeException
))
547 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
551 SvxEditSource
& rEditSource
= GetEditSource();
552 SvxEditViewForwarder
* pViewForwarder
= rEditSource
.GetEditViewForwarder();
554 if( !pViewForwarder
)
557 if( pViewForwarder
->IsValid() )
562 catch( const uno::RuntimeException
& )
568 void AccessibleTextHelper_Impl::UpdateSelection()
570 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
574 ESelection aSelection
;
575 if( GetEditViewForwarder().GetSelection( aSelection
) )
577 if( !maLastSelection
.IsEqual( aSelection
) &&
578 aSelection
.nEndPara
< maParaManager
.GetNum() )
580 // #103998# Not that important, changed from assertion to trace
583 DBG_TRACE("AccessibleTextHelper_Impl::UpdateSelection(): Parent has focus!");
586 USHORT
nMaxValidParaIndex( static_cast< USHORT
>( GetTextForwarder().GetParagraphCount() ) - 1 );
588 // notify all affected paragraphs (TODO: may be suboptimal,
589 // since some paragraphs might stay selected)
590 if( maLastSelection
.nStartPara
!= EE_PARA_NOT_FOUND
)
592 // Did the caret move from one paragraph to another?
593 // #100530# no caret events if not focused.
594 if( mbGroupHasFocus
&&
595 maLastSelection
.nEndPara
!= aSelection
.nEndPara
)
597 if( maLastSelection
.nEndPara
< maParaManager
.GetNum() )
599 maParaManager
.FireEvent( ::std::min( maLastSelection
.nEndPara
, nMaxValidParaIndex
),
600 ::std::min( maLastSelection
.nEndPara
, nMaxValidParaIndex
)+1,
601 AccessibleEventId::CARET_CHANGED
,
602 uno::makeAny(static_cast<sal_Int32
>(-1)),
603 uno::makeAny(static_cast<sal_Int32
>(maLastSelection
.nEndPos
)) );
606 ChangeChildFocus( aSelection
.nEndPara
);
608 DBG_TRACE3("AccessibleTextHelper_Impl::UpdateSelection(): focus changed, Object: %d, Paragraph: %d, Last paragraph: %d",
609 this, aSelection
.nEndPara
, maLastSelection
.nEndPara
);
613 // #100530# no caret events if not focused.
614 if( mbGroupHasFocus
)
618 // #i13705# The old cursor can only contain valid
619 // values if it's the same paragraph!
620 if( maLastSelection
.nStartPara
!= EE_PARA_NOT_FOUND
&&
621 maLastSelection
.nEndPara
== aSelection
.nEndPara
)
623 aOldCursor
<<= static_cast<sal_Int32
>(maLastSelection
.nEndPos
);
627 aOldCursor
<<= static_cast<sal_Int32
>(-1);
630 maParaManager
.FireEvent( aSelection
.nEndPara
,
631 aSelection
.nEndPara
+1,
632 AccessibleEventId::CARET_CHANGED
,
633 uno::makeAny(static_cast<sal_Int32
>(aSelection
.nEndPos
)),
637 DBG_TRACE5("AccessibleTextHelper_Impl::UpdateSelection(): caret changed, Object: %d, New pos: %d, Old pos: %d, New para: %d, Old para: %d",
638 this, aSelection
.nEndPos
, maLastSelection
.nEndPos
, aSelection
.nEndPara
, maLastSelection
.nEndPara
);
640 // #108947# Sort new range before calling FireEvent
641 ::std::pair
< xub_StrLen
, xub_StrLen
> sortedSelection(
642 makeSortedPair(::std::min( aSelection
.nStartPara
, nMaxValidParaIndex
),
643 ::std::min( aSelection
.nEndPara
, nMaxValidParaIndex
) ) );
645 // #108947# Sort last range before calling FireEvent
646 ::std::pair
< xub_StrLen
, xub_StrLen
> sortedLastSelection(
647 makeSortedPair(::std::min( maLastSelection
.nStartPara
, nMaxValidParaIndex
),
648 ::std::min( maLastSelection
.nEndPara
, nMaxValidParaIndex
) ) );
650 // --> OD 2005-12-15 #i27299#
651 // event TEXT_SELECTION_CHANGED has to be submitted.
652 const sal_Int16 nTextSelChgEventId
=
653 AccessibleEventId::TEXT_SELECTION_CHANGED
;
655 // #107037# notify selection change
656 if( maLastSelection
.nStartPara
== EE_PARA_NOT_FOUND
)
658 // last selection is undefined
659 // --> OD 2005-12-15 #i27299# - use method <ESelection::HasRange()>
660 if ( aSelection
.HasRange() )
663 // selection was undefined, now is on
664 maParaManager
.FireEvent( sortedSelection
.first
,
665 sortedSelection
.second
+1,
666 nTextSelChgEventId
);
671 // last selection is valid
672 // --> OD 2005-12-15 #i27299# - use method <ESelection::HasRange()>
673 if ( maLastSelection
.HasRange() &&
674 !aSelection
.HasRange() )
677 // selection was on, now is empty
678 maParaManager
.FireEvent( sortedLastSelection
.first
,
679 sortedLastSelection
.second
+1,
680 nTextSelChgEventId
);
682 // --> OD 2005-12-15 #i27299# - use method <ESelection::HasRange()>
683 else if( !maLastSelection
.HasRange() &&
684 aSelection
.HasRange() )
687 // selection was empty, now is on
688 maParaManager
.FireEvent( sortedSelection
.first
,
689 sortedSelection
.second
+1,
690 nTextSelChgEventId
);
692 // --> OD 2005-12-15 #i27299#
693 // - no event TEXT_SELECTION_CHANGED event, if new and
694 // last selection are empty.
695 else if ( maLastSelection
.HasRange() &&
696 aSelection
.HasRange() )
699 // --> OD 2005-12-16 #i27299#
700 // - send event TEXT_SELECTION_CHANGED for difference
701 // between last and new selection.
702 // // selection was on, now is different: take union of ranges
703 // maParaManager.FireEvent( ::std::min(sortedSelection.first,
704 // sortedLastSelection.second),
705 // ::std::max(sortedSelection.first,
706 // sortedLastSelection.second)+1,
707 // nTextSelChgEventId );
708 // use sorted last and new selection
709 ESelection
aTmpLastSel( maLastSelection
);
710 aTmpLastSel
.Adjust();
711 ESelection
aTmpSel( aSelection
);
713 // first submit event for new and changed selection
714 sal_uInt32 nPara
= aTmpSel
.nStartPara
;
715 for ( ; nPara
<= aTmpSel
.nEndPara
; ++nPara
)
717 if ( nPara
< aTmpLastSel
.nStartPara
||
718 nPara
> aTmpLastSel
.nEndPara
)
720 // new selection on paragraph <nPara>
721 maParaManager
.FireEvent( nPara
,
722 nTextSelChgEventId
);
726 // check for changed selection on paragraph <nPara>
727 const xub_StrLen nParaStartPos
=
728 nPara
== aTmpSel
.nStartPara
729 ? aTmpSel
.nStartPos
: 0;
730 const xub_StrLen nParaEndPos
=
731 nPara
== aTmpSel
.nEndPara
732 ? aTmpSel
.nEndPos
: STRING_LEN
;
733 const xub_StrLen nLastParaStartPos
=
734 nPara
== aTmpLastSel
.nStartPara
735 ? aTmpLastSel
.nStartPos
: 0;
736 const xub_StrLen nLastParaEndPos
=
737 nPara
== aTmpLastSel
.nEndPara
738 ? aTmpLastSel
.nEndPos
: STRING_LEN
;
739 if ( nParaStartPos
!= nLastParaStartPos
||
740 nParaEndPos
!= nLastParaEndPos
)
742 maParaManager
.FireEvent(
743 nPara
, nTextSelChgEventId
);
747 // second submit event for 'old' selections
748 nPara
= aTmpLastSel
.nStartPara
;
749 for ( ; nPara
<= aTmpLastSel
.nEndPara
; ++nPara
)
751 if ( nPara
< aTmpSel
.nStartPara
||
752 nPara
> aTmpSel
.nEndPara
)
754 maParaManager
.FireEvent( nPara
,
755 nTextSelChgEventId
);
761 maLastSelection
= aSelection
;
765 // no selection? no update actions
766 catch( const uno::RuntimeException
& ) {}
769 void AccessibleTextHelper_Impl::ShutdownEditSource() SAL_THROW((uno::RuntimeException
))
771 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
773 // This should only be called with solar mutex locked, i.e. from the main office thread
775 // This here is somewhat clumsy: As soon as our children have
776 // a NULL EditSource (maParaManager.SetEditSource()), they
777 // enter the disposed state and cannot be reanimated. Thus, it
778 // is unavoidable and a hard requirement to let go and create
779 // from scratch each and every child.
781 // invalidate children
782 maParaManager
.Dispose();
783 maParaManager
.SetNum(0);
786 if( mxFrontEnd
.is() )
787 FireEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN
);
789 // quit listen on stale edit source
790 if( maEditSource
.IsValid() )
791 EndListening( maEditSource
.GetBroadcaster() );
793 maEditSource
.SetEditSource( ::std::auto_ptr
< SvxEditSource
>(NULL
) );
796 void AccessibleTextHelper_Impl::SetEditSource( ::std::auto_ptr
< SvxEditSource
> pEditSource
) SAL_THROW((uno::RuntimeException
))
798 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
800 // This should only be called with solar mutex locked, i.e. from the main office thread
802 // shutdown old edit source
803 ShutdownEditSource();
805 // set new edit source
806 maEditSource
.SetEditSource( pEditSource
);
808 // init child vector to the current child count
809 if( maEditSource
.IsValid() )
811 maParaManager
.SetNum( GetTextForwarder().GetParagraphCount() );
813 // listen on new edit source
814 StartListening( maEditSource
.GetBroadcaster() );
816 UpdateVisibleChildren();
820 void AccessibleTextHelper_Impl::SetOffset( const Point
& rPoint
)
822 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
824 // guard against non-atomic access to maOffset data structure
826 ::osl::MutexGuard
aGuard( maMutex
);
830 maParaManager
.SetEEOffset( rPoint
);
832 // in all cases, check visibility afterwards.
833 UpdateVisibleChildren();
837 void AccessibleTextHelper_Impl::UpdateVisibleChildren( bool bBroadcastEvents
)
839 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
843 SvxTextForwarder
& rCacheTF
= GetTextForwarder();
844 SvxViewForwarder
& rCacheVF
= GetViewForwarder();
846 Rectangle aViewArea
= rCacheVF
.GetVisArea();
850 // maybe the edit view scrolls, adapt aViewArea
851 Rectangle aEditViewArea
= GetEditViewForwarder().GetVisArea();
852 aViewArea
+= aEditViewArea
.TopLeft();
854 // now determine intersection
855 aViewArea
.Intersection( aEditViewArea
);
858 Rectangle aTmpBB
, aParaBB
;
859 sal_Bool bFirstChild
= sal_True
;
861 sal_Int32 nParas
=rCacheTF
.GetParagraphCount();
863 mnFirstVisibleChild
= -1;
864 mnLastVisibleChild
= -2;
866 for( nCurrPara
=0; nCurrPara
<nParas
; ++nCurrPara
)
868 DBG_ASSERT(nCurrPara
>= 0 && nCurrPara
<= USHRT_MAX
,
869 "AccessibleTextHelper_Impl::UpdateVisibleChildren: index value overflow");
871 aTmpBB
= rCacheTF
.GetParaBounds( static_cast< USHORT
>( nCurrPara
) );
873 // convert to screen coordinates
874 aParaBB
= ::accessibility::AccessibleEditableTextPara::LogicToPixel( aTmpBB
, rCacheTF
.GetMapMode(), rCacheVF
);
876 if( aParaBB
.IsOver( aViewArea
) )
878 // at least partially visible
881 bFirstChild
= sal_False
;
882 mnFirstVisibleChild
= nCurrPara
;
885 mnLastVisibleChild
= nCurrPara
;
887 // child not yet created?
888 ::accessibility::AccessibleParaManager::WeakChild
aChild( maParaManager
.GetChild(nCurrPara
) );
889 if( aChild
.second
.Width
== 0 &&
890 aChild
.second
.Height
== 0 &&
894 GotPropertyEvent( uno::makeAny( maParaManager
.CreateChild( nCurrPara
- mnFirstVisibleChild
,
895 mxFrontEnd
, GetEditSource(), nCurrPara
).first
),
896 AccessibleEventId::CHILD
);
901 // not or no longer visible
902 if( maParaManager
.IsReferencable( nCurrPara
) )
904 if( bBroadcastEvents
)
905 LostPropertyEvent( uno::makeAny( maParaManager
.GetChild( nCurrPara
).first
.get().getRef() ),
906 AccessibleEventId::CHILD
);
909 maParaManager
.Release( nCurrPara
);
914 catch( const uno::Exception
& )
916 DBG_ERROR("AccessibleTextHelper_Impl::UpdateVisibleChildren error while determining visible children");
918 // something failed - currently no children
919 mnFirstVisibleChild
= -1;
920 mnLastVisibleChild
= -2;
921 maParaManager
.SetNum(0);
924 if( bBroadcastEvents
)
925 FireEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN
);
929 // functor for checking changes in paragraph bounding boxes (no stand-alone function, maybe not inlined)
930 class AccessibleTextHelper_UpdateChildBounds
: public ::std::unary_function
< const ::accessibility::AccessibleParaManager::WeakChild
&,
931 ::accessibility::AccessibleParaManager::WeakChild
>
934 AccessibleTextHelper_UpdateChildBounds( AccessibleTextHelper_Impl
& rImpl
) : mrImpl(rImpl
) {}
935 ::accessibility::AccessibleParaManager::WeakChild
operator()( const ::accessibility::AccessibleParaManager::WeakChild
& rChild
)
937 // retrieve hard reference from weak one
938 ::accessibility::AccessibleParaManager::WeakPara::HardRefType
aHardRef( rChild
.first
.get() );
942 awt::Rectangle aNewRect
= aHardRef
->getBounds();
943 const awt::Rectangle
& aOldRect
= rChild
.second
;
945 if( aNewRect
.X
!= aOldRect
.X
||
946 aNewRect
.Y
!= aOldRect
.Y
||
947 aNewRect
.Width
!= aOldRect
.Width
||
948 aNewRect
.Height
!= aOldRect
.Height
)
950 // visible data changed
951 aHardRef
->FireEvent( AccessibleEventId::BOUNDRECT_CHANGED
);
953 // update internal bounds
954 return ::accessibility::AccessibleParaManager::WeakChild( rChild
.first
, aNewRect
);
958 // identity transform
963 AccessibleTextHelper_Impl
& mrImpl
;
966 void AccessibleTextHelper_Impl::UpdateBoundRect()
968 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
970 // send BOUNDRECT_CHANGED to affected children
971 AccessibleTextHelper_UpdateChildBounds
aFunctor( *this );
972 ::std::transform( maParaManager
.begin(), maParaManager
.end(), maParaManager
.begin(), aFunctor
);
976 void AccessibleTextHelper_Impl::CheckInvariants() const
978 if( mnFirstVisibleChild
>= 0 &&
979 mnFirstVisibleChild
> mnLastVisibleChild
)
981 DBG_ERROR( "AccessibleTextHelper: range invalid" );
986 // functor for sending child events (no stand-alone function, they are maybe not inlined)
987 class AccessibleTextHelper_LostChildEvent
: public ::std::unary_function
< const ::accessibility::AccessibleParaManager::WeakChild
&, void >
990 AccessibleTextHelper_LostChildEvent( AccessibleTextHelper_Impl
& rImpl
) : mrImpl(rImpl
) {}
991 void operator()( const ::accessibility::AccessibleParaManager::WeakChild
& rPara
)
993 // retrieve hard reference from weak one
994 ::accessibility::AccessibleParaManager::WeakPara::HardRefType
aHardRef( rPara
.first
.get() );
997 mrImpl
.FireEvent(AccessibleEventId::CHILD
, uno::Any(), uno::makeAny( aHardRef
.getRef() ) );
1001 AccessibleTextHelper_Impl
& mrImpl
;
1004 void AccessibleTextHelper_Impl::ParagraphsMoved( sal_Int32 nFirst
, sal_Int32 nMiddle
, sal_Int32 nLast
)
1006 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
1008 const sal_Int32 nParas
= GetTextForwarder().GetParagraphCount();
1010 /* rotate paragraphs
1016 * ... nParagraph ... nParam1 ... nParam2 ...
1017 * |______________[xxxxxxxxxxx]
1019 * [xxxxxxxxxxx]|______________
1024 * ... nParam1 ... nParagraph ... nParam2 ...
1025 * [xxxxxxxxxxx|xxxxxxxxxxxxxx]____________
1027 * ____________[xxxxxxxxxxx|xxxxxxxxxxxxxx]
1029 * tail is nParagraph - nParam1
1032 * ... nParam1 ... nParam2 ... nParagraph ...
1033 * [xxxxxxxxxxx]___________|____________
1035 * ___________|____________[xxxxxxxxxxx]
1037 * tail is nParam2 - nParam1
1040 // sort nParagraph, nParam1 and nParam2 in ascending order, calc range
1041 if( nMiddle
< nFirst
)
1043 ::std::swap(nFirst
, nMiddle
);
1045 else if( nMiddle
< nLast
)
1047 nLast
= nLast
+ nMiddle
- nFirst
;
1051 ::std::swap(nMiddle
, nLast
);
1052 nLast
= nLast
+ nMiddle
- nFirst
;
1055 if( nFirst
< nParas
&& nMiddle
< nParas
&& nLast
< nParas
)
1057 // since we have no "paragraph index
1058 // changed" event on UAA, remove
1059 // [first,last] and insert again later (in
1060 // UpdateVisibleChildren)
1062 // maParaManager.Rotate( nFirst, nMiddle, nLast );
1064 // send CHILD_EVENT to affected children
1065 ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator begin
= maParaManager
.begin();
1066 ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator end
= begin
;
1068 ::std::advance( begin
, nFirst
);
1069 ::std::advance( end
, nLast
+1 );
1071 // TODO: maybe optimize here in the following way. If the
1072 // number of removed children exceeds a certain threshold,
1073 // use INVALIDATE_CHILDREN
1074 AccessibleTextHelper_LostChildEvent
aFunctor( *this );
1076 ::std::for_each( begin
, end
, aFunctor
);
1078 maParaManager
.Release(nFirst
, nLast
+1);
1079 // should be no need for UpdateBoundRect, since all affected children are cleared.
1083 // functor for sending child events (no stand-alone function, they are maybe not inlined)
1084 class AccessibleTextHelper_ChildrenTextChanged
: public ::std::unary_function
< ::accessibility::AccessibleEditableTextPara
&, void >
1087 void operator()( ::accessibility::AccessibleEditableTextPara
& rPara
)
1089 rPara
.TextChanged();
1093 /** functor processing queue events
1095 Reacts on TEXT_HINT_PARAINSERTED/REMOVED events and stores
1098 class AccessibleTextHelper_QueueFunctor
: public ::std::unary_function
< const SfxHint
*, void >
1101 AccessibleTextHelper_QueueFunctor() :
1102 mnParasChanged( 0 ),
1106 void operator()( const SfxHint
* pEvent
)
1109 mnParasChanged
!= -1 )
1111 // determine hint type
1112 const TextHint
* pTextHint
= PTR_CAST( TextHint
, pEvent
);
1113 const SvxEditSourceHint
* pEditSourceHint
= PTR_CAST( SvxEditSourceHint
, pEvent
);
1115 if( !pEditSourceHint
&& pTextHint
&&
1116 (pTextHint
->GetId() == TEXT_HINT_PARAINSERTED
||
1117 pTextHint
->GetId() == TEXT_HINT_PARAREMOVED
) )
1119 if( pTextHint
->GetValue() == EE_PARA_ALL
)
1121 mnParasChanged
= -1;
1125 mnHintId
= pTextHint
->GetId();
1126 mnParaIndex
= pTextHint
->GetValue();
1133 /** Query number of paragraphs changed during queue processing.
1135 @return number of changed paragraphs, -1 for
1136 "every paragraph changed"
1138 int GetNumberOfParasChanged() { return mnParasChanged
; }
1139 /** Query index of last added/removed paragraph
1141 @return index of lastly added paragraphs, -1 for none
1144 int GetParaIndex() { return mnParaIndex
; }
1145 /** Query hint id of last interesting event
1147 @return hint id of last interesting event (REMOVED/INSERTED).
1149 int GetHintId() { return mnHintId
; }
1152 /** number of paragraphs changed during queue processing. -1 for
1153 "every paragraph changed"
1156 /// index of paragraph added/removed last
1158 /// TextHint ID (removed/inserted) of last interesting event
1162 void AccessibleTextHelper_Impl::ProcessQueue()
1164 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
1166 // inspect queue for paragraph insert/remove events. If there
1167 // is exactly _one_ of those in the queue, and the number of
1168 // paragraphs has changed by exactly one, use that event to
1169 // determine a priori which paragraph was added/removed. This
1170 // is necessary, since I must sync right here with the
1171 // EditEngine state (number of paragraphs etc.), since I'm
1172 // potentially sending listener events right away.
1173 AccessibleTextHelper_QueueFunctor aFunctor
;
1174 maEventQueue
.ForEach( aFunctor
);
1176 const sal_Int32
nNewParas( GetTextForwarder().GetParagraphCount() );
1177 const sal_Int32
nCurrParas( maParaManager
.GetNum() );
1179 // whether every paragraph already is updated (no need to
1180 // repeat that later on, e.g. for PARA_MOVED events)
1181 bool bEverythingUpdated( false );
1183 if( labs( nNewParas
- nCurrParas
) == 1 &&
1184 aFunctor
.GetNumberOfParasChanged() == 1 )
1186 // #103483# Exactly one paragraph added/removed. This is
1187 // the normal case, optimize event handling here.
1189 if( aFunctor
.GetHintId() == TEXT_HINT_PARAINSERTED
)
1191 // update num of paras
1192 maParaManager
.SetNum( nNewParas
);
1194 // release everything from the insertion position until the end
1195 maParaManager
.Release(aFunctor
.GetParaIndex(), nCurrParas
);
1197 // TODO: Clarify whether this behaviour _really_ saves
1198 // anybody anything!
1199 // update children, _don't_ broadcast
1200 UpdateVisibleChildren( false );
1203 // send insert event
1204 // #109864# Enforce creation of this paragraph
1207 GotPropertyEvent( uno::makeAny( getAccessibleChild( aFunctor
.GetParaIndex() -
1208 mnFirstVisibleChild
+ GetStartIndex() ) ),
1209 AccessibleEventId::CHILD
);
1211 catch( const uno::Exception
& )
1213 DBG_ERROR("AccessibleTextHelper_Impl::ProcessQueue: could not create new paragraph");
1216 else if( aFunctor
.GetHintId() == TEXT_HINT_PARAREMOVED
)
1218 ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator begin
= maParaManager
.begin();
1219 ::std::advance( begin
, aFunctor
.GetParaIndex() );
1220 ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator end
= begin
;
1221 ::std::advance( end
, 1 );
1223 // #i61812# remember para to be removed for later notification
1224 // AFTER the new state is applied (that after the para got removed)
1225 ::uno::Reference
< XAccessible
> xPara
;
1226 ::accessibility::AccessibleParaManager::WeakPara::HardRefType
aHardRef( begin
->first
.get() );
1228 xPara
= ::uno::Reference
< XAccessible
>( aHardRef
.getRef(), ::uno::UNO_QUERY
);
1230 // release everything from the remove position until the end
1231 maParaManager
.Release(aFunctor
.GetParaIndex(), nCurrParas
);
1233 // update num of paras
1234 maParaManager
.SetNum( nNewParas
);
1236 // TODO: Clarify whether this behaviour _really_ saves
1237 // anybody anything!
1238 // update children, _don't_ broadcast
1239 UpdateVisibleChildren( false );
1242 // #i61812# notification for removed para
1244 FireEvent(AccessibleEventId::CHILD
, uno::Any(), uno::makeAny( xPara
) );
1248 DBG_ERROR("AccessibleTextHelper_Impl::ProcessQueue() invalid hint id");
1251 else if( nNewParas
!= nCurrParas
)
1253 // release all paras
1254 maParaManager
.Release(0, nCurrParas
);
1256 // update num of paras
1257 maParaManager
.SetNum( nNewParas
);
1259 // #109864# create from scratch, don't broadcast
1260 UpdateVisibleChildren( false );
1263 // number of paragraphs somehow changed - but we have no
1264 // chance determining how. Thus, throw away everything and
1265 // create from scratch.
1266 // (child events should be broadcast after the changes are done...)
1267 FireEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN
);
1269 // no need for further updates later on
1270 bEverythingUpdated
= true;
1273 while( !maEventQueue
.IsEmpty() )
1275 ::std::auto_ptr
< SfxHint
> pHint( maEventQueue
.PopFront() );
1278 const SfxHint
& rHint
= *(pHint
.get());
1280 // determine hint type
1281 const SdrHint
* pSdrHint
= PTR_CAST( SdrHint
, &rHint
);
1282 const SfxSimpleHint
* pSimpleHint
= PTR_CAST( SfxSimpleHint
, &rHint
);
1283 const TextHint
* pTextHint
= PTR_CAST( TextHint
, &rHint
);
1284 const SvxViewHint
* pViewHint
= PTR_CAST( SvxViewHint
, &rHint
);
1285 const SvxEditSourceHint
* pEditSourceHint
= PTR_CAST( SvxEditSourceHint
, &rHint
);
1289 const sal_Int32 nParas
= GetTextForwarder().GetParagraphCount();
1291 if( pEditSourceHint
)
1293 switch( pEditSourceHint
->GetId() )
1295 case EDITSOURCE_HINT_PARASMOVED
:
1297 DBG_ASSERT( pEditSourceHint
->GetStartValue() < GetTextForwarder().GetParagraphCount() &&
1298 pEditSourceHint
->GetEndValue() < GetTextForwarder().GetParagraphCount(),
1299 "AccessibleTextHelper_Impl::NotifyHdl: Invalid notification");
1301 if( !bEverythingUpdated
)
1303 ParagraphsMoved(pEditSourceHint
->GetStartValue(),
1304 pEditSourceHint
->GetValue(),
1305 pEditSourceHint
->GetEndValue());
1307 // in all cases, check visibility afterwards.
1308 UpdateVisibleChildren();
1313 case EDITSOURCE_HINT_SELECTIONCHANGED
:
1319 // maybe we're not in edit mode (this is not an error)
1320 catch( const uno::Exception
& ) {}
1324 else if( pTextHint
)
1326 switch( pTextHint
->GetId() )
1328 case TEXT_HINT_MODIFIED
:
1331 sal_Int32
nPara( pTextHint
->GetValue() );
1333 // #108900# Delegate change event to children
1334 AccessibleTextHelper_ChildrenTextChanged aNotifyChildrenFunctor
;
1336 if( nPara
== static_cast<sal_Int32
>(EE_PARA_ALL
) )
1338 // #108900# Call every child
1339 ::std::for_each( maParaManager
.begin(), maParaManager
.end(),
1340 AccessibleParaManager::WeakChildAdapter
< AccessibleTextHelper_ChildrenTextChanged
> (aNotifyChildrenFunctor
) );
1343 if( nPara
< nParas
)
1345 // #108900# Call child at index nPara
1346 ::std::for_each( maParaManager
.begin()+nPara
, maParaManager
.begin()+nPara
+1,
1347 AccessibleParaManager::WeakChildAdapter
< AccessibleTextHelper_ChildrenTextChanged
> (aNotifyChildrenFunctor
) );
1352 case TEXT_HINT_PARAINSERTED
:
1353 // already happened above
1356 case TEXT_HINT_PARAREMOVED
:
1357 // already happened above
1360 case TEXT_HINT_TEXTHEIGHTCHANGED
:
1361 // visibility changed, done below
1364 case TEXT_HINT_VIEWSCROLLED
:
1365 // visibility changed, done below
1369 // in all cases, check visibility afterwards.
1370 UpdateVisibleChildren();
1373 else if( pViewHint
)
1375 switch( pViewHint
->GetHintType() )
1377 case SvxViewHint::SVX_HINT_VIEWCHANGED
:
1378 // just check visibility
1379 UpdateVisibleChildren();
1386 switch( pSdrHint
->GetKind() )
1390 // change children state
1391 maParaManager
.SetActive();
1393 // per definition, edit mode text has the focus
1394 SetFocus( sal_True
);
1400 // focused child now looses focus
1401 ESelection aSelection
;
1402 if( GetEditViewForwarder().GetSelection( aSelection
) )
1403 SetChildFocus( aSelection
.nEndPara
, sal_False
);
1405 // change children state
1406 maParaManager
.SetActive( sal_False
);
1408 maLastSelection
= ESelection( EE_PARA_NOT_FOUND
, EE_PARA_NOT_FOUND
,
1409 EE_PARA_NOT_FOUND
, EE_PARA_NOT_FOUND
);
1416 // it's VITAL to keep the SfxSimpleHint last! It's the base of some classes above!
1417 else if( pSimpleHint
)
1419 switch( pSimpleHint
->GetId() )
1421 case SFX_HINT_DYING
:
1422 // edit source is dying under us, become defunc then
1425 // make edit source inaccessible
1426 // Note: cannot destroy it here, since we're called from there!
1427 ShutdownEditSource();
1429 catch( const uno::Exception
& ) {}
1435 catch( const uno::Exception
& )
1438 OSL_TRACE("AccessibleTextHelper_Impl::ProcessQueue: Unhandled exception.");
1445 void AccessibleTextHelper_Impl::Notify( SfxBroadcaster
& /*rBC*/, const SfxHint
& rHint
)
1447 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
1449 // precondition: solar mutex locked
1450 DBG_TESTSOLARMUTEX();
1452 // precondition: not in a recursion
1456 mbInNotify
= sal_True
;
1458 // determine hint type
1459 const SdrHint
* pSdrHint
= PTR_CAST( SdrHint
, &rHint
);
1460 const SfxSimpleHint
* pSimpleHint
= PTR_CAST( SfxSimpleHint
, &rHint
);
1461 const TextHint
* pTextHint
= PTR_CAST( TextHint
, &rHint
);
1462 const SvxViewHint
* pViewHint
= PTR_CAST( SvxViewHint
, &rHint
);
1463 const SvxEditSourceHint
* pEditSourceHint
= PTR_CAST( SvxEditSourceHint
, &rHint
);
1467 // Process notification event
1468 if( pEditSourceHint
)
1470 maEventQueue
.Append( *pEditSourceHint
);
1471 // --> OD 2005-12-19 #i27299#
1472 if( maEventOpenFrames
== 0 )
1476 else if( pTextHint
)
1478 switch( pTextHint
->GetId() )
1480 case TEXT_HINT_BLOCKNOTIFICATION_END
:
1481 case TEXT_HINT_INPUT_END
:
1482 --maEventOpenFrames
;
1484 if( maEventOpenFrames
== 0 )
1487 /* All information should have arrived
1488 * now, process queue. As stated in the
1489 * above bug, we can often avoid throwing
1490 * away all paragraphs by looking forward
1491 * in the event queue (searching for
1492 * PARAINSERT/REMOVE events). Furthermore,
1493 * processing the event queue only at the
1494 * end of an interaction cycle, ensures
1495 * that the EditEngine state and the
1496 * AccessibleText state are the same
1497 * (well, mostly. If there are _multiple_
1498 * interaction cycles in the EE queues, it
1499 * can still happen that EE state is
1500 * different. That's so to say broken by
1501 * design with that delayed EE event
1508 case TEXT_HINT_BLOCKNOTIFICATION_START
:
1509 case TEXT_HINT_INPUT_START
:
1510 ++maEventOpenFrames
;
1511 // --> OD 2005-12-19 #i27299# - no FALLTROUGH
1512 // reason: event will not be processes, thus appending
1513 // the event isn't necessary.
1517 maEventQueue
.Append( *pTextHint
);
1518 // --> OD 2005-12-19 #i27299#
1519 if( maEventOpenFrames
== 0 )
1525 else if( pViewHint
)
1527 maEventQueue
.Append( *pViewHint
);
1529 // process visibility right away, if not within an
1530 // open EE notification frame. Otherwise, event
1531 // processing would be delayed until next EE
1532 // notification sequence.
1533 if( maEventOpenFrames
== 0 )
1538 maEventQueue
.Append( *pSdrHint
);
1540 // process drawing layer events right away, if not
1541 // within an open EE notification frame. Otherwise,
1542 // event processing would be delayed until next EE
1543 // notification sequence.
1544 if( maEventOpenFrames
== 0 )
1547 // it's VITAL to keep the SfxSimpleHint last! It's the base of some classes above!
1548 else if( pSimpleHint
)
1550 // handle this event _at once_, because after that, objects are invalid
1551 switch( pSimpleHint
->GetId() )
1553 case SFX_HINT_DYING
:
1554 // edit source is dying under us, become defunc then
1555 maEventQueue
.Clear();
1558 // make edit source inaccessible
1559 // Note: cannot destroy it here, since we're called from there!
1560 ShutdownEditSource();
1562 catch( const uno::Exception
& ) {}
1568 catch( const uno::Exception
& )
1571 OSL_TRACE("AccessibleTextHelper_Impl::Notify: Unhandled exception.");
1573 mbInNotify
= sal_False
;
1576 mbInNotify
= sal_False
;
1579 void AccessibleTextHelper_Impl::Dispose()
1581 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
1583 if( getNotifierClientId() != -1 )
1587 // #106234# Unregister from EventNotifier
1588 ::comphelper::AccessibleEventNotifier::revokeClient( getNotifierClientId() );
1590 OSL_TRACE( "AccessibleTextHelper_Impl disposed ID: %d", mnNotifierClientId
);
1593 catch( const uno::Exception
& ) {}
1595 mnNotifierClientId
= -1;
1601 maParaManager
.Dispose();
1603 catch( const uno::Exception
& ) {}
1605 // quit listen on stale edit source
1606 if( maEditSource
.IsValid() )
1607 EndListening( maEditSource
.GetBroadcaster() );
1610 maEditSource
.SetEditSource( ::std::auto_ptr
< SvxEditSource
>(NULL
) );
1614 void AccessibleTextHelper_Impl::FireEvent( const sal_Int16 nEventId
, const uno::Any
& rNewValue
, const uno::Any
& rOldValue
) const
1616 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
1618 // -- object locked --
1619 ::osl::ClearableMutexGuard
aGuard( maMutex
);
1621 AccessibleEventObject aEvent
;
1623 DBG_ASSERT(mxFrontEnd
.is(), "AccessibleTextHelper::FireEvent: no event source set" );
1625 if( mxFrontEnd
.is() )
1626 aEvent
= AccessibleEventObject(mxFrontEnd
->getAccessibleContext(), nEventId
, rNewValue
, rOldValue
);
1628 aEvent
= AccessibleEventObject(uno::Reference
< uno::XInterface
>(), nEventId
, rNewValue
, rOldValue
);
1630 // no locking necessary, FireEvent internally copies listeners
1631 // if someone removes/adds in between Further locking,
1632 // actually, might lead to deadlocks, since we're calling out
1640 void AccessibleTextHelper_Impl::FireEvent( const AccessibleEventObject
& rEvent
) const
1642 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
1644 // #102261# Call global queue for focus events
1645 if( rEvent
.EventId
== AccessibleStateType::FOCUSED
)
1646 vcl::unohelper::NotifyAccessibleStateEventGlobally( rEvent
);
1648 // #106234# Delegate to EventNotifier
1649 ::comphelper::AccessibleEventNotifier::addEvent( getNotifierClientId(),
1653 // XAccessibleContext
1654 sal_Int32 SAL_CALL
AccessibleTextHelper_Impl::getAccessibleChildCount() SAL_THROW((uno::RuntimeException
))
1656 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
1658 return mnLastVisibleChild
- mnFirstVisibleChild
+ 1;
1661 uno::Reference
< XAccessible
> SAL_CALL
AccessibleTextHelper_Impl::getAccessibleChild( sal_Int32 i
) SAL_THROW((lang::IndexOutOfBoundsException
, uno::RuntimeException
))
1663 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
1665 i
-= GetStartIndex();
1667 if( 0 > i
|| i
>= getAccessibleChildCount() ||
1668 GetTextForwarder().GetParagraphCount() <= i
)
1670 throw lang::IndexOutOfBoundsException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Invalid child index")), mxFrontEnd
);
1673 DBG_ASSERT(mxFrontEnd
.is(), "AccessibleTextHelper_Impl::UpdateVisibleChildren: no frontend set");
1675 if( mxFrontEnd
.is() )
1676 return maParaManager
.CreateChild( i
, mxFrontEnd
, GetEditSource(), mnFirstVisibleChild
+ i
).first
;
1681 void SAL_CALL
AccessibleTextHelper_Impl::addEventListener( const uno::Reference
< XAccessibleEventListener
>& xListener
) SAL_THROW((uno::RuntimeException
))
1683 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
1685 if( getNotifierClientId() != -1 )
1686 ::comphelper::AccessibleEventNotifier::addEventListener( getNotifierClientId(), xListener
);
1689 void SAL_CALL
AccessibleTextHelper_Impl::removeEventListener( const uno::Reference
< XAccessibleEventListener
>& xListener
) SAL_THROW((uno::RuntimeException
))
1691 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
1693 if( getNotifierClientId() != -1 )
1694 ::comphelper::AccessibleEventNotifier::removeEventListener( getNotifierClientId(), xListener
);
1697 uno::Reference
< XAccessible
> SAL_CALL
AccessibleTextHelper_Impl::getAccessibleAtPoint( const awt::Point
& _aPoint
) SAL_THROW((uno::RuntimeException
))
1699 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
1701 // make given position relative
1702 if( !mxFrontEnd
.is() )
1703 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("AccessibleTextHelper_Impl::getAccessibleAt: frontend invalid")), mxFrontEnd
);
1705 uno::Reference
< XAccessibleContext
> xFrontEndContext
= mxFrontEnd
->getAccessibleContext();
1707 if( !xFrontEndContext
.is() )
1708 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("AccessibleTextHelper_Impl::getAccessibleAt: frontend invalid")), mxFrontEnd
);
1710 uno::Reference
< XAccessibleComponent
> xFrontEndComponent( xFrontEndContext
, uno::UNO_QUERY
);
1712 if( !xFrontEndComponent
.is() )
1713 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("AccessibleTextHelper_Impl::getAccessibleAt: frontend is no XAccessibleComponent")),
1716 // #103862# No longer need to make given position relative
1717 Point
aPoint( _aPoint
.X
, _aPoint
.Y
);
1719 // respect EditEngine offset to surrounding shape/cell
1720 aPoint
-= GetOffset();
1722 // convert to EditEngine coordinate system
1723 SvxTextForwarder
& rCacheTF
= GetTextForwarder();
1724 Point
aLogPoint( GetViewForwarder().PixelToLogic( aPoint
, rCacheTF
.GetMapMode() ) );
1726 // iterate over all visible children (including those not yet created)
1728 for( nChild
=mnFirstVisibleChild
; nChild
<= mnLastVisibleChild
; ++nChild
)
1730 DBG_ASSERT(nChild
>= 0 && nChild
<= USHRT_MAX
,
1731 "AccessibleTextHelper_Impl::getAccessibleAt: index value overflow");
1733 Rectangle
aParaBounds( rCacheTF
.GetParaBounds( static_cast< USHORT
> (nChild
) ) );
1735 if( aParaBounds
.IsInside( aLogPoint
) )
1736 return getAccessibleChild( nChild
- mnFirstVisibleChild
+ GetStartIndex() );
1743 //------------------------------------------------------------------------
1745 // AccessibleTextHelper implementation (simply forwards to impl)
1747 //------------------------------------------------------------------------
1749 AccessibleTextHelper::AccessibleTextHelper( ::std::auto_ptr
< SvxEditSource
> pEditSource
) :
1750 mpImpl( new AccessibleTextHelper_Impl() )
1752 ::vos::OGuard
aGuard( Application::GetSolarMutex() );
1754 SetEditSource( pEditSource
);
1757 AccessibleTextHelper::~AccessibleTextHelper()
1761 const SvxEditSource
& AccessibleTextHelper::GetEditSource() const SAL_THROW((uno::RuntimeException
))
1764 mpImpl
->CheckInvariants();
1766 const SvxEditSource
& aEditSource
= mpImpl
->GetEditSource();
1768 mpImpl
->CheckInvariants();
1772 return mpImpl
->GetEditSource();
1776 void AccessibleTextHelper::SetEditSource( ::std::auto_ptr
< SvxEditSource
> pEditSource
) SAL_THROW((uno::RuntimeException
))
1779 // precondition: solar mutex locked
1780 DBG_TESTSOLARMUTEX();
1782 mpImpl
->CheckInvariants();
1785 mpImpl
->SetEditSource( pEditSource
);
1788 mpImpl
->CheckInvariants();
1792 void AccessibleTextHelper::SetEventSource( const uno::Reference
< XAccessible
>& rInterface
)
1795 mpImpl
->CheckInvariants();
1798 mpImpl
->SetEventSource( rInterface
);
1801 mpImpl
->CheckInvariants();
1805 uno::Reference
< XAccessible
> AccessibleTextHelper::GetEventSource() const
1808 mpImpl
->CheckInvariants();
1810 uno::Reference
< XAccessible
> xRet( mpImpl
->GetEventSource() );
1812 mpImpl
->CheckInvariants();
1816 return mpImpl
->GetEventSource();
1820 void AccessibleTextHelper::SetFocus( sal_Bool bHaveFocus
) SAL_THROW((::com::sun::star::uno::RuntimeException
))
1823 // precondition: solar mutex locked
1824 DBG_TESTSOLARMUTEX();
1826 mpImpl
->CheckInvariants();
1829 mpImpl
->SetFocus( bHaveFocus
);
1832 mpImpl
->CheckInvariants();
1836 sal_Bool
AccessibleTextHelper::HaveFocus() SAL_THROW((::com::sun::star::uno::RuntimeException
))
1839 mpImpl
->CheckInvariants();
1841 sal_Bool
bRet( mpImpl
->HaveFocus() );
1843 mpImpl
->CheckInvariants();
1847 return mpImpl
->HaveFocus();
1851 void AccessibleTextHelper::FireEvent( const sal_Int16 nEventId
, const uno::Any
& rNewValue
, const uno::Any
& rOldValue
) const
1854 mpImpl
->CheckInvariants();
1857 mpImpl
->FireEvent( nEventId
, rNewValue
, rOldValue
);
1860 mpImpl
->CheckInvariants();
1864 void AccessibleTextHelper::FireEvent( const AccessibleEventObject
& rEvent
) const
1867 mpImpl
->CheckInvariants();
1870 mpImpl
->FireEvent( rEvent
);
1873 mpImpl
->CheckInvariants();
1877 void AccessibleTextHelper::SetOffset( const Point
& rPoint
)
1880 // precondition: solar mutex locked
1881 DBG_TESTSOLARMUTEX();
1883 mpImpl
->CheckInvariants();
1886 mpImpl
->SetOffset( rPoint
);
1889 mpImpl
->CheckInvariants();
1893 Point
AccessibleTextHelper::GetOffset() const
1896 mpImpl
->CheckInvariants();
1898 Point
aPoint( mpImpl
->GetOffset() );
1900 mpImpl
->CheckInvariants();
1904 return mpImpl
->GetOffset();
1908 void AccessibleTextHelper::SetStartIndex( sal_Int32 nOffset
)
1911 // precondition: solar mutex locked
1912 DBG_TESTSOLARMUTEX();
1914 mpImpl
->CheckInvariants();
1917 mpImpl
->SetStartIndex( nOffset
);
1920 mpImpl
->CheckInvariants();
1924 sal_Int32
AccessibleTextHelper::GetStartIndex() const
1927 mpImpl
->CheckInvariants();
1929 sal_Int32 nOffset
= mpImpl
->GetStartIndex();
1931 mpImpl
->CheckInvariants();
1935 return mpImpl
->GetStartIndex();
1939 void AccessibleTextHelper::SetAdditionalChildStates( const VectorOfStates
& rChildStates
)
1941 mpImpl
->SetAdditionalChildStates( rChildStates
);
1944 const AccessibleTextHelper::VectorOfStates
& AccessibleTextHelper::GetAdditionalChildStates() const
1946 return mpImpl
->GetAdditionalChildStates();
1949 void AccessibleTextHelper::UpdateChildren() SAL_THROW((::com::sun::star::uno::RuntimeException
))
1952 // precondition: solar mutex locked
1953 DBG_TESTSOLARMUTEX();
1955 mpImpl
->CheckInvariants();
1958 mpImpl
->UpdateVisibleChildren();
1959 mpImpl
->UpdateBoundRect();
1961 mpImpl
->UpdateSelection();
1964 mpImpl
->CheckInvariants();
1968 void AccessibleTextHelper::Dispose()
1970 // As Dispose calls ShutdownEditSource, which in turn
1971 // deregisters as listener on the edit source, have to lock
1973 ::vos::OGuard
aGuard( Application::GetSolarMutex() );
1976 mpImpl
->CheckInvariants();
1982 mpImpl
->CheckInvariants();
1986 sal_Bool
AccessibleTextHelper::IsSelected() const
1988 ::vos::OGuard
aGuard( Application::GetSolarMutex() );
1991 mpImpl
->CheckInvariants();
1993 sal_Bool aRet
= mpImpl
->IsSelected();
1995 mpImpl
->CheckInvariants();
1999 return mpImpl
->IsSelected();
2003 // XAccessibleContext
2004 sal_Int32
AccessibleTextHelper::GetChildCount() SAL_THROW((uno::RuntimeException
))
2006 ::vos::OGuard
aGuard( Application::GetSolarMutex() );
2009 mpImpl
->CheckInvariants();
2011 sal_Int32 nRet
= mpImpl
->getAccessibleChildCount();
2013 mpImpl
->CheckInvariants();
2017 return mpImpl
->getAccessibleChildCount();
2021 uno::Reference
< XAccessible
> AccessibleTextHelper::GetChild( sal_Int32 i
) SAL_THROW((lang::IndexOutOfBoundsException
, uno::RuntimeException
))
2023 ::vos::OGuard
aGuard( Application::GetSolarMutex() );
2026 mpImpl
->CheckInvariants();
2028 uno::Reference
< XAccessible
> xRet
= mpImpl
->getAccessibleChild( i
);
2030 mpImpl
->CheckInvariants();
2034 return mpImpl
->getAccessibleChild( i
);
2038 void AccessibleTextHelper::AddEventListener( const uno::Reference
< XAccessibleEventListener
>& xListener
) SAL_THROW((uno::RuntimeException
))
2041 mpImpl
->CheckInvariants();
2043 mpImpl
->addEventListener( xListener
);
2045 mpImpl
->CheckInvariants();
2047 mpImpl
->addEventListener( xListener
);
2051 void AccessibleTextHelper::RemoveEventListener( const uno::Reference
< XAccessibleEventListener
>& xListener
) SAL_THROW((uno::RuntimeException
))
2054 mpImpl
->CheckInvariants();
2056 mpImpl
->removeEventListener( xListener
);
2058 mpImpl
->CheckInvariants();
2060 mpImpl
->removeEventListener( xListener
);
2064 // XAccessibleComponent
2065 uno::Reference
< XAccessible
> AccessibleTextHelper::GetAt( const awt::Point
& aPoint
) SAL_THROW((uno::RuntimeException
))
2067 ::vos::OGuard
aGuard( Application::GetSolarMutex() );
2070 mpImpl
->CheckInvariants();
2072 uno::Reference
< XAccessible
> xChild
= mpImpl
->getAccessibleAtPoint( aPoint
);
2074 mpImpl
->CheckInvariants();
2078 return mpImpl
->getAccessibleAtPoint( aPoint
);
2082 } // end of namespace accessibility
2084 //------------------------------------------------------------------------