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 <com/sun/star/accessibility/AccessibleRole.hpp>
21 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
22 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
23 #include <com/sun/star/accessibility/IllegalAccessibleComponentStateException.hpp>
24 #include <com/sun/star/lang/DisposedException.hpp>
25 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
26 #include <cppuhelper/supportsservice.hxx>
27 #include <vcl/svapp.hxx>
28 #include <vcl/settings.hxx>
29 #include <o3tl/safeint.hxx>
30 #include <osl/mutex.hxx>
31 #include <tools/gen.hxx>
32 #include <svtools/colorcfg.hxx>
33 #include <comphelper/accessibleeventnotifier.hxx>
34 #include <svx/sdrpaintwindow.hxx>
36 #include <svx/ShapeTypeHandler.hxx>
37 #include <svx/AccessibleShapeInfo.hxx>
38 #include <GraphCtlAccessibleContext.hxx>
39 #include <svx/graphctl.hxx>
40 #include <svx/strings.hrc>
41 #include <svx/svdpage.hxx>
42 #include <svx/dialmgr.hxx>
43 #include <svx/sdrhittesthelper.hxx>
46 using namespace ::cppu
;
47 using namespace ::osl
;
48 using namespace ::accessibility
;
49 using namespace ::com::sun::star
;
50 using namespace ::com::sun::star::uno
;
51 using namespace ::com::sun::star::drawing
;
52 using namespace ::com::sun::star::lang
;
53 using namespace ::com::sun::star::accessibility
;
56 /** initialize this component and set default values */
57 SvxGraphCtrlAccessibleContext::SvxGraphCtrlAccessibleContext(
60 SvxGraphCtrlAccessibleContext_Base( m_aMutex
),
68 if (mpControl
!= nullptr)
70 mpModel
= mpControl
->GetSdrModel();
71 if (mpModel
!= nullptr)
72 mpPage
= mpModel
->GetPage( 0 );
73 mpView
= mpControl
->GetSdrView();
75 if( mpModel
== nullptr || mpPage
== nullptr || mpView
== nullptr )
78 // Set all the pointers to NULL just in case they are used as
87 ::SolarMutexGuard aSolarGuard
;
88 msName
= SvxResId( RID_SVXSTR_GRAPHCTRL_ACC_NAME
);
89 msDescription
= SvxResId( RID_SVXSTR_GRAPHCTRL_ACC_DESCRIPTION
);
92 maTreeInfo
.SetSdrView( mpView
);
93 maTreeInfo
.SetWindow(mpControl
->GetDrawingArea()->get_ref_device().GetOwnerWindow());
94 maTreeInfo
.SetViewForwarder( this );
98 /** on destruction, this component is disposed and all dispose listeners
99 are called, except if this component was already disposed */
100 SvxGraphCtrlAccessibleContext::~SvxGraphCtrlAccessibleContext()
106 /** returns the XAccessible interface for a given SdrObject.
107 Multiple calls for the same SdrObject return the same XAccessible.
109 Reference
< XAccessible
> SvxGraphCtrlAccessibleContext::getAccessible( const SdrObject
* pObj
)
111 Reference
<XAccessible
> xAccessibleShape
;
115 // see if we already created an XAccessible for the given SdrObject
116 ShapesMapType::const_iterator iter
= mxShapes
.find( pObj
);
118 if( iter
!= mxShapes
.end() )
120 // if we already have one, return it
121 xAccessibleShape
= (*iter
).second
.get();
125 // create a new one and remember in our internal map
126 Reference
< XShape
> xShape( Reference
< XShape
>::query( const_cast<SdrObject
*>(pObj
)->getUnoShape() ) );
128 css::uno::Reference
<css::accessibility::XAccessible
> xParent(getAccessibleParent());
129 AccessibleShapeInfo
aShapeInfo (xShape
,xParent
);
130 // Create accessible object that corresponds to the descriptor's shape.
131 rtl::Reference
<AccessibleShape
> pAcc(ShapeTypeHandler::Instance().CreateAccessibleObject(
132 aShapeInfo
, maTreeInfo
));
133 xAccessibleShape
= pAcc
.get();
138 mxShapes
[pObj
] = std::move(pAcc
);
140 // Create event and inform listeners of the object creation.
141 CommitChange( AccessibleEventId::CHILD
, Any( xAccessibleShape
), Any( Reference
<XAccessible
>() ) );
145 return xAccessibleShape
;
149 Reference
< XAccessibleContext
> SAL_CALL
SvxGraphCtrlAccessibleContext::getAccessibleContext()
154 // XAccessibleComponent
155 sal_Bool SAL_CALL
SvxGraphCtrlAccessibleContext::containsPoint( const awt::Point
& rPoint
)
157 // no guard -> done in getSize()
158 awt::Size
aSize (getSize());
159 return (rPoint
.X
>= 0)
160 && (rPoint
.X
< aSize
.Width
)
162 && (rPoint
.Y
< aSize
.Height
);
166 Reference
< XAccessible
> SAL_CALL
SvxGraphCtrlAccessibleContext::getAccessibleAtPoint( const awt::Point
& rPoint
)
168 ::osl::MutexGuard
aGuard( m_aMutex
);
170 Reference
< XAccessible
> xAccessible
;
174 throw DisposedException();
177 Point
aPnt( rPoint
.X
, rPoint
.Y
);
178 aPnt
= mpControl
->GetDrawingArea()->get_ref_device().PixelToLogic(aPnt
);
180 SdrObject
* pObj
= nullptr;
182 if(mpView
&& mpView
->GetSdrPageView())
184 pObj
= SdrObjListPrimitiveHit(*mpPage
, aPnt
, {1, 1}, *mpView
->GetSdrPageView(), nullptr, false);
188 xAccessible
= getAccessible( pObj
);
193 awt::Rectangle SAL_CALL
SvxGraphCtrlAccessibleContext::getBounds()
195 const SolarMutexGuard aSolarGuard
;
197 if (nullptr == mpControl
)
198 throw DisposedException();
201 const Size
aOutSize( mpControl
->GetOutputSizePixel() );
204 aRet
.X
= aOutPos
.X();
205 aRet
.Y
= aOutPos
.Y();
206 aRet
.Width
= aOutSize
.Width();
207 aRet
.Height
= aOutSize
.Height();
212 awt::Point SAL_CALL
SvxGraphCtrlAccessibleContext::getLocation()
214 const SolarMutexGuard aSolarGuard
;
216 if (nullptr == mpControl
)
217 throw DisposedException();
219 const awt::Rectangle
aRect( getBounds() );
228 awt::Point SAL_CALL
SvxGraphCtrlAccessibleContext::getLocationOnScreen()
230 const SolarMutexGuard aSolarGuard
;
232 if (nullptr == mpControl
)
233 throw DisposedException();
235 awt::Point
aScreenLoc(0, 0);
237 auto xParent(getAccessibleParent());
240 css::uno::Reference
<css::accessibility::XAccessibleContext
> xParentContext(xParent
->getAccessibleContext());
241 css::uno::Reference
<css::accessibility::XAccessibleComponent
> xParentComponent(xParentContext
, css::uno::UNO_QUERY
);
242 OSL_ENSURE( xParentComponent
.is(), "ValueSetAcc::getLocationOnScreen: no parent component!" );
243 if ( xParentComponent
.is() )
245 awt::Point
aParentScreenLoc( xParentComponent
->getLocationOnScreen() );
246 awt::Point
aOwnRelativeLoc( getLocation() );
247 aScreenLoc
.X
= aParentScreenLoc
.X
+ aOwnRelativeLoc
.X
;
248 aScreenLoc
.Y
= aParentScreenLoc
.Y
+ aOwnRelativeLoc
.Y
;
255 awt::Size SAL_CALL
SvxGraphCtrlAccessibleContext::getSize()
257 const SolarMutexGuard aSolarGuard
;
259 if (nullptr == mpControl
)
260 throw DisposedException();
262 const awt::Rectangle
aRect( getBounds() );
265 aRet
.Width
= aRect
.Width
;
266 aRet
.Height
= aRect
.Height
;
271 // XAccessibleContext
272 sal_Int64 SAL_CALL
SvxGraphCtrlAccessibleContext::getAccessibleChildCount()
274 ::SolarMutexGuard aGuard
;
276 if( nullptr == mpPage
)
277 throw DisposedException();
279 return mpPage
->GetObjCount();
283 /** returns the SdrObject at index nIndex from the model of this graph */
284 SdrObject
* SvxGraphCtrlAccessibleContext::getSdrObject( sal_Int64 nIndex
)
286 ::SolarMutexGuard aGuard
;
288 if( nullptr == mpPage
)
289 throw DisposedException();
291 if( (nIndex
< 0) || ( o3tl::make_unsigned(nIndex
) >= mpPage
->GetObjCount() ) )
292 throw lang::IndexOutOfBoundsException();
294 return mpPage
->GetObj( nIndex
);
298 /** sends an AccessibleEventObject to all added XAccessibleEventListeners */
299 void SvxGraphCtrlAccessibleContext::CommitChange (
301 const uno::Any
& rNewValue
,
302 const uno::Any
& rOldValue
)
304 AccessibleEventObject
aEvent (
311 comphelper::AccessibleEventNotifier::addEvent( mnClientId
, aEvent
);
314 Reference
< XAccessible
> SAL_CALL
SvxGraphCtrlAccessibleContext::getAccessibleChild( sal_Int64 nIndex
)
316 ::SolarMutexGuard aGuard
;
318 return getAccessible( getSdrObject( nIndex
) );
321 Reference
< XAccessible
> SAL_CALL
SvxGraphCtrlAccessibleContext::getAccessibleParent()
323 ::SolarMutexGuard aGuard
;
325 if( nullptr == mpControl
)
326 throw DisposedException();
328 return mpControl
->GetDrawingArea()->get_accessible_parent();
331 sal_Int64 SAL_CALL
SvxGraphCtrlAccessibleContext::getAccessibleIndexInParent()
333 ::SolarMutexGuard aGuard
;
334 // Use a simple but slow solution for now. Optimize later.
336 // Iterate over all the parent's children and search for this object.
337 css::uno::Reference
<css::accessibility::XAccessible
> xParent(getAccessibleParent());
340 Reference
< XAccessibleContext
> xParentContext( xParent
->getAccessibleContext() );
341 if( xParentContext
.is() )
343 sal_Int64 nChildCount
= xParentContext
->getAccessibleChildCount();
344 for( sal_Int64 i
= 0 ; i
< nChildCount
; ++i
)
346 Reference
< XAccessible
> xChild( xParentContext
->getAccessibleChild( i
) );
349 Reference
< XAccessibleContext
> xChildContext
= xChild
->getAccessibleContext();
350 if( xChildContext
== static_cast<XAccessibleContext
*>(this) )
357 // Return -1 to indicate that this object's parent does not know about the
363 sal_Int16 SAL_CALL
SvxGraphCtrlAccessibleContext::getAccessibleRole()
365 return AccessibleRole::PANEL
;
369 OUString SAL_CALL
SvxGraphCtrlAccessibleContext::getAccessibleDescription()
371 ::SolarMutexGuard aGuard
;
372 return msDescription
;
376 OUString SAL_CALL
SvxGraphCtrlAccessibleContext::getAccessibleName()
378 ::SolarMutexGuard aGuard
;
383 /** Return empty reference to indicate that the relation set is not
386 Reference
< XAccessibleRelationSet
> SAL_CALL
SvxGraphCtrlAccessibleContext::getAccessibleRelationSet()
388 return Reference
< XAccessibleRelationSet
>();
392 sal_Int64 SAL_CALL
SvxGraphCtrlAccessibleContext::getAccessibleStateSet()
394 ::SolarMutexGuard aGuard
;
396 sal_Int64 nStateSet
= 0;
398 if ( rBHelper
.bDisposed
|| mbDisposed
)
400 nStateSet
|= AccessibleStateType::DEFUNC
;
404 nStateSet
|= AccessibleStateType::FOCUSABLE
;
405 if( mpControl
->HasFocus() )
406 nStateSet
|= AccessibleStateType::FOCUSED
;
407 nStateSet
|= AccessibleStateType::OPAQUE
;
408 nStateSet
|= AccessibleStateType::SHOWING
;
409 nStateSet
|= AccessibleStateType::VISIBLE
;
416 lang::Locale SAL_CALL
SvxGraphCtrlAccessibleContext::getLocale()
418 ::SolarMutexGuard aGuard
;
420 css::uno::Reference
<css::accessibility::XAccessible
> xParent(getAccessibleParent());
423 Reference
< XAccessibleContext
> xParentContext( xParent
->getAccessibleContext() );
424 if( xParentContext
.is() )
425 return xParentContext
->getLocale();
428 // No parent. Therefore throw exception to indicate this cluelessness.
429 throw IllegalAccessibleComponentStateException();
432 // XAccessibleEventListener
433 void SAL_CALL
SvxGraphCtrlAccessibleContext::addAccessibleEventListener( const Reference
< XAccessibleEventListener
>& xListener
)
437 ::SolarMutexGuard aGuard
;
439 mnClientId
= comphelper::AccessibleEventNotifier::registerClient( );
440 comphelper::AccessibleEventNotifier::addEventListener( mnClientId
, xListener
);
445 void SAL_CALL
SvxGraphCtrlAccessibleContext::removeAccessibleEventListener( const Reference
< XAccessibleEventListener
>& xListener
)
450 ::SolarMutexGuard aGuard
;
452 sal_Int32 nListenerCount
= comphelper::AccessibleEventNotifier::removeEventListener( mnClientId
, xListener
);
453 if ( !nListenerCount
)
455 // no listeners anymore
456 // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client),
457 // and at least to us not firing any events anymore, in case somebody calls
458 // NotifyAccessibleEvent, again
459 comphelper::AccessibleEventNotifier::revokeClient( mnClientId
);
464 void SAL_CALL
SvxGraphCtrlAccessibleContext::grabFocus()
466 ::SolarMutexGuard aGuard
;
468 if( nullptr == mpControl
)
469 throw DisposedException();
471 mpControl
->GrabFocus();
474 sal_Int32 SAL_CALL
SvxGraphCtrlAccessibleContext::getForeground()
476 svtools::ColorConfig aColorConfig
;
477 Color nColor
= aColorConfig
.GetColorValue( svtools::FONTCOLOR
).nColor
;
478 return static_cast<sal_Int32
>(nColor
);
481 sal_Int32 SAL_CALL
SvxGraphCtrlAccessibleContext::getBackground()
483 Color nColor
= Application::GetSettings().GetStyleSettings().GetWindowColor();
484 return static_cast<sal_Int32
>(nColor
);
488 OUString SAL_CALL
SvxGraphCtrlAccessibleContext::getImplementationName()
490 return u
"com.sun.star.comp.ui.SvxGraphCtrlAccessibleContext"_ustr
;
493 sal_Bool SAL_CALL
SvxGraphCtrlAccessibleContext::supportsService( const OUString
& sServiceName
)
495 return cppu::supportsService(this, sServiceName
);
498 Sequence
< OUString
> SAL_CALL
SvxGraphCtrlAccessibleContext::getSupportedServiceNames()
500 return { u
"com.sun.star.accessibility.Accessible"_ustr
,
501 u
"com.sun.star.accessibility.AccessibleContext"_ustr
,
502 u
"com.sun.star.drawing.AccessibleGraphControl"_ustr
};
506 Sequence
<sal_Int8
> SAL_CALL
SvxGraphCtrlAccessibleContext::getImplementationId()
508 return css::uno::Sequence
<sal_Int8
>();
512 OUString
SvxGraphCtrlAccessibleContext::getServiceName()
514 return u
"com.sun.star.accessibility.AccessibleContext"_ustr
;
517 // XAccessibleSelection
518 void SAL_CALL
SvxGraphCtrlAccessibleContext::selectAccessibleChild( sal_Int64 nIndex
)
520 ::SolarMutexGuard aGuard
;
522 if( nullptr == mpView
)
523 throw DisposedException();
525 if (nIndex
< 0 || nIndex
>= getAccessibleChildCount())
526 throw lang::IndexOutOfBoundsException();
528 SdrObject
* pObj
= getSdrObject( nIndex
);
531 mpView
->MarkObj( pObj
, mpView
->GetSdrPageView());
535 sal_Bool SAL_CALL
SvxGraphCtrlAccessibleContext::isAccessibleChildSelected( sal_Int64 nIndex
)
537 ::SolarMutexGuard aGuard
;
539 if( nullptr == mpView
)
540 throw DisposedException();
542 if (nIndex
< 0 || nIndex
>= getAccessibleChildCount())
543 throw lang::IndexOutOfBoundsException();
545 return mpView
->IsObjMarked( getSdrObject( nIndex
) );
549 void SAL_CALL
SvxGraphCtrlAccessibleContext::clearAccessibleSelection()
551 ::SolarMutexGuard aGuard
;
553 if( nullptr == mpView
)
554 throw DisposedException();
556 mpView
->UnmarkAllObj();
560 void SAL_CALL
SvxGraphCtrlAccessibleContext::selectAllAccessibleChildren()
562 ::SolarMutexGuard aGuard
;
564 if( nullptr == mpView
)
565 throw DisposedException();
567 mpView
->MarkAllObj();
571 sal_Int64 SAL_CALL
SvxGraphCtrlAccessibleContext::getSelectedAccessibleChildCount()
573 ::SolarMutexGuard aGuard
;
575 if( nullptr == mpView
)
576 throw DisposedException();
578 const SdrMarkList
& rList
= mpView
->GetMarkedObjectList();
579 return static_cast<sal_Int64
>(rList
.GetMarkCount());
583 Reference
< XAccessible
> SAL_CALL
SvxGraphCtrlAccessibleContext::getSelectedAccessibleChild( sal_Int64 nIndex
)
585 ::SolarMutexGuard aGuard
;
587 checkChildIndexOnSelection( nIndex
);
589 Reference
< XAccessible
> xAccessible
;
591 const SdrMarkList
& rList
= mpView
->GetMarkedObjectList();
592 SdrObject
* pObj
= rList
.GetMark(static_cast<size_t>(nIndex
))->GetMarkedSdrObj();
594 xAccessible
= getAccessible( pObj
);
600 void SAL_CALL
SvxGraphCtrlAccessibleContext::deselectAccessibleChild( sal_Int64 nIndex
)
602 ::SolarMutexGuard aGuard
;
604 checkChildIndexOnSelection( nIndex
);
609 const SdrMarkList
& rList
= mpView
->GetMarkedObjectList();
611 SdrObject
* pObj
= getSdrObject( nIndex
);
615 SdrMarkList
aRefList( rList
);
617 SdrPageView
* pPV
= mpView
->GetSdrPageView();
618 mpView
->UnmarkAllObj( pPV
);
620 const size_t nCount
= aRefList
.GetMarkCount();
621 for( size_t nMark
= 0; nMark
< nCount
; ++nMark
)
623 if( aRefList
.GetMark(nMark
)->GetMarkedSdrObj() != pObj
)
624 mpView
->MarkObj( aRefList
.GetMark(nMark
)->GetMarkedSdrObj(), pPV
);
629 void SvxGraphCtrlAccessibleContext::checkChildIndexOnSelection(sal_Int64 nIndex
)
631 if( nIndex
< 0 || nIndex
>= getSelectedAccessibleChildCount() )
632 throw lang::IndexOutOfBoundsException();
636 /** Replace the model, page, and view pointers by the ones provided
637 (explicitly and implicitly).
639 void SvxGraphCtrlAccessibleContext::setModelAndView (
643 ::SolarMutexGuard aGuard
;
646 if (mpModel
!= nullptr)
647 mpPage
= mpModel
->GetPage( 0 );
650 if (mpModel
== nullptr || mpPage
== nullptr || mpView
== nullptr)
654 // Set all the pointers to NULL just in case they are used as
661 maTreeInfo
.SetSdrView (mpView
);
665 void SAL_CALL
SvxGraphCtrlAccessibleContext::disposing()
667 ::SolarMutexGuard aGuard
;
674 mpControl
= nullptr; // object dies with representation
679 for (const auto& rEntry
: mxShapes
)
681 rtl::Reference
<XAccessible
> pAcc(rEntry
.second
);
682 Reference
< XComponent
> xComp( pAcc
.get(), UNO_QUERY
);
690 // Send a disposing to all listeners.
693 comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing( mnClientId
, *this );
698 void SvxGraphCtrlAccessibleContext::Notify( SfxBroadcaster
& /*rBC*/, const SfxHint
& rHint
)
700 if (rHint
.GetId() == SfxHintId::ThisIsAnSdrHint
)
702 const SdrHint
* pSdrHint
= static_cast<const SdrHint
*>( &rHint
);
703 switch( pSdrHint
->GetKind() )
705 case SdrHintKind::ObjectChange
:
707 ShapesMapType::iterator iter
= mxShapes
.find( pSdrHint
->GetObject() );
709 if( iter
!= mxShapes
.end() )
711 // if we already have one, return it
712 rtl::Reference
<AccessibleShape
> pShape((*iter
).second
);
715 pShape
->CommitChange( AccessibleEventId::VISIBLE_DATA_CHANGED
, uno::Any(), uno::Any(), -1 );
720 case SdrHintKind::ObjectInserted
:
721 CommitChange( AccessibleEventId::CHILD
, Any( getAccessible( pSdrHint
->GetObject() ) ) , uno::Any());
723 case SdrHintKind::ObjectRemoved
:
724 CommitChange( AccessibleEventId::CHILD
, uno::Any(), Any( getAccessible( pSdrHint
->GetObject() ) ) );
726 case SdrHintKind::ModelCleared
:
735 // Has our SdDrawDocument just died?
736 if(rHint
.GetId() == SfxHintId::Dying
)
743 // IAccessibleViewforwarder
744 tools::Rectangle
SvxGraphCtrlAccessibleContext::GetVisibleArea() const
746 tools::Rectangle aVisArea
;
748 if( mpView
&& mpView
->PaintWindowCount())
750 SdrPaintWindow
* pPaintWindow
= mpView
->GetPaintWindow(0);
751 aVisArea
= pPaintWindow
->GetVisibleArea();
757 Point
SvxGraphCtrlAccessibleContext::LogicToPixel (const Point
& rPoint
) const
761 return mpControl
->GetDrawingArea()->get_ref_device().LogicToPixel (rPoint
) + mpControl
->GetPositionInDialog();
769 Size
SvxGraphCtrlAccessibleContext::LogicToPixel (const Size
& rSize
) const
772 return mpControl
->GetDrawingArea()->get_ref_device().LogicToPixel(rSize
);
777 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */