LibreOffice Module svx (master)  1
AccessibleControlShape.cxx
Go to the documentation of this file.
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 
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>
35 #include <comphelper/property.hxx>
39 #include <svx/svdouno.hxx>
40 #include <svx/unoapi.hxx>
41 #include <svx/ShapeTypeHandler.hxx>
42 #include <svx/SvxShapeTypes.hxx>
44 #include <svx/svdview.hxx>
45 #include <svx/svdpagv.hxx>
46 #include <svx/strings.hrc>
47 #include <vcl/svapp.hxx>
48 #include <sal/log.hxx>
49 #include <tools/debug.hxx>
50 #include <tools/diagnose_ex.h>
51 
52 using namespace ::accessibility;
53 using namespace ::com::sun::star::accessibility;
54 using namespace ::com::sun::star::uno;
55 using namespace ::com::sun::star::awt;
56 using namespace ::com::sun::star::beans;
57 using namespace ::com::sun::star::util;
58 using namespace ::com::sun::star::lang;
59 using namespace ::com::sun::star::reflection;
60 using namespace ::com::sun::star::drawing;
61 using namespace ::com::sun::star::container;
62 
63 namespace
64 {
65  OUString lcl_getNamePropertyName( )
66  {
67  return "Name";
68  }
69  OUString lcl_getDescPropertyName( )
70  {
71  return "HelpText";
72  }
73  OUString lcl_getLabelPropertyName( )
74  {
75  return "Label";
76  }
77  OUString lcl_getLabelControlPropertyName( )
78  {
79  return "LabelControl";
80  }
81  // return the property which should be used as AccessibleName
82  OUString lcl_getPreferredAccNameProperty( const Reference< XPropertySetInfo >& _rxPSI )
83  {
84  if ( _rxPSI.is() && _rxPSI->hasPropertyByName( lcl_getLabelPropertyName() ) )
85  return lcl_getLabelPropertyName();
86  else
87  return lcl_getNamePropertyName();
88  }
89 
90  // determines whether or not a state which belongs to the inner context needs to be forwarded to the "composed"
91  // context
92  bool isComposedState( const sal_Int16 _nState )
93  {
94  return ( ( AccessibleStateType::INVALID != _nState )
95  && ( AccessibleStateType::DEFUNC != _nState )
96  && ( AccessibleStateType::ICONIFIED != _nState )
97  && ( AccessibleStateType::RESIZABLE != _nState )
98  && ( AccessibleStateType::SELECTABLE != _nState )
99  && ( AccessibleStateType::SHOWING != _nState )
100  && ( AccessibleStateType::MANAGES_DESCENDANTS != _nState )
101  && ( AccessibleStateType::VISIBLE != _nState )
102  );
103  }
104 
106  bool isAliveMode( const Reference< XControl >& _rxControl )
107  {
108  OSL_PRECOND( _rxControl.is(), "AccessibleControlShape::isAliveMode: invalid control" );
109  return _rxControl.is() && !_rxControl->isDesignMode();
110  }
111 }
112 
113 AccessibleControlShape::AccessibleControlShape (
114  const AccessibleShapeInfo& rShapeInfo,
115  const AccessibleShapeTreeInfo& rShapeTreeInfo)
116  : AccessibleShape (rShapeInfo, rShapeTreeInfo)
117  , m_bListeningForName( false )
118  , m_bListeningForDesc( false )
119  , m_bMultiplexingStates( false )
120  , m_bDisposeNativeContext( false )
121  , m_bWaitingForControl( false )
122 {
123  m_pChildManager = new comphelper::OWrappedAccessibleChildrenManager( comphelper::getProcessComponentContext() );
124 
125  osl_atomic_increment( &m_refCount );
126  {
127  m_pChildManager->setOwningAccessible( this );
128  }
129  osl_atomic_decrement( &m_refCount );
130 }
131 
133 {
134  m_pChildManager.clear();
135 
136  if ( m_xControlContextProxy.is() )
137  m_xControlContextProxy->setDelegator( nullptr );
138  m_xControlContextProxy.clear();
141  // this should remove the _only_ three "real" reference (means not delegated to
142  // ourself) to this proxy, and thus delete it
143 }
144 
145 namespace {
146  Reference< XContainer > lcl_getControlContainer( const OutputDevice* _pWin, const SdrView* _pView )
147  {
148  Reference< XContainer > xReturn;
149  DBG_ASSERT( _pView, "lcl_getControlContainer: invalid view!" );
150  if ( _pView && _pView->GetSdrPageView())
151  {
152  xReturn.set(_pView->GetSdrPageView()->GetControlContainer( *_pWin ), css::uno::UNO_QUERY);
153  }
154  return xReturn;
155  }
156 }
157 
159 {
161 
162  OSL_ENSURE( !m_xControlContextProxy.is(), "AccessibleControlShape::Init: already initialized!" );
163  try
164  {
165  // What we need to do here is merge the functionality of the AccessibleContext of our UNO control
166  // with our own AccessibleContext-related functionality.
167 
168  // The problem is that we do not know the interfaces our "inner" context supports - this may be any
169  // XAccessibleXXX interface (or even any other) which makes sense for it.
170 
171  // In theory, we could implement all possible interfaces ourself, and re-route all functionality to
172  // the inner context (except those we implement ourself, like XAccessibleComponent). But this is in no
173  // way future-proof - as soon as an inner context appears which implements an additional interface,
174  // we would need to adjust our implementation to support this new interface, too. Bad idea.
175 
176  // The usual solution for such a problem is aggregation. Aggregation means using UNO's own mechanism
177  // for merging an inner with an outer component, and get a component which behaves as it is exactly one.
178  // This is what XAggregation is for. Unfortunately, aggregation requires _exact_ control over the ref count
179  // of the inner object, which we do not have at all.
180  // Bad, too.
181 
182  // But there is a solution: com.sun.star.reflection.ProxyFactory. This service is able to create a proxy
183  // for any component, which supports _exactly_ the same interfaces as the component. In addition, it can
184  // be aggregated, as by definition the proxy's ref count is exactly 1 when returned from the factory.
185  // Sounds better. Though this yields the problem of slightly degraded performance, it's the only solution
186  // I'm aware of at the moment...
187 
188  // get the control which belongs to our model (relative to our view)
189  const OutputDevice* pViewWindow = maShapeTreeInfo.GetDevice();
190  SdrUnoObj* pUnoObjectImpl = dynamic_cast<SdrUnoObj*>( GetSdrObjectFromXShape(mxShape) );
192  OSL_ENSURE( pView && pViewWindow && pUnoObjectImpl, "AccessibleControlShape::Init: no view, or no view window, no SdrUnoObj!" );
193 
194  if ( pView && pViewWindow && pUnoObjectImpl )
195  {
196  // get the context of the control - it will be our "inner" context
197  m_xUnoControl = pUnoObjectImpl->GetUnoControl( *pView, *pViewWindow );
198 
199  if ( !m_xUnoControl.is() )
200  {
201  // the control has not yet been created. Though speaking strictly, it is a bug that
202  // our instance here is created without an existing control (because an AccessibleControlShape
203  // is a representation of a view object, and can only live if the view it should represent
204  // is complete, which implies a living control), it's by far the easiest and most riskless way
205  // to fix this here in this class.
206  // Okay, we will add as listener to the control container where we expect our control to appear.
207  OSL_ENSURE( !m_bWaitingForControl, "AccessibleControlShape::Init: already waiting for the control!" );
208 
209  Reference< XContainer > xControlContainer = lcl_getControlContainer( pViewWindow, maShapeTreeInfo.GetSdrView() );
210  OSL_ENSURE( xControlContainer.is(), "AccessibleControlShape::Init: unable to find my ControlContainer!" );
211  if ( xControlContainer.is() )
212  {
213  xControlContainer->addContainerListener( this );
214  m_bWaitingForControl = true;
215  }
216  }
217  else
218  {
219  Reference< XModeChangeBroadcaster > xControlModes( m_xUnoControl, UNO_QUERY );
220  Reference< XAccessible > xControlAccessible( xControlModes, UNO_QUERY );
221  Reference< XAccessibleContext > xNativeControlContext;
222  if ( xControlAccessible.is() )
223  xNativeControlContext = xControlAccessible->getAccessibleContext();
224  OSL_ENSURE( xNativeControlContext.is(), "AccessibleControlShape::Init: no AccessibleContext for the control!" );
225  m_aControlContext = WeakReference< XAccessibleContext >( xNativeControlContext );
226 
227  // add as listener to the context - we want to multiplex some states
228  if ( isAliveMode( m_xUnoControl ) && xNativeControlContext.is() )
229  { // (but only in alive mode)
231  }
232 
233  // now that we have all information about our control, do some adjustments
236 
237  // some initialization for our child manager, which is used in alive mode only
238  if ( isAliveMode( m_xUnoControl ) )
239  {
240  Reference< XAccessibleStateSet > xStates( getAccessibleStateSet( ) );
241  OSL_ENSURE( xStates.is(), "AccessibleControlShape::AccessibleControlShape: no inner state set!" );
242  m_pChildManager->setTransientChildren( !xStates.is() || xStates->contains( AccessibleStateType::MANAGES_DESCENDANTS ) );
243  }
244 
245  // finally, aggregate a proxy for the control context
246  // first a factory for the proxy
247  Reference< XProxyFactory > xFactory = ProxyFactory::create( comphelper::getProcessComponentContext() );
248  // then the proxy itself
249  if ( xNativeControlContext.is() )
250  {
251  m_xControlContextProxy = xFactory->createProxy( xNativeControlContext );
252  m_xControlContextTypeAccess.set( xNativeControlContext, UNO_QUERY_THROW );
253  m_xControlContextComponent.set( xNativeControlContext, UNO_QUERY_THROW );
254 
255  // aggregate the proxy
256  osl_atomic_increment( &m_refCount );
257  if ( m_xControlContextProxy.is() )
258  {
259  // At this point in time, the proxy has a ref count of exactly one - in m_xControlContextProxy.
260  // Remember to _not_ reset this member unless the delegator of the proxy has been reset, too!
261  m_xControlContextProxy->setDelegator( *this );
262  }
263  osl_atomic_decrement( &m_refCount );
264 
266 
267  // Finally, we need to add ourself as mode listener to the control. In case the mode switches,
268  // we need to dispose ourself.
269  xControlModes->addModeChangeListener( this );
270  }
271  }
272  }
273  }
274  catch( const Exception& )
275  {
276  OSL_FAIL( "AccessibleControlShape::Init: could not \"aggregate\" the controls XAccessibleContext!" );
277  }
278 }
279 
281 {
282  if ( !m_xUnoControl.is() || !isAliveMode( m_xUnoControl ) )
283  {
284  // in design mode, we simply forward the request to the base class
286  }
287  else
288  {
289  Reference< XWindow > xWindow( m_xUnoControl, UNO_QUERY );
290  OSL_ENSURE( xWindow.is(), "AccessibleControlShape::grabFocus: invalid control!" );
291  if ( xWindow.is() )
292  xWindow->setFocus();
293  }
294 }
295 
297 {
298  return "com.sun.star.comp.accessibility.AccessibleControlShape";
299 }
300 
302 {
303  OUString sName;
304 
306  switch (nShapeType)
307  {
308  case DRAWING_CONTROL:
309  sName = "ControlShape";
310  break;
311  default:
312  sName = "UnknownAccessibleControlShape";
313  if (mxShape.is())
314  sName += ": " + mxShape->getShapeType();
315  }
316 
317  return sName;
318 }
319 
320 OUString
322 {
325  switch (nShapeType)
326  {
327  case DRAWING_CONTROL:
328  {
329  // check if we can obtain the "Desc" property from the model
330  OUString sDesc( getControlModelStringProperty( lcl_getDescPropertyName() ) );
331  if ( sDesc.isEmpty() )
332  { // no -> use the default
333  aDG.Initialize (STR_ObjNameSingulUno);
334  aDG.AddProperty ("ControlBackground", DescriptionGenerator::PropertyType::Color);
336  }
337  // ensure that we are listening to the Name property
338  m_bListeningForDesc = ensureListeningState( m_bListeningForDesc, true, lcl_getDescPropertyName() );
339  }
340  break;
341 
342  default:
343  aDG.Initialize ("Unknown accessible control shape");
344  if (mxShape.is())
345  {
346  aDG.AppendString ("service name=");
347  aDG.AppendString (mxShape->getShapeType());
348  }
349  }
350 
351  return aDG();
352 }
353 
355 IMPLEMENT_GET_IMPLEMENTATION_ID( AccessibleControlShape )
356 
357 void SAL_CALL AccessibleControlShape::propertyChange( const PropertyChangeEvent& _rEvent )
358 {
359  ::osl::MutexGuard aGuard( maMutex );
360 
361  // check if it is the name or the description
362  if ( _rEvent.PropertyName == lcl_getNamePropertyName()
363  || _rEvent.PropertyName == lcl_getLabelPropertyName() )
364  {
365  SetAccessibleName(
366  CreateAccessibleName(),
368  }
369  else if ( _rEvent.PropertyName == lcl_getDescPropertyName() )
370  {
371  SetAccessibleDescription(
372  CreateAccessibleDescription(),
374  }
375 #if OSL_DEBUG_LEVEL > 0
376  else
377  {
378  OSL_FAIL( "AccessibleControlShape::propertyChange: where did this come from?" );
379  }
380 #endif
381 }
382 
383 Any SAL_CALL AccessibleControlShape::queryInterface( const Type& _rType )
384 {
385  Any aReturn = AccessibleShape::queryInterface( _rType );
386  if ( !aReturn.hasValue() )
387  {
389  if ( !aReturn.hasValue() && m_xControlContextProxy.is() )
390  aReturn = m_xControlContextProxy->queryAggregation( _rType );
391  }
392  return aReturn;
393 }
394 
395 Sequence< Type > SAL_CALL AccessibleControlShape::getTypes()
396 {
397  Sequence< Type > aShapeTypes = AccessibleShape::getTypes();
398  Sequence< Type > aOwnTypes = AccessibleControlShape_Base::getTypes();
399 
400  Sequence< Type > aAggregateTypes;
401  if ( m_xControlContextTypeAccess.is() )
402  aAggregateTypes = m_xControlContextTypeAccess->getTypes();
403 
404  // remove duplicates
405  return comphelper::combineSequences(comphelper::concatSequences( aShapeTypes, aOwnTypes), aAggregateTypes );
406 }
407 
408 void SAL_CALL AccessibleControlShape::notifyEvent( const AccessibleEventObject& _rEvent )
409 {
410  if ( AccessibleEventId::STATE_CHANGED == _rEvent.EventId )
411  {
412  // multiplex this change
413  sal_Int16 nLostState( 0 ), nGainedState( 0 );
414  _rEvent.OldValue >>= nLostState;
415  _rEvent.NewValue >>= nGainedState;
416 
417  // don't multiplex states which the inner context is not responsible for
418  if ( isComposedState( nLostState ) )
419  AccessibleShape::ResetState( nLostState );
420 
421  if ( isComposedState( nGainedState ) )
422  AccessibleShape::SetState( nGainedState );
423  }
424  else
425  {
426  AccessibleEventObject aTranslatedEvent( _rEvent );
427 
428  {
429  ::osl::MutexGuard aGuard( maMutex );
430 
431  // let the child manager translate the event
432  aTranslatedEvent.Source = *this;
433  m_pChildManager->translateAccessibleEvent( _rEvent, aTranslatedEvent );
434 
435  // see if any of these notifications affect our child manager
436  m_pChildManager->handleChildNotification( _rEvent );
437  }
438 
439  FireEvent( aTranslatedEvent );
440  }
441 }
442 
443 void SAL_CALL AccessibleControlShape::modeChanged(const ModeChangeEvent& rSource)
444 {
445  // did it come from our inner context (the real one, not it's proxy!)?
446  SAL_INFO("sw.uno", "AccessibleControlShape::modeChanged");
447  Reference<XControl> xSource(rSource.Source, UNO_QUERY); // for faster compare
448  if(xSource.get() != m_xUnoControl.get())
449  {
450  SAL_WARN("sw.uno", "AccessibleControlShape::modeChanged: where did this come from?");
451  return;
452  }
453  SolarMutexGuard g;
454  // If our "pseudo-aggregated" inner context does not live anymore,
455  // we don't want to live, too. This is accomplished by asking our
456  // parent to replace this object with a new one. Disposing this
457  // object and sending notifications about the replacement are in
458  // the responsibility of our parent.
459  const bool bReplaced = mpParent->ReplaceChild(this, mxShape, 0, maShapeTreeInfo);
460  SAL_WARN_IF(!bReplaced, "sw.uno", "AccessibleControlShape::modeChanged: replacing ourselves away did fail");
461 }
462 
463 void SAL_CALL AccessibleControlShape::disposing (const EventObject& _rSource)
464 {
465  AccessibleShape::disposing( _rSource );
466 }
467 
469  const bool _bCurrentlyListening, const bool _bNeedNewListening,
470  const OUString& _rPropertyName )
471 {
472  if ( ( _bCurrentlyListening == _bNeedNewListening ) || !ensureControlModelAccess() )
473  // nothing to do
474  return _bCurrentlyListening;
475 
476  try
477  {
478  if ( !m_xModelPropsMeta.is() || m_xModelPropsMeta->hasPropertyByName( _rPropertyName ) )
479  {
480  // add or revoke as listener
481  if ( _bNeedNewListening )
482  m_xControlModel->addPropertyChangeListener( _rPropertyName, static_cast< XPropertyChangeListener* >( this ) );
483  else
484  m_xControlModel->removePropertyChangeListener( _rPropertyName, static_cast< XPropertyChangeListener* >( this ) );
485  }
486  else
487  OSL_FAIL( "AccessibleControlShape::ensureListeningState: this property does not exist at this model!" );
488  }
489  catch( const Exception& )
490  {
491  OSL_FAIL( "AccessibleControlShape::ensureListeningState: could not change the listening state!" );
492  }
493 
494  return _bNeedNewListening;
495 }
496 
498 {
499  if ( !m_xUnoControl.is() )
500  return 0;
501  else if ( !isAliveMode( m_xUnoControl ) )
502  // no special action required when in design mode
504  else
505  {
506  // in alive mode, we have the full control over our children - they are determined by the children
507  // of the context of our UNO control
508  Reference< XAccessibleContext > xControlContext( m_aControlContext );
509  OSL_ENSURE( xControlContext.is(), "AccessibleControlShape::getAccessibleChildCount: control context already dead! How this!" );
510  return xControlContext.is() ? xControlContext->getAccessibleChildCount() : 0;
511  }
512 }
513 
514 Reference< XAccessible > SAL_CALL AccessibleControlShape::getAccessibleChild( sal_Int32 i )
515 {
516  Reference< XAccessible > xChild;
517  if ( !m_xUnoControl.is() )
518  {
519  throw IndexOutOfBoundsException();
520  }
521  if ( !isAliveMode( m_xUnoControl ) )
522  {
523  // no special action required when in design mode - let the base class handle this
525  }
526  else
527  {
528  // in alive mode, we have the full control over our children - they are determined by the children
529  // of the context of our UNO control
530 
531  Reference< XAccessibleContext > xControlContext( m_aControlContext );
532  OSL_ENSURE( xControlContext.is(), "AccessibleControlShape::getAccessibleChildCount: control context already dead! How this!" );
533  if ( xControlContext.is() )
534  {
535  Reference< XAccessible > xInnerChild( xControlContext->getAccessibleChild( i ) );
536  OSL_ENSURE( xInnerChild.is(), "AccessibleControlShape::getAccessibleChild: control context returned nonsense!" );
537  if ( xInnerChild.is() )
538  {
539  // we need to wrap this inner child into an own implementation
540  xChild = m_pChildManager->getAccessibleWrapperFor( xInnerChild );
541  }
542  }
543  }
544 
545 #if OSL_DEBUG_LEVEL > 0
546  sal_Int32 nChildIndex = -1;
547  Reference< XAccessibleContext > xContext;
548  if ( xChild.is() )
549  xContext = xChild->getAccessibleContext( );
550  if ( xContext.is() )
551  nChildIndex = xContext->getAccessibleIndexInParent( );
552  SAL_WARN_IF( nChildIndex != i, "svx", "AccessibleControlShape::getAccessibleChild: index mismatch,"
553  " nChildIndex=" << nChildIndex << " vs i=" << i );
554 #endif
555  return xChild;
556 }
557 
558 Reference< XAccessibleRelationSet > SAL_CALL AccessibleControlShape::getAccessibleRelationSet( )
559 {
563  if(pCtlAccShape)
564  {
565  Reference < XAccessible > xAcc (pCtlAccShape->getAccessibleContext(), UNO_QUERY);
566 
567  css::uno::Sequence< css::uno::Reference< css::uno::XInterface > > aSequence { xAcc };
568  if( getAccessibleRole() == AccessibleRole::RADIO_BUTTON )
569  {
570  pRelationSetHelper->AddRelation( AccessibleRelation( AccessibleRelationType::MEMBER_OF, aSequence ) );
571  }
572  else
573  {
574  pRelationSetHelper->AddRelation( AccessibleRelation( AccessibleRelationType::LABELED_BY, aSequence ) );
575  }
576  }
577  Reference< XAccessibleRelationSet > xSet = pRelationSetHelper;
578  return xSet;
579 }
580 
582 {
584 
585  OUString sName;
586  sal_Int16 aAccessibleRole = getAccessibleRole();
587  if ( aAccessibleRole != AccessibleRole::SHAPE
588  && aAccessibleRole != AccessibleRole::RADIO_BUTTON )
589  {
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
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 
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 )
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.GetDevice(), 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
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
664 }
665 
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 
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 
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 
723 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 
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 
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 
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_Int32 i=0; i<aInitStates.getLength(); ++i )
789  OSL_ENSURE( !isComposedState( aInitStates.getConstArray()[i] ),
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  {
799  // get all states of the inner context
800  Reference< XAccessibleStateSet > xInnerStates( xInnerContext->getAccessibleStateSet() );
801  OSL_ENSURE( xInnerStates.is(), "AccessibleControlShape::initializeComposedState: no inner states!" );
802  Sequence< sal_Int16 > aInnerStates;
803  if ( xInnerStates.is() )
804  aInnerStates = xInnerStates->getStates();
805 
806  // look which one are to be propagated to the composed context
807  for ( const sal_Int16 nState : aInnerStates )
808  {
809  if ( isComposedState( nState ) && !pComposedStates->contains( nState ) )
810  {
811  pComposedStates->AddState( nState );
812  }
813  }
814  }
815 }
816 
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 
829 
830  Reference< XInterface > xNewNormalized( xControl->getModel(), UNO_QUERY );
831  Reference< XInterface > xMyModelNormalized( m_xControlModel, UNO_QUERY );
832  if ( xNewNormalized.get() && xMyModelNormalized.get() )
833  {
834  // now finally the control for the model we're responsible for has been inserted into the container
835  Reference< XInterface > xKeepAlive( *this );
836 
837  // first, we're not interested in any more container events
838  if ( xContainer.is() )
839  {
840  xContainer->removeContainerListener( this );
841  m_bWaitingForControl = false;
842  }
843 
844  // second, we need to replace ourself with a new version, which now can be based on the
845  // control
846  OSL_VERIFY( mpParent->ReplaceChild ( this, mxShape, 0, maShapeTreeInfo ) );
847  }
848 }
849 
850 void SAL_CALL AccessibleControlShape::elementRemoved( const css::container::ContainerEvent& )
851 {
852  // not interested in
853 }
854 
855 void SAL_CALL AccessibleControlShape::elementReplaced( const css::container::ContainerEvent& )
856 {
857  // not interested in
858 }
859 
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: */
Type
css::uno::WeakReference< css::accessibility::XAccessibleContext > m_aControlContext
AccessibleControlShape * GetLabeledByControlShape()
virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleChild(sal_Int32 i) override
Return the specified child.
css::uno::Reference< css::awt::XControl > m_xUnoControl
virtual void SAL_CALL disposing() override
This method is called from the component helper base class while disposing.
osl::Mutex maMutex
virtual void SAL_CALL modeChanged(const css::util::ModeChangeEvent &_rSource) override
void adjustAccessibleRole()
adjusts our AccessibleRole, depending on the control type we're working for
OUString getControlModelStringProperty(const OUString &_rPropertyName) const
(safely) reads the given property from the model of the UNO control
css::uno::Reference< css::accessibility::XAccessibleStateSet > mxStateSet
SdrView * GetSdrView() const
Return the current SdrView.
void stopStateMultiplexing()
stops multiplexing the state changes of our aggregate context
css::uno::Reference< css::uno::XAggregation > m_xControlContextProxy
ULONG m_refCount
void SetAccessibleRole(sal_Int16 _nRole)
void startStateMultiplexing()
starts multiplexing the state changes of our aggregate context
virtual sal_Int32 SAL_CALL getAccessibleChildCount() override
Return the number of currently visible children.
virtual void SAL_CALL elementRemoved(const css::container::ContainerEvent &Event) override
virtual void SAL_CALL elementReplaced(const css::container::ContainerEvent &Event) override
IAccessibleParent * mpParent
This object can be used to modify the child list of our parent.
mutable::osl::Mutex maMutex
virtual css::uno::Any SAL_CALL queryInterface(css::uno::Type const &rType) SAL_OVERRIDE
virtual bool SetState(sal_Int16 _nState) override
Set the specified state.
rtl::Reference<::comphelper::OWrappedAccessibleChildrenManager > m_pChildManager
void AddRelation(const css::accessibility::AccessibleRelation &rRelation)
void initializeComposedState()
initializes composed states of the context
virtual sal_Int16 SAL_CALL getAccessibleRole() override
Return this object's role.
OUString CreateAccessibleDescription()
Create a description string that contains the accessible description.
virtual OUString SAL_CALL getImplementationName() override
Returns an identifier for the implementation of this object.
virtual void SAL_CALL notifyEvent(const css::accessibility::AccessibleEventObject &aEvent) override
css::uno::Reference< css::beans::XPropertySetInfo > m_xModelPropsMeta
virtual void Init() override
Initialize a new shape.
css::uno::Sequence< T > combineSequences(css::uno::Sequence< T > const &left, css::uno::Sequence< T > const &right)
bool ensureControlModelAccess()
ensure that our control model exists(will be retrieved upon need only)
#define TOOLS_WARN_EXCEPTION(area, stream)
#define DBG_ASSERT(sCon, aError)
css::uno::Reference< css::drawing::XShape > mxShape
Reference to the actual shape.
int i
virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() SAL_OVERRIDE
OUString sName
int ShapeTypeId
Use an integer to represent shape type ids.
void Initialize(const OUString &sPrefix)
Initialize the description with the given prefix followed by the shape style in parentheses and a col...
virtual sal_Bool SAL_CALL contains(sal_Int16 aState) override
static ShapeTypeHandler & Instance()
This function returns a reference to the only instance of this class.
virtual css::uno::Reference< css::accessibility::XAccessibleContext > SAL_CALL getAccessibleContext() override
void FireEvent(const css::accessibility::AccessibleEventObject &aEvent)
virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override
virtual css::uno::Any SAL_CALL queryInterface(const css::uno::Type &rType) override
virtual void SAL_CALL disposing() override
This method is called from the component helper base class while disposing.
virtual css::uno::Reference< css::accessibility::XAccessibleRelationSet > SAL_CALL getAccessibleRelationSet() override
css::uno::Sequence< T > concatSequences(const css::uno::Sequence< T > &rS1, const Ss &...rSn)
This class creates description strings for shapes.
AccessibleShapeTreeInfo maShapeTreeInfo
Bundle of information passed to all shapes in a document tree.
#define IMPLEMENT_FORWARD_REFCOUNT(classname, refcountbase)
This base class provides a base implementation for all shapes.
virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleChild(sal_Int32 nIndex) override
Return the specified child.
virtual void SAL_CALL grabFocus() override
forward the focus to the contained control(in alive mode)
virtual css::uno::Reference< css::accessibility::XAccessibleStateSet > SAL_CALL getAccessibleStateSet() override
Return the set of current states.
virtual OUString CreateAccessibleName() override
Create a unique name string that contains the accessible name.
#define SAL_WARN_IF(condition, area, stream)
virtual OUString CreateAccessibleName() override
Create a unique name string that contains the accessible name.
void AppendString(const OUString &sString)
Append the given string as is to the current description.
virtual void SAL_CALL elementInserted(const css::container::ContainerEvent &Event) override
ShapeTypeId GetTypeId(const OUString &aServiceName) const
Determines the type id of a shape with the given service name.
#define SAL_INFO(area, stream)
css::uno::Reference< css::lang::XComponent > m_xControlContextComponent
virtual bool ReplaceChild(AccessibleShape *pCurrentChild, const css::uno::Reference< css::drawing::XShape > &_rxShape, const long _nIndex, const AccessibleShapeTreeInfo &_rShapeTreeInfo)=0
A call to this method requests the implementor to replace one child with another and send the appropr...
virtual OUString CreateAccessibleBaseName() override
Create a name string that contains the accessible name.
virtual sal_Int32 SAL_CALL getAccessibleChildCount() override
Return the number of currently visible children.
void AddProperty(const OUString &sPropertyName, PropertyType aType)
Add the given property name and its associated value to the description string.
virtual void Init()
Initialize a new shape.
Reference< XComponentContext > getProcessComponentContext()
virtual void SAL_CALL grabFocus() override
css::uno::Reference< css::awt::XControl > GetUnoControl(const SdrView &_rView, const OutputDevice &_rOut) const
Definition: svdouno.cxx:458
virtual bool ResetState(sal_Int16 aState) override
Reset the specified state.
SdrPageView * GetSdrPageView() const
Definition: svdpntv.hxx:310
css::uno::Reference< css::awt::XControlContainer > GetControlContainer(const OutputDevice &_rDevice) const
Looks up the control container belonging to given output device.
Definition: svdpagv.cxx:140
bool hasProperty(const OUString &_rName, const Reference< XPropertySet > &_rxSet)
virtual bool SetState(sal_Int16 aState) override
Set the specified state.
css::uno::Reference< css::beans::XPropertySet > m_xControlModel
This class is a container for the information specific for a single shape that is passed to the const...
#define SAL_WARN(area, stream)
Reference< XSingleServiceFactory > xFactory
SdrObject * GetSdrObjectFromXShape(const uno::Reference< drawing::XShape > &xShape)
returns the SdrObject from the given StarOffice API wrapper
Definition: unoshape.cxx:3933
bool ensureListeningState(const bool _bCurrentlyListening, const bool _bNeedNewListening, const OUString &_rPropertyName)
ensures that we're listening for the given property if(and only if!) necessary
virtual AccessibleControlShape * GetAccControlShapeFromModel(css::beans::XPropertySet *)
void AddState(sal_Int16 aState)
sal_Int32 nState
This class bundles all information that is passed down the tree of accessible shapes so that each sha...
virtual css::uno::Sequence< sal_Int16 > SAL_CALL getStates() override
css::uno::Reference< css::lang::XTypeProvider > m_xControlContextTypeAccess
void RemoveState(sal_Int16 aState)