1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <svx/AccessibleControlShape.hxx>
21 #include <svx/AccessibleShapeInfo.hxx>
22 #include <DescriptionGenerator.hxx>
23 #include <com/sun/star/awt/XWindow.hpp>
24 #include <com/sun/star/beans/XPropertySet.hpp>
25 #include <com/sun/star/drawing/XControlShape.hpp>
26 #include <com/sun/star/accessibility/AccessibleRelationType.hpp>
27 #include <com/sun/star/accessibility/AccessibleRole.hpp>
28 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
29 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
30 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
31 #include <com/sun/star/reflection/ProxyFactory.hpp>
32 #include <com/sun/star/util/XModeChangeBroadcaster.hpp>
33 #include <com/sun/star/container/XContainer.hpp>
34 #include <comphelper/processfactory.hxx>
35 #include <comphelper/property.hxx>
36 #include <unotools/accessiblestatesethelper.hxx>
37 #include <unotools/accessiblerelationsethelper.hxx>
38 #include <svx/IAccessibleParent.hxx>
39 #include <svx/svdouno.hxx>
40 #include <svx/unoapi.hxx>
41 #include <svx/ShapeTypeHandler.hxx>
42 #include <svx/SvxShapeTypes.hxx>
43 #include <comphelper/accessiblewrapper.hxx>
44 #include <svx/svdview.hxx>
45 #include <svx/svdpagv.hxx>
46 #include <svx/strings.hrc>
47 #include <vcl/svapp.hxx>
48 #include <vcl/window.hxx>
49 #include <sal/log.hxx>
50 #include <tools/debug.hxx>
51 #include <tools/diagnose_ex.h>
52 
53 using namespace ::accessibility;
54 using namespace ::com::sun::star::accessibility;
55 using namespace ::com::sun::star::uno;
56 using namespace ::com::sun::star::awt;
57 using namespace ::com::sun::star::beans;
58 using namespace ::com::sun::star::util;
59 using namespace ::com::sun::star::lang;
60 using namespace ::com::sun::star::reflection;
61 using namespace ::com::sun::star::drawing;
62 using namespace ::com::sun::star::container;
63 
64 namespace
65 {
lcl_getNamePropertyName()66     OUString lcl_getNamePropertyName( )
67     {
68         return "Name";
69     }
lcl_getDescPropertyName()70     OUString lcl_getDescPropertyName( )
71     {
72         return "HelpText";
73     }
lcl_getLabelPropertyName()74     OUString lcl_getLabelPropertyName( )
75     {
76         return "Label";
77     }
lcl_getLabelControlPropertyName()78     OUString lcl_getLabelControlPropertyName( )
79     {
80         return "LabelControl";
81     }
82     // return the property which should be used as AccessibleName
lcl_getPreferredAccNameProperty(const Reference<XPropertySetInfo> & _rxPSI)83     OUString lcl_getPreferredAccNameProperty( const Reference< XPropertySetInfo >& _rxPSI )
84     {
85         if ( _rxPSI.is() && _rxPSI->hasPropertyByName( lcl_getLabelPropertyName() ) )
86             return lcl_getLabelPropertyName();
87         else
88             return lcl_getNamePropertyName();
89     }
90 
91     // determines whether or not a state which belongs to the inner context needs to be forwarded to the "composed"
92     // context
isComposedState(const sal_Int16 _nState)93     bool    isComposedState( const sal_Int16 _nState )
94     {
95         return  (   ( AccessibleStateType::INVALID != _nState )
96                 &&  ( AccessibleStateType::DEFUNC != _nState )
97                 &&  ( AccessibleStateType::ICONIFIED != _nState )
98                 &&  ( AccessibleStateType::RESIZABLE != _nState )
99                 &&  ( AccessibleStateType::SELECTABLE != _nState )
100                 &&  ( AccessibleStateType::SHOWING != _nState )
101                 &&  ( AccessibleStateType::MANAGES_DESCENDANTS != _nState )
102                 &&  ( AccessibleStateType::VISIBLE != _nState )
103                 );
104     }
105 
106     /// determines whether the given control is in alive mode
isAliveMode(const Reference<XControl> & _rxControl)107     bool    isAliveMode( const Reference< XControl >& _rxControl )
108     {
109         OSL_PRECOND( _rxControl.is(), "AccessibleControlShape::isAliveMode: invalid control" );
110         return _rxControl.is() && !_rxControl->isDesignMode();
111     }
112 }
113 
AccessibleControlShape(const AccessibleShapeInfo & rShapeInfo,const AccessibleShapeTreeInfo & rShapeTreeInfo)114 AccessibleControlShape::AccessibleControlShape (
115     const AccessibleShapeInfo& rShapeInfo,
116     const AccessibleShapeTreeInfo& rShapeTreeInfo)
117     :      AccessibleShape (rShapeInfo, rShapeTreeInfo)
118     ,   m_bListeningForName( false )
119     ,   m_bListeningForDesc( false )
120     ,   m_bMultiplexingStates( false )
121     ,   m_bDisposeNativeContext( false )
122     ,   m_bWaitingForControl( false )
123 {
124     m_pChildManager = new comphelper::OWrappedAccessibleChildrenManager( comphelper::getProcessComponentContext() );
125 
126     osl_atomic_increment( &m_refCount );
127     {
128         m_pChildManager->setOwningAccessible( this );
129     }
130     osl_atomic_decrement( &m_refCount );
131 }
132 
~AccessibleControlShape()133 AccessibleControlShape::~AccessibleControlShape()
134 {
135     m_pChildManager.clear();
136 
137     if ( m_xControlContextProxy.is() )
138         m_xControlContextProxy->setDelegator( nullptr );
139     m_xControlContextProxy.clear();
140     m_xControlContextTypeAccess.clear();
141     m_xControlContextComponent.clear();
142         // this should remove the _only_ three "real" reference (means not delegated to
143         // ourself) to this proxy, and thus delete it
144 }
145 
146 namespace {
lcl_getControlContainer(const OutputDevice * _pWin,const SdrView * _pView)147     Reference< XContainer > lcl_getControlContainer( const OutputDevice* _pWin, const SdrView* _pView )
148     {
149         Reference< XContainer > xReturn;
150         DBG_ASSERT( _pView, "lcl_getControlContainer: invalid view!" );
151         if ( _pView  && _pView->GetSdrPageView())
152         {
153             xReturn.set(_pView->GetSdrPageView()->GetControlContainer( *_pWin ), css::uno::UNO_QUERY);
154         }
155         return xReturn;
156     }
157 }
158 
Init()159 void AccessibleControlShape::Init()
160 {
161     AccessibleShape::Init();
162 
163     OSL_ENSURE( !m_xControlContextProxy.is(), "AccessibleControlShape::Init: already initialized!" );
164     try
165     {
166         // What we need to do here is merge the functionality of the AccessibleContext of our UNO control
167         // with our own AccessibleContext-related functionality.
168 
169         // The problem is that we do not know the interfaces our "inner" context supports - this may be any
170         // XAccessibleXXX interface (or even any other) which makes sense for it.
171 
172         // In theory, we could implement all possible interfaces ourself, and re-route all functionality to
173         // the inner context (except those we implement ourself, like XAccessibleComponent). But this is in no
174         // way future-proof - as soon as an inner context appears which implements an additional interface,
175         // we would need to adjust our implementation to support this new interface, too. Bad idea.
176 
177         // The usual solution for such a problem is aggregation. Aggregation means using UNO's own mechanism
178         // for merging an inner with an outer component, and get a component which behaves as it is exactly one.
179         // This is what XAggregation is for. Unfortunately, aggregation requires _exact_ control over the ref count
180         // of the inner object, which we do not have at all.
181         // Bad, too.
182 
183         // But there is a solution: com.sun.star.reflection.ProxyFactory. This service is able to create a proxy
184         // for any component, which supports _exactly_ the same interfaces as the component. In addition, it can
185         // be aggregated, as by definition the proxy's ref count is exactly 1 when returned from the factory.
186         // Sounds better. Though this yields the problem of slightly degraded performance, it's the only solution
187         // I'm aware of at the moment...
188 
189         // get the control which belongs to our model (relative to our view)
190         const vcl::Window* pViewWindow = maShapeTreeInfo.GetWindow();
191         SdrUnoObj* pUnoObjectImpl = dynamic_cast<SdrUnoObj*>( GetSdrObjectFromXShape(mxShape)  );
192         SdrView* pView = maShapeTreeInfo.GetSdrView();
193         OSL_ENSURE( pView && pViewWindow && pUnoObjectImpl, "AccessibleControlShape::Init: no view, or no view window, no SdrUnoObj!" );
194 
195         if ( pView && pViewWindow && pUnoObjectImpl )
196         {
197             // get the context of the control - it will be our "inner" context
198             m_xUnoControl = pUnoObjectImpl->GetUnoControl( *pView, *pViewWindow->GetOutDev() );
199 
200             if ( !m_xUnoControl.is() )
201             {
202                 // the control has not yet been created. Though speaking strictly, it is a bug that
203                 // our instance here is created without an existing control (because an AccessibleControlShape
204                 // is a representation of a view object, and can only live if the view it should represent
205                 // is complete, which implies a living control), it's by far the easiest and most riskless way
206                 // to fix this here in this class.
207                 // Okay, we will add as listener to the control container where we expect our control to appear.
208                 OSL_ENSURE( !m_bWaitingForControl, "AccessibleControlShape::Init: already waiting for the control!" );
209 
210                 Reference< XContainer > xControlContainer = lcl_getControlContainer( pViewWindow->GetOutDev(), maShapeTreeInfo.GetSdrView() );
211                 OSL_ENSURE( xControlContainer.is(), "AccessibleControlShape::Init: unable to find my ControlContainer!" );
212                 if ( xControlContainer.is() )
213                 {
214                     xControlContainer->addContainerListener( this );
215                     m_bWaitingForControl = true;
216                 }
217             }
218             else
219             {
220                 Reference< XModeChangeBroadcaster > xControlModes( m_xUnoControl, UNO_QUERY );
221                 Reference< XAccessible > xControlAccessible( xControlModes, UNO_QUERY );
222                 Reference< XAccessibleContext > xNativeControlContext;
223                 if ( xControlAccessible.is() )
224                     xNativeControlContext = xControlAccessible->getAccessibleContext();
225                 OSL_ENSURE( xNativeControlContext.is(), "AccessibleControlShape::Init: no AccessibleContext for the control!" );
226                 m_aControlContext = WeakReference< XAccessibleContext >( xNativeControlContext );
227 
228                 // add as listener to the context - we want to multiplex some states
229                 if ( isAliveMode( m_xUnoControl ) && xNativeControlContext.is() )
230                 {   // (but only in alive mode)
231                     startStateMultiplexing( );
232                 }
233 
234                 // now that we have all information about our control, do some adjustments
235                 adjustAccessibleRole();
236                 initializeComposedState();
237 
238                 // some initialization for our child manager, which is used in alive mode only
239                 if ( isAliveMode( m_xUnoControl ) )
240                 {
241                     Reference< XAccessibleStateSet > xStates( getAccessibleStateSet( ) );
242                     OSL_ENSURE( xStates.is(), "AccessibleControlShape::AccessibleControlShape: no inner state set!" );
243                     m_pChildManager->setTransientChildren( !xStates.is() || xStates->contains( AccessibleStateType::MANAGES_DESCENDANTS ) );
244                 }
245 
246                 // finally, aggregate a proxy for the control context
247                 // first a factory for the proxy
248                 Reference< XProxyFactory > xFactory = ProxyFactory::create( comphelper::getProcessComponentContext() );
249                 // then the proxy itself
250                 if ( xNativeControlContext.is() )
251                 {
252                     m_xControlContextProxy = xFactory->createProxy( xNativeControlContext );
253                     m_xControlContextTypeAccess.set( xNativeControlContext, UNO_QUERY_THROW );
254                     m_xControlContextComponent.set( xNativeControlContext, UNO_QUERY_THROW );
255 
256                     // aggregate the proxy
257                     osl_atomic_increment( &m_refCount );
258                     if ( m_xControlContextProxy.is() )
259                     {
260                         // At this point in time, the proxy has a ref count of exactly one - in m_xControlContextProxy.
261                         // Remember to _not_ reset this member unless the delegator of the proxy has been reset, too!
262                         m_xControlContextProxy->setDelegator( *this );
263                     }
264                     osl_atomic_decrement( &m_refCount );
265 
266                     m_bDisposeNativeContext = true;
267 
268                     // Finally, we need to add ourself as mode listener to the control. In case the mode switches,
269                     // we need to dispose ourself.
270                     xControlModes->addModeChangeListener( this );
271                 }
272             }
273         }
274     }
275     catch( const Exception& )
276     {
277         OSL_FAIL( "AccessibleControlShape::Init: could not \"aggregate\" the controls XAccessibleContext!" );
278     }
279 }
280 
grabFocus()281 void SAL_CALL AccessibleControlShape::grabFocus()
282 {
283     if ( !m_xUnoControl.is() || !isAliveMode( m_xUnoControl ) )
284     {
285         // in design mode, we simply forward the request to the base class
286         AccessibleShape::grabFocus();
287     }
288     else
289     {
290         Reference< XWindow > xWindow( m_xUnoControl, UNO_QUERY );
291         OSL_ENSURE( xWindow.is(), "AccessibleControlShape::grabFocus: invalid control!" );
292         if ( xWindow.is() )
293             xWindow->setFocus();
294     }
295 }
296 
getImplementationName()297 OUString SAL_CALL AccessibleControlShape::getImplementationName()
298 {
299     return "com.sun.star.comp.accessibility.AccessibleControlShape";
300 }
301 
CreateAccessibleBaseName()302 OUString AccessibleControlShape::CreateAccessibleBaseName()
303 {
304     OUString sName;
305 
306     ShapeTypeId nShapeType = ShapeTypeHandler::Instance().GetTypeId (mxShape);
307     switch (nShapeType)
308     {
309         case DRAWING_CONTROL:
310             sName = "ControlShape";
311             break;
312         default:
313             sName = "UnknownAccessibleControlShape";
314             if (mxShape.is())
315                 sName += ": " + mxShape->getShapeType();
316     }
317 
318     return sName;
319 }
320 
321 OUString
CreateAccessibleDescription()322     AccessibleControlShape::CreateAccessibleDescription()
323 {
324     DescriptionGenerator aDG (mxShape);
325     ShapeTypeId nShapeType = ShapeTypeHandler::Instance().GetTypeId (mxShape);
326     switch (nShapeType)
327     {
328         case DRAWING_CONTROL:
329         {
330             // check if we can obtain the "Desc" property from the model
331             OUString sDesc( getControlModelStringProperty( lcl_getDescPropertyName() ) );
332             if ( sDesc.isEmpty() )
333             {   // no -> use the default
334                 aDG.Initialize (STR_ObjNameSingulUno);
335                 aDG.AddProperty ("ControlBackground", DescriptionGenerator::PropertyType::Color);
336                 aDG.AddProperty ( "ControlBorder", DescriptionGenerator::PropertyType::Integer);
337             }
338             // ensure that we are listening to the Name property
339             m_bListeningForDesc = ensureListeningState( m_bListeningForDesc, true, lcl_getDescPropertyName() );
340         }
341         break;
342 
343         default:
344             aDG.Initialize ("Unknown accessible control shape");
345             if (mxShape.is())
346             {
347                 aDG.AppendString (u"service name=");
348                 aDG.AppendString (mxShape->getShapeType());
349             }
350     }
351 
352     return aDG();
353 }
354 
IMPLEMENT_FORWARD_REFCOUNT(AccessibleControlShape,AccessibleShape)355 IMPLEMENT_FORWARD_REFCOUNT( AccessibleControlShape, AccessibleShape )
356 IMPLEMENT_GET_IMPLEMENTATION_ID( AccessibleControlShape )
357 
358 void SAL_CALL AccessibleControlShape::propertyChange( const PropertyChangeEvent& _rEvent )
359 {
360     ::osl::MutexGuard aGuard( maMutex );
361 
362     // check if it is the name or the description
363     if  (   _rEvent.PropertyName == lcl_getNamePropertyName()
364             ||  _rEvent.PropertyName == lcl_getLabelPropertyName() )
365     {
366         SetAccessibleName(
367             CreateAccessibleName(),
368             AccessibleContextBase::AutomaticallyCreated);
369     }
370     else if ( _rEvent.PropertyName == lcl_getDescPropertyName() )
371     {
372         SetAccessibleDescription(
373             CreateAccessibleDescription(),
374             AccessibleContextBase::AutomaticallyCreated);
375     }
376 #if OSL_DEBUG_LEVEL > 0
377     else
378     {
379         OSL_FAIL( "AccessibleControlShape::propertyChange: where did this come from?" );
380     }
381 #endif
382 }
383 
queryInterface(const Type & _rType)384 Any SAL_CALL AccessibleControlShape::queryInterface( const Type& _rType )
385 {
386     Any aReturn = AccessibleShape::queryInterface( _rType );
387     if ( !aReturn.hasValue() )
388     {
389         aReturn = AccessibleControlShape_Base::queryInterface( _rType );
390         if ( !aReturn.hasValue() && m_xControlContextProxy.is() )
391             aReturn = m_xControlContextProxy->queryAggregation( _rType );
392     }
393     return aReturn;
394 }
395 
getTypes()396 Sequence< Type > SAL_CALL AccessibleControlShape::getTypes()
397 {
398     Sequence< Type > aShapeTypes = AccessibleShape::getTypes();
399     Sequence< Type > aOwnTypes = AccessibleControlShape_Base::getTypes();
400 
401     Sequence< Type > aAggregateTypes;
402     if ( m_xControlContextTypeAccess.is() )
403         aAggregateTypes = m_xControlContextTypeAccess->getTypes();
404 
405     // remove duplicates
406     return comphelper::combineSequences(comphelper::concatSequences( aShapeTypes, aOwnTypes), aAggregateTypes );
407 }
408 
notifyEvent(const AccessibleEventObject & _rEvent)409 void SAL_CALL AccessibleControlShape::notifyEvent( const AccessibleEventObject& _rEvent )
410 {
411     if ( AccessibleEventId::STATE_CHANGED == _rEvent.EventId )
412     {
413         // multiplex this change
414         sal_Int16 nLostState( 0 ), nGainedState( 0 );
415         _rEvent.OldValue >>= nLostState;
416         _rEvent.NewValue >>= nGainedState;
417 
418         // don't multiplex states which the inner context is not responsible for
419         if  ( isComposedState( nLostState ) )
420             AccessibleShape::ResetState( nLostState );
421 
422         if  ( isComposedState( nGainedState ) )
423             AccessibleShape::SetState( nGainedState );
424     }
425     else
426     {
427         AccessibleEventObject aTranslatedEvent( _rEvent );
428 
429         {
430             ::osl::MutexGuard aGuard( maMutex );
431 
432             // let the child manager translate the event
433             aTranslatedEvent.Source = *this;
434             m_pChildManager->translateAccessibleEvent( _rEvent, aTranslatedEvent );
435 
436             // see if any of these notifications affect our child manager
437             m_pChildManager->handleChildNotification( _rEvent );
438         }
439 
440         FireEvent( aTranslatedEvent );
441     }
442 }
443 
modeChanged(const ModeChangeEvent & rSource)444 void SAL_CALL AccessibleControlShape::modeChanged(const ModeChangeEvent& rSource)
445 {
446     // did it come from our inner context (the real one, not it's proxy!)?
447     SAL_INFO("sw.uno", "AccessibleControlShape::modeChanged");
448     Reference<XControl> xSource(rSource.Source, UNO_QUERY); // for faster compare
449     if(xSource.get() != m_xUnoControl.get())
450     {
451         SAL_WARN("sw.uno", "AccessibleControlShape::modeChanged: where did this come from?");
452         return;
453     }
454     SolarMutexGuard g;
455     // If our "pseudo-aggregated" inner context does not live anymore,
456     // we don't want to live, too.  This is accomplished by asking our
457     // parent to replace this object with a new one.  Disposing this
458     // object and sending notifications about the replacement are in
459     // the responsibility of our parent.
460     const bool bReplaced = mpParent->ReplaceChild(this, mxShape, 0, maShapeTreeInfo);
461     SAL_WARN_IF(!bReplaced, "sw.uno", "AccessibleControlShape::modeChanged: replacing ourselves away did fail");
462 }
463 
disposing(const EventObject & _rSource)464 void SAL_CALL AccessibleControlShape::disposing (const EventObject& _rSource)
465 {
466     AccessibleShape::disposing( _rSource );
467 }
468 
ensureListeningState(const bool _bCurrentlyListening,const bool _bNeedNewListening,const OUString & _rPropertyName)469 bool AccessibleControlShape::ensureListeningState(
470         const bool _bCurrentlyListening, const bool _bNeedNewListening,
471         const OUString& _rPropertyName )
472 {
473     if ( ( _bCurrentlyListening == _bNeedNewListening ) || !ensureControlModelAccess() )
474         //  nothing to do
475         return _bCurrentlyListening;
476 
477     try
478     {
479         if ( !m_xModelPropsMeta.is() || m_xModelPropsMeta->hasPropertyByName( _rPropertyName ) )
480         {
481             // add or revoke as listener
482             if ( _bNeedNewListening )
483                 m_xControlModel->addPropertyChangeListener( _rPropertyName, static_cast< XPropertyChangeListener* >( this ) );
484             else
485                 m_xControlModel->removePropertyChangeListener( _rPropertyName, static_cast< XPropertyChangeListener* >( this ) );
486         }
487         else
488             OSL_FAIL( "AccessibleControlShape::ensureListeningState: this property does not exist at this model!" );
489     }
490     catch( const Exception& )
491     {
492         OSL_FAIL( "AccessibleControlShape::ensureListeningState: could not change the listening state!" );
493     }
494 
495     return _bNeedNewListening;
496 }
497 
getAccessibleChildCount()498 sal_Int32 SAL_CALL AccessibleControlShape::getAccessibleChildCount( )
499 {
500     if ( !m_xUnoControl.is() )
501         return 0;
502     else if ( !isAliveMode( m_xUnoControl ) )
503         // no special action required when in design mode
504         return AccessibleShape::getAccessibleChildCount( );
505     else
506     {
507         // in alive mode, we have the full control over our children - they are determined by the children
508         // of the context of our UNO control
509         Reference< XAccessibleContext > xControlContext( m_aControlContext );
510         OSL_ENSURE( xControlContext.is(), "AccessibleControlShape::getAccessibleChildCount: control context already dead! How this!" );
511         return xControlContext.is() ? xControlContext->getAccessibleChildCount() : 0;
512     }
513 }
514 
getAccessibleChild(sal_Int32 i)515 Reference< XAccessible > SAL_CALL AccessibleControlShape::getAccessibleChild( sal_Int32 i )
516 {
517     Reference< XAccessible > xChild;
518     if ( !m_xUnoControl.is() )
519     {
520         throw IndexOutOfBoundsException();
521     }
522     if ( !isAliveMode( m_xUnoControl ) )
523     {
524         // no special action required when in design mode - let the base class handle this
525         xChild = AccessibleShape::getAccessibleChild( i );
526     }
527     else
528     {
529         // in alive mode, we have the full control over our children - they are determined by the children
530         // of the context of our UNO control
531 
532         Reference< XAccessibleContext > xControlContext( m_aControlContext );
533         OSL_ENSURE( xControlContext.is(), "AccessibleControlShape::getAccessibleChildCount: control context already dead! How this!" );
534         if ( xControlContext.is() )
535         {
536             Reference< XAccessible > xInnerChild( xControlContext->getAccessibleChild( i ) );
537             OSL_ENSURE( xInnerChild.is(), "AccessibleControlShape::getAccessibleChild: control context returned nonsense!" );
538             if ( xInnerChild.is() )
539             {
540                 // we need to wrap this inner child into an own implementation
541                 xChild = m_pChildManager->getAccessibleWrapperFor( xInnerChild );
542             }
543         }
544     }
545 
546 #if OSL_DEBUG_LEVEL > 0
547     sal_Int32 nChildIndex = -1;
548     Reference< XAccessibleContext > xContext;
549     if ( xChild.is() )
550         xContext = xChild->getAccessibleContext( );
551     if ( xContext.is() )
552         nChildIndex = xContext->getAccessibleIndexInParent( );
553     SAL_WARN_IF( nChildIndex != i, "svx", "AccessibleControlShape::getAccessibleChild: index mismatch,"
554             " nChildIndex=" << nChildIndex << " vs i=" << i );
555 #endif
556     return xChild;
557 }
558 
getAccessibleRelationSet()559 Reference< XAccessibleRelationSet > SAL_CALL AccessibleControlShape::getAccessibleRelationSet(  )
560 {
561     rtl::Reference<utl::AccessibleRelationSetHelper> pRelationSetHelper = new utl::AccessibleRelationSetHelper;
562     ensureControlModelAccess();
563     AccessibleControlShape* pCtlAccShape = GetLabeledByControlShape();
564     if(pCtlAccShape)
565     {
566         Reference < XAccessible > xAcc (pCtlAccShape->getAccessibleContext(), UNO_QUERY);
567 
568         css::uno::Sequence< css::uno::Reference< css::uno::XInterface > > aSequence { xAcc };
569         if( getAccessibleRole() == AccessibleRole::RADIO_BUTTON )
570         {
571             pRelationSetHelper->AddRelation( AccessibleRelation( AccessibleRelationType::MEMBER_OF, aSequence ) );
572         }
573         else
574         {
575             pRelationSetHelper->AddRelation( AccessibleRelation( AccessibleRelationType::LABELED_BY, aSequence ) );
576         }
577     }
578     return pRelationSetHelper;
579 }
580 
CreateAccessibleName()581 OUString AccessibleControlShape::CreateAccessibleName()
582 {
583     ensureControlModelAccess();
584 
585     OUString sName;
586     sal_Int16 aAccessibleRole = getAccessibleRole();
587     if ( aAccessibleRole != AccessibleRole::SHAPE
588         && aAccessibleRole != AccessibleRole::RADIO_BUTTON  )
589     {
590         AccessibleControlShape* pCtlAccShape = GetLabeledByControlShape();
591         if(pCtlAccShape)
592         {
593             sName = pCtlAccShape->CreateAccessibleName();
594         }
595     }
596 
597     if (sName.isEmpty())
598     {
599         // check if we can obtain the "Name" resp. "Label" property from the model
600         const OUString& rAccNameProperty = lcl_getPreferredAccNameProperty( m_xModelPropsMeta );
601         sName = getControlModelStringProperty( rAccNameProperty );
602         if ( !sName.getLength() )
603         {   // no -> use the default
604             sName = AccessibleShape::CreateAccessibleName();
605         }
606     }
607 
608     // now that somebody first asked us for our name, ensure that we are listening to name changes on the model
609     m_bListeningForName = ensureListeningState( m_bListeningForName, true, lcl_getPreferredAccNameProperty( m_xModelPropsMeta ) );
610 
611     return sName;
612 }
613 
disposing()614 void SAL_CALL AccessibleControlShape::disposing()
615 {
616     // ensure we're not listening
617     m_bListeningForName = ensureListeningState( m_bListeningForName, false, lcl_getPreferredAccNameProperty( m_xModelPropsMeta ) );
618     m_bListeningForDesc = ensureListeningState( m_bListeningForDesc, false, lcl_getDescPropertyName() );
619 
620     if ( m_bMultiplexingStates )
621         stopStateMultiplexing( );
622 
623     // dispose the child cache/map
624     m_pChildManager->dispose();
625 
626     // release the model
627     m_xControlModel.clear();
628     m_xModelPropsMeta.clear();
629     m_aControlContext = WeakReference< XAccessibleContext >();
630 
631     // stop listening at the control container (should never be necessary here, but who knows...)
632     if ( m_bWaitingForControl )
633     {
634         OSL_FAIL( "AccessibleControlShape::disposing: this should never happen!" );
635         Reference< XContainer > xContainer = lcl_getControlContainer( maShapeTreeInfo.GetWindow()->GetOutDev(), maShapeTreeInfo.GetSdrView() );
636         if ( xContainer.is() )
637         {
638             m_bWaitingForControl = false;
639             xContainer->removeContainerListener( this );
640         }
641     }
642 
643     // forward the disposal to our inner context
644     if ( m_bDisposeNativeContext )
645     {
646         // don't listen for mode changes anymore
647         Reference< XModeChangeBroadcaster > xControlModes( m_xUnoControl, UNO_QUERY );
648         OSL_ENSURE( xControlModes.is(), "AccessibleControlShape::disposing: don't have a mode broadcaster anymore!" );
649         if ( xControlModes.is() )
650             xControlModes->removeModeChangeListener( this );
651 
652         if ( m_xControlContextComponent.is() )
653             m_xControlContextComponent->dispose();
654         // do _not_ clear m_xControlContextProxy! This has to be done in the dtor for correct ref-count handling
655 
656         // no need to dispose the proxy/inner context anymore
657         m_bDisposeNativeContext = false;
658     }
659 
660     m_xUnoControl.clear();
661 
662     // let the base do its stuff
663     AccessibleShape::disposing();
664 }
665 
ensureControlModelAccess()666 bool AccessibleControlShape::ensureControlModelAccess()
667 {
668     if ( m_xControlModel.is() )
669         return true;
670 
671     try
672     {
673         Reference< XControlShape > xShape( mxShape, UNO_QUERY );
674         if ( xShape.is() )
675             m_xControlModel.set(xShape->getControl(), css::uno::UNO_QUERY);
676 
677         if ( m_xControlModel.is() )
678             m_xModelPropsMeta = m_xControlModel->getPropertySetInfo();
679     }
680     catch( const Exception& )
681     {
682         TOOLS_WARN_EXCEPTION( "svx", "AccessibleControlShape::ensureControlModelAccess" );
683     }
684 
685     return m_xControlModel.is();
686 }
687 
startStateMultiplexing()688 void AccessibleControlShape::startStateMultiplexing()
689 {
690     OSL_PRECOND( !m_bMultiplexingStates, "AccessibleControlShape::startStateMultiplexing: already multiplexing!" );
691 
692 #if OSL_DEBUG_LEVEL > 0
693     // we should have a control, and it should be in alive mode
694     OSL_PRECOND( isAliveMode( m_xUnoControl ),
695         "AccessibleControlShape::startStateMultiplexing: should be done in alive mode only!" );
696 #endif
697     // we should have the native context of the control
698     Reference< XAccessibleEventBroadcaster > xBroadcaster( m_aControlContext.get(), UNO_QUERY );
699     OSL_ENSURE( xBroadcaster.is(), "AccessibleControlShape::startStateMultiplexing: no AccessibleEventBroadcaster on the native context!" );
700 
701     if ( xBroadcaster.is() )
702     {
703         xBroadcaster->addAccessibleEventListener( this );
704         m_bMultiplexingStates = true;
705     }
706 }
707 
stopStateMultiplexing()708 void AccessibleControlShape::stopStateMultiplexing()
709 {
710     OSL_PRECOND( m_bMultiplexingStates, "AccessibleControlShape::stopStateMultiplexing: not multiplexing!" );
711 
712     // we should have the native context of the control
713     Reference< XAccessibleEventBroadcaster > xBroadcaster( m_aControlContext.get(), UNO_QUERY );
714     OSL_ENSURE( xBroadcaster.is(), "AccessibleControlShape::stopStateMultiplexing: no AccessibleEventBroadcaster on the native context!" );
715 
716     if ( xBroadcaster.is() )
717     {
718         xBroadcaster->removeAccessibleEventListener( this );
719         m_bMultiplexingStates = false;
720     }
721 }
722 
getControlModelStringProperty(const OUString & _rPropertyName) const723 OUString AccessibleControlShape::getControlModelStringProperty( const OUString& _rPropertyName ) const
724 {
725     OUString sReturn;
726     try
727     {
728         if ( const_cast< AccessibleControlShape* >( this )->ensureControlModelAccess() )
729         {
730             if ( !m_xModelPropsMeta.is() || m_xModelPropsMeta->hasPropertyByName( _rPropertyName ) )
731                 // ask only if a) the control does not have a PropertySetInfo object or b) it has, and the
732                 // property in question is available
733                 m_xControlModel->getPropertyValue( _rPropertyName ) >>= sReturn;
734         }
735     }
736     catch( const Exception& )
737     {
738         TOOLS_WARN_EXCEPTION( "svx", "OAccessibleControlContext::getModelStringProperty" );
739     }
740     return sReturn;
741 }
742 
adjustAccessibleRole()743 void AccessibleControlShape::adjustAccessibleRole( )
744 {
745     // if we're in design mode, we are a simple SHAPE, in alive mode, we use the role of our inner context
746     if ( !isAliveMode( m_xUnoControl ) )
747         return;
748 
749     // we're in alive mode -> determine the role of the inner context
750     Reference< XAccessibleContext > xNativeContext( m_aControlContext );
751     OSL_PRECOND( xNativeContext.is(), "AccessibleControlShape::adjustAccessibleRole: no inner context!" );
752     if ( xNativeContext.is() )
753         SetAccessibleRole( xNativeContext->getAccessibleRole( ) );
754 }
755 
756 #ifdef DBG_UTIL
757 
SetState(sal_Int16 _nState)758 bool AccessibleControlShape::SetState( sal_Int16 _nState )
759 {
760     OSL_ENSURE( !isAliveMode( m_xUnoControl ) || !isComposedState( _nState ),
761         "AccessibleControlShape::SetState: a state which should be determined by the control context is set from outside!" );
762     return AccessibleShape::SetState( _nState );
763 }
764 #endif // DBG_UTIL
765 
initializeComposedState()766 void AccessibleControlShape::initializeComposedState()
767 {
768     if ( !isAliveMode( m_xUnoControl ) )
769         // no action necessary for design mode
770         return;
771 
772     // get our own state set implementation
773     ::utl::AccessibleStateSetHelper* pComposedStates =
774         static_cast< ::utl::AccessibleStateSetHelper* >( mxStateSet.get() );
775     OSL_PRECOND( pComposedStates,
776         "AccessibleControlShape::initializeComposedState: no composed set!" );
777 
778     // we need to reset some states of the composed set, because they either do not apply
779     // for controls in alive mode, or are in the responsibility of the UNO-control, anyway
780     pComposedStates->RemoveState( AccessibleStateType::ENABLED );       // this is controlled by the UNO-control
781     pComposedStates->RemoveState( AccessibleStateType::SENSITIVE );     // this is controlled by the UNO-control
782     pComposedStates->RemoveState( AccessibleStateType::FOCUSABLE );     // this is controlled by the UNO-control
783     pComposedStates->RemoveState( AccessibleStateType::SELECTABLE );    // this does not hold for an alive UNO-control
784 #if OSL_DEBUG_LEVEL > 0
785     // now, only states which are not in the responsibility of the UNO control should be part of this state set
786     {
787         Sequence< sal_Int16 > aInitStates = pComposedStates->getStates();
788         for ( sal_Int16 state : aInitStates )
789             OSL_ENSURE( !isComposedState( state ),
790                 "AccessibleControlShape::initializeComposedState: invalid initial composed state (should be controlled by the UNO-control)!" );
791     }
792 #endif
793 
794     // get my inner context
795     Reference< XAccessibleContext > xInnerContext( m_aControlContext );
796     OSL_PRECOND( xInnerContext.is(), "AccessibleControlShape::initializeComposedState: no inner context!" );
797     if ( !xInnerContext.is() )
798         return;
799 
800     // get all states of the inner context
801     Reference< XAccessibleStateSet > xInnerStates( xInnerContext->getAccessibleStateSet() );
802     OSL_ENSURE( xInnerStates.is(), "AccessibleControlShape::initializeComposedState: no inner states!" );
803     Sequence< sal_Int16 > aInnerStates;
804     if ( xInnerStates.is() )
805         aInnerStates = xInnerStates->getStates();
806 
807     // look which one are to be propagated to the composed context
808     for ( const sal_Int16 nState : aInnerStates )
809     {
810         if ( isComposedState( nState ) && !pComposedStates->contains( nState ) )
811         {
812             pComposedStates->AddState( nState );
813         }
814     }
815 }
816 
elementInserted(const css::container::ContainerEvent & _rEvent)817 void SAL_CALL AccessibleControlShape::elementInserted( const css::container::ContainerEvent& _rEvent )
818 {
819     Reference< XContainer > xContainer( _rEvent.Source, UNO_QUERY );
820     Reference< XControl > xControl( _rEvent.Element, UNO_QUERY );
821 
822     OSL_ENSURE( xContainer.is() && xControl.is(),
823         "AccessibleControlShape::elementInserted: invalid event description!" );
824 
825     if ( !xControl.is() )
826         return;
827 
828     ensureControlModelAccess();
829 
830     Reference< XInterface > xNewNormalized( xControl->getModel(), UNO_QUERY );
831     Reference< XInterface > xMyModelNormalized( m_xControlModel, UNO_QUERY );
832     if ( !(xNewNormalized && xMyModelNormalized) )
833         return;
834 
835     // now finally the control for the model we're responsible for has been inserted into the container
836     Reference< XInterface > xKeepAlive( *this );
837 
838     // first, we're not interested in any more container events
839     if ( xContainer.is() )
840     {
841         xContainer->removeContainerListener( this );
842         m_bWaitingForControl = false;
843     }
844 
845     // second, we need to replace ourself with a new version, which now can be based on the
846     // control
847     OSL_VERIFY( mpParent->ReplaceChild ( this, mxShape, 0, maShapeTreeInfo ) );
848 }
849 
elementRemoved(const css::container::ContainerEvent &)850 void SAL_CALL AccessibleControlShape::elementRemoved( const css::container::ContainerEvent& )
851 {
852     // not interested in
853 }
854 
elementReplaced(const css::container::ContainerEvent &)855 void SAL_CALL AccessibleControlShape::elementReplaced( const css::container::ContainerEvent& )
856 {
857     // not interested in
858 }
859 
GetLabeledByControlShape()860 AccessibleControlShape* AccessibleControlShape::GetLabeledByControlShape( )
861 {
862     if(m_xControlModel.is())
863     {
864         const OUString& rAccLabelControlProperty = lcl_getLabelControlPropertyName();
865         Any sCtlLabelBy;
866         // get the "label by" property value of the control
867         if (::comphelper::hasProperty(rAccLabelControlProperty, m_xControlModel))
868         {
869             sCtlLabelBy = m_xControlModel->getPropertyValue(rAccLabelControlProperty);
870             if( sCtlLabelBy.hasValue() )
871             {
872                 Reference< XPropertySet >  xAsSet (sCtlLabelBy, UNO_QUERY);
873                 AccessibleControlShape* pCtlAccShape = mpParent->GetAccControlShapeFromModel(xAsSet.get());
874                 return pCtlAccShape;
875             }
876         }
877     }
878     return nullptr;
879 }
880 
881 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
882