1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*************************************************************************
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * Copyright 2000, 2010 Oracle and/or its affiliates.
8 * OpenOffice.org - a multi-platform office productivity suite
10 * This file is part of OpenOffice.org.
12 * OpenOffice.org is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU Lesser General Public License version 3
14 * only, as published by the Free Software Foundation.
16 * OpenOffice.org is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License version 3 for more details
20 * (a copy is included in the LICENSE file that accompanied this code).
22 * You should have received a copy of the GNU Lesser General Public License
23 * version 3 along with OpenOffice.org. If not, see
24 * <http://www.openoffice.org/license.html>
25 * for a copy of the LGPLv3 License.
27 ************************************************************************/
30 //------------------------------------------------------------------------
34 //------------------------------------------------------------------------
40 #include <osl/mutex.hxx>
41 #include <com/sun/star/uno/Any.hxx>
42 #include <com/sun/star/uno/Reference.hxx>
43 #include <cppuhelper/weakref.hxx>
44 #include <com/sun/star/awt/Point.hpp>
45 #include <com/sun/star/awt/Rectangle.hpp>
46 #include <com/sun/star/lang/DisposedException.hpp>
47 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
48 #include <com/sun/star/accessibility/XAccessible.hpp>
49 #include <com/sun/star/accessibility/XAccessibleContext.hpp>
50 #include <com/sun/star/accessibility/XAccessibleComponent.hpp>
51 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
52 #include <comphelper/accessibleeventnotifier.hxx>
53 #include <unotools/accessiblestatesethelper.hxx>
54 #include <vcl/unohelp.hxx>
55 #include <vcl/svapp.hxx>
57 //------------------------------------------------------------------------
59 // Project-local header
61 //------------------------------------------------------------------------
62 #include "AccessibleTextEventQueue.hxx"
63 #include <svx/AccessibleTextHelper.hxx>
64 #include <svx/unoshape.hxx>
65 #include "editeng/unolingu.hxx"
66 #include <editeng/unotext.hxx>
68 #include "editeng/unoedhlp.hxx"
69 #include "editeng/unopracc.hxx"
70 #include "editeng/AccessibleParaManager.hxx"
71 #include "editeng/AccessibleEditableTextPara.hxx"
72 #include <svx/svdmodel.hxx>
73 #include <svx/svdpntv.hxx>
74 #include <editeng/editdata.hxx>
75 #include <editeng/editeng.hxx>
76 #include <editeng/editview.hxx>
78 using namespace ::com::sun::star
;
79 using namespace ::com::sun::star::accessibility
;
81 namespace accessibility
84 //------------------------------------------------------------------------
86 // AccessibleTextHelper_Impl declaration
88 //------------------------------------------------------------------------
90 DBG_NAME( AccessibleTextHelper_Impl
)
92 template < typename first_type
, typename second_type
>
93 ::std::pair
< first_type
, second_type
> makeSortedPair( first_type first
,
97 return ::std::make_pair( second
, first
);
99 return ::std::make_pair( first
, second
);
102 class AccessibleTextHelper_Impl
: public SfxListener
106 typedef ::std::vector
< sal_Int16
> VectorOfStates
;
108 // receive pointer to our frontend class and view window
109 AccessibleTextHelper_Impl();
110 ~AccessibleTextHelper_Impl();
112 // XAccessibleContext child handling methods
113 sal_Int32 SAL_CALL
getAccessibleChildCount() SAL_THROW((uno::RuntimeException
));
114 uno::Reference
< XAccessible
> SAL_CALL
getAccessibleChild( sal_Int32 i
) SAL_THROW((lang::IndexOutOfBoundsException
, uno::RuntimeException
));
116 // XAccessibleEventBroadcaster child related methods
117 void SAL_CALL
addEventListener( const uno::Reference
< XAccessibleEventListener
>& xListener
) SAL_THROW((uno::RuntimeException
));
118 void SAL_CALL
removeEventListener( const uno::Reference
< XAccessibleEventListener
>& xListener
) SAL_THROW((uno::RuntimeException
));
120 // XAccessibleComponent child related methods
121 uno::Reference
< XAccessible
> SAL_CALL
getAccessibleAtPoint( const awt::Point
& aPoint
) SAL_THROW((uno::RuntimeException
));
123 SvxEditSourceAdapter
& GetEditSource() const SAL_THROW((uno::RuntimeException
));
124 SAL_WNODEPRECATED_DECLARATIONS_PUSH
125 void SetEditSource( ::std::auto_ptr
< SvxEditSource
> pEditSource
) SAL_THROW((uno::RuntimeException
));
126 SAL_WNODEPRECATED_DECLARATIONS_POP
128 void SetEventSource( const uno::Reference
< XAccessible
>& rInterface
)
130 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
131 mxFrontEnd
= rInterface
;
133 uno::Reference
< XAccessible
> GetEventSource() const
135 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
139 void SetOffset( const Point
& );
140 Point
GetOffset() const
142 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
143 ::osl::MutexGuard
aGuard( maMutex
); Point
aPoint( maOffset
);
147 void SetStartIndex( sal_Int32 nOffset
);
148 sal_Int32
GetStartIndex() const
150 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
151 // Strictly correct only with locked solar mutex, // but
152 // here we rely on the fact that sal_Int32 access is
157 void SetAdditionalChildStates( const VectorOfStates
& rChildStates
);
159 sal_Bool
IsSelected() const;
163 // do NOT hold object mutex when calling this! Danger of deadlock
164 void FireEvent( const sal_Int16 nEventId
, const uno::Any
& rNewValue
= uno::Any(), const uno::Any
& rOldValue
= uno::Any() ) const;
165 void FireEvent( const AccessibleEventObject
& rEvent
) const;
167 void SetFocus( sal_Bool bHaveFocus
) SAL_THROW((::com::sun::star::uno::RuntimeException
));
168 sal_Bool
HaveFocus() SAL_THROW((::com::sun::star::uno::RuntimeException
));
169 void SetChildFocus( sal_Int32 nChild
, sal_Bool bHaveFocus
) SAL_THROW((::com::sun::star::uno::RuntimeException
));
170 void SetShapeFocus( sal_Bool bHaveFocus
) SAL_THROW((::com::sun::star::uno::RuntimeException
));
171 void ChangeChildFocus( sal_Int32 nNewChild
) SAL_THROW((::com::sun::star::uno::RuntimeException
));
174 void CheckInvariants() const;
177 // checks all children for visibility, throws away invisible ones
178 void UpdateVisibleChildren( bool bBroadcastEvents
=true );
180 // check all children for changes in position and size
181 void UpdateBoundRect();
183 // calls SetSelection on the forwarder and updates maLastSelection
185 void UpdateSelection();
189 // Process event queue
192 // syntactic sugar for FireEvent
193 void GotPropertyEvent( const uno::Any
& rNewValue
, const sal_Int16 nEventId
) const { FireEvent( nEventId
, rNewValue
); }
194 void LostPropertyEvent( const uno::Any
& rOldValue
, const sal_Int16 nEventId
) const { FireEvent( nEventId
, uno::Any(), rOldValue
); }
196 // shutdown usage of current edit source on myself and the children.
197 void ShutdownEditSource() SAL_THROW((uno::RuntimeException
));
199 void ParagraphsMoved( sal_Int32 nFirst
, sal_Int32 nMiddle
, sal_Int32 nLast
);
201 virtual void Notify( SfxBroadcaster
& rBC
, const SfxHint
& rHint
);
203 int getNotifierClientId() const { return mnNotifierClientId
; }
205 // lock solar mutex before
206 SvxTextForwarder
& GetTextForwarder() const SAL_THROW((uno::RuntimeException
));
207 // lock solar mutex before
208 SvxViewForwarder
& GetViewForwarder() const SAL_THROW((uno::RuntimeException
));
209 // lock solar mutex before
210 SvxEditViewForwarder
& GetEditViewForwarder( sal_Bool bCreate
= sal_False
) const SAL_THROW((uno::RuntimeException
));
212 // are we in edit mode?
213 sal_Bool
IsActive() const SAL_THROW((uno::RuntimeException
));
215 // our frontend class (the one implementing the actual
216 // interface). That's not necessarily the one containing the impl
218 uno::Reference
< XAccessible
> mxFrontEnd
;
220 // a wrapper for the text forwarders (guarded by solar mutex)
221 mutable SvxEditSourceAdapter maEditSource
;
223 // store last selection (to correctly report selection changes, guarded by solar mutex)
224 ESelection maLastSelection
;
226 // cache range of visible children (guarded by solar mutex)
227 sal_Int32 mnFirstVisibleChild
;
228 sal_Int32 mnLastVisibleChild
;
230 // offset to add to all our children (unguarded, relying on
231 // the fact that sal_Int32 access is atomic)
232 sal_Int32 mnStartIndex
;
234 // the object handling our children (guarded by solar mutex)
235 ::accessibility::AccessibleParaManager maParaManager
;
237 // number of not-yet-closed event frames (BEGIN/END sequences) (guarded by solar mutex)
238 sal_Int32 maEventOpenFrames
;
240 // Queued events from Notify() (guarded by solar mutex)
241 AccessibleTextEventQueue maEventQueue
;
243 // spin lock to prevent notify in notify (guarded by solar mutex)
246 // whether the object or it's children has the focus set (guarded by solar mutex)
247 sal_Bool mbGroupHasFocus
;
249 // whether we (this object) has the focus set (guarded by solar mutex)
250 sal_Bool mbThisHasFocus
;
252 mutable ::osl::Mutex maMutex
;
254 /// our current offset to the containing shape/cell (guarded by maMutex)
257 /// client Id from AccessibleEventNotifier
258 int mnNotifierClientId
;
261 //------------------------------------------------------------------------
263 // AccessibleTextHelper_Impl implementation
265 //------------------------------------------------------------------------
267 AccessibleTextHelper_Impl::AccessibleTextHelper_Impl() :
269 maLastSelection( EE_PARA_NOT_FOUND
,EE_PARA_NOT_FOUND
,EE_PARA_NOT_FOUND
,EE_PARA_NOT_FOUND
),
270 mnFirstVisibleChild( -1 ),
271 mnLastVisibleChild( -2 ),
273 maEventOpenFrames( 0 ),
274 mbInNotify( sal_False
),
275 mbGroupHasFocus( sal_False
),
276 mbThisHasFocus( sal_False
),
278 // well, that's strictly exception safe, though not really
279 // robust. We rely on the fact that this member is constructed
280 // last, and that the constructor body is empty, thus no
281 // chance for exceptions once the Id is fetched. Nevertheless,
282 // normally should employ RAII here...
283 mnNotifierClientId(::comphelper::AccessibleEventNotifier::registerClient())
285 DBG_CTOR( AccessibleTextHelper_Impl
, NULL
);
288 OSL_TRACE( "AccessibleTextHelper_Impl received ID: %d", mnNotifierClientId
);
292 AccessibleTextHelper_Impl::~AccessibleTextHelper_Impl()
294 DBG_DTOR( AccessibleTextHelper_Impl
, NULL
);
296 SolarMutexGuard aGuard
;
300 // call Dispose here, too, since we've some resources not
301 // automatically freed otherwise
304 catch( const uno::Exception
& ) {}
307 SvxTextForwarder
& AccessibleTextHelper_Impl::GetTextForwarder() const SAL_THROW((uno::RuntimeException
))
309 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
311 if( !maEditSource
.IsValid() )
312 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Unknown edit source")), mxFrontEnd
);
314 SvxTextForwarder
* pTextForwarder
= maEditSource
.GetTextForwarder();
316 if( !pTextForwarder
)
317 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Unable to fetch text forwarder, model might be dead")), mxFrontEnd
);
319 if( pTextForwarder
->IsValid() )
320 return *pTextForwarder
;
322 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Text forwarder is invalid, model might be dead")), mxFrontEnd
);
325 SvxViewForwarder
& AccessibleTextHelper_Impl::GetViewForwarder() const SAL_THROW((uno::RuntimeException
))
327 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
329 if( !maEditSource
.IsValid() )
330 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Unknown edit source")), mxFrontEnd
);
332 SvxViewForwarder
* pViewForwarder
= maEditSource
.GetViewForwarder();
334 if( !pViewForwarder
)
335 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Unable to fetch view forwarder, model might be dead")), mxFrontEnd
);
337 if( pViewForwarder
->IsValid() )
338 return *pViewForwarder
;
340 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("View forwarder is invalid, model might be dead")), mxFrontEnd
);
343 SvxEditViewForwarder
& AccessibleTextHelper_Impl::GetEditViewForwarder( sal_Bool bCreate
) const SAL_THROW((uno::RuntimeException
))
345 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
347 if( !maEditSource
.IsValid() )
348 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Unknown edit source")), mxFrontEnd
);
350 SvxEditViewForwarder
* pViewForwarder
= maEditSource
.GetEditViewForwarder( bCreate
);
352 if( !pViewForwarder
)
355 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Unable to fetch edit view forwarder, model might be dead")), mxFrontEnd
);
357 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("No edit view forwarder, object not in edit mode")), mxFrontEnd
);
360 if( pViewForwarder
->IsValid() )
361 return *pViewForwarder
;
365 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("View forwarder is invalid, model might be dead")), mxFrontEnd
);
367 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("View forwarder is invalid, object not in edit mode")), mxFrontEnd
);
371 SvxEditSourceAdapter
& AccessibleTextHelper_Impl::GetEditSource() const SAL_THROW((uno::RuntimeException
))
373 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
375 if( maEditSource
.IsValid() )
378 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("AccessibleTextHelper_Impl::GetEditSource: no edit source")), mxFrontEnd
);
381 sal_Bool
AccessibleTextHelper_Impl::IsSelected() const
383 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
385 sal_Bool bRet
= sal_False
;
389 ESelection aSelection
;
390 bRet
= GetEditViewForwarder().GetSelection( aSelection
);
392 catch( const uno::Exception
& ) {}
397 // functor for sending child events (no stand-alone function, they are maybe not inlined)
398 class AccessibleTextHelper_OffsetChildIndex
: public ::std::unary_function
< ::accessibility::AccessibleEditableTextPara
&, void >
401 AccessibleTextHelper_OffsetChildIndex( sal_Int32 nDifference
) : mnDifference(nDifference
) {}
402 void operator()( ::accessibility::AccessibleEditableTextPara
& rPara
)
404 rPara
.SetIndexInParent( rPara
.GetIndexInParent() + mnDifference
);
408 const sal_Int32 mnDifference
;
411 void AccessibleTextHelper_Impl::SetStartIndex( sal_Int32 nOffset
)
413 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
415 sal_Int32
nOldOffset( mnStartIndex
);
417 mnStartIndex
= nOffset
;
419 if( nOldOffset
!= nOffset
)
422 AccessibleTextHelper_OffsetChildIndex
aFunctor( nOffset
- nOldOffset
);
424 ::std::for_each( maParaManager
.begin(), maParaManager
.end(),
425 AccessibleParaManager::WeakChildAdapter
< AccessibleTextHelper_OffsetChildIndex
> (aFunctor
) );
429 void AccessibleTextHelper_Impl::SetAdditionalChildStates( const VectorOfStates
& rChildStates
)
431 maParaManager
.SetAdditionalChildStates( rChildStates
);
434 void AccessibleTextHelper_Impl::SetChildFocus( sal_Int32 nChild
, sal_Bool bHaveFocus
) SAL_THROW((::com::sun::star::uno::RuntimeException
))
436 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
441 SetShapeFocus( sal_False
);
443 maParaManager
.SetFocus( nChild
);
445 // we just received the focus, also send caret event then
448 OSL_TRACE("AccessibleTextHelper_Impl::SetChildFocus(): Paragraph %d received focus", nChild
);
452 maParaManager
.SetFocus( -1 );
454 OSL_TRACE("AccessibleTextHelper_Impl::SetChildFocus(): Paragraph %d lost focus", nChild
);
456 if( mbGroupHasFocus
)
457 SetShapeFocus( sal_True
);
461 void AccessibleTextHelper_Impl::ChangeChildFocus( sal_Int32 nNewChild
) SAL_THROW((::com::sun::star::uno::RuntimeException
))
463 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
466 SetShapeFocus( sal_False
);
468 mbGroupHasFocus
= sal_True
;
469 maParaManager
.SetFocus( nNewChild
);
471 OSL_TRACE("AccessibleTextHelper_Impl::ChangeChildFocus(): Paragraph %d received focus", nNewChild
);
474 void AccessibleTextHelper_Impl::SetShapeFocus( sal_Bool bHaveFocus
) SAL_THROW((::com::sun::star::uno::RuntimeException
))
476 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
478 sal_Bool
bOldFocus( mbThisHasFocus
);
480 mbThisHasFocus
= bHaveFocus
;
482 if( bOldFocus
!= bHaveFocus
)
486 GotPropertyEvent( uno::makeAny(AccessibleStateType::FOCUSED
), AccessibleEventId::STATE_CHANGED
);
487 OSL_TRACE("AccessibleTextHelper_Impl::SetShapeFocus(): Parent object received focus" );
491 LostPropertyEvent( uno::makeAny(AccessibleStateType::FOCUSED
), AccessibleEventId::STATE_CHANGED
);
492 OSL_TRACE("AccessibleTextHelper_Impl::SetShapeFocus(): Parent object lost focus" );
497 void AccessibleTextHelper_Impl::SetFocus( sal_Bool bHaveFocus
) SAL_THROW((::com::sun::star::uno::RuntimeException
))
499 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
501 sal_Bool
bOldFocus( mbGroupHasFocus
);
503 mbGroupHasFocus
= bHaveFocus
;
509 // find the one with the cursor and get/set focus accordingly
510 ESelection aSelection
;
511 if( GetEditViewForwarder().GetSelection( aSelection
) )
512 SetChildFocus( aSelection
.nEndPara
, bHaveFocus
);
514 catch( const uno::Exception
& ) {}
516 else if( bOldFocus
!= bHaveFocus
)
518 SetShapeFocus( bHaveFocus
);
521 OSL_TRACE("AccessibleTextHelper_Impl::SetFocus: focus changed, Object %d, state: %s", this, bHaveFocus
? "focused" : "not focused");
524 sal_Bool
AccessibleTextHelper_Impl::HaveFocus() SAL_THROW((::com::sun::star::uno::RuntimeException
))
526 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
528 // No locking of solar mutex here, since we rely on the fact
529 // that sal_Bool access is atomic
530 return mbThisHasFocus
;
533 sal_Bool
AccessibleTextHelper_Impl::IsActive() const SAL_THROW((uno::RuntimeException
))
535 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
539 SvxEditSource
& rEditSource
= GetEditSource();
540 SvxEditViewForwarder
* pViewForwarder
= rEditSource
.GetEditViewForwarder();
542 if( !pViewForwarder
)
545 if( pViewForwarder
->IsValid() )
550 catch( const uno::RuntimeException
& )
556 void AccessibleTextHelper_Impl::UpdateSelection()
558 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
562 ESelection aSelection
;
563 if( GetEditViewForwarder().GetSelection( aSelection
) )
565 if( !maLastSelection
.IsEqual( aSelection
) &&
566 aSelection
.nEndPara
< maParaManager
.GetNum() )
568 // #103998# Not that important, changed from assertion to trace
571 OSL_TRACE("AccessibleTextHelper_Impl::UpdateSelection(): Parent has focus!");
574 sal_uInt16
nMaxValidParaIndex( static_cast< sal_uInt16
>( GetTextForwarder().GetParagraphCount() ) - 1 );
576 // notify all affected paragraphs (TODO: may be suboptimal,
577 // since some paragraphs might stay selected)
578 if( maLastSelection
.nStartPara
!= EE_PARA_NOT_FOUND
)
580 // Did the caret move from one paragraph to another?
581 // #100530# no caret events if not focused.
582 if( mbGroupHasFocus
&&
583 maLastSelection
.nEndPara
!= aSelection
.nEndPara
)
585 if( maLastSelection
.nEndPara
< maParaManager
.GetNum() )
587 maParaManager
.FireEvent( ::std::min( maLastSelection
.nEndPara
, nMaxValidParaIndex
),
588 ::std::min( maLastSelection
.nEndPara
, nMaxValidParaIndex
)+1,
589 AccessibleEventId::CARET_CHANGED
,
590 uno::makeAny(static_cast<sal_Int32
>(-1)),
591 uno::makeAny(static_cast<sal_Int32
>(maLastSelection
.nEndPos
)) );
594 ChangeChildFocus( aSelection
.nEndPara
);
596 OSL_TRACE("AccessibleTextHelper_Impl::UpdateSelection(): focus changed, Object: %d, Paragraph: %d, Last paragraph: %d",
597 this, aSelection
.nEndPara
, maLastSelection
.nEndPara
);
601 // #100530# no caret events if not focused.
602 if( mbGroupHasFocus
)
606 // #i13705# The old cursor can only contain valid
607 // values if it's the same paragraph!
608 if( maLastSelection
.nStartPara
!= EE_PARA_NOT_FOUND
&&
609 maLastSelection
.nEndPara
== aSelection
.nEndPara
)
611 aOldCursor
<<= static_cast<sal_Int32
>(maLastSelection
.nEndPos
);
615 aOldCursor
<<= static_cast<sal_Int32
>(-1);
618 maParaManager
.FireEvent( aSelection
.nEndPara
,
619 aSelection
.nEndPara
+1,
620 AccessibleEventId::CARET_CHANGED
,
621 uno::makeAny(static_cast<sal_Int32
>(aSelection
.nEndPos
)),
625 OSL_TRACE("AccessibleTextHelper_Impl::UpdateSelection(): caret changed, Object: %d, New pos: %d, Old pos: %d, New para: %d, Old para: %d",
626 this, aSelection
.nEndPos
, maLastSelection
.nEndPos
, aSelection
.nEndPara
, maLastSelection
.nEndPara
);
628 // #108947# Sort new range before calling FireEvent
629 ::std::pair
< xub_StrLen
, xub_StrLen
> sortedSelection(
630 makeSortedPair(::std::min( aSelection
.nStartPara
, nMaxValidParaIndex
),
631 ::std::min( aSelection
.nEndPara
, nMaxValidParaIndex
) ) );
633 // #108947# Sort last range before calling FireEvent
634 ::std::pair
< xub_StrLen
, xub_StrLen
> sortedLastSelection(
635 makeSortedPair(::std::min( maLastSelection
.nStartPara
, nMaxValidParaIndex
),
636 ::std::min( maLastSelection
.nEndPara
, nMaxValidParaIndex
) ) );
638 // event TEXT_SELECTION_CHANGED has to be submitted. (#i27299#)
639 const sal_Int16 nTextSelChgEventId
=
640 AccessibleEventId::TEXT_SELECTION_CHANGED
;
641 // #107037# notify selection change
642 if( maLastSelection
.nStartPara
== EE_PARA_NOT_FOUND
)
644 // last selection is undefined
645 // use method <ESelection::HasRange()> (#i27299#)
646 if ( aSelection
.HasRange() )
648 // selection was undefined, now is on
649 maParaManager
.FireEvent( sortedSelection
.first
,
650 sortedSelection
.second
+1,
651 nTextSelChgEventId
);
656 // last selection is valid
657 // use method <ESelection::HasRange()> (#i27299#)
658 if ( maLastSelection
.HasRange() &&
659 !aSelection
.HasRange() )
661 // selection was on, now is empty
662 maParaManager
.FireEvent( sortedLastSelection
.first
,
663 sortedLastSelection
.second
+1,
664 nTextSelChgEventId
);
666 // use method <ESelection::HasRange()> (#i27299#)
667 else if( !maLastSelection
.HasRange() &&
668 aSelection
.HasRange() )
670 // selection was empty, now is on
671 maParaManager
.FireEvent( sortedSelection
.first
,
672 sortedSelection
.second
+1,
673 nTextSelChgEventId
);
675 // no event TEXT_SELECTION_CHANGED event, if new and
676 // last selection are empty. (#i27299#)
677 else if ( maLastSelection
.HasRange() &&
678 aSelection
.HasRange() )
680 // use sorted last and new selection
681 ESelection
aTmpLastSel( maLastSelection
);
682 aTmpLastSel
.Adjust();
683 ESelection
aTmpSel( aSelection
);
685 // first submit event for new and changed selection
686 sal_uInt32 nPara
= aTmpSel
.nStartPara
;
687 for ( ; nPara
<= aTmpSel
.nEndPara
; ++nPara
)
689 if ( nPara
< aTmpLastSel
.nStartPara
||
690 nPara
> aTmpLastSel
.nEndPara
)
692 // new selection on paragraph <nPara>
693 maParaManager
.FireEvent( nPara
,
694 nTextSelChgEventId
);
698 // check for changed selection on paragraph <nPara>
699 const xub_StrLen nParaStartPos
=
700 nPara
== aTmpSel
.nStartPara
701 ? aTmpSel
.nStartPos
: 0;
702 const xub_StrLen nParaEndPos
=
703 nPara
== aTmpSel
.nEndPara
704 ? aTmpSel
.nEndPos
: STRING_LEN
;
705 const xub_StrLen nLastParaStartPos
=
706 nPara
== aTmpLastSel
.nStartPara
707 ? aTmpLastSel
.nStartPos
: 0;
708 const xub_StrLen nLastParaEndPos
=
709 nPara
== aTmpLastSel
.nEndPara
710 ? aTmpLastSel
.nEndPos
: STRING_LEN
;
711 if ( nParaStartPos
!= nLastParaStartPos
||
712 nParaEndPos
!= nLastParaEndPos
)
714 maParaManager
.FireEvent(
715 nPara
, nTextSelChgEventId
);
719 // second submit event for 'old' selections
720 nPara
= aTmpLastSel
.nStartPara
;
721 for ( ; nPara
<= aTmpLastSel
.nEndPara
; ++nPara
)
723 if ( nPara
< aTmpSel
.nStartPara
||
724 nPara
> aTmpSel
.nEndPara
)
726 maParaManager
.FireEvent( nPara
,
727 nTextSelChgEventId
);
733 maLastSelection
= aSelection
;
737 // no selection? no update actions
738 catch( const uno::RuntimeException
& ) {}
741 void AccessibleTextHelper_Impl::ShutdownEditSource() SAL_THROW((uno::RuntimeException
))
743 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
745 // This should only be called with solar mutex locked, i.e. from the main office thread
747 // This here is somewhat clumsy: As soon as our children have
748 // a NULL EditSource (maParaManager.SetEditSource()), they
749 // enter the disposed state and cannot be reanimated. Thus, it
750 // is unavoidable and a hard requirement to let go and create
751 // from scratch each and every child.
753 // invalidate children
754 maParaManager
.Dispose();
755 maParaManager
.SetNum(0);
758 if( mxFrontEnd
.is() )
759 FireEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN
);
761 // quit listen on stale edit source
762 if( maEditSource
.IsValid() )
763 EndListening( maEditSource
.GetBroadcaster() );
765 SAL_WNODEPRECATED_DECLARATIONS_PUSH
766 maEditSource
.SetEditSource( ::std::auto_ptr
< SvxEditSource
>(NULL
) );
767 SAL_WNODEPRECATED_DECLARATIONS_POP
770 SAL_WNODEPRECATED_DECLARATIONS_PUSH
771 void AccessibleTextHelper_Impl::SetEditSource( ::std::auto_ptr
< SvxEditSource
> pEditSource
) SAL_THROW((uno::RuntimeException
))
773 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
775 // This should only be called with solar mutex locked, i.e. from the main office thread
777 // shutdown old edit source
778 ShutdownEditSource();
780 // set new edit source
781 maEditSource
.SetEditSource( pEditSource
);
783 // init child vector to the current child count
784 if( maEditSource
.IsValid() )
786 maParaManager
.SetNum( GetTextForwarder().GetParagraphCount() );
788 // listen on new edit source
789 StartListening( maEditSource
.GetBroadcaster() );
791 UpdateVisibleChildren();
794 SAL_WNODEPRECATED_DECLARATIONS_POP
796 void AccessibleTextHelper_Impl::SetOffset( const Point
& rPoint
)
798 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
800 // guard against non-atomic access to maOffset data structure
802 ::osl::MutexGuard
aGuard( maMutex
);
806 maParaManager
.SetEEOffset( rPoint
);
808 // in all cases, check visibility afterwards.
809 UpdateVisibleChildren();
813 void AccessibleTextHelper_Impl::UpdateVisibleChildren( bool bBroadcastEvents
)
815 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
819 SvxTextForwarder
& rCacheTF
= GetTextForwarder();
820 SvxViewForwarder
& rCacheVF
= GetViewForwarder();
822 Rectangle aViewArea
= rCacheVF
.GetVisArea();
826 // maybe the edit view scrolls, adapt aViewArea
827 Rectangle aEditViewArea
= GetEditViewForwarder().GetVisArea();
828 aViewArea
+= aEditViewArea
.TopLeft();
830 // now determine intersection
831 aViewArea
.Intersection( aEditViewArea
);
834 Rectangle aTmpBB
, aParaBB
;
835 sal_Bool bFirstChild
= sal_True
;
837 sal_Int32 nParas
=rCacheTF
.GetParagraphCount();
839 mnFirstVisibleChild
= -1;
840 mnLastVisibleChild
= -2;
842 for( nCurrPara
=0; nCurrPara
<nParas
; ++nCurrPara
)
844 DBG_ASSERT(nCurrPara
>= 0 && nCurrPara
<= USHRT_MAX
,
845 "AccessibleTextHelper_Impl::UpdateVisibleChildren: index value overflow");
847 aTmpBB
= rCacheTF
.GetParaBounds( static_cast< sal_uInt16
>( nCurrPara
) );
849 // convert to screen coordinates
850 aParaBB
= ::accessibility::AccessibleEditableTextPara::LogicToPixel( aTmpBB
, rCacheTF
.GetMapMode(), rCacheVF
);
852 if( aParaBB
.IsOver( aViewArea
) )
854 // at least partially visible
857 bFirstChild
= sal_False
;
858 mnFirstVisibleChild
= nCurrPara
;
861 mnLastVisibleChild
= nCurrPara
;
863 // child not yet created?
864 ::accessibility::AccessibleParaManager::WeakChild
aChild( maParaManager
.GetChild(nCurrPara
) );
865 if( aChild
.second
.Width
== 0 &&
866 aChild
.second
.Height
== 0 &&
870 GotPropertyEvent( uno::makeAny( maParaManager
.CreateChild( nCurrPara
- mnFirstVisibleChild
,
871 mxFrontEnd
, GetEditSource(), nCurrPara
).first
),
872 AccessibleEventId::CHILD
);
877 // not or no longer visible
878 if( maParaManager
.IsReferencable( nCurrPara
) )
880 if( bBroadcastEvents
)
881 LostPropertyEvent( uno::makeAny( maParaManager
.GetChild( nCurrPara
).first
.get().getRef() ),
882 AccessibleEventId::CHILD
);
885 maParaManager
.Release( nCurrPara
);
890 catch( const uno::Exception
& )
892 OSL_FAIL("AccessibleTextHelper_Impl::UpdateVisibleChildren error while determining visible children");
894 // something failed - currently no children
895 mnFirstVisibleChild
= -1;
896 mnLastVisibleChild
= -2;
897 maParaManager
.SetNum(0);
900 if( bBroadcastEvents
)
901 FireEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN
);
905 // functor for checking changes in paragraph bounding boxes (no stand-alone function, maybe not inlined)
906 class AccessibleTextHelper_UpdateChildBounds
: public ::std::unary_function
< const ::accessibility::AccessibleParaManager::WeakChild
&,
907 ::accessibility::AccessibleParaManager::WeakChild
>
910 AccessibleTextHelper_UpdateChildBounds( AccessibleTextHelper_Impl
& rImpl
) : mrImpl(rImpl
) {}
911 ::accessibility::AccessibleParaManager::WeakChild
operator()( const ::accessibility::AccessibleParaManager::WeakChild
& rChild
)
913 // retrieve hard reference from weak one
914 ::accessibility::AccessibleParaManager::WeakPara::HardRefType
aHardRef( rChild
.first
.get() );
918 awt::Rectangle aNewRect
= aHardRef
->getBounds();
919 const awt::Rectangle
& aOldRect
= rChild
.second
;
921 if( aNewRect
.X
!= aOldRect
.X
||
922 aNewRect
.Y
!= aOldRect
.Y
||
923 aNewRect
.Width
!= aOldRect
.Width
||
924 aNewRect
.Height
!= aOldRect
.Height
)
926 // visible data changed
927 aHardRef
->FireEvent( AccessibleEventId::BOUNDRECT_CHANGED
);
929 // update internal bounds
930 return ::accessibility::AccessibleParaManager::WeakChild( rChild
.first
, aNewRect
);
934 // identity transform
939 AccessibleTextHelper_Impl
& mrImpl
;
942 void AccessibleTextHelper_Impl::UpdateBoundRect()
944 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
946 // send BOUNDRECT_CHANGED to affected children
947 AccessibleTextHelper_UpdateChildBounds
aFunctor( *this );
948 ::std::transform( maParaManager
.begin(), maParaManager
.end(), maParaManager
.begin(), aFunctor
);
952 void AccessibleTextHelper_Impl::CheckInvariants() const
954 if( mnFirstVisibleChild
>= 0 &&
955 mnFirstVisibleChild
> mnLastVisibleChild
)
957 OSL_FAIL( "AccessibleTextHelper: range invalid" );
962 // functor for sending child events (no stand-alone function, they are maybe not inlined)
963 class AccessibleTextHelper_LostChildEvent
: public ::std::unary_function
< const ::accessibility::AccessibleParaManager::WeakChild
&, void >
966 AccessibleTextHelper_LostChildEvent( AccessibleTextHelper_Impl
& rImpl
) : mrImpl(rImpl
) {}
967 void operator()( const ::accessibility::AccessibleParaManager::WeakChild
& rPara
)
969 // retrieve hard reference from weak one
970 ::accessibility::AccessibleParaManager::WeakPara::HardRefType
aHardRef( rPara
.first
.get() );
973 mrImpl
.FireEvent(AccessibleEventId::CHILD
, uno::Any(), uno::makeAny( aHardRef
.getRef() ) );
977 AccessibleTextHelper_Impl
& mrImpl
;
980 void AccessibleTextHelper_Impl::ParagraphsMoved( sal_Int32 nFirst
, sal_Int32 nMiddle
, sal_Int32 nLast
)
982 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
984 const sal_Int32 nParas
= GetTextForwarder().GetParagraphCount();
992 * ... nParagraph ... nParam1 ... nParam2 ...
993 * |______________[xxxxxxxxxxx]
995 * [xxxxxxxxxxx]|______________
1000 * ... nParam1 ... nParagraph ... nParam2 ...
1001 * [xxxxxxxxxxx|xxxxxxxxxxxxxx]____________
1003 * ____________[xxxxxxxxxxx|xxxxxxxxxxxxxx]
1005 * tail is nParagraph - nParam1
1008 * ... nParam1 ... nParam2 ... nParagraph ...
1009 * [xxxxxxxxxxx]___________|____________
1011 * ___________|____________[xxxxxxxxxxx]
1013 * tail is nParam2 - nParam1
1016 // sort nParagraph, nParam1 and nParam2 in ascending order, calc range
1017 if( nMiddle
< nFirst
)
1019 ::std::swap(nFirst
, nMiddle
);
1021 else if( nMiddle
< nLast
)
1023 nLast
= nLast
+ nMiddle
- nFirst
;
1027 ::std::swap(nMiddle
, nLast
);
1028 nLast
= nLast
+ nMiddle
- nFirst
;
1031 if( nFirst
< nParas
&& nMiddle
< nParas
&& nLast
< nParas
)
1033 // since we have no "paragraph index
1034 // changed" event on UAA, remove
1035 // [first,last] and insert again later (in
1036 // UpdateVisibleChildren)
1038 // maParaManager.Rotate( nFirst, nMiddle, nLast );
1040 // send CHILD_EVENT to affected children
1041 ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator begin
= maParaManager
.begin();
1042 ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator end
= begin
;
1044 ::std::advance( begin
, nFirst
);
1045 ::std::advance( end
, nLast
+1 );
1047 // TODO: maybe optimize here in the following way. If the
1048 // number of removed children exceeds a certain threshold,
1049 // use INVALIDATE_CHILDREN
1050 AccessibleTextHelper_LostChildEvent
aFunctor( *this );
1052 ::std::for_each( begin
, end
, aFunctor
);
1054 maParaManager
.Release(nFirst
, nLast
+1);
1055 // should be no need for UpdateBoundRect, since all affected children are cleared.
1059 // functor for sending child events (no stand-alone function, they are maybe not inlined)
1060 class AccessibleTextHelper_ChildrenTextChanged
: public ::std::unary_function
< ::accessibility::AccessibleEditableTextPara
&, void >
1063 void operator()( ::accessibility::AccessibleEditableTextPara
& rPara
)
1065 rPara
.TextChanged();
1069 /** functor processing queue events
1071 Reacts on TEXT_HINT_PARAINSERTED/REMOVED events and stores
1074 class AccessibleTextHelper_QueueFunctor
: public ::std::unary_function
< const SfxHint
*, void >
1077 AccessibleTextHelper_QueueFunctor() :
1078 mnParasChanged( 0 ),
1082 void operator()( const SfxHint
* pEvent
)
1085 mnParasChanged
!= -1 )
1087 // determine hint type
1088 const TextHint
* pTextHint
= PTR_CAST( TextHint
, pEvent
);
1089 const SvxEditSourceHint
* pEditSourceHint
= PTR_CAST( SvxEditSourceHint
, pEvent
);
1091 if( !pEditSourceHint
&& pTextHint
&&
1092 (pTextHint
->GetId() == TEXT_HINT_PARAINSERTED
||
1093 pTextHint
->GetId() == TEXT_HINT_PARAREMOVED
) )
1095 if( pTextHint
->GetValue() == EE_PARA_ALL
)
1097 mnParasChanged
= -1;
1101 mnHintId
= pTextHint
->GetId();
1102 mnParaIndex
= pTextHint
->GetValue();
1109 /** Query number of paragraphs changed during queue processing.
1111 @return number of changed paragraphs, -1 for
1112 "every paragraph changed"
1114 int GetNumberOfParasChanged() { return mnParasChanged
; }
1115 /** Query index of last added/removed paragraph
1117 @return index of lastly added paragraphs, -1 for none
1120 int GetParaIndex() { return mnParaIndex
; }
1121 /** Query hint id of last interesting event
1123 @return hint id of last interesting event (REMOVED/INSERTED).
1125 int GetHintId() { return mnHintId
; }
1128 /** number of paragraphs changed during queue processing. -1 for
1129 "every paragraph changed"
1132 /// index of paragraph added/removed last
1134 /// TextHint ID (removed/inserted) of last interesting event
1138 void AccessibleTextHelper_Impl::ProcessQueue()
1140 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
1142 // inspect queue for paragraph insert/remove events. If there
1143 // is exactly _one_ of those in the queue, and the number of
1144 // paragraphs has changed by exactly one, use that event to
1145 // determine a priori which paragraph was added/removed. This
1146 // is necessary, since I must sync right here with the
1147 // EditEngine state (number of paragraphs etc.), since I'm
1148 // potentially sending listener events right away.
1149 AccessibleTextHelper_QueueFunctor aFunctor
;
1150 maEventQueue
.ForEach( aFunctor
);
1152 const sal_Int32
nNewParas( GetTextForwarder().GetParagraphCount() );
1153 const sal_Int32
nCurrParas( maParaManager
.GetNum() );
1155 // whether every paragraph already is updated (no need to
1156 // repeat that later on, e.g. for PARA_MOVED events)
1157 bool bEverythingUpdated( false );
1159 if( labs( nNewParas
- nCurrParas
) == 1 &&
1160 aFunctor
.GetNumberOfParasChanged() == 1 )
1162 // #103483# Exactly one paragraph added/removed. This is
1163 // the normal case, optimize event handling here.
1165 if( aFunctor
.GetHintId() == TEXT_HINT_PARAINSERTED
)
1167 // update num of paras
1168 maParaManager
.SetNum( nNewParas
);
1170 // release everything from the insertion position until the end
1171 maParaManager
.Release(aFunctor
.GetParaIndex(), nCurrParas
);
1173 // TODO: Clarify whether this behaviour _really_ saves
1174 // anybody anything!
1175 // update children, _don't_ broadcast
1176 UpdateVisibleChildren( false );
1179 // send insert event
1180 // #109864# Enforce creation of this paragraph
1183 GotPropertyEvent( uno::makeAny( getAccessibleChild( aFunctor
.GetParaIndex() -
1184 mnFirstVisibleChild
+ GetStartIndex() ) ),
1185 AccessibleEventId::CHILD
);
1187 catch( const uno::Exception
& )
1189 OSL_FAIL("AccessibleTextHelper_Impl::ProcessQueue: could not create new paragraph");
1192 else if( aFunctor
.GetHintId() == TEXT_HINT_PARAREMOVED
)
1194 ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator begin
= maParaManager
.begin();
1195 ::std::advance( begin
, aFunctor
.GetParaIndex() );
1196 ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator end
= begin
;
1197 ::std::advance( end
, 1 );
1199 // #i61812# remember para to be removed for later notification
1200 // AFTER the new state is applied (that after the para got removed)
1201 ::uno::Reference
< XAccessible
> xPara
;
1202 ::accessibility::AccessibleParaManager::WeakPara::HardRefType
aHardRef( begin
->first
.get() );
1204 xPara
= ::uno::Reference
< XAccessible
>( aHardRef
.getRef(), ::uno::UNO_QUERY
);
1206 // release everything from the remove position until the end
1207 maParaManager
.Release(aFunctor
.GetParaIndex(), nCurrParas
);
1209 // update num of paras
1210 maParaManager
.SetNum( nNewParas
);
1212 // TODO: Clarify whether this behaviour _really_ saves
1213 // anybody anything!
1214 // update children, _don't_ broadcast
1215 UpdateVisibleChildren( false );
1218 // #i61812# notification for removed para
1220 FireEvent(AccessibleEventId::CHILD
, uno::Any(), uno::makeAny( xPara
) );
1224 OSL_FAIL("AccessibleTextHelper_Impl::ProcessQueue() invalid hint id");
1227 else if( nNewParas
!= nCurrParas
)
1229 // release all paras
1230 maParaManager
.Release(0, nCurrParas
);
1232 // update num of paras
1233 maParaManager
.SetNum( nNewParas
);
1235 // #109864# create from scratch, don't broadcast
1236 UpdateVisibleChildren( false );
1239 // number of paragraphs somehow changed - but we have no
1240 // chance determining how. Thus, throw away everything and
1241 // create from scratch.
1242 // (child events should be broadcast after the changes are done...)
1243 FireEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN
);
1245 // no need for further updates later on
1246 bEverythingUpdated
= true;
1249 while( !maEventQueue
.IsEmpty() )
1251 SAL_WNODEPRECATED_DECLARATIONS_PUSH
1252 ::std::auto_ptr
< SfxHint
> pHint( maEventQueue
.PopFront() );
1253 SAL_WNODEPRECATED_DECLARATIONS_POP
1256 const SfxHint
& rHint
= *(pHint
.get());
1258 // determine hint type
1259 const SdrHint
* pSdrHint
= PTR_CAST( SdrHint
, &rHint
);
1260 const SfxSimpleHint
* pSimpleHint
= PTR_CAST( SfxSimpleHint
, &rHint
);
1261 const TextHint
* pTextHint
= PTR_CAST( TextHint
, &rHint
);
1262 const SvxViewHint
* pViewHint
= PTR_CAST( SvxViewHint
, &rHint
);
1263 const SvxEditSourceHint
* pEditSourceHint
= PTR_CAST( SvxEditSourceHint
, &rHint
);
1267 const sal_Int32 nParas
= GetTextForwarder().GetParagraphCount();
1269 if( pEditSourceHint
)
1271 switch( pEditSourceHint
->GetId() )
1273 case EDITSOURCE_HINT_PARASMOVED
:
1275 DBG_ASSERT( pEditSourceHint
->GetStartValue() < GetTextForwarder().GetParagraphCount() &&
1276 pEditSourceHint
->GetEndValue() < GetTextForwarder().GetParagraphCount(),
1277 "AccessibleTextHelper_Impl::NotifyHdl: Invalid notification");
1279 if( !bEverythingUpdated
)
1281 ParagraphsMoved(pEditSourceHint
->GetStartValue(),
1282 pEditSourceHint
->GetValue(),
1283 pEditSourceHint
->GetEndValue());
1285 // in all cases, check visibility afterwards.
1286 UpdateVisibleChildren();
1291 case EDITSOURCE_HINT_SELECTIONCHANGED
:
1297 // maybe we're not in edit mode (this is not an error)
1298 catch( const uno::Exception
& ) {}
1302 else if( pTextHint
)
1304 switch( pTextHint
->GetId() )
1306 case TEXT_HINT_MODIFIED
:
1309 sal_Int32
nPara( pTextHint
->GetValue() );
1311 // #108900# Delegate change event to children
1312 AccessibleTextHelper_ChildrenTextChanged aNotifyChildrenFunctor
;
1314 if( nPara
== static_cast<sal_Int32
>(EE_PARA_ALL
) )
1316 // #108900# Call every child
1317 ::std::for_each( maParaManager
.begin(), maParaManager
.end(),
1318 AccessibleParaManager::WeakChildAdapter
< AccessibleTextHelper_ChildrenTextChanged
> (aNotifyChildrenFunctor
) );
1321 if( nPara
< nParas
)
1323 // #108900# Call child at index nPara
1324 ::std::for_each( maParaManager
.begin()+nPara
, maParaManager
.begin()+nPara
+1,
1325 AccessibleParaManager::WeakChildAdapter
< AccessibleTextHelper_ChildrenTextChanged
> (aNotifyChildrenFunctor
) );
1330 case TEXT_HINT_PARAINSERTED
:
1331 // already happened above
1334 case TEXT_HINT_PARAREMOVED
:
1335 // already happened above
1338 case TEXT_HINT_TEXTHEIGHTCHANGED
:
1339 // visibility changed, done below
1342 case TEXT_HINT_VIEWSCROLLED
:
1343 // visibility changed, done below
1347 // in all cases, check visibility afterwards.
1348 UpdateVisibleChildren();
1351 else if( pViewHint
)
1353 switch( pViewHint
->GetHintType() )
1355 case SvxViewHint::SVX_HINT_VIEWCHANGED
:
1356 // just check visibility
1357 UpdateVisibleChildren();
1364 switch( pSdrHint
->GetKind() )
1368 // change children state
1369 maParaManager
.SetActive();
1371 // per definition, edit mode text has the focus
1372 SetFocus( sal_True
);
1378 // focused child now looses focus
1379 ESelection aSelection
;
1380 if( GetEditViewForwarder().GetSelection( aSelection
) )
1381 SetChildFocus( aSelection
.nEndPara
, sal_False
);
1383 // change children state
1384 maParaManager
.SetActive( sal_False
);
1386 maLastSelection
= ESelection( EE_PARA_NOT_FOUND
, EE_PARA_NOT_FOUND
,
1387 EE_PARA_NOT_FOUND
, EE_PARA_NOT_FOUND
);
1394 // it's VITAL to keep the SfxSimpleHint last! It's the base of some classes above!
1395 else if( pSimpleHint
)
1397 switch( pSimpleHint
->GetId() )
1399 case SFX_HINT_DYING
:
1400 // edit source is dying under us, become defunc then
1403 // make edit source inaccessible
1404 // Note: cannot destroy it here, since we're called from there!
1405 ShutdownEditSource();
1407 catch( const uno::Exception
& ) {}
1413 catch( const uno::Exception
& )
1416 OSL_TRACE("AccessibleTextHelper_Impl::ProcessQueue: Unhandled exception.");
1423 void AccessibleTextHelper_Impl::Notify( SfxBroadcaster
& /*rBC*/, const SfxHint
& rHint
)
1425 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
1427 // precondition: solar mutex locked
1428 DBG_TESTSOLARMUTEX();
1430 // precondition: not in a recursion
1434 mbInNotify
= sal_True
;
1436 // determine hint type
1437 const SdrHint
* pSdrHint
= PTR_CAST( SdrHint
, &rHint
);
1438 const SfxSimpleHint
* pSimpleHint
= PTR_CAST( SfxSimpleHint
, &rHint
);
1439 const TextHint
* pTextHint
= PTR_CAST( TextHint
, &rHint
);
1440 const SvxViewHint
* pViewHint
= PTR_CAST( SvxViewHint
, &rHint
);
1441 const SvxEditSourceHint
* pEditSourceHint
= PTR_CAST( SvxEditSourceHint
, &rHint
);
1445 // Process notification event
1446 if( pEditSourceHint
)
1448 maEventQueue
.Append( *pEditSourceHint
);
1449 // EditEngine should emit TEXT_SELECTION_CHANGED events (#i27299#)
1450 if( maEventOpenFrames
== 0 )
1453 else if( pTextHint
)
1455 switch( pTextHint
->GetId() )
1457 case TEXT_HINT_BLOCKNOTIFICATION_END
:
1458 case TEXT_HINT_INPUT_END
:
1459 --maEventOpenFrames
;
1461 if( maEventOpenFrames
== 0 )
1464 /* All information should have arrived
1465 * now, process queue. As stated in the
1466 * above bug, we can often avoid throwing
1467 * away all paragraphs by looking forward
1468 * in the event queue (searching for
1469 * PARAINSERT/REMOVE events). Furthermore,
1470 * processing the event queue only at the
1471 * end of an interaction cycle, ensures
1472 * that the EditEngine state and the
1473 * AccessibleText state are the same
1474 * (well, mostly. If there are _multiple_
1475 * interaction cycles in the EE queues, it
1476 * can still happen that EE state is
1477 * different. That's so to say broken by
1478 * design with that delayed EE event
1485 case TEXT_HINT_BLOCKNOTIFICATION_START
:
1486 case TEXT_HINT_INPUT_START
:
1487 ++maEventOpenFrames
;
1488 // no FALLTHROUGH reason: event will not be processed,
1489 // thus appending the event isn't necessary. (#i27299#)
1492 maEventQueue
.Append( *pTextHint
);
1493 // EditEngine should emit TEXT_SELECTION_CHANGED events (#i27299#)
1494 if( maEventOpenFrames
== 0 )
1499 else if( pViewHint
)
1501 maEventQueue
.Append( *pViewHint
);
1503 // process visibility right away, if not within an
1504 // open EE notification frame. Otherwise, event
1505 // processing would be delayed until next EE
1506 // notification sequence.
1507 if( maEventOpenFrames
== 0 )
1512 maEventQueue
.Append( *pSdrHint
);
1514 // process drawing layer events right away, if not
1515 // within an open EE notification frame. Otherwise,
1516 // event processing would be delayed until next EE
1517 // notification sequence.
1518 if( maEventOpenFrames
== 0 )
1521 // it's VITAL to keep the SfxSimpleHint last! It's the base of some classes above!
1522 else if( pSimpleHint
)
1524 // handle this event _at once_, because after that, objects are invalid
1525 switch( pSimpleHint
->GetId() )
1527 case SFX_HINT_DYING
:
1528 // edit source is dying under us, become defunc then
1529 maEventQueue
.Clear();
1532 // make edit source inaccessible
1533 // Note: cannot destroy it here, since we're called from there!
1534 ShutdownEditSource();
1536 catch( const uno::Exception
& ) {}
1542 catch( const uno::Exception
& )
1545 OSL_TRACE("AccessibleTextHelper_Impl::Notify: Unhandled exception.");
1547 mbInNotify
= sal_False
;
1550 mbInNotify
= sal_False
;
1553 void AccessibleTextHelper_Impl::Dispose()
1555 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
1557 if( getNotifierClientId() != -1 )
1561 // #106234# Unregister from EventNotifier
1562 ::comphelper::AccessibleEventNotifier::revokeClient( getNotifierClientId() );
1564 OSL_TRACE( "AccessibleTextHelper_Impl disposed ID: %d", mnNotifierClientId
);
1567 catch( const uno::Exception
& ) {}
1569 mnNotifierClientId
= -1;
1575 maParaManager
.Dispose();
1577 catch( const uno::Exception
& ) {}
1579 // quit listen on stale edit source
1580 if( maEditSource
.IsValid() )
1581 EndListening( maEditSource
.GetBroadcaster() );
1584 SAL_WNODEPRECATED_DECLARATIONS_PUSH
1585 maEditSource
.SetEditSource( ::std::auto_ptr
< SvxEditSource
>(NULL
) );
1586 SAL_WNODEPRECATED_DECLARATIONS_POP
1590 void AccessibleTextHelper_Impl::FireEvent( const sal_Int16 nEventId
, const uno::Any
& rNewValue
, const uno::Any
& rOldValue
) const
1592 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
1594 // -- object locked --
1595 ::osl::ClearableMutexGuard
aGuard( maMutex
);
1597 AccessibleEventObject aEvent
;
1599 DBG_ASSERT(mxFrontEnd
.is(), "AccessibleTextHelper::FireEvent: no event source set" );
1601 if( mxFrontEnd
.is() )
1602 aEvent
= AccessibleEventObject(mxFrontEnd
->getAccessibleContext(), nEventId
, rNewValue
, rOldValue
);
1604 aEvent
= AccessibleEventObject(uno::Reference
< uno::XInterface
>(), nEventId
, rNewValue
, rOldValue
);
1606 // no locking necessary, FireEvent internally copies listeners
1607 // if someone removes/adds in between Further locking,
1608 // actually, might lead to deadlocks, since we're calling out
1616 void AccessibleTextHelper_Impl::FireEvent( const AccessibleEventObject
& rEvent
) const
1618 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
1620 // #102261# Call global queue for focus events
1621 if( rEvent
.EventId
== AccessibleStateType::FOCUSED
)
1622 vcl::unohelper::NotifyAccessibleStateEventGlobally( rEvent
);
1624 // #106234# Delegate to EventNotifier
1625 ::comphelper::AccessibleEventNotifier::addEvent( getNotifierClientId(),
1629 // XAccessibleContext
1630 sal_Int32 SAL_CALL
AccessibleTextHelper_Impl::getAccessibleChildCount() SAL_THROW((uno::RuntimeException
))
1632 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
1634 return mnLastVisibleChild
- mnFirstVisibleChild
+ 1;
1637 uno::Reference
< XAccessible
> SAL_CALL
AccessibleTextHelper_Impl::getAccessibleChild( sal_Int32 i
) SAL_THROW((lang::IndexOutOfBoundsException
, uno::RuntimeException
))
1639 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
1641 i
-= GetStartIndex();
1643 if( 0 > i
|| i
>= getAccessibleChildCount() ||
1644 GetTextForwarder().GetParagraphCount() <= i
)
1646 throw lang::IndexOutOfBoundsException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Invalid child index")), mxFrontEnd
);
1649 DBG_ASSERT(mxFrontEnd
.is(), "AccessibleTextHelper_Impl::UpdateVisibleChildren: no frontend set");
1651 if( mxFrontEnd
.is() )
1652 return maParaManager
.CreateChild( i
, mxFrontEnd
, GetEditSource(), mnFirstVisibleChild
+ i
).first
;
1657 void SAL_CALL
AccessibleTextHelper_Impl::addEventListener( const uno::Reference
< XAccessibleEventListener
>& xListener
) SAL_THROW((uno::RuntimeException
))
1659 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
1661 if( getNotifierClientId() != -1 )
1662 ::comphelper::AccessibleEventNotifier::addEventListener( getNotifierClientId(), xListener
);
1665 void SAL_CALL
AccessibleTextHelper_Impl::removeEventListener( const uno::Reference
< XAccessibleEventListener
>& xListener
) SAL_THROW((uno::RuntimeException
))
1667 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
1669 if( getNotifierClientId() != -1 )
1670 ::comphelper::AccessibleEventNotifier::removeEventListener( getNotifierClientId(), xListener
);
1673 uno::Reference
< XAccessible
> SAL_CALL
AccessibleTextHelper_Impl::getAccessibleAtPoint( const awt::Point
& _aPoint
) SAL_THROW((uno::RuntimeException
))
1675 DBG_CHKTHIS( AccessibleTextHelper_Impl
, NULL
);
1677 // make given position relative
1678 if( !mxFrontEnd
.is() )
1679 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("AccessibleTextHelper_Impl::getAccessibleAt: frontend invalid")), mxFrontEnd
);
1681 uno::Reference
< XAccessibleContext
> xFrontEndContext
= mxFrontEnd
->getAccessibleContext();
1683 if( !xFrontEndContext
.is() )
1684 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("AccessibleTextHelper_Impl::getAccessibleAt: frontend invalid")), mxFrontEnd
);
1686 uno::Reference
< XAccessibleComponent
> xFrontEndComponent( xFrontEndContext
, uno::UNO_QUERY
);
1688 if( !xFrontEndComponent
.is() )
1689 throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("AccessibleTextHelper_Impl::getAccessibleAt: frontend is no XAccessibleComponent")),
1692 // #103862# No longer need to make given position relative
1693 Point
aPoint( _aPoint
.X
, _aPoint
.Y
);
1695 // respect EditEngine offset to surrounding shape/cell
1696 aPoint
-= GetOffset();
1698 // convert to EditEngine coordinate system
1699 SvxTextForwarder
& rCacheTF
= GetTextForwarder();
1700 Point
aLogPoint( GetViewForwarder().PixelToLogic( aPoint
, rCacheTF
.GetMapMode() ) );
1702 // iterate over all visible children (including those not yet created)
1704 for( nChild
=mnFirstVisibleChild
; nChild
<= mnLastVisibleChild
; ++nChild
)
1706 DBG_ASSERT(nChild
>= 0 && nChild
<= USHRT_MAX
,
1707 "AccessibleTextHelper_Impl::getAccessibleAt: index value overflow");
1709 Rectangle
aParaBounds( rCacheTF
.GetParaBounds( static_cast< sal_uInt16
> (nChild
) ) );
1711 if( aParaBounds
.IsInside( aLogPoint
) )
1712 return getAccessibleChild( nChild
- mnFirstVisibleChild
+ GetStartIndex() );
1719 //------------------------------------------------------------------------
1721 // AccessibleTextHelper implementation (simply forwards to impl)
1723 //------------------------------------------------------------------------
1725 SAL_WNODEPRECATED_DECLARATIONS_PUSH
1726 AccessibleTextHelper::AccessibleTextHelper( ::std::auto_ptr
< SvxEditSource
> pEditSource
) :
1727 mpImpl( new AccessibleTextHelper_Impl() )
1729 SolarMutexGuard aGuard
;
1731 SetEditSource( pEditSource
);
1733 SAL_WNODEPRECATED_DECLARATIONS_PUSH
1735 AccessibleTextHelper::~AccessibleTextHelper()
1739 const SvxEditSource
& AccessibleTextHelper::GetEditSource() const SAL_THROW((uno::RuntimeException
))
1742 mpImpl
->CheckInvariants();
1744 const SvxEditSource
& aEditSource
= mpImpl
->GetEditSource();
1746 mpImpl
->CheckInvariants();
1750 return mpImpl
->GetEditSource();
1754 SAL_WNODEPRECATED_DECLARATIONS_PUSH
1755 void AccessibleTextHelper::SetEditSource( ::std::auto_ptr
< SvxEditSource
> pEditSource
) SAL_THROW((uno::RuntimeException
))
1758 // precondition: solar mutex locked
1759 DBG_TESTSOLARMUTEX();
1761 mpImpl
->CheckInvariants();
1764 mpImpl
->SetEditSource( pEditSource
);
1767 mpImpl
->CheckInvariants();
1770 SAL_WNODEPRECATED_DECLARATIONS_POP
1772 void AccessibleTextHelper::SetEventSource( const uno::Reference
< XAccessible
>& rInterface
)
1775 mpImpl
->CheckInvariants();
1778 mpImpl
->SetEventSource( rInterface
);
1781 mpImpl
->CheckInvariants();
1785 uno::Reference
< XAccessible
> AccessibleTextHelper::GetEventSource() const
1788 mpImpl
->CheckInvariants();
1790 uno::Reference
< XAccessible
> xRet( mpImpl
->GetEventSource() );
1792 mpImpl
->CheckInvariants();
1796 return mpImpl
->GetEventSource();
1800 void AccessibleTextHelper::SetFocus( sal_Bool bHaveFocus
) SAL_THROW((::com::sun::star::uno::RuntimeException
))
1803 // precondition: solar mutex locked
1804 DBG_TESTSOLARMUTEX();
1806 mpImpl
->CheckInvariants();
1809 mpImpl
->SetFocus( bHaveFocus
);
1812 mpImpl
->CheckInvariants();
1816 sal_Bool
AccessibleTextHelper::HaveFocus() SAL_THROW((::com::sun::star::uno::RuntimeException
))
1819 mpImpl
->CheckInvariants();
1821 sal_Bool
bRet( mpImpl
->HaveFocus() );
1823 mpImpl
->CheckInvariants();
1827 return mpImpl
->HaveFocus();
1831 void AccessibleTextHelper::FireEvent( const sal_Int16 nEventId
, const uno::Any
& rNewValue
, const uno::Any
& rOldValue
) const
1834 mpImpl
->CheckInvariants();
1837 mpImpl
->FireEvent( nEventId
, rNewValue
, rOldValue
);
1840 mpImpl
->CheckInvariants();
1844 void AccessibleTextHelper::FireEvent( const AccessibleEventObject
& rEvent
) const
1847 mpImpl
->CheckInvariants();
1850 mpImpl
->FireEvent( rEvent
);
1853 mpImpl
->CheckInvariants();
1857 void AccessibleTextHelper::SetOffset( const Point
& rPoint
)
1860 // precondition: solar mutex locked
1861 DBG_TESTSOLARMUTEX();
1863 mpImpl
->CheckInvariants();
1866 mpImpl
->SetOffset( rPoint
);
1869 mpImpl
->CheckInvariants();
1873 Point
AccessibleTextHelper::GetOffset() const
1876 mpImpl
->CheckInvariants();
1878 Point
aPoint( mpImpl
->GetOffset() );
1880 mpImpl
->CheckInvariants();
1884 return mpImpl
->GetOffset();
1888 void AccessibleTextHelper::SetStartIndex( sal_Int32 nOffset
)
1891 // precondition: solar mutex locked
1892 DBG_TESTSOLARMUTEX();
1894 mpImpl
->CheckInvariants();
1897 mpImpl
->SetStartIndex( nOffset
);
1900 mpImpl
->CheckInvariants();
1904 sal_Int32
AccessibleTextHelper::GetStartIndex() const
1907 mpImpl
->CheckInvariants();
1909 sal_Int32 nOffset
= mpImpl
->GetStartIndex();
1911 mpImpl
->CheckInvariants();
1915 return mpImpl
->GetStartIndex();
1919 void AccessibleTextHelper::SetAdditionalChildStates( const VectorOfStates
& rChildStates
)
1921 mpImpl
->SetAdditionalChildStates( rChildStates
);
1924 void AccessibleTextHelper::UpdateChildren() SAL_THROW((::com::sun::star::uno::RuntimeException
))
1927 // precondition: solar mutex locked
1928 DBG_TESTSOLARMUTEX();
1930 mpImpl
->CheckInvariants();
1933 mpImpl
->UpdateVisibleChildren();
1934 mpImpl
->UpdateBoundRect();
1936 mpImpl
->UpdateSelection();
1939 mpImpl
->CheckInvariants();
1943 void AccessibleTextHelper::Dispose()
1945 // As Dispose calls ShutdownEditSource, which in turn
1946 // deregisters as listener on the edit source, have to lock
1948 SolarMutexGuard aGuard
;
1951 mpImpl
->CheckInvariants();
1957 mpImpl
->CheckInvariants();
1961 sal_Bool
AccessibleTextHelper::IsSelected() const
1963 SolarMutexGuard aGuard
;
1966 mpImpl
->CheckInvariants();
1968 sal_Bool aRet
= mpImpl
->IsSelected();
1970 mpImpl
->CheckInvariants();
1974 return mpImpl
->IsSelected();
1978 // XAccessibleContext
1979 sal_Int32
AccessibleTextHelper::GetChildCount() SAL_THROW((uno::RuntimeException
))
1981 SolarMutexGuard aGuard
;
1984 mpImpl
->CheckInvariants();
1986 sal_Int32 nRet
= mpImpl
->getAccessibleChildCount();
1988 mpImpl
->CheckInvariants();
1992 return mpImpl
->getAccessibleChildCount();
1996 uno::Reference
< XAccessible
> AccessibleTextHelper::GetChild( sal_Int32 i
) SAL_THROW((lang::IndexOutOfBoundsException
, uno::RuntimeException
))
1998 SolarMutexGuard aGuard
;
2001 mpImpl
->CheckInvariants();
2003 uno::Reference
< XAccessible
> xRet
= mpImpl
->getAccessibleChild( i
);
2005 mpImpl
->CheckInvariants();
2009 return mpImpl
->getAccessibleChild( i
);
2013 void AccessibleTextHelper::AddEventListener( const uno::Reference
< XAccessibleEventListener
>& xListener
) SAL_THROW((uno::RuntimeException
))
2016 mpImpl
->CheckInvariants();
2018 mpImpl
->addEventListener( xListener
);
2020 mpImpl
->CheckInvariants();
2022 mpImpl
->addEventListener( xListener
);
2026 void AccessibleTextHelper::RemoveEventListener( const uno::Reference
< XAccessibleEventListener
>& xListener
) SAL_THROW((uno::RuntimeException
))
2029 mpImpl
->CheckInvariants();
2031 mpImpl
->removeEventListener( xListener
);
2033 mpImpl
->CheckInvariants();
2035 mpImpl
->removeEventListener( xListener
);
2039 // XAccessibleComponent
2040 uno::Reference
< XAccessible
> AccessibleTextHelper::GetAt( const awt::Point
& aPoint
) SAL_THROW((uno::RuntimeException
))
2042 SolarMutexGuard aGuard
;
2045 mpImpl
->CheckInvariants();
2047 uno::Reference
< XAccessible
> xChild
= mpImpl
->getAccessibleAtPoint( aPoint
);
2049 mpImpl
->CheckInvariants();
2053 return mpImpl
->getAccessibleAtPoint( aPoint
);
2057 } // end of namespace accessibility
2059 //------------------------------------------------------------------------
2061 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */