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 uno::Reference
< XAccessible
> xParent( pParent
);
69 m_xWeakParent
= xParent
;
72 uno::Reference
< XAccessible
> SwAccessibleContext::GetWeakParent() const
74 std::scoped_lock
aGuard( m_Mutex
);
76 uno::Reference
< XAccessible
> xParent( m_xWeakParent
);
80 vcl::Window
*SwAccessibleContext::GetWindow()
82 vcl::Window
*pWin
= nullptr;
86 const SwViewShell
*pVSh
= GetMap()->GetShell();
87 OSL_ENSURE( pVSh
, "no view shell" );
89 pWin
= pVSh
->GetWin();
91 OSL_ENSURE( pWin
, "no window" );
97 // get SwViewShell from accessibility map, and cast to cursor shell
98 SwCursorShell
* SwAccessibleContext::GetCursorShell()
100 SwViewShell
* pViewShell
= GetMap() ? GetMap()->GetShell() : nullptr;
101 OSL_ENSURE( pViewShell
, "no view shell" );
102 return dynamic_cast<SwCursorShell
*>( pViewShell
);
105 const SwCursorShell
* SwAccessibleContext::GetCursorShell() const
107 // just like non-const GetCursorShell
108 const SwViewShell
* pViewShell
= GetMap() ? GetMap()->GetShell() : nullptr;
109 OSL_ENSURE( pViewShell
, "no view shell" );
110 return dynamic_cast<const SwCursorShell
*>( pViewShell
);
115 enum class Action
{ NONE
, SCROLLED
, SCROLLED_WITHIN
,
116 SCROLLED_IN
, SCROLLED_OUT
};
120 void SwAccessibleContext::ChildrenScrolled( const SwFrame
*pFrame
,
121 const SwRect
& rOldVisArea
)
123 const SwRect
& rNewVisArea
= GetVisArea();
124 const bool bVisibleChildrenOnly
= SwAccessibleChild( pFrame
).IsVisibleChildrenOnly();
126 const SwAccessibleChildSList
aList( *pFrame
, *(GetMap()) );
127 SwAccessibleChildSList::const_iterator
aIter( aList
.begin() );
128 while( aIter
!= aList
.end() )
130 const SwAccessibleChild
& rLower
= *aIter
;
131 const SwRect
aBox( rLower
.GetBox( *(GetMap()) ) );
132 if( rLower
.IsAccessible( GetShell()->IsPreview() ) )
134 Action eAction
= Action::NONE
;
135 if( aBox
.Overlaps( rNewVisArea
) )
137 if( aBox
.Overlaps( rOldVisArea
) )
139 eAction
= Action::SCROLLED_WITHIN
;
143 if ( bVisibleChildrenOnly
&&
144 !rLower
.AlwaysIncludeAsChild() )
146 eAction
= Action::SCROLLED_IN
;
150 eAction
= Action::SCROLLED
;
154 else if( aBox
.Overlaps( rOldVisArea
) )
156 if ( bVisibleChildrenOnly
&&
157 !rLower
.AlwaysIncludeAsChild() )
159 eAction
= Action::SCROLLED_OUT
;
163 eAction
= Action::SCROLLED
;
166 else if( !bVisibleChildrenOnly
||
167 rLower
.AlwaysIncludeAsChild() )
169 // This wouldn't be required if the SwAccessibleFrame,
170 // wouldn't know about the visible area.
171 eAction
= Action::SCROLLED
;
173 if( Action::NONE
!= eAction
)
175 if ( rLower
.GetSwFrame() )
177 OSL_ENSURE( !rLower
.AlwaysIncludeAsChild(),
178 "<SwAccessibleContext::ChildrenScrolled(..)> - always included child not considered!" );
179 const SwFrame
* pLower( rLower
.GetSwFrame() );
180 ::rtl::Reference
< SwAccessibleContext
> xAccImpl
=
181 GetMap()->GetContextImpl( pLower
);
186 case Action::SCROLLED
:
187 xAccImpl
->Scrolled( rOldVisArea
);
189 case Action::SCROLLED_WITHIN
:
190 xAccImpl
->ScrolledWithin( rOldVisArea
);
192 case Action::SCROLLED_IN
:
193 xAccImpl
->ScrolledIn();
195 case Action::SCROLLED_OUT
:
196 xAccImpl
->ScrolledOut( rOldVisArea
);
204 ChildrenScrolled( pLower
, rOldVisArea
);
207 else if ( rLower
.GetDrawObject() )
209 OSL_ENSURE( !rLower
.AlwaysIncludeAsChild(),
210 "<SwAccessibleContext::ChildrenScrolled(..)> - always included child not considered!" );
211 ::rtl::Reference
< ::accessibility::AccessibleShape
> xAccImpl
=
212 GetMap()->GetContextImpl( rLower
.GetDrawObject(),
218 case Action::SCROLLED
:
219 case Action::SCROLLED_WITHIN
:
220 xAccImpl
->ViewForwarderChanged();
222 case Action::SCROLLED_IN
:
223 ScrolledInShape( xAccImpl
.get() );
225 case Action::SCROLLED_OUT
:
227 xAccImpl
->ViewForwarderChanged();
228 // this DisposeShape call was removed by
229 // IAccessibility2 implementation
230 // without giving any reason why
231 DisposeShape( rLower
.GetDrawObject(),
235 // coverity[dead_error_begin] - following conditions exist to avoid compiler warning
241 else if ( rLower
.GetWindow() )
243 // nothing to do - as such children are always included as children.
244 OSL_ENSURE( rLower
.AlwaysIncludeAsChild(),
245 "<SwAccessibleContext::ChildrenScrolled(..)> - not always included child not considered!" );
249 else if ( rLower
.GetSwFrame() &&
250 ( !bVisibleChildrenOnly
||
251 aBox
.Overlaps( rOldVisArea
) ||
252 aBox
.Overlaps( rNewVisArea
) ) )
254 // There are no unaccessible SdrObjects that need to be notified
255 ChildrenScrolled( rLower
.GetSwFrame(), rOldVisArea
);
261 void SwAccessibleContext::Scrolled( const SwRect
& rOldVisArea
)
263 SetVisArea( GetMap()->GetVisArea() );
265 ChildrenScrolled( GetFrame(), rOldVisArea
);
267 bool bIsOldShowingState
;
268 bool bIsNewShowingState
= IsShowing( *(GetMap()) );
270 std::scoped_lock
aGuard( m_Mutex
);
271 bIsOldShowingState
= m_isShowingState
;
272 m_isShowingState
= bIsNewShowingState
;
275 if( bIsOldShowingState
!= bIsNewShowingState
)
276 FireStateChangedEvent( AccessibleStateType::SHOWING
,
277 bIsNewShowingState
);
280 void SwAccessibleContext::ScrolledWithin( const SwRect
& rOldVisArea
)
282 SetVisArea( GetMap()->GetVisArea() );
284 ChildrenScrolled( GetFrame(), rOldVisArea
);
286 FireVisibleDataEvent();
289 void SwAccessibleContext::ScrolledIn()
291 // This accessible should be freshly created, because it
292 // was not visible before. Therefore, its visible area must already
293 // reflect the scrolling.
294 OSL_ENSURE( GetVisArea() == GetMap()->GetVisArea(),
295 "Visible area of child is wrong. Did it exist already?" );
297 // Send child event at parent. That's all we have to do here.
298 const SwFrame
* pParent
= GetParent();
299 ::rtl::Reference
< SwAccessibleContext
> xParentImpl(
300 GetMap()->GetContextImpl( pParent
, false ) );
301 uno::Reference
< XAccessibleContext
> xThis( this );
302 if( !xParentImpl
.is() )
305 SetParent( xParentImpl
.get() );
307 AccessibleEventObject aEvent
;
308 aEvent
.EventId
= AccessibleEventId::CHILD
;
309 aEvent
.NewValue
<<= xThis
;
310 aEvent
.IndexHint
= -1;
312 xParentImpl
->FireAccessibleEvent( aEvent
);
316 vcl::Window
*pWin
= GetWindow();
317 if( pWin
&& pWin
->HasFocus() )
319 FireStateChangedEvent( AccessibleStateType::FOCUSED
, true );
324 void SwAccessibleContext::ScrolledOut( const SwRect
& rOldVisArea
)
326 SetVisArea( GetMap()->GetVisArea() );
328 // First of all, update the children. That's required to dispose
329 // all children that are existing only if they are visible. They
330 // are not disposed by the recursive Dispose call that follows later on,
331 // because this call will only dispose children that are in the
332 // new visible area. The children we want to dispose however are in the
333 // old visible area all.
334 ChildrenScrolled( GetFrame(), rOldVisArea
);
336 // Broadcast a state changed event for the showing state.
337 // It might be that the child is freshly created just to send
338 // the child event. In this case no listener will exist.
339 FireStateChangedEvent( AccessibleStateType::SHOWING
, false );
341 // this Dispose call was removed by IAccessibility2 implementation
342 // without giving any reason why - without it we get stale
343 // entries in SwAccessibleMap::mpFrameMap.
347 // #i27301# - use new type definition for <_nStates>
348 void SwAccessibleContext::InvalidateChildrenStates( const SwFrame
* _pFrame
,
349 AccessibleStates _nStates
)
351 const SwAccessibleChildSList
aVisList( GetVisArea(), *_pFrame
, *(GetMap()) );
353 SwAccessibleChildSList::const_iterator
aIter( aVisList
.begin() );
354 while( aIter
!= aVisList
.end() )
356 const SwAccessibleChild
& rLower
= *aIter
;
357 const SwFrame
* pLower
= rLower
.GetSwFrame();
360 ::rtl::Reference
< SwAccessibleContext
> xAccImpl
;
361 if( rLower
.IsAccessible( GetShell()->IsPreview() ) )
362 xAccImpl
= GetMap()->GetContextImpl( pLower
, false );
364 xAccImpl
->InvalidateStates( _nStates
);
366 InvalidateChildrenStates( pLower
, _nStates
);
368 else if ( rLower
.GetDrawObject() )
372 else if ( rLower
.GetWindow() )
381 void SwAccessibleContext::DisposeChildren(const SwFrame
*pFrame
,
383 bool bCanSkipInvisible
)
385 const SwAccessibleChildSList
aVisList( GetVisArea(), *pFrame
, *(GetMap()) );
386 SwAccessibleChildSList::const_iterator
aIter( aVisList
.begin() );
387 while( aIter
!= aVisList
.end() )
389 const SwAccessibleChild
& rLower
= *aIter
;
390 const SwFrame
* pLower
= rLower
.GetSwFrame();
393 // tdf#117601 dispose the darn thing if it ever was accessible
394 ::rtl::Reference
<SwAccessibleContext
> xAccImpl
= GetMap()->GetContextImpl(pLower
, false);
396 xAccImpl
->Dispose( bRecursive
);
399 // it's possible that the xAccImpl *does* exist with a
400 // ref-count of 0 and blocked in its dtor in another thread -
401 // this call here could be from SwAccessibleMap dtor so
402 // remove it from any maps now!
403 GetMap()->RemoveContext(pLower
);
404 // in this case the context will check with a weak_ptr
405 // that the map is still alive so it's not necessary
406 // to clear its m_pMap here.
409 DisposeChildren(pLower
, bRecursive
, bCanSkipInvisible
);
413 else if ( rLower
.GetDrawObject() )
415 ::rtl::Reference
< ::accessibility::AccessibleShape
> xAccImpl(
416 GetMap()->GetContextImpl( rLower
.GetDrawObject(),
419 DisposeShape( rLower
.GetDrawObject(), xAccImpl
.get() );
421 else if ( rLower
.GetWindow() )
423 DisposeChild(rLower
, false, bCanSkipInvisible
);
429 void SwAccessibleContext::InvalidateContent_( bool )
433 void SwAccessibleContext::InvalidateCursorPos_()
437 void SwAccessibleContext::InvalidateFocus_()
441 void SwAccessibleContext::FireAccessibleEvent( AccessibleEventObject
& rEvent
)
445 SAL_INFO("sw.a11y", "SwAccessibleContext::FireAccessibleEvent called for already disposed frame?");
449 if( !rEvent
.Source
.is() )
451 uno::Reference
< XAccessibleContext
> xThis( this );
452 rEvent
.Source
= xThis
;
456 comphelper::AccessibleEventNotifier::addEvent( m_nClientId
, rEvent
);
459 void SwAccessibleContext::FireVisibleDataEvent()
461 AccessibleEventObject aEvent
;
462 aEvent
.EventId
= AccessibleEventId::VISIBLE_DATA_CHANGED
;
464 FireAccessibleEvent( aEvent
);
467 void SwAccessibleContext::FireStateChangedEvent( sal_Int64 nState
,
470 AccessibleEventObject aEvent
;
472 aEvent
.EventId
= AccessibleEventId::STATE_CHANGED
;
474 aEvent
.NewValue
<<= nState
;
476 aEvent
.OldValue
<<= nState
;
478 FireAccessibleEvent( aEvent
);
481 void SwAccessibleContext::GetStates( sal_Int64
& rStateSet
)
483 SolarMutexGuard aGuard
;
486 if (m_isShowingState
)
487 rStateSet
|= AccessibleStateType::SHOWING
;
490 if (m_isEditableState
)
491 //Set editable state to graphic and other object when the document is editable
493 rStateSet
|= AccessibleStateType::EDITABLE
;
494 rStateSet
|= AccessibleStateType::RESIZABLE
;
495 rStateSet
|= AccessibleStateType::MOVEABLE
;
498 rStateSet
|= AccessibleStateType::ENABLED
;
502 rStateSet
|= AccessibleStateType::OPAQUE
;
505 rStateSet
|= AccessibleStateType::VISIBLE
;
508 rStateSet
|= AccessibleStateType::DEFUNC
;
511 bool SwAccessibleContext::IsEditableState()
515 std::scoped_lock
aGuard( m_Mutex
);
516 bRet
= m_isEditableState
;
522 bool SwAccessibleContext::IsDisposed() const
524 return !(GetFrame() && GetMap());
527 void SwAccessibleContext::ThrowIfDisposed()
531 throw lang::DisposedException("object is nonfunctional",
532 static_cast<cppu::OWeakObject
*>(this));
536 SwAccessibleContext::SwAccessibleContext(std::shared_ptr
<SwAccessibleMap
> const& pMap
,
537 sal_Int16
const nRole
,
539 : SwAccessibleFrame( pMap
->GetVisArea(), pF
,
540 pMap
->GetShell()->IsPreview() )
545 , m_isDisposing( false )
546 , m_isRegisteredAtAccessibleMap( true )
547 , m_isSelectedInDoc(false)
552 SwAccessibleContext::~SwAccessibleContext()
554 // must have for 2 reasons: 2. as long as this thread has SolarMutex
555 // another thread cannot destroy the SwAccessibleMap so our temporary
556 // taking a hard ref to SwAccessibleMap won't delay its destruction
557 SolarMutexGuard aGuard
;
558 // must check with weak_ptr that m_pMap is still alive
559 std::shared_ptr
<SwAccessibleMap
> pMap(m_wMap
.lock());
560 if (m_isRegisteredAtAccessibleMap
&& GetFrame() && pMap
)
562 pMap
->RemoveContext( GetFrame() );
566 uno::Reference
< XAccessibleContext
> SAL_CALL
567 SwAccessibleContext::getAccessibleContext()
569 uno::Reference
< XAccessibleContext
> xRet( this );
573 sal_Int64 SAL_CALL
SwAccessibleContext::getAccessibleChildCount()
575 SolarMutexGuard aGuard
;
579 return m_isDisposing
? 0 : GetChildCount( *(GetMap()) );
582 uno::Reference
< XAccessible
> SAL_CALL
583 SwAccessibleContext::getAccessibleChild( sal_Int64 nIndex
)
585 SolarMutexGuard aGuard
;
589 if (nIndex
< 0 || nIndex
>= getAccessibleChildCount())
590 throw lang::IndexOutOfBoundsException();
592 const SwAccessibleChild
aChild( GetChild( *(GetMap()), nIndex
) );
593 if( !aChild
.IsValid() )
595 uno::Reference
< XAccessibleContext
> xThis( this );
596 lang::IndexOutOfBoundsException
aExcept(
597 "index out of bounds",
602 uno::Reference
< XAccessible
> xChild
;
603 if( aChild
.GetSwFrame() )
605 ::rtl::Reference
< SwAccessibleContext
> xChildImpl(
606 GetMap()->GetContextImpl( aChild
.GetSwFrame(), !m_isDisposing
) );
607 if( xChildImpl
.is() )
609 xChildImpl
->SetParent( this );
610 xChild
= xChildImpl
.get();
613 else if ( aChild
.GetDrawObject() )
615 ::rtl::Reference
< ::accessibility::AccessibleShape
> xChildImpl(
616 GetMap()->GetContextImpl( aChild
.GetDrawObject(),
617 this, !m_isDisposing
) );
618 if( xChildImpl
.is() )
619 xChild
= xChildImpl
.get();
621 else if ( aChild
.GetWindow() )
623 xChild
= aChild
.GetWindow()->GetAccessible();
629 css::uno::Sequence
<uno::Reference
<XAccessible
>> SAL_CALL
630 SwAccessibleContext::getAccessibleChildren()
632 SolarMutexGuard aGuard
;
636 std::list
< sw::access::SwAccessibleChild
> aChildren
;
637 GetChildren( *GetMap(), aChildren
);
639 std::vector
<uno::Reference
<XAccessible
>> aRet
;
640 aRet
.reserve(aChildren
.size());
641 for (const auto & rSwChild
: aChildren
)
643 uno::Reference
< XAccessible
> xChild
;
644 if( rSwChild
.GetSwFrame() )
646 ::rtl::Reference
< SwAccessibleContext
> xChildImpl(
647 GetMap()->GetContextImpl( rSwChild
.GetSwFrame(), !m_isDisposing
) );
648 if( xChildImpl
.is() )
650 xChildImpl
->SetParent( this );
651 xChild
= xChildImpl
.get();
654 else if ( rSwChild
.GetDrawObject() )
656 ::rtl::Reference
< ::accessibility::AccessibleShape
> xChildImpl(
657 GetMap()->GetContextImpl( rSwChild
.GetDrawObject(),
658 this, !m_isDisposing
) );
659 if( xChildImpl
.is() )
660 xChild
= xChildImpl
.get();
662 else if ( rSwChild
.GetWindow() )
664 xChild
= rSwChild
.GetWindow()->GetAccessible();
666 aRet
.push_back(xChild
);
668 return comphelper::containerToSequence(aRet
);
671 uno::Reference
< XAccessible
> SwAccessibleContext::getAccessibleParentImpl()
673 SolarMutexGuard aGuard
;
675 const SwFrame
*pUpper
= GetParent();
676 OSL_ENSURE( pUpper
!= nullptr || m_isDisposing
, "no upper found" );
678 uno::Reference
< XAccessible
> xAcc
;
680 xAcc
= GetMap()->GetContext( pUpper
, !m_isDisposing
);
682 OSL_ENSURE( xAcc
.is() || m_isDisposing
, "no parent found" );
684 // Remember the parent as weak ref.
686 std::scoped_lock
aWeakParentGuard( m_Mutex
);
687 m_xWeakParent
= xAcc
;
693 uno::Reference
< XAccessible
> SAL_CALL
SwAccessibleContext::getAccessibleParent()
695 SolarMutexGuard aGuard
;
699 return getAccessibleParentImpl();
702 sal_Int64 SAL_CALL
SwAccessibleContext::getAccessibleIndexInParent()
704 SolarMutexGuard aGuard
;
708 const SwFrame
*pUpper
= GetParent();
709 OSL_ENSURE( pUpper
!= nullptr || m_isDisposing
, "no upper found" );
711 sal_Int64 nIndex
= -1;
714 ::rtl::Reference
< SwAccessibleContext
> xAccImpl(
715 GetMap()->GetContextImpl(pUpper
, !m_isDisposing
) );
716 OSL_ENSURE( xAccImpl
.is() || m_isDisposing
, "no parent found" );
718 nIndex
= xAccImpl
->GetChildIndex( *(GetMap()), SwAccessibleChild(GetFrame()) );
724 sal_Int16 SAL_CALL
SwAccessibleContext::getAccessibleRole()
729 OUString SAL_CALL
SwAccessibleContext::getAccessibleName()
734 uno::Reference
< XAccessibleRelationSet
> SAL_CALL
735 SwAccessibleContext::getAccessibleRelationSet()
737 // by default there are no relations
738 uno::Reference
< XAccessibleRelationSet
> xRet( new utl::AccessibleRelationSetHelper() );
742 sal_Int64 SAL_CALL
SwAccessibleContext::getAccessibleStateSet()
744 SolarMutexGuard aGuard
;
748 sal_Int64 nStateSet
= 0;
750 if (m_isSelectedInDoc
)
751 nStateSet
|= AccessibleStateType::SELECTED
;
753 GetStates( nStateSet
);
758 lang::Locale SAL_CALL
SwAccessibleContext::getLocale()
760 SolarMutexGuard aGuard
;
762 lang::Locale
aLoc( Application::GetSettings().GetLanguageTag().getLocale() );
766 void SAL_CALL
SwAccessibleContext::addAccessibleEventListener(
767 const uno::Reference
< XAccessibleEventListener
>& xListener
)
771 SolarMutexGuard aGuard
;
773 m_nClientId
= comphelper::AccessibleEventNotifier::registerClient( );
774 comphelper::AccessibleEventNotifier::addEventListener( m_nClientId
, xListener
);
778 void SAL_CALL
SwAccessibleContext::removeAccessibleEventListener(
779 const uno::Reference
< XAccessibleEventListener
>& xListener
)
781 if (!(xListener
.is() && m_nClientId
))
784 SolarMutexGuard aGuard
;
785 sal_Int32 nListenerCount
= comphelper::AccessibleEventNotifier::removeEventListener( m_nClientId
, xListener
);
786 if ( !nListenerCount
)
788 // no listeners anymore
789 // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client),
790 // and at least to us not firing any events anymore, in case somebody calls
791 // NotifyAccessibleEvent, again
792 comphelper::AccessibleEventNotifier::revokeClient( m_nClientId
);
797 static bool lcl_PointInRectangle(const awt::Point
& aPoint
,
798 const awt::Rectangle
& aRect
)
800 tools::Long nDiffX
= aPoint
.X
- aRect
.X
;
801 tools::Long nDiffY
= aPoint
.Y
- aRect
.Y
;
804 nDiffX
>= 0 && nDiffX
< aRect
.Width
&& nDiffY
>= 0 &&
805 nDiffY
< aRect
.Height
;
809 sal_Bool SAL_CALL
SwAccessibleContext::containsPoint(
810 const awt::Point
& aPoint
)
812 awt::Rectangle aPixBounds
= getBoundsImpl(true);
816 return lcl_PointInRectangle(aPoint
, aPixBounds
);
819 uno::Reference
< XAccessible
> SAL_CALL
SwAccessibleContext::getAccessibleAtPoint(
820 const awt::Point
& aPoint
)
822 SolarMutexGuard aGuard
;
826 uno::Reference
< XAccessible
> xAcc
;
828 vcl::Window
*pWin
= GetWindow();
831 throw uno::RuntimeException("no Window", static_cast<cppu::OWeakObject
*>(this));
834 Point
aPixPoint( aPoint
.X
, aPoint
.Y
); // px rel to parent
835 if( !GetFrame()->IsRootFrame() )
837 SwRect
aLogBounds( GetBounds( *(GetMap()), GetFrame() ) ); // twip rel to doc root
838 Point
aPixPos( GetMap()->CoreToPixel( aLogBounds
).TopLeft() );
839 aPixPoint
.setX(aPixPoint
.getX() + aPixPos
.getX());
840 aPixPoint
.setY(aPixPoint
.getY() + aPixPos
.getY());
843 const SwAccessibleChild
aChild( GetChildAtPixel( aPixPoint
, *(GetMap()) ) );
844 if( aChild
.GetSwFrame() )
846 xAcc
= GetMap()->GetContext( aChild
.GetSwFrame() );
848 else if( aChild
.GetDrawObject() )
850 xAcc
= GetMap()->GetContext( aChild
.GetDrawObject(), this );
852 else if ( aChild
.GetWindow() )
854 xAcc
= aChild
.GetWindow()->GetAccessible();
867 Return bounding box relative to parent if parent is no root
868 frame. Otherwise return the absolute bounding box.
872 Return the absolute bounding box.
875 true: Use relative mode.
876 false: Use absolute mode.
878 awt::Rectangle
SwAccessibleContext::getBoundsImpl(bool bRelative
)
880 SolarMutexGuard aGuard
;
884 const SwFrame
*pParent
= GetParent();
885 OSL_ENSURE( pParent
, "no Parent found" );
886 vcl::Window
*pWin
= GetWindow();
890 throw uno::RuntimeException("no Parent", static_cast<cppu::OWeakObject
*>(this));
894 throw uno::RuntimeException("no Window", static_cast<cppu::OWeakObject
*>(this));
897 SwRect
aLogBounds( GetBounds( *(GetMap()), GetFrame() ) ); // twip relative to document root
898 tools::Rectangle
aPixBounds( 0, 0, 0, 0 );
899 if( GetFrame()->IsPageFrame() &&
900 static_cast < const SwPageFrame
* >( GetFrame() )->IsEmptyPage() )
902 OSL_ENSURE( GetShell()->IsPreview(), "empty page accessible?" );
903 if( GetShell()->IsPreview() )
905 // adjust method call <GetMap()->GetPreviewPageSize()>
906 sal_uInt16 nPageNum
=
907 static_cast < const SwPageFrame
* >( GetFrame() )->GetPhyPageNum();
908 aLogBounds
.SSize( GetMap()->GetPreviewPageSize( nPageNum
) );
911 if( !aLogBounds
.IsEmpty() )
913 aPixBounds
= GetMap()->CoreToPixel( aLogBounds
);
914 if( !pParent
->IsRootFrame() && bRelative
)
916 SwRect
aParentLogBounds( GetBounds( *(GetMap()), pParent
) ); // twip rel to doc root
917 Point
aParentPixPos( GetMap()->CoreToPixel( aParentLogBounds
).TopLeft() );
918 aPixBounds
.Move( -aParentPixPos
.getX(), -aParentPixPos
.getY() );
922 awt::Rectangle
aBox( aPixBounds
.Left(), aPixBounds
.Top(),
923 aPixBounds
.GetWidth(), aPixBounds
.GetHeight() );
928 awt::Rectangle SAL_CALL
SwAccessibleContext::getBounds()
930 return getBoundsImpl(true);
933 awt::Point SAL_CALL
SwAccessibleContext::getLocation()
935 awt::Rectangle aRect
= getBoundsImpl(true);
936 awt::Point
aPoint(aRect
.X
, aRect
.Y
);
941 awt::Point SAL_CALL
SwAccessibleContext::getLocationOnScreen()
943 awt::Rectangle aRect
= getBoundsImpl(false);
945 Point
aPixPos(aRect
.X
, aRect
.Y
);
947 vcl::Window
*pWin
= GetWindow();
950 throw uno::RuntimeException("no Window", static_cast<cppu::OWeakObject
*>(this));
953 aPixPos
= pWin
->OutputToAbsoluteScreenPixel(aPixPos
);
954 awt::Point
aPoint(aPixPos
.getX(), aPixPos
.getY());
959 awt::Size SAL_CALL
SwAccessibleContext::getSize()
961 awt::Rectangle aRect
= getBoundsImpl(false);
962 awt::Size
aSize( aRect
.Width
, aRect
.Height
);
967 void SAL_CALL
SwAccessibleContext::grabFocus()
969 SolarMutexGuard aGuard
;
973 if( GetFrame()->IsFlyFrame() )
975 const SdrObject
*pObj
=
976 static_cast < const SwFlyFrame
* >( GetFrame() )->GetVirtDrawObj();
978 Select( const_cast < SdrObject
* >( pObj
), false );
982 const SwContentFrame
*pCFrame
= nullptr;
983 if( GetFrame()->IsContentFrame() )
984 pCFrame
= static_cast< const SwContentFrame
* >( GetFrame() );
985 else if( GetFrame()->IsLayoutFrame() )
986 pCFrame
= static_cast< const SwLayoutFrame
* >( GetFrame() )->ContainsContent();
988 if( pCFrame
&& pCFrame
->IsTextFrame() )
990 const SwTextFrame
*pTextFrame
= static_cast< const SwTextFrame
* >( pCFrame
);
991 const SwTextNode
*pTextNd
= pTextFrame
->GetTextNodeFirst();
992 assert(pTextNd
); // can it actually be null? probably not=>simplify
995 // create pam for selection
996 SwPosition
const aStartPos(pTextFrame
->MapViewToModelPos(pTextFrame
->GetOffset()));
997 SwPaM
aPaM( aStartPos
);
999 // set PaM at cursor shell
1006 sal_Int32 SAL_CALL
SwAccessibleContext::getForeground()
1008 return sal_Int32(COL_BLACK
);
1011 sal_Int32 SAL_CALL
SwAccessibleContext::getBackground()
1013 return sal_Int32(COL_WHITE
);
1016 sal_Bool SAL_CALL
SwAccessibleContext::supportsService (const OUString
& ServiceName
)
1018 return cppu::supportsService(this, ServiceName
);
1021 void SwAccessibleContext::DisposeShape( const SdrObject
*pObj
,
1022 ::accessibility::AccessibleShape
*pAccImpl
)
1024 ::rtl::Reference
< ::accessibility::AccessibleShape
> xAccImpl( pAccImpl
);
1025 if( !xAccImpl
.is() )
1026 xAccImpl
= GetMap()->GetContextImpl( pObj
, this );
1028 AccessibleEventObject aEvent
;
1029 aEvent
.EventId
= AccessibleEventId::CHILD
;
1030 uno::Reference
< XAccessible
> xAcc( xAccImpl
);
1031 aEvent
.OldValue
<<= xAcc
;
1032 aEvent
.IndexHint
= -1;
1033 FireAccessibleEvent( aEvent
);
1035 GetMap()->RemoveContext( pObj
);
1036 xAccImpl
->dispose();
1039 void SwAccessibleContext::ScrolledInShape( ::accessibility::AccessibleShape
*pAccImpl
)
1041 if(nullptr == pAccImpl
)
1045 AccessibleEventObject aEvent
;
1046 aEvent
.EventId
= AccessibleEventId::CHILD
;
1047 uno::Reference
< XAccessible
> xAcc( pAccImpl
);
1048 aEvent
.NewValue
<<= xAcc
;
1049 aEvent
.IndexHint
= -1;
1050 FireAccessibleEvent( aEvent
);
1052 if( !pAccImpl
->GetState( AccessibleStateType::FOCUSED
) )
1055 vcl::Window
*pWin
= GetWindow();
1056 if( pWin
&& pWin
->HasFocus() )
1058 AccessibleEventObject aStateChangedEvent
;
1059 aStateChangedEvent
.EventId
= AccessibleEventId::STATE_CHANGED
;
1060 aStateChangedEvent
.NewValue
<<= AccessibleStateType::FOCUSED
;
1061 aStateChangedEvent
.Source
= xAcc
;
1063 FireAccessibleEvent( aStateChangedEvent
);
1067 void SwAccessibleContext::Dispose(bool bRecursive
, bool bCanSkipInvisible
)
1069 SolarMutexGuard aGuard
;
1071 OSL_ENSURE( GetFrame() && GetMap(), "already disposed" );
1072 OSL_ENSURE( GetMap()->GetVisArea() == GetVisArea(),
1073 "invalid visible area for dispose" );
1075 m_isDisposing
= true;
1079 DisposeChildren(GetFrame(), bRecursive
, bCanSkipInvisible
);
1082 uno::Reference
< XAccessible
> xParent( GetWeakParent() );
1083 uno::Reference
< XAccessibleContext
> xThis( this );
1085 // send child event at parent
1088 SwAccessibleContext
*pAcc
= static_cast<SwAccessibleContext
*>(xParent
.get());
1090 AccessibleEventObject aEvent
;
1091 aEvent
.EventId
= AccessibleEventId::CHILD
;
1092 aEvent
.OldValue
<<= xThis
;
1093 aEvent
.IndexHint
= -1;
1094 pAcc
->FireAccessibleEvent( aEvent
);
1097 // set defunc state (it's not required to broadcast a state changed
1098 // event if the object is disposed afterwards)
1100 std::scoped_lock
aDefuncStateGuard( m_Mutex
);
1101 m_isDefuncState
= true;
1104 // broadcast dispose event
1107 comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing( m_nClientId
, *this );
1111 RemoveFrameFromAccessibleMap();
1116 m_isDisposing
= false;
1119 void SwAccessibleContext::DisposeChild( const SwAccessibleChild
& rChildFrameOrObj
,
1120 bool bRecursive
, bool bCanSkipInvisible
)
1122 SolarMutexGuard aGuard
;
1124 if ( !bCanSkipInvisible
||
1125 rChildFrameOrObj
.AlwaysIncludeAsChild() ||
1126 IsShowing( *(GetMap()), rChildFrameOrObj
) ||
1127 !SwAccessibleChild( GetFrame() ).IsVisibleChildrenOnly() )
1129 // If the object could have existed before, then there is nothing to do,
1130 // because no wrapper exists now and therefore no one is interested to
1131 // get notified of the movement.
1132 if( rChildFrameOrObj
.GetSwFrame() )
1134 ::rtl::Reference
< SwAccessibleContext
> xAccImpl
=
1135 GetMap()->GetContextImpl( rChildFrameOrObj
.GetSwFrame(), false );
1137 xAccImpl
->Dispose( bRecursive
);
1139 else if ( rChildFrameOrObj
.GetDrawObject() )
1141 ::rtl::Reference
< ::accessibility::AccessibleShape
> xAccImpl
=
1142 GetMap()->GetContextImpl( rChildFrameOrObj
.GetDrawObject(),
1145 DisposeShape( rChildFrameOrObj
.GetDrawObject(),
1148 else if ( rChildFrameOrObj
.GetWindow() )
1150 AccessibleEventObject aEvent
;
1151 aEvent
.EventId
= AccessibleEventId::CHILD
;
1152 uno::Reference
< XAccessible
> xAcc
=
1153 rChildFrameOrObj
.GetWindow()->GetAccessible();
1154 aEvent
.OldValue
<<= xAcc
;
1155 aEvent
.IndexHint
= -1;
1156 FireAccessibleEvent( aEvent
);
1159 else if( bRecursive
&& rChildFrameOrObj
.GetSwFrame() )
1160 DisposeChildren(rChildFrameOrObj
.GetSwFrame(), bRecursive
, bCanSkipInvisible
);
1163 void SwAccessibleContext::InvalidatePosOrSize( const SwRect
& )
1165 SolarMutexGuard aGuard
;
1167 OSL_ENSURE( GetFrame() && !GetFrame()->getFrameArea().IsEmpty(), "context should have a size" );
1169 bool bIsOldShowingState
;
1170 bool bIsNewShowingState
= IsShowing( *(GetMap()) );
1172 std::scoped_lock
aShowingStateGuard( m_Mutex
);
1173 bIsOldShowingState
= m_isShowingState
;
1174 m_isShowingState
= bIsNewShowingState
;
1177 if( bIsOldShowingState
!= bIsNewShowingState
)
1179 FireStateChangedEvent( AccessibleStateType::SHOWING
,
1180 bIsNewShowingState
);
1182 else if( bIsNewShowingState
)
1184 // The frame stays visible -> broadcast event
1185 FireVisibleDataEvent();
1188 // note: InvalidatePosOrSize must call InvalidateContent_ so that
1189 // SwAccessibleParagraph updates its portions, or dispose it
1190 // (see accmap.cxx: INVALID_CONTENT is contained in POS_CHANGED)
1191 if( !bIsNewShowingState
&&
1192 SwAccessibleChild( GetParent() ).IsVisibleChildrenOnly() )
1194 // this Dispose call was removed by IAccessibility2 implementation
1195 // without giving any reason why - without it we get stale
1196 // entries in SwAccessibleMap::mpFrameMap.
1201 InvalidateContent_( true );
1205 void SwAccessibleContext::InvalidateChildPosOrSize(
1206 const SwAccessibleChild
& rChildFrameOrObj
,
1207 const SwRect
& rOldFrame
)
1209 SolarMutexGuard aGuard
;
1211 // this happens during layout, e.g. when a page is deleted and next page's
1212 // header/footer moves backward such an event is generated
1213 SAL_INFO_IF(rChildFrameOrObj
.GetSwFrame() &&
1214 rChildFrameOrObj
.GetSwFrame()->getFrameArea().IsEmpty(),
1215 "sw.a11y", "child context should have a size");
1217 if ( rChildFrameOrObj
.AlwaysIncludeAsChild() )
1223 const bool bVisibleChildrenOnly
= SwAccessibleChild( GetFrame() ).IsVisibleChildrenOnly();
1224 const bool bNew
= rOldFrame
.IsEmpty() ||
1225 ( rOldFrame
.Left() == 0 && rOldFrame
.Top() == 0 );
1226 if( IsShowing( *(GetMap()), rChildFrameOrObj
) )
1228 // If the object could have existed before, then there is nothing to do,
1229 // because no wrapper exists now and therefore no one is interested to
1230 // get notified of the movement.
1231 if( bNew
|| (bVisibleChildrenOnly
&& !IsShowing( rOldFrame
)) )
1233 if( rChildFrameOrObj
.GetSwFrame() )
1235 // The frame becomes visible. A child event must be send.
1236 ::rtl::Reference
< SwAccessibleContext
> xAccImpl
=
1237 GetMap()->GetContextImpl( rChildFrameOrObj
.GetSwFrame() );
1238 xAccImpl
->ScrolledIn();
1240 else if ( rChildFrameOrObj
.GetDrawObject() )
1242 ::rtl::Reference
< ::accessibility::AccessibleShape
> xAccImpl
=
1243 GetMap()->GetContextImpl( rChildFrameOrObj
.GetDrawObject(),
1246 if ( xAccImpl
.is() )
1248 ScrolledInShape( xAccImpl
.get() );
1252 OSL_FAIL( "<SwAccessibleContext::InvalidateChildPosOrSize(..)> - no accessible shape found." );
1255 else if ( rChildFrameOrObj
.GetWindow() )
1257 AccessibleEventObject aEvent
;
1258 aEvent
.EventId
= AccessibleEventId::CHILD
;
1259 aEvent
.NewValue
<<= rChildFrameOrObj
.GetWindow()->GetAccessible();
1260 FireAccessibleEvent( aEvent
);
1266 // If the frame was visible before, then a child event for the parent
1267 // needs to be send. However, there is no wrapper existing, and so
1268 // no notifications for grandchildren are required. If the are
1269 // grandgrandchildren, they would be notified by the layout.
1270 if( bVisibleChildrenOnly
&&
1271 !bNew
&& IsShowing( rOldFrame
) )
1273 if( rChildFrameOrObj
.GetSwFrame() )
1275 ::rtl::Reference
< SwAccessibleContext
> xAccImpl
=
1276 GetMap()->GetContextImpl( rChildFrameOrObj
.GetSwFrame() );
1277 xAccImpl
->SetParent( this );
1278 xAccImpl
->Dispose( true );
1280 else if ( rChildFrameOrObj
.GetDrawObject() )
1282 ::rtl::Reference
< ::accessibility::AccessibleShape
> xAccImpl
=
1283 GetMap()->GetContextImpl( rChildFrameOrObj
.GetDrawObject(),
1285 DisposeShape( rChildFrameOrObj
.GetDrawObject(),
1288 else if ( rChildFrameOrObj
.GetWindow() )
1290 OSL_FAIL( "<SwAccessibleContext::InvalidateChildPosOrSize(..)> - not expected to handle dispose of child of type <vcl::Window>." );
1296 void SwAccessibleContext::InvalidateContent()
1298 SolarMutexGuard aGuard
;
1300 InvalidateContent_( false );
1303 void SwAccessibleContext::InvalidateCursorPos()
1305 SolarMutexGuard aGuard
;
1307 InvalidateCursorPos_();
1310 void SwAccessibleContext::InvalidateFocus()
1312 SolarMutexGuard aGuard
;
1317 // #i27301# - use new type definition for <_nStates>
1318 void SwAccessibleContext::InvalidateStates( AccessibleStates _nStates
)
1323 SwViewShell
*pVSh
= GetMap()->GetShell();
1326 if( _nStates
& AccessibleStates::EDITABLE
)
1328 bool bIsOldEditableState
;
1329 bool bIsNewEditableState
= IsEditable( pVSh
);
1331 std::scoped_lock
aGuard( m_Mutex
);
1332 bIsOldEditableState
= m_isEditableState
;
1333 m_isEditableState
= bIsNewEditableState
;
1336 if( bIsOldEditableState
!= bIsNewEditableState
)
1337 FireStateChangedEvent( AccessibleStateType::EDITABLE
,
1338 bIsNewEditableState
);
1340 if( _nStates
& AccessibleStates::OPAQUE
)
1342 bool bIsOldOpaqueState
;
1343 bool bIsNewOpaqueState
= IsOpaque( pVSh
);
1345 std::scoped_lock
aGuard( m_Mutex
);
1346 bIsOldOpaqueState
= m_isOpaqueState
;
1347 m_isOpaqueState
= bIsNewOpaqueState
;
1350 if( bIsOldOpaqueState
!= bIsNewOpaqueState
)
1351 FireStateChangedEvent( AccessibleStateType::OPAQUE
,
1352 bIsNewOpaqueState
);
1356 InvalidateChildrenStates( GetFrame(), _nStates
);
1359 void SwAccessibleContext::InvalidateRelation( sal_uInt16 nType
)
1361 AccessibleEventObject aEvent
;
1362 aEvent
.EventId
= nType
;
1364 FireAccessibleEvent( aEvent
);
1367 /** #i27301# - text selection has changed */
1368 void SwAccessibleContext::InvalidateTextSelection()
1370 AccessibleEventObject aEvent
;
1371 aEvent
.EventId
= AccessibleEventId::TEXT_SELECTION_CHANGED
;
1373 FireAccessibleEvent( aEvent
);
1376 /** #i88069# - attributes has changed */
1377 void SwAccessibleContext::InvalidateAttr()
1379 AccessibleEventObject aEvent
;
1380 aEvent
.EventId
= AccessibleEventId::TEXT_ATTRIBUTE_CHANGED
;
1382 FireAccessibleEvent( aEvent
);
1385 bool SwAccessibleContext::HasCursor()
1390 bool SwAccessibleContext::Select( SwPaM
*pPaM
, SdrObject
*pObj
,
1393 SwCursorShell
* pCursorShell
= GetCursorShell();
1397 SwFEShell
* pFEShell
= dynamic_cast<SwFEShell
*>(pCursorShell
);
1398 // Get rid of activated OLE object
1400 pFEShell
->FinishOLEObj();
1402 SwWrtShell
* pWrtShell
= dynamic_cast<SwWrtShell
*>(pCursorShell
);
1409 sal_uInt8 nFlags
= bAdd
? SW_ADD_SELECT
: 0;
1410 pFEShell
->SelectObj( Point(), nFlags
, pObj
);
1416 // Get rid of frame selection. If there is one, make text cursor
1418 bool bCallShowCursor
= false;
1419 if( pFEShell
&& (pFEShell
->IsFrameSelected() ||
1420 pFEShell
->IsObjSelected()) )
1422 Point
aPt( LONG_MIN
, LONG_MIN
);
1423 pFEShell
->SelectObj( aPt
);
1424 bCallShowCursor
= true;
1426 pCursorShell
->KillPams();
1427 if( pWrtShell
&& pPaM
->HasMark() )
1428 // We have to do this or SwWrtShell can't figure out that it needs
1429 // to kill the selection later, when the user moves the cursor.
1430 pWrtShell
->SttSelect();
1431 pCursorShell
->SetSelection( *pPaM
);
1432 if( pPaM
->HasMark() && *pPaM
->GetPoint() == *pPaM
->GetMark())
1433 // Setting a "Selection" that starts and ends at the same spot
1434 // should remove the selection rather than create an empty one, so
1435 // that we get defined behavior if accessibility sets the cursor
1437 pCursorShell
->ClearMark();
1438 if( bCallShowCursor
)
1439 pCursorShell
->ShowCursor();
1446 OUString
SwAccessibleContext::GetResource(TranslateId pResId
,
1447 const OUString
*pArg1
,
1448 const OUString
*pArg2
)
1450 OUString sStr
= SwResId(pResId
);
1454 sStr
= sStr
.replaceFirst( "$(ARG1)", *pArg1
);
1458 sStr
= sStr
.replaceFirst( "$(ARG2)", *pArg2
);
1464 void SwAccessibleContext::RemoveFrameFromAccessibleMap()
1466 assert(m_refCount
> 0); // must be alive to do this without using m_wMap
1467 if (m_isRegisteredAtAccessibleMap
&& GetFrame() && GetMap())
1468 GetMap()->RemoveContext( GetFrame() );
1471 bool SwAccessibleContext::HasAdditionalAccessibleChildren()
1475 if ( GetFrame()->IsTextFrame() )
1477 SwPostItMgr
* pPostItMgr
= GetMap()->GetShell()->GetPostItMgr();
1478 if ( pPostItMgr
&& pPostItMgr
->HasNotes() && pPostItMgr
->ShowNotes() )
1480 bRet
= pPostItMgr
->HasFrameConnectedSidebarWins( *(GetFrame()) );
1487 /** #i88070# - get additional accessible child by index */
1488 vcl::Window
* SwAccessibleContext::GetAdditionalAccessibleChild( const sal_Int32 nIndex
)
1490 vcl::Window
* pAdditionalAccessibleChild( nullptr );
1492 if ( GetFrame()->IsTextFrame() )
1494 SwPostItMgr
* pPostItMgr
= GetMap()->GetShell()->GetPostItMgr();
1495 if ( pPostItMgr
&& pPostItMgr
->HasNotes() && pPostItMgr
->ShowNotes() )
1497 pAdditionalAccessibleChild
=
1498 pPostItMgr
->GetSidebarWinForFrameByIndex( *(GetFrame()), nIndex
);
1502 return pAdditionalAccessibleChild
;
1505 /** #i88070# - get all additional accessible children */
1506 void SwAccessibleContext::GetAdditionalAccessibleChildren( std::vector
< vcl::Window
* >* pChildren
)
1508 if ( GetFrame()->IsTextFrame() )
1510 SwPostItMgr
* pPostItMgr
= GetMap()->GetShell()->GetPostItMgr();
1511 if ( pPostItMgr
&& pPostItMgr
->HasNotes() && pPostItMgr
->ShowNotes() )
1513 pPostItMgr
->GetAllSidebarWinForFrame( *(GetFrame()), pChildren
);
1518 bool SwAccessibleContext::SetSelectedState(bool const bSelected
)
1520 if (m_isSelectedInDoc
!= bSelected
)
1522 m_isSelectedInDoc
= bSelected
;
1523 FireStateChangedEvent( AccessibleStateType::SELECTED
, bSelected
);
1529 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */