1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <vcl/window.hxx>
21 #include <swtypes.hxx>
23 #include <com/sun/star/accessibility/XAccessible.hpp>
24 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
25 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
26 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
27 #include <sal/log.hxx>
28 #include <vcl/svapp.hxx>
29 #include <vcl/settings.hxx>
30 #include <i18nlangtag/languagetag.hxx>
31 #include <unotools/accessiblerelationsethelper.hxx>
38 #include <pagefrm.hxx>
40 #include <dflyobj.hxx>
43 #include "accfrmobjslist.hxx"
44 #include "acccontext.hxx"
45 #include <svx/AccessibleShape.hxx>
46 #include <comphelper/accessibleeventnotifier.hxx>
47 #include <cppuhelper/supportsservice.hxx>
48 #include <PostItMgr.hxx>
50 using namespace sw::access
;
51 using namespace ::com::sun::star
;
52 using namespace ::com::sun::star::accessibility
;
54 void SwAccessibleContext::InitStates()
56 m_isShowingState
= GetMap() && IsShowing( *(GetMap()) );
58 SwViewShell
*pVSh
= GetMap()->GetShell();
59 m_isEditableState
= pVSh
&& IsEditable( pVSh
);
60 m_isOpaqueState
= pVSh
&& IsOpaque( pVSh
);
61 m_isDefuncState
= false;
64 void SwAccessibleContext::SetParent( SwAccessibleContext
*pParent
)
66 std::scoped_lock
aGuard( m_Mutex
);
68 m_xWeakParent
= pParent
;
71 rtl::Reference
< SwAccessibleContext
> SwAccessibleContext::GetWeakParent() const
73 std::scoped_lock
aGuard( m_Mutex
);
75 return m_xWeakParent
.get();
78 vcl::Window
*SwAccessibleContext::GetWindow()
80 vcl::Window
*pWin
= nullptr;
84 const SwViewShell
*pVSh
= GetMap()->GetShell();
85 OSL_ENSURE( pVSh
, "no view shell" );
87 pWin
= pVSh
->GetWin();
89 OSL_ENSURE( pWin
, "no window" );
95 // get SwViewShell from accessibility map, and cast to cursor shell
96 SwCursorShell
* SwAccessibleContext::GetCursorShell()
98 SwViewShell
* pViewShell
= GetMap() ? GetMap()->GetShell() : nullptr;
99 OSL_ENSURE( pViewShell
, "no view shell" );
100 return dynamic_cast<SwCursorShell
*>( pViewShell
);
103 const SwCursorShell
* SwAccessibleContext::GetCursorShell() const
105 // just like non-const GetCursorShell
106 const SwViewShell
* pViewShell
= GetMap() ? GetMap()->GetShell() : nullptr;
107 OSL_ENSURE( pViewShell
, "no view shell" );
108 return dynamic_cast<const SwCursorShell
*>( pViewShell
);
113 enum class Action
{ NONE
, SCROLLED
, SCROLLED_WITHIN
,
114 SCROLLED_IN
, SCROLLED_OUT
};
118 void SwAccessibleContext::ChildrenScrolled( const SwFrame
*pFrame
,
119 const SwRect
& rOldVisArea
)
121 const SwRect
& rNewVisArea
= GetVisArea();
122 const bool bVisibleChildrenOnly
= SwAccessibleChild( pFrame
).IsVisibleChildrenOnly();
124 const SwAccessibleChildSList
aList( *pFrame
, *(GetMap()) );
125 SwAccessibleChildSList::const_iterator
aIter( aList
.begin() );
126 while( aIter
!= aList
.end() )
128 const SwAccessibleChild
& rLower
= *aIter
;
129 const SwRect
aBox( rLower
.GetBox( *(GetMap()) ) );
130 if( rLower
.IsAccessible( GetShell()->IsPreview() ) )
132 Action eAction
= Action::NONE
;
133 if( aBox
.Overlaps( rNewVisArea
) )
135 if( aBox
.Overlaps( rOldVisArea
) )
137 eAction
= Action::SCROLLED_WITHIN
;
141 if ( bVisibleChildrenOnly
&&
142 !rLower
.AlwaysIncludeAsChild() )
144 eAction
= Action::SCROLLED_IN
;
148 eAction
= Action::SCROLLED
;
152 else if( aBox
.Overlaps( rOldVisArea
) )
154 if ( bVisibleChildrenOnly
&&
155 !rLower
.AlwaysIncludeAsChild() )
157 eAction
= Action::SCROLLED_OUT
;
161 eAction
= Action::SCROLLED
;
164 else if( !bVisibleChildrenOnly
||
165 rLower
.AlwaysIncludeAsChild() )
167 // This wouldn't be required if the SwAccessibleFrame,
168 // wouldn't know about the visible area.
169 eAction
= Action::SCROLLED
;
171 if( Action::NONE
!= eAction
)
173 if ( rLower
.GetSwFrame() )
175 OSL_ENSURE( !rLower
.AlwaysIncludeAsChild(),
176 "<SwAccessibleContext::ChildrenScrolled(..)> - always included child not considered!" );
177 const SwFrame
* pLower( rLower
.GetSwFrame() );
178 ::rtl::Reference
< SwAccessibleContext
> xAccImpl
=
179 GetMap()->GetContextImpl( pLower
);
184 case Action::SCROLLED
:
185 xAccImpl
->Scrolled( rOldVisArea
);
187 case Action::SCROLLED_WITHIN
:
188 xAccImpl
->ScrolledWithin( rOldVisArea
);
190 case Action::SCROLLED_IN
:
191 xAccImpl
->ScrolledIn();
193 case Action::SCROLLED_OUT
:
194 xAccImpl
->ScrolledOut( rOldVisArea
);
202 ChildrenScrolled( pLower
, rOldVisArea
);
205 else if ( rLower
.GetDrawObject() )
207 OSL_ENSURE( !rLower
.AlwaysIncludeAsChild(),
208 "<SwAccessibleContext::ChildrenScrolled(..)> - always included child not considered!" );
209 ::rtl::Reference
< ::accessibility::AccessibleShape
> xAccImpl
=
210 GetMap()->GetContextImpl( rLower
.GetDrawObject(),
216 case Action::SCROLLED
:
217 case Action::SCROLLED_WITHIN
:
218 xAccImpl
->ViewForwarderChanged();
220 case Action::SCROLLED_IN
:
221 ScrolledInShape( xAccImpl
.get() );
223 case Action::SCROLLED_OUT
:
225 xAccImpl
->ViewForwarderChanged();
226 // this DisposeShape call was removed by
227 // IAccessibility2 implementation
228 // without giving any reason why
229 DisposeShape( rLower
.GetDrawObject(),
233 // coverity[dead_error_begin] - following conditions exist to avoid compiler warning
239 else if ( rLower
.GetWindow() )
241 // nothing to do - as such children are always included as children.
242 OSL_ENSURE( rLower
.AlwaysIncludeAsChild(),
243 "<SwAccessibleContext::ChildrenScrolled(..)> - not always included child not considered!" );
247 else if ( rLower
.GetSwFrame() &&
248 ( !bVisibleChildrenOnly
||
249 aBox
.Overlaps( rOldVisArea
) ||
250 aBox
.Overlaps( rNewVisArea
) ) )
252 // There are no unaccessible SdrObjects that need to be notified
253 ChildrenScrolled( rLower
.GetSwFrame(), rOldVisArea
);
259 void SwAccessibleContext::Scrolled( const SwRect
& rOldVisArea
)
261 SetVisArea( GetMap()->GetVisArea() );
263 ChildrenScrolled( GetFrame(), rOldVisArea
);
265 bool bIsOldShowingState
;
266 bool bIsNewShowingState
= IsShowing( *(GetMap()) );
268 std::scoped_lock
aGuard( m_Mutex
);
269 bIsOldShowingState
= m_isShowingState
;
270 m_isShowingState
= bIsNewShowingState
;
273 if( bIsOldShowingState
!= bIsNewShowingState
)
274 FireStateChangedEvent( AccessibleStateType::SHOWING
,
275 bIsNewShowingState
);
278 void SwAccessibleContext::ScrolledWithin( const SwRect
& rOldVisArea
)
280 SetVisArea( GetMap()->GetVisArea() );
282 ChildrenScrolled( GetFrame(), rOldVisArea
);
284 FireVisibleDataEvent();
287 void SwAccessibleContext::ScrolledIn()
289 // This accessible should be freshly created, because it
290 // was not visible before. Therefore, its visible area must already
291 // reflect the scrolling.
292 OSL_ENSURE( GetVisArea() == GetMap()->GetVisArea(),
293 "Visible area of child is wrong. Did it exist already?" );
295 // Send child event at parent. That's all we have to do here.
296 const SwFrame
* pParent
= GetParent();
297 ::rtl::Reference
< SwAccessibleContext
> xParentImpl(
298 GetMap()->GetContextImpl( pParent
, false ) );
299 uno::Reference
< XAccessibleContext
> xThis( this );
300 if( !xParentImpl
.is() )
303 SetParent( xParentImpl
.get() );
305 AccessibleEventObject aEvent
;
306 aEvent
.EventId
= AccessibleEventId::CHILD
;
307 aEvent
.NewValue
<<= xThis
;
308 aEvent
.IndexHint
= -1;
310 xParentImpl
->FireAccessibleEvent( aEvent
);
314 vcl::Window
*pWin
= GetWindow();
315 if( pWin
&& pWin
->HasFocus() )
317 FireStateChangedEvent( AccessibleStateType::FOCUSED
, true );
322 void SwAccessibleContext::ScrolledOut( const SwRect
& rOldVisArea
)
324 SetVisArea( GetMap()->GetVisArea() );
326 // First of all, update the children. That's required to dispose
327 // all children that are existing only if they are visible. They
328 // are not disposed by the recursive Dispose call that follows later on,
329 // because this call will only dispose children that are in the
330 // new visible area. The children we want to dispose however are in the
331 // old visible area all.
332 ChildrenScrolled( GetFrame(), rOldVisArea
);
334 // Broadcast a state changed event for the showing state.
335 // It might be that the child is freshly created just to send
336 // the child event. In this case no listener will exist.
337 FireStateChangedEvent( AccessibleStateType::SHOWING
, false );
339 // this Dispose call was removed by IAccessibility2 implementation
340 // without giving any reason why - without it we get stale
341 // entries in SwAccessibleMap::mpFrameMap.
345 // #i27301# - use new type definition for <_nStates>
346 void SwAccessibleContext::InvalidateChildrenStates( const SwFrame
* _pFrame
,
347 AccessibleStates _nStates
)
349 const SwAccessibleChildSList
aVisList( GetVisArea(), *_pFrame
, *(GetMap()) );
351 SwAccessibleChildSList::const_iterator
aIter( aVisList
.begin() );
352 while( aIter
!= aVisList
.end() )
354 const SwAccessibleChild
& rLower
= *aIter
;
355 const SwFrame
* pLower
= rLower
.GetSwFrame();
358 ::rtl::Reference
< SwAccessibleContext
> xAccImpl
;
359 if( rLower
.IsAccessible( GetShell()->IsPreview() ) )
360 xAccImpl
= GetMap()->GetContextImpl( pLower
, false );
362 xAccImpl
->InvalidateStates( _nStates
);
364 InvalidateChildrenStates( pLower
, _nStates
);
366 else if ( rLower
.GetDrawObject() )
370 else if ( rLower
.GetWindow() )
379 void SwAccessibleContext::DisposeChildren(const SwFrame
*pFrame
,
381 bool bCanSkipInvisible
)
383 const SwAccessibleChildSList
aVisList( GetVisArea(), *pFrame
, *(GetMap()) );
384 SwAccessibleChildSList::const_iterator
aIter( aVisList
.begin() );
385 while( aIter
!= aVisList
.end() )
387 const SwAccessibleChild
& rLower
= *aIter
;
388 const SwFrame
* pLower
= rLower
.GetSwFrame();
391 // tdf#117601 dispose the darn thing if it ever was accessible
392 ::rtl::Reference
<SwAccessibleContext
> xAccImpl
= GetMap()->GetContextImpl(pLower
, false);
394 xAccImpl
->Dispose( bRecursive
);
397 // it's possible that the xAccImpl *does* exist with a
398 // ref-count of 0 and blocked in its dtor in another thread -
399 // this call here could be from SwAccessibleMap dtor so
400 // remove it from any maps now!
401 GetMap()->RemoveContext(pLower
);
402 // in this case the context will check with a weak_ptr
403 // that the map is still alive so it's not necessary
404 // to clear its m_pMap here.
407 DisposeChildren(pLower
, bRecursive
, bCanSkipInvisible
);
411 else if ( rLower
.GetDrawObject() )
413 ::rtl::Reference
< ::accessibility::AccessibleShape
> xAccImpl(
414 GetMap()->GetContextImpl( rLower
.GetDrawObject(),
417 DisposeShape( rLower
.GetDrawObject(), xAccImpl
.get() );
419 else if ( rLower
.GetWindow() )
421 DisposeChild(rLower
, false, bCanSkipInvisible
);
427 void SwAccessibleContext::InvalidateContent_( bool )
431 void SwAccessibleContext::InvalidateCursorPos_()
435 void SwAccessibleContext::InvalidateFocus_()
439 void SwAccessibleContext::FireAccessibleEvent( AccessibleEventObject
& rEvent
)
443 SAL_INFO("sw.a11y", "SwAccessibleContext::FireAccessibleEvent called for already disposed frame?");
447 if( !rEvent
.Source
.is() )
449 uno::Reference
< XAccessibleContext
> xThis( this );
450 rEvent
.Source
= xThis
;
454 comphelper::AccessibleEventNotifier::addEvent( m_nClientId
, rEvent
);
457 void SwAccessibleContext::FireVisibleDataEvent()
459 AccessibleEventObject aEvent
;
460 aEvent
.EventId
= AccessibleEventId::VISIBLE_DATA_CHANGED
;
462 FireAccessibleEvent( aEvent
);
465 void SwAccessibleContext::FireStateChangedEvent( sal_Int64 nState
,
468 AccessibleEventObject aEvent
;
470 aEvent
.EventId
= AccessibleEventId::STATE_CHANGED
;
472 aEvent
.NewValue
<<= nState
;
474 aEvent
.OldValue
<<= nState
;
476 FireAccessibleEvent( aEvent
);
479 void SwAccessibleContext::GetStates( sal_Int64
& rStateSet
)
481 std::scoped_lock
aGuard( m_Mutex
);
484 if (m_isShowingState
)
485 rStateSet
|= AccessibleStateType::SHOWING
;
488 if (m_isEditableState
)
489 //Set editable state to graphic and other object when the document is editable
491 rStateSet
|= AccessibleStateType::EDITABLE
;
492 rStateSet
|= AccessibleStateType::RESIZABLE
;
493 rStateSet
|= AccessibleStateType::MOVEABLE
;
496 rStateSet
|= AccessibleStateType::ENABLED
;
500 rStateSet
|= AccessibleStateType::OPAQUE
;
503 rStateSet
|= AccessibleStateType::VISIBLE
;
506 rStateSet
|= AccessibleStateType::DEFUNC
;
509 bool SwAccessibleContext::IsEditableState()
513 std::scoped_lock
aGuard( m_Mutex
);
514 bRet
= m_isEditableState
;
520 bool SwAccessibleContext::IsDisposed() const
522 return !(GetFrame() && GetMap());
525 void SwAccessibleContext::ThrowIfDisposed()
529 throw lang::DisposedException(u
"object is nonfunctional"_ustr
,
534 SwAccessibleContext::SwAccessibleContext(std::shared_ptr
<SwAccessibleMap
> const& pMap
,
535 sal_Int16
const nRole
,
537 : SwAccessibleFrame( pMap
->GetVisArea(), pF
,
538 pMap
->GetShell()->IsPreview() )
543 , m_isDisposing( false )
544 , m_isRegisteredAtAccessibleMap( true )
545 , m_isSelectedInDoc(false)
550 SwAccessibleContext::~SwAccessibleContext()
552 // must have for 2 reasons: 2. as long as this thread has SolarMutex
553 // another thread cannot destroy the SwAccessibleMap so our temporary
554 // taking a hard ref to SwAccessibleMap won't delay its destruction
555 SolarMutexGuard aGuard
;
556 // must check with weak_ptr that m_pMap is still alive
557 std::shared_ptr
<SwAccessibleMap
> pMap(m_wMap
.lock());
558 if (m_isRegisteredAtAccessibleMap
&& GetFrame() && pMap
)
560 pMap
->RemoveContext( GetFrame() );
564 uno::Reference
< XAccessibleContext
> SAL_CALL
565 SwAccessibleContext::getAccessibleContext()
567 uno::Reference
< XAccessibleContext
> xRet( this );
571 sal_Int64 SAL_CALL
SwAccessibleContext::getAccessibleChildCount()
573 SolarMutexGuard aGuard
;
577 return m_isDisposing
? 0 : GetChildCount( *(GetMap()) );
580 uno::Reference
< XAccessible
> SAL_CALL
581 SwAccessibleContext::getAccessibleChild( sal_Int64 nIndex
)
583 SolarMutexGuard aGuard
;
587 if (nIndex
< 0 || nIndex
>= getAccessibleChildCount())
588 throw lang::IndexOutOfBoundsException();
590 const SwAccessibleChild
aChild( GetChild( *(GetMap()), nIndex
) );
591 if( !aChild
.IsValid() )
593 uno::Reference
< XAccessibleContext
> xThis( this );
594 lang::IndexOutOfBoundsException
aExcept(
595 u
"index out of bounds"_ustr
,
600 uno::Reference
< XAccessible
> xChild
;
601 if( aChild
.GetSwFrame() )
603 ::rtl::Reference
< SwAccessibleContext
> xChildImpl(
604 GetMap()->GetContextImpl( aChild
.GetSwFrame(), !m_isDisposing
) );
605 if( xChildImpl
.is() )
607 xChildImpl
->SetParent( this );
608 xChild
= xChildImpl
.get();
611 else if ( aChild
.GetDrawObject() )
613 ::rtl::Reference
< ::accessibility::AccessibleShape
> xChildImpl(
614 GetMap()->GetContextImpl( aChild
.GetDrawObject(),
615 this, !m_isDisposing
) );
616 if( xChildImpl
.is() )
617 xChild
= xChildImpl
.get();
619 else if ( aChild
.GetWindow() )
621 xChild
= aChild
.GetWindow()->GetAccessible();
627 css::uno::Sequence
<uno::Reference
<XAccessible
>> SAL_CALL
628 SwAccessibleContext::getAccessibleChildren()
630 SolarMutexGuard aGuard
;
634 std::list
< sw::access::SwAccessibleChild
> aChildren
;
635 GetChildren( *GetMap(), aChildren
);
637 std::vector
<uno::Reference
<XAccessible
>> aRet
;
638 aRet
.reserve(aChildren
.size());
639 for (const auto & rSwChild
: aChildren
)
641 uno::Reference
< XAccessible
> xChild
;
642 if( rSwChild
.GetSwFrame() )
644 ::rtl::Reference
< SwAccessibleContext
> xChildImpl(
645 GetMap()->GetContextImpl( rSwChild
.GetSwFrame(), !m_isDisposing
) );
646 if( xChildImpl
.is() )
648 xChildImpl
->SetParent( this );
649 xChild
= xChildImpl
.get();
652 else if ( rSwChild
.GetDrawObject() )
654 ::rtl::Reference
< ::accessibility::AccessibleShape
> xChildImpl(
655 GetMap()->GetContextImpl( rSwChild
.GetDrawObject(),
656 this, !m_isDisposing
) );
657 if( xChildImpl
.is() )
658 xChild
= xChildImpl
.get();
660 else if ( rSwChild
.GetWindow() )
662 xChild
= rSwChild
.GetWindow()->GetAccessible();
664 aRet
.push_back(xChild
);
666 return comphelper::containerToSequence(aRet
);
669 rtl::Reference
< SwAccessibleContext
> SwAccessibleContext::getAccessibleParentImpl()
671 SolarMutexGuard aGuard
;
673 const SwFrame
*pUpper
= GetParent();
674 OSL_ENSURE( pUpper
!= nullptr || m_isDisposing
, "no upper found" );
676 rtl::Reference
< SwAccessibleContext
> xAcc
;
678 xAcc
= GetMap()->GetContextImpl( pUpper
, !m_isDisposing
);
680 OSL_ENSURE( xAcc
.is() || m_isDisposing
, "no parent found" );
682 // Remember the parent as weak ref.
684 std::scoped_lock
aWeakParentGuard( m_Mutex
);
685 m_xWeakParent
= xAcc
.get();
691 uno::Reference
< XAccessible
> SAL_CALL
SwAccessibleContext::getAccessibleParent()
693 SolarMutexGuard aGuard
;
697 return getAccessibleParentImpl();
700 sal_Int64 SAL_CALL
SwAccessibleContext::getAccessibleIndexInParent()
702 SolarMutexGuard aGuard
;
706 const SwFrame
*pUpper
= GetParent();
707 OSL_ENSURE( pUpper
!= nullptr || m_isDisposing
, "no upper found" );
709 sal_Int64 nIndex
= -1;
712 ::rtl::Reference
< SwAccessibleContext
> xAccImpl(
713 GetMap()->GetContextImpl(pUpper
, !m_isDisposing
) );
714 OSL_ENSURE( xAccImpl
.is() || m_isDisposing
, "no parent found" );
716 nIndex
= xAccImpl
->GetChildIndex( *(GetMap()), SwAccessibleChild(GetFrame()) );
722 sal_Int16 SAL_CALL
SwAccessibleContext::getAccessibleRole()
727 OUString SAL_CALL
SwAccessibleContext::getAccessibleName()
732 uno::Reference
< XAccessibleRelationSet
> SAL_CALL
733 SwAccessibleContext::getAccessibleRelationSet()
735 // by default there are no relations
736 uno::Reference
< XAccessibleRelationSet
> xRet( new utl::AccessibleRelationSetHelper() );
740 sal_Int64 SAL_CALL
SwAccessibleContext::getAccessibleStateSet()
742 SolarMutexGuard aGuard
;
746 sal_Int64 nStateSet
= 0;
748 if (m_isSelectedInDoc
)
749 nStateSet
|= AccessibleStateType::SELECTED
;
751 GetStates( nStateSet
);
756 lang::Locale SAL_CALL
SwAccessibleContext::getLocale()
758 SolarMutexGuard aGuard
;
760 lang::Locale
aLoc( Application::GetSettings().GetLanguageTag().getLocale() );
764 void SAL_CALL
SwAccessibleContext::addAccessibleEventListener(
765 const uno::Reference
< XAccessibleEventListener
>& xListener
)
769 SolarMutexGuard aGuard
;
771 m_nClientId
= comphelper::AccessibleEventNotifier::registerClient( );
772 comphelper::AccessibleEventNotifier::addEventListener( m_nClientId
, xListener
);
776 void SAL_CALL
SwAccessibleContext::removeAccessibleEventListener(
777 const uno::Reference
< XAccessibleEventListener
>& xListener
)
779 if (!(xListener
.is() && m_nClientId
))
782 SolarMutexGuard aGuard
;
783 sal_Int32 nListenerCount
= comphelper::AccessibleEventNotifier::removeEventListener( m_nClientId
, xListener
);
784 if ( !nListenerCount
)
786 // no listeners anymore
787 // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client),
788 // and at least to us not firing any events anymore, in case somebody calls
789 // NotifyAccessibleEvent, again
790 comphelper::AccessibleEventNotifier::revokeClient( m_nClientId
);
795 static bool lcl_PointInRectangle(const awt::Point
& aPoint
,
796 const awt::Rectangle
& aRect
)
798 tools::Long nDiffX
= aPoint
.X
- aRect
.X
;
799 tools::Long nDiffY
= aPoint
.Y
- aRect
.Y
;
802 nDiffX
>= 0 && nDiffX
< aRect
.Width
&& nDiffY
>= 0 &&
803 nDiffY
< aRect
.Height
;
807 sal_Bool SAL_CALL
SwAccessibleContext::containsPoint(
808 const awt::Point
& aPoint
)
810 awt::Rectangle aPixBounds
= getBoundsImpl(true);
814 return lcl_PointInRectangle(aPoint
, aPixBounds
);
817 uno::Reference
< XAccessible
> SAL_CALL
SwAccessibleContext::getAccessibleAtPoint(
818 const awt::Point
& aPoint
)
820 SolarMutexGuard aGuard
;
824 uno::Reference
< XAccessible
> xAcc
;
826 vcl::Window
*pWin
= GetWindow();
829 throw uno::RuntimeException(u
"no Window"_ustr
, getXWeak());
832 Point
aPixPoint( aPoint
.X
, aPoint
.Y
); // px rel to parent
833 if( !GetFrame()->IsRootFrame() )
835 SwRect
aLogBounds( GetBounds( *(GetMap()), GetFrame() ) ); // twip rel to doc root
836 Point
aPixPos( GetMap()->CoreToPixel( aLogBounds
).TopLeft() );
837 aPixPoint
.setX(aPixPoint
.getX() + aPixPos
.getX());
838 aPixPoint
.setY(aPixPoint
.getY() + aPixPos
.getY());
841 const SwAccessibleChild
aChild( GetChildAtPixel( aPixPoint
, *(GetMap()) ) );
842 if( aChild
.GetSwFrame() )
844 xAcc
= GetMap()->GetContext( aChild
.GetSwFrame() );
846 else if( aChild
.GetDrawObject() )
848 xAcc
= GetMap()->GetContext( aChild
.GetDrawObject(), this );
850 else if ( aChild
.GetWindow() )
852 xAcc
= aChild
.GetWindow()->GetAccessible();
865 Return bounding box relative to parent if parent is no root
866 frame. Otherwise return the absolute bounding box.
870 Return the absolute bounding box.
873 true: Use relative mode.
874 false: Use absolute mode.
876 awt::Rectangle
SwAccessibleContext::getBoundsImpl(bool bRelative
)
878 SolarMutexGuard aGuard
;
882 const SwFrame
*pParent
= GetParent();
883 OSL_ENSURE( pParent
, "no Parent found" );
884 vcl::Window
*pWin
= GetWindow();
888 throw uno::RuntimeException(u
"no Parent"_ustr
, getXWeak());
892 throw uno::RuntimeException(u
"no Window"_ustr
, getXWeak());
895 SwRect
aLogBounds( GetBounds( *(GetMap()), GetFrame() ) ); // twip relative to document root
896 tools::Rectangle
aPixBounds( 0, 0, 0, 0 );
897 if( GetFrame()->IsPageFrame() &&
898 static_cast < const SwPageFrame
* >( GetFrame() )->IsEmptyPage() )
900 OSL_ENSURE( GetShell()->IsPreview(), "empty page accessible?" );
901 if( GetShell()->IsPreview() )
903 // adjust method call <GetMap()->GetPreviewPageSize()>
904 sal_uInt16 nPageNum
=
905 static_cast < const SwPageFrame
* >( GetFrame() )->GetPhyPageNum();
906 aLogBounds
.SSize( GetMap()->GetPreviewPageSize( nPageNum
) );
909 if( !aLogBounds
.IsEmpty() )
911 aPixBounds
= GetMap()->CoreToPixel( aLogBounds
);
912 if( !pParent
->IsRootFrame() && bRelative
)
914 SwRect
aParentLogBounds( GetBounds( *(GetMap()), pParent
) ); // twip rel to doc root
915 Point
aParentPixPos( GetMap()->CoreToPixel( aParentLogBounds
).TopLeft() );
916 aPixBounds
.Move( -aParentPixPos
.getX(), -aParentPixPos
.getY() );
920 awt::Rectangle
aBox( aPixBounds
.Left(), aPixBounds
.Top(),
921 aPixBounds
.GetWidth(), aPixBounds
.GetHeight() );
926 awt::Rectangle SAL_CALL
SwAccessibleContext::getBounds()
928 return getBoundsImpl(true);
931 awt::Point SAL_CALL
SwAccessibleContext::getLocation()
933 awt::Rectangle aRect
= getBoundsImpl(true);
934 awt::Point
aPoint(aRect
.X
, aRect
.Y
);
939 awt::Point SAL_CALL
SwAccessibleContext::getLocationOnScreen()
941 awt::Rectangle aRect
= getBoundsImpl(false);
943 Point
aPixPos(aRect
.X
, aRect
.Y
);
945 vcl::Window
*pWin
= GetWindow();
948 throw uno::RuntimeException(u
"no Window"_ustr
, getXWeak());
951 AbsoluteScreenPixelPoint aPixPosAbs
= pWin
->OutputToAbsoluteScreenPixel(aPixPos
);
952 awt::Point
aPoint(aPixPosAbs
.getX(), aPixPosAbs
.getY());
957 awt::Size SAL_CALL
SwAccessibleContext::getSize()
959 awt::Rectangle aRect
= getBoundsImpl(false);
960 awt::Size
aSize( aRect
.Width
, aRect
.Height
);
965 void SAL_CALL
SwAccessibleContext::grabFocus()
967 SolarMutexGuard aGuard
;
971 if( GetFrame()->IsFlyFrame() )
973 const SdrObject
*pObj
=
974 static_cast < const SwFlyFrame
* >( GetFrame() )->GetVirtDrawObj();
976 Select( const_cast < SdrObject
* >( pObj
), false );
980 const SwContentFrame
*pCFrame
= nullptr;
981 if( GetFrame()->IsContentFrame() )
982 pCFrame
= static_cast< const SwContentFrame
* >( GetFrame() );
983 else if( GetFrame()->IsLayoutFrame() )
984 pCFrame
= static_cast< const SwLayoutFrame
* >( GetFrame() )->ContainsContent();
986 if( pCFrame
&& pCFrame
->IsTextFrame() )
988 const SwTextFrame
*pTextFrame
= static_cast< const SwTextFrame
* >( pCFrame
);
989 const SwTextNode
*pTextNd
= pTextFrame
->GetTextNodeFirst();
990 assert(pTextNd
); // can it actually be null? probably not=>simplify
993 // create pam for selection
994 SwPosition
const aStartPos(pTextFrame
->MapViewToModelPos(pTextFrame
->GetOffset()));
995 SwPaM
aPaM( aStartPos
);
997 // set PaM at cursor shell
1004 sal_Int32 SAL_CALL
SwAccessibleContext::getForeground()
1006 return sal_Int32(COL_BLACK
);
1009 sal_Int32 SAL_CALL
SwAccessibleContext::getBackground()
1011 return sal_Int32(COL_WHITE
);
1014 sal_Bool SAL_CALL
SwAccessibleContext::supportsService (const OUString
& ServiceName
)
1016 return cppu::supportsService(this, ServiceName
);
1019 void SwAccessibleContext::DisposeShape( const SdrObject
*pObj
,
1020 ::accessibility::AccessibleShape
*pAccImpl
)
1022 ::rtl::Reference
< ::accessibility::AccessibleShape
> xAccImpl( pAccImpl
);
1023 if( !xAccImpl
.is() )
1024 xAccImpl
= GetMap()->GetContextImpl( pObj
, this );
1026 AccessibleEventObject aEvent
;
1027 aEvent
.EventId
= AccessibleEventId::CHILD
;
1028 uno::Reference
< XAccessible
> xAcc( xAccImpl
);
1029 aEvent
.OldValue
<<= xAcc
;
1030 aEvent
.IndexHint
= -1;
1031 FireAccessibleEvent( aEvent
);
1033 GetMap()->RemoveContext( pObj
);
1034 xAccImpl
->dispose();
1037 void SwAccessibleContext::ScrolledInShape( ::accessibility::AccessibleShape
*pAccImpl
)
1039 if(nullptr == pAccImpl
)
1043 AccessibleEventObject aEvent
;
1044 aEvent
.EventId
= AccessibleEventId::CHILD
;
1045 uno::Reference
< XAccessible
> xAcc( pAccImpl
);
1046 aEvent
.NewValue
<<= xAcc
;
1047 aEvent
.IndexHint
= -1;
1048 FireAccessibleEvent( aEvent
);
1050 if( !pAccImpl
->GetState( AccessibleStateType::FOCUSED
) )
1053 vcl::Window
*pWin
= GetWindow();
1054 if( pWin
&& pWin
->HasFocus() )
1056 AccessibleEventObject aStateChangedEvent
;
1057 aStateChangedEvent
.EventId
= AccessibleEventId::STATE_CHANGED
;
1058 aStateChangedEvent
.NewValue
<<= AccessibleStateType::FOCUSED
;
1059 aStateChangedEvent
.Source
= xAcc
;
1061 FireAccessibleEvent( aStateChangedEvent
);
1065 void SwAccessibleContext::Dispose(bool bRecursive
, bool bCanSkipInvisible
)
1067 SolarMutexGuard aGuard
;
1069 OSL_ENSURE( GetFrame() && GetMap(), "already disposed" );
1070 OSL_ENSURE( GetMap()->GetVisArea() == GetVisArea(),
1071 "invalid visible area for dispose" );
1073 m_isDisposing
= true;
1077 DisposeChildren(GetFrame(), bRecursive
, bCanSkipInvisible
);
1080 uno::Reference
< XAccessible
> xParent( GetWeakParent() );
1081 uno::Reference
< XAccessibleContext
> xThis( this );
1083 // send child event at parent
1086 SwAccessibleContext
*pAcc
= static_cast<SwAccessibleContext
*>(xParent
.get());
1088 AccessibleEventObject aEvent
;
1089 aEvent
.EventId
= AccessibleEventId::CHILD
;
1090 aEvent
.OldValue
<<= xThis
;
1091 aEvent
.IndexHint
= -1;
1092 pAcc
->FireAccessibleEvent( aEvent
);
1095 // set defunc state (it's not required to broadcast a state changed
1096 // event if the object is disposed afterwards)
1098 std::scoped_lock
aDefuncStateGuard( m_Mutex
);
1099 m_isDefuncState
= true;
1102 // broadcast dispose event
1105 comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing( m_nClientId
, *this );
1109 RemoveFrameFromAccessibleMap();
1114 m_isDisposing
= false;
1117 void SwAccessibleContext::DisposeChild( const SwAccessibleChild
& rChildFrameOrObj
,
1118 bool bRecursive
, bool bCanSkipInvisible
)
1120 SolarMutexGuard aGuard
;
1122 if ( !bCanSkipInvisible
||
1123 rChildFrameOrObj
.AlwaysIncludeAsChild() ||
1124 IsShowing( *(GetMap()), rChildFrameOrObj
) ||
1125 !SwAccessibleChild( GetFrame() ).IsVisibleChildrenOnly() )
1127 // If the object could have existed before, then there is nothing to do,
1128 // because no wrapper exists now and therefore no one is interested to
1129 // get notified of the movement.
1130 if( rChildFrameOrObj
.GetSwFrame() )
1132 ::rtl::Reference
< SwAccessibleContext
> xAccImpl
=
1133 GetMap()->GetContextImpl( rChildFrameOrObj
.GetSwFrame(), false );
1135 xAccImpl
->Dispose( bRecursive
);
1137 else if ( rChildFrameOrObj
.GetDrawObject() )
1139 ::rtl::Reference
< ::accessibility::AccessibleShape
> xAccImpl
=
1140 GetMap()->GetContextImpl( rChildFrameOrObj
.GetDrawObject(),
1143 DisposeShape( rChildFrameOrObj
.GetDrawObject(),
1146 else if ( rChildFrameOrObj
.GetWindow() )
1148 AccessibleEventObject aEvent
;
1149 aEvent
.EventId
= AccessibleEventId::CHILD
;
1150 uno::Reference
< XAccessible
> xAcc
=
1151 rChildFrameOrObj
.GetWindow()->GetAccessible();
1152 aEvent
.OldValue
<<= xAcc
;
1153 aEvent
.IndexHint
= -1;
1154 FireAccessibleEvent( aEvent
);
1157 else if( bRecursive
&& rChildFrameOrObj
.GetSwFrame() )
1158 DisposeChildren(rChildFrameOrObj
.GetSwFrame(), bRecursive
, bCanSkipInvisible
);
1161 void SwAccessibleContext::InvalidatePosOrSize( const SwRect
& )
1163 SolarMutexGuard aGuard
;
1165 OSL_ENSURE( GetFrame() && !GetFrame()->getFrameArea().IsEmpty(), "context should have a size" );
1167 bool bIsOldShowingState
;
1168 bool bIsNewShowingState
= IsShowing( *(GetMap()) );
1170 std::scoped_lock
aShowingStateGuard( m_Mutex
);
1171 bIsOldShowingState
= m_isShowingState
;
1172 m_isShowingState
= bIsNewShowingState
;
1175 if( bIsOldShowingState
!= bIsNewShowingState
)
1177 FireStateChangedEvent( AccessibleStateType::SHOWING
,
1178 bIsNewShowingState
);
1180 else if( bIsNewShowingState
)
1182 // The frame stays visible -> broadcast event
1183 FireVisibleDataEvent();
1186 // note: InvalidatePosOrSize must call InvalidateContent_ so that
1187 // SwAccessibleParagraph updates its portions, or dispose it
1188 // (see accmap.cxx: INVALID_CONTENT is contained in POS_CHANGED)
1189 if( !bIsNewShowingState
&&
1190 SwAccessibleChild( GetParent() ).IsVisibleChildrenOnly() )
1192 // this Dispose call was removed by IAccessibility2 implementation
1193 // without giving any reason why - without it we get stale
1194 // entries in SwAccessibleMap::mpFrameMap.
1199 InvalidateContent_( true );
1203 void SwAccessibleContext::InvalidateChildPosOrSize(
1204 const SwAccessibleChild
& rChildFrameOrObj
,
1205 const SwRect
& rOldFrame
)
1207 SolarMutexGuard aGuard
;
1209 // this happens during layout, e.g. when a page is deleted and next page's
1210 // header/footer moves backward such an event is generated
1211 SAL_INFO_IF(rChildFrameOrObj
.GetSwFrame() &&
1212 rChildFrameOrObj
.GetSwFrame()->getFrameArea().IsEmpty(),
1213 "sw.a11y", "child context should have a size");
1215 if ( rChildFrameOrObj
.AlwaysIncludeAsChild() )
1221 const bool bVisibleChildrenOnly
= SwAccessibleChild( GetFrame() ).IsVisibleChildrenOnly();
1222 const bool bNew
= rOldFrame
.IsEmpty() ||
1223 ( rOldFrame
.Left() == 0 && rOldFrame
.Top() == 0 );
1224 if( IsShowing( *(GetMap()), rChildFrameOrObj
) )
1226 // If the object could have existed before, then there is nothing to do,
1227 // because no wrapper exists now and therefore no one is interested to
1228 // get notified of the movement.
1229 if( bNew
|| (bVisibleChildrenOnly
&& !IsShowing( rOldFrame
)) )
1231 if( rChildFrameOrObj
.GetSwFrame() )
1233 // The frame becomes visible. A child event must be send.
1234 ::rtl::Reference
< SwAccessibleContext
> xAccImpl
=
1235 GetMap()->GetContextImpl( rChildFrameOrObj
.GetSwFrame() );
1236 xAccImpl
->ScrolledIn();
1238 else if ( rChildFrameOrObj
.GetDrawObject() )
1240 ::rtl::Reference
< ::accessibility::AccessibleShape
> xAccImpl
=
1241 GetMap()->GetContextImpl( rChildFrameOrObj
.GetDrawObject(),
1244 if ( xAccImpl
.is() )
1246 ScrolledInShape( xAccImpl
.get() );
1250 OSL_FAIL( "<SwAccessibleContext::InvalidateChildPosOrSize(..)> - no accessible shape found." );
1253 else if ( rChildFrameOrObj
.GetWindow() )
1255 AccessibleEventObject aEvent
;
1256 aEvent
.EventId
= AccessibleEventId::CHILD
;
1257 aEvent
.NewValue
<<= rChildFrameOrObj
.GetWindow()->GetAccessible();
1258 FireAccessibleEvent( aEvent
);
1264 // If the frame was visible before, then a child event for the parent
1265 // needs to be send. However, there is no wrapper existing, and so
1266 // no notifications for grandchildren are required. If the are
1267 // grandgrandchildren, they would be notified by the layout.
1268 if( bVisibleChildrenOnly
&&
1269 !bNew
&& IsShowing( rOldFrame
) )
1271 if( rChildFrameOrObj
.GetSwFrame() )
1273 ::rtl::Reference
< SwAccessibleContext
> xAccImpl
=
1274 GetMap()->GetContextImpl( rChildFrameOrObj
.GetSwFrame() );
1275 xAccImpl
->SetParent( this );
1276 xAccImpl
->Dispose( true );
1278 else if ( rChildFrameOrObj
.GetDrawObject() )
1280 ::rtl::Reference
< ::accessibility::AccessibleShape
> xAccImpl
=
1281 GetMap()->GetContextImpl( rChildFrameOrObj
.GetDrawObject(),
1283 DisposeShape( rChildFrameOrObj
.GetDrawObject(),
1286 else if ( rChildFrameOrObj
.GetWindow() )
1288 OSL_FAIL( "<SwAccessibleContext::InvalidateChildPosOrSize(..)> - not expected to handle dispose of child of type <vcl::Window>." );
1294 void SwAccessibleContext::InvalidateContent()
1296 SolarMutexGuard aGuard
;
1298 InvalidateContent_( false );
1301 void SwAccessibleContext::InvalidateCursorPos()
1303 SolarMutexGuard aGuard
;
1305 InvalidateCursorPos_();
1308 void SwAccessibleContext::InvalidateFocus()
1310 SolarMutexGuard aGuard
;
1315 // #i27301# - use new type definition for <_nStates>
1316 void SwAccessibleContext::InvalidateStates( AccessibleStates _nStates
)
1321 SwViewShell
*pVSh
= GetMap()->GetShell();
1324 if( _nStates
& AccessibleStates::EDITABLE
)
1326 bool bIsOldEditableState
;
1327 bool bIsNewEditableState
= IsEditable( pVSh
);
1329 std::scoped_lock
aGuard( m_Mutex
);
1330 bIsOldEditableState
= m_isEditableState
;
1331 m_isEditableState
= bIsNewEditableState
;
1334 if( bIsOldEditableState
!= bIsNewEditableState
)
1335 FireStateChangedEvent( AccessibleStateType::EDITABLE
,
1336 bIsNewEditableState
);
1338 if( _nStates
& AccessibleStates::OPAQUE
)
1340 bool bIsOldOpaqueState
;
1341 bool bIsNewOpaqueState
= IsOpaque( pVSh
);
1343 std::scoped_lock
aGuard( m_Mutex
);
1344 bIsOldOpaqueState
= m_isOpaqueState
;
1345 m_isOpaqueState
= bIsNewOpaqueState
;
1348 if( bIsOldOpaqueState
!= bIsNewOpaqueState
)
1349 FireStateChangedEvent( AccessibleStateType::OPAQUE
,
1350 bIsNewOpaqueState
);
1354 InvalidateChildrenStates( GetFrame(), _nStates
);
1357 void SwAccessibleContext::InvalidateRelation( sal_uInt16 nType
)
1359 AccessibleEventObject aEvent
;
1360 aEvent
.EventId
= nType
;
1362 FireAccessibleEvent( aEvent
);
1365 /** #i27301# - text selection has changed */
1366 void SwAccessibleContext::InvalidateTextSelection()
1368 AccessibleEventObject aEvent
;
1369 aEvent
.EventId
= AccessibleEventId::TEXT_SELECTION_CHANGED
;
1371 FireAccessibleEvent( aEvent
);
1374 /** #i88069# - attributes has changed */
1375 void SwAccessibleContext::InvalidateAttr()
1377 AccessibleEventObject aEvent
;
1378 aEvent
.EventId
= AccessibleEventId::TEXT_ATTRIBUTE_CHANGED
;
1380 FireAccessibleEvent( aEvent
);
1383 bool SwAccessibleContext::HasCursor()
1388 bool SwAccessibleContext::Select( SwPaM
*pPaM
, SdrObject
*pObj
,
1391 SwCursorShell
* pCursorShell
= GetCursorShell();
1395 SwFEShell
* pFEShell
= dynamic_cast<SwFEShell
*>(pCursorShell
);
1396 // Get rid of activated OLE object
1398 pFEShell
->FinishOLEObj();
1400 SwWrtShell
* pWrtShell
= dynamic_cast<SwWrtShell
*>(pCursorShell
);
1407 sal_uInt8 nFlags
= bAdd
? SW_ADD_SELECT
: 0;
1408 pFEShell
->SelectObj( Point(), nFlags
, pObj
);
1414 // Get rid of frame selection. If there is one, make text cursor
1416 bool bCallShowCursor
= false;
1417 if( pFEShell
&& (pFEShell
->IsFrameSelected() ||
1418 pFEShell
->IsObjSelected()) )
1420 Point
aPt( LONG_MIN
, LONG_MIN
);
1421 pFEShell
->SelectObj( aPt
);
1422 bCallShowCursor
= true;
1424 pCursorShell
->KillPams();
1425 if( pWrtShell
&& pPaM
->HasMark() )
1426 // We have to do this or SwWrtShell can't figure out that it needs
1427 // to kill the selection later, when the user moves the cursor.
1428 pWrtShell
->SttSelect();
1429 pCursorShell
->SetSelection( *pPaM
);
1430 if( pPaM
->HasMark() && *pPaM
->GetPoint() == *pPaM
->GetMark())
1431 // Setting a "Selection" that starts and ends at the same spot
1432 // should remove the selection rather than create an empty one, so
1433 // that we get defined behavior if accessibility sets the cursor
1435 pCursorShell
->ClearMark();
1436 if( bCallShowCursor
)
1437 pCursorShell
->ShowCursor();
1444 OUString
SwAccessibleContext::GetResource(TranslateId pResId
,
1445 const OUString
*pArg1
,
1446 const OUString
*pArg2
)
1448 OUString sStr
= SwResId(pResId
);
1452 sStr
= sStr
.replaceFirst( "$(ARG1)", *pArg1
);
1456 sStr
= sStr
.replaceFirst( "$(ARG2)", *pArg2
);
1462 void SwAccessibleContext::RemoveFrameFromAccessibleMap()
1464 assert(m_refCount
> 0); // must be alive to do this without using m_wMap
1465 if (m_isRegisteredAtAccessibleMap
&& GetFrame() && GetMap())
1466 GetMap()->RemoveContext( GetFrame() );
1469 bool SwAccessibleContext::HasAdditionalAccessibleChildren()
1473 if ( GetFrame()->IsTextFrame() )
1475 SwPostItMgr
* pPostItMgr
= GetMap()->GetShell()->GetPostItMgr();
1476 if ( pPostItMgr
&& pPostItMgr
->HasNotes() && pPostItMgr
->ShowNotes() )
1478 bRet
= pPostItMgr
->HasFrameConnectedSidebarWins( *(GetFrame()) );
1485 /** #i88070# - get additional accessible child by index */
1486 vcl::Window
* SwAccessibleContext::GetAdditionalAccessibleChild( const sal_Int32 nIndex
)
1488 vcl::Window
* pAdditionalAccessibleChild( nullptr );
1490 if ( GetFrame()->IsTextFrame() )
1492 SwPostItMgr
* pPostItMgr
= GetMap()->GetShell()->GetPostItMgr();
1493 if ( pPostItMgr
&& pPostItMgr
->HasNotes() && pPostItMgr
->ShowNotes() )
1495 pAdditionalAccessibleChild
=
1496 pPostItMgr
->GetSidebarWinForFrameByIndex( *(GetFrame()), nIndex
);
1500 return pAdditionalAccessibleChild
;
1503 /** #i88070# - get all additional accessible children */
1504 void SwAccessibleContext::GetAdditionalAccessibleChildren( std::vector
< vcl::Window
* >* pChildren
)
1506 if ( GetFrame()->IsTextFrame() )
1508 SwPostItMgr
* pPostItMgr
= GetMap()->GetShell()->GetPostItMgr();
1509 if ( pPostItMgr
&& pPostItMgr
->HasNotes() && pPostItMgr
->ShowNotes() )
1511 pPostItMgr
->GetAllSidebarWinForFrame( *(GetFrame()), pChildren
);
1516 bool SwAccessibleContext::SetSelectedState(bool const bSelected
)
1518 if (m_isSelectedInDoc
!= bSelected
)
1520 m_isSelectedInDoc
= bSelected
;
1521 FireStateChangedEvent( AccessibleStateType::SELECTED
, bSelected
);
1527 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */