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