diff options
Diffstat (limited to 'svx/source/accessibility')
24 files changed, 12888 insertions, 0 deletions
diff --git a/svx/source/accessibility/AccessibleControlShape.cxx b/svx/source/accessibility/AccessibleControlShape.cxx new file mode 100644 index 000000000000..610f219c21cf --- /dev/null +++ b/svx/source/accessibility/AccessibleControlShape.cxx @@ -0,0 +1,920 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svx.hxx" +#include <svx/AccessibleControlShape.hxx> +#include <svx/AccessibleShapeInfo.hxx> +#include "DescriptionGenerator.hxx" +#include <com/sun/star/drawing/XControlShape.hpp> +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/form/FormComponentType.hpp> +#include <com/sun/star/reflection/XProxyFactory.hpp> +#include <com/sun/star/container/XContainer.hpp> +#include <comphelper/processfactory.hxx> +#include <unotools/accessiblestatesethelper.hxx> +#include <svx/svdouno.hxx> +#include "unoapi.hxx" +#include <svx/ShapeTypeHandler.hxx> +#include <svx/SvxShapeTypes.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <comphelper/accessiblewrapper.hxx> +#include <svx/svdview.hxx> +#include <svx/svdpagv.hxx> +#include "svdstr.hrc" +#include <algorithm> + +using namespace ::comphelper; +using namespace ::accessibility; +using namespace ::com::sun::star::accessibility; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::reflection; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::container; + +//-------------------------------------------------------------------- +namespace +{ + //................................................................ + const ::rtl::OUString& lcl_getNamePropertyName( ) + { + static ::rtl::OUString s_sNamePropertyName( RTL_CONSTASCII_USTRINGPARAM( "Name" ) ); + return s_sNamePropertyName; + } + //................................................................ + const ::rtl::OUString& lcl_getDescPropertyName( ) + { + static ::rtl::OUString s_sDescPropertyDesc( RTL_CONSTASCII_USTRINGPARAM( "HelpText" ) ); + return s_sDescPropertyDesc; + } + //................................................................ + const ::rtl::OUString& lcl_getLabelPropertyName( ) + { + static ::rtl::OUString s_sLabelPropertyLabel( RTL_CONSTASCII_USTRINGPARAM( "Label" ) ); + return s_sLabelPropertyLabel; + } + //................................................................ + // return the property which should be used as AccessibleName + const ::rtl::OUString& lcl_getPreferredAccNameProperty( const Reference< XPropertySetInfo >& _rxPSI ) + { + if ( _rxPSI.is() && _rxPSI->hasPropertyByName( lcl_getLabelPropertyName() ) ) + return lcl_getLabelPropertyName(); + else + return lcl_getNamePropertyName(); + } + + //................................................................ + // determines whether or not a state which belongs to the inner context needs to be forwarded to the "composed" + // context + sal_Bool isComposedState( const sal_Int16 _nState ) + { + return ( ( AccessibleStateType::INVALID != _nState ) + && ( AccessibleStateType::DEFUNC != _nState ) + && ( AccessibleStateType::ICONIFIED != _nState ) + && ( AccessibleStateType::RESIZABLE != _nState ) + && ( AccessibleStateType::SELECTABLE != _nState ) + && ( AccessibleStateType::SHOWING != _nState ) + && ( AccessibleStateType::MANAGES_DESCENDANTS != _nState ) + && ( AccessibleStateType::VISIBLE != _nState ) + ); + } + + //................................................................ + /** determines whether the given control is in alive mode + */ + inline sal_Bool isAliveMode( const Reference< XControl >& _rxControl ) + { + OSL_PRECOND( _rxControl.is(), "AccessibleControlShape::isAliveMode: invalid control" ); + return _rxControl.is() && !_rxControl->isDesignMode(); + } +} + +//============================================================================= +//= AccessibleControlShape +//============================================================================= + +//----------------------------------------------------------------------------- +AccessibleControlShape::AccessibleControlShape ( + const AccessibleShapeInfo& rShapeInfo, + const AccessibleShapeTreeInfo& rShapeTreeInfo) + : AccessibleShape (rShapeInfo, rShapeTreeInfo) + , m_bListeningForName( sal_False ) + , m_bListeningForDesc( sal_False ) + , m_bMultiplexingStates( sal_False ) + , m_bDisposeNativeContext( sal_False ) + , m_bWaitingForControl( sal_False ) +{ + m_pChildManager = new OWrappedAccessibleChildrenManager( getProcessServiceFactory() ); + m_pChildManager->acquire(); + + osl_incrementInterlockedCount( &m_refCount ); + { + m_pChildManager->setOwningAccessible( this ); + } + osl_decrementInterlockedCount( &m_refCount ); +} + +//----------------------------------------------------------------------------- +AccessibleControlShape::~AccessibleControlShape (void) +{ + m_pChildManager->release(); + m_pChildManager = NULL; + + if ( m_xControlContextProxy.is() ) + m_xControlContextProxy->setDelegator( NULL ); + m_xControlContextProxy.clear(); + m_xControlContextTypeAccess.clear(); + m_xControlContextComponent.clear(); + // this should remove the _only_ three "real" reference (means not delegated to + // ourself) to this proxy, and thus delete it +} + +//----------------------------------------------------------------------------- +SdrObject* AccessibleControlShape::getSdrObject() const +{ + return GetSdrObjectFromXShape (mxShape); +} + +namespace { + Reference< XContainer > lcl_getControlContainer( const Window* _pWin, const SdrView* _pView ) + { + Reference< XContainer > xReturn; + DBG_ASSERT( _pView, "lcl_getControlContainer: invalid view!" ); + if ( _pView && _pView->GetSdrPageView()) + { + xReturn = xReturn.query( _pView->GetSdrPageView()->GetControlContainer( *_pWin ) ); + } + return xReturn; + } +} + +//----------------------------------------------------------------------------- +void AccessibleControlShape::Init() +{ + AccessibleShape::Init(); + + OSL_ENSURE( !m_xControlContextProxy.is(), "AccessibleControlShape::Init: already initialized!" ); + try + { + // What we need to do here is merge the functionality of the AccessibleContext of our UNO control + // with our own AccessibleContext-related functionality. + // + // The problem is that we do not know the interfaces our "inner" context supports - this may be any + // XAccessibleXXX interface (or even any other) which makes sense for it. + // + // In theory, we could implement all possible interfaces ourself, and re-route all functionality to + // the inner context (except those we implement ourself, like XAccessibleComponent). But this is in no + // way future-proof - as soon as an inner context appears which implements an additional interface, + // we would need to adjust our implementation to support this new interface, too. Bad idea. + // + // The usual solution for such a problem is aggregation. Aggregation means using UNO's own meachnisms + // for merging an inner with an outer component, and get a component which behaves as it is exactly one. + // This is what XAggregation is for. Unfortunately, aggregation requires _exact_ control over the ref count + // of the inner object, which we do not have at all. + // Bad, too. + // + // But there is a solution: com.sun.star.reflection.ProxyFactory. This service is able to create a proxy + // for any component, which supports _exactly_ the same interfaces as the component. In addition, it can + // be aggregated, as by definition the proxy's ref count is exactly 1 when returned from the factory. + // Sounds better. Though this yields the problem of slightly degraded performance, it's the only solution + // I'm aware of at the moment ..... + // + // 98750 - 30.04.2002 - fs@openoffice.org + // + + // get the control which belongs to our model (relative to our view) + const Window* pViewWindow = maShapeTreeInfo.GetWindow(); + SdrUnoObj* pUnoObjectImpl = PTR_CAST( SdrUnoObj, getSdrObject() ); + SdrView* pView = maShapeTreeInfo.GetSdrView(); + OSL_ENSURE( pView && pViewWindow && pUnoObjectImpl, "AccessibleControlShape::Init: no view, or no view window, no SdrUnoObj!" ); + + if ( pView && pViewWindow && pUnoObjectImpl ) + { + // ................................................................. + // get the context of the control - it will be our "inner" context + m_xUnoControl = pUnoObjectImpl->GetUnoControl( *pView, *pViewWindow ); + + if ( !m_xUnoControl.is() ) + { + // the control has not yet been created. Though speaking strictly, it is a bug that + // our instance here is created without an existing control (because an AccessibleControlShape + // is a representation of a view object, and can only live if the view it should represent + // is complete, which implies a living control), it's by far the easiest and most riskless way + // to fix this here in this class. + // Okay, we will add as listener to the control container where we expect our control to appear. + OSL_ENSURE( !m_bWaitingForControl, "AccessibleControlShape::Init: already waiting for the control!" ); + + Reference< XContainer > xControlContainer = lcl_getControlContainer( pViewWindow, maShapeTreeInfo.GetSdrView() ); + OSL_ENSURE( xControlContainer.is(), "AccessibleControlShape::Init: unable to find my ControlContainer!" ); + if ( xControlContainer.is() ) + { + xControlContainer->addContainerListener( this ); + m_bWaitingForControl = sal_True; + } + } + else + { + Reference< XModeChangeBroadcaster > xControlModes( m_xUnoControl, UNO_QUERY ); + Reference< XAccessible > xControlAccessible( xControlModes, UNO_QUERY ); + Reference< XAccessibleContext > xNativeControlContext; + if ( xControlAccessible.is() ) + xNativeControlContext = xControlAccessible->getAccessibleContext(); + OSL_ENSURE( xNativeControlContext.is(), "AccessibleControlShape::Init: no AccessibleContext for the control!" ); + m_aControlContext = WeakReference< XAccessibleContext >( xNativeControlContext ); + + // ................................................................. + // add as listener to the context - we want to multiplex some states + if ( isAliveMode( m_xUnoControl ) && xNativeControlContext.is() ) + { // (but only in alive mode) + startStateMultiplexing( ); + } + + // now that we have all information about our control, do some adjustments + adjustAccessibleRole(); + initializeComposedState(); + + // some initialization for our child manager, which is used in alive mode only + if ( isAliveMode( m_xUnoControl ) ) + { + Reference< XAccessibleStateSet > xStates( getAccessibleStateSet( ) ); + OSL_ENSURE( xStates.is(), "AccessibleControlShape::AccessibleControlShape: no inner state set!" ); + m_pChildManager->setTransientChildren( !xStates.is() || xStates->contains( AccessibleStateType::MANAGES_DESCENDANTS ) ); + } + + // ................................................................. + // finally, aggregate a proxy for the control context + // first a factory for the proxy + Reference< XProxyFactory > xFactory; + xFactory = xFactory.query( createProcessComponent( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.reflection.ProxyFactory" ) ) ) ); + OSL_ENSURE( xFactory.is(), "AccessibleControlShape::Init: could not create a proxy factory!" ); + // then the proxy itself + if ( xFactory.is() && xNativeControlContext.is() ) + { + m_xControlContextProxy = xFactory->createProxy( xNativeControlContext ); + OSL_VERIFY( xNativeControlContext->queryInterface( ::getCppuType( &m_xControlContextTypeAccess ) ) >>= m_xControlContextTypeAccess ); + OSL_VERIFY( xNativeControlContext->queryInterface( ::getCppuType( &m_xControlContextComponent ) ) >>= m_xControlContextComponent ); + + // aggregate the proxy + osl_incrementInterlockedCount( &m_refCount ); + if ( m_xControlContextProxy.is() ) + { + // At this point in time, the proxy has a ref count of exactly one - in m_xControlContextProxy. + // Remember to _not_ reset this member unles the delegator of the proxy has been reset, too! + m_xControlContextProxy->setDelegator( *this ); + } + osl_decrementInterlockedCount( &m_refCount ); + + m_bDisposeNativeContext = sal_True; + + // Finally, we need to add ourself as mode listener to the control. In case the mode switches, + // we need to dispose ourself. + xControlModes->addModeChangeListener( this ); + } + } + } + } + catch( const Exception& ) + { + OSL_ENSURE( sal_False, "AccessibleControlShape::Init: could not \"aggregate\" the controls XAccessibleContext!" ); + } +} + +//----------------------------------------------------------------------------- +Reference< XAccessibleContext > SAL_CALL AccessibleControlShape::getAccessibleContext(void) throw (RuntimeException) +{ + return AccessibleShape::getAccessibleContext (); +} + + +//----------------------------------------------------------------------------- +void SAL_CALL AccessibleControlShape::grabFocus(void) throw (RuntimeException) +{ + if ( !m_xUnoControl.is() || !isAliveMode( m_xUnoControl ) ) + { + // in design mode, we simply forward the request to the base class + AccessibleShape::grabFocus(); + } + else + { + Reference< XWindow > xWindow( m_xUnoControl, UNO_QUERY ); + OSL_ENSURE( xWindow.is(), "AccessibleControlShape::grabFocus: invalid control!" ); + if ( xWindow.is() ) + xWindow->setFocus(); + } +} + +//----------------------------------------------------------------------------- +::rtl::OUString SAL_CALL AccessibleControlShape::getImplementationName(void) throw (RuntimeException) +{ + return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.comp.accessibility.AccessibleControlShape" ) ); +} + +//----------------------------------------------------------------------------- +::rtl::OUString AccessibleControlShape::CreateAccessibleBaseName(void) throw (RuntimeException) +{ + ::rtl::OUString sName; + + ShapeTypeId nShapeType = ShapeTypeHandler::Instance().GetTypeId (mxShape); + switch (nShapeType) + { + case DRAWING_CONTROL: + sName = ::rtl::OUString (RTL_CONSTASCII_USTRINGPARAM("ControlShape")); + break; + default: + sName = ::rtl::OUString (RTL_CONSTASCII_USTRINGPARAM("UnknownAccessibleControlShape")); + Reference< XShapeDescriptor > xDescriptor (mxShape, UNO_QUERY); + if (xDescriptor.is()) + sName += ::rtl::OUString (RTL_CONSTASCII_USTRINGPARAM(": ")) + + xDescriptor->getShapeType(); + } + + return sName; +} + + + + +//-------------------------------------------------------------------- +::rtl::OUString + AccessibleControlShape::CreateAccessibleDescription (void) + throw (RuntimeException) +{ + DescriptionGenerator aDG (mxShape); + ShapeTypeId nShapeType = ShapeTypeHandler::Instance().GetTypeId (mxShape); + switch (nShapeType) + { + case DRAWING_CONTROL: + { + // check if we can obtain the "Desc" property from the model + ::rtl::OUString sDesc( getControlModelStringProperty( lcl_getDescPropertyName() ) ); + if ( !sDesc.getLength() ) + { // no -> use the default + aDG.Initialize (STR_ObjNameSingulUno); + aDG.AddProperty (::rtl::OUString::createFromAscii ("ControlBackground"), + DescriptionGenerator::COLOR, + ::rtl::OUString()); + aDG.AddProperty (::rtl::OUString::createFromAscii ("ControlBorder"), + DescriptionGenerator::INTEGER, + ::rtl::OUString()); + } + // ensure that we are listening to the Name property + m_bListeningForDesc = ensureListeningState( m_bListeningForDesc, sal_True, lcl_getDescPropertyName() ); + } + break; + + default: + aDG.Initialize (::rtl::OUString::createFromAscii ( + "Unknown accessible control shape")); + Reference< XShapeDescriptor > xDescriptor (mxShape, UNO_QUERY); + if (xDescriptor.is()) + { + aDG.AppendString (::rtl::OUString (RTL_CONSTASCII_USTRINGPARAM("service name="))); + aDG.AppendString (xDescriptor->getShapeType()); + } + } + + return aDG(); +} + +//-------------------------------------------------------------------- +IMPLEMENT_FORWARD_REFCOUNT( AccessibleControlShape, AccessibleShape ) +IMPLEMENT_GET_IMPLEMENTATION_ID( AccessibleControlShape ) + +//-------------------------------------------------------------------- +void SAL_CALL AccessibleControlShape::propertyChange( const PropertyChangeEvent& _rEvent ) throw (RuntimeException) +{ + ::osl::MutexGuard aGuard( maMutex ); + + // check if it is the name or the description + if ( _rEvent.PropertyName.equals( lcl_getNamePropertyName() ) + || _rEvent.PropertyName.equals( lcl_getLabelPropertyName( ) ) + ) + { + SetAccessibleName( + CreateAccessibleName(), + AccessibleContextBase::AutomaticallyCreated); + } + else if ( _rEvent.PropertyName.equals( lcl_getDescPropertyName() ) ) + { + SetAccessibleDescription( + CreateAccessibleDescription(), + AccessibleContextBase::AutomaticallyCreated); + } +#if OSL_DEBUG_LEVEL > 0 + else + { + OSL_ENSURE( sal_False, "AccessibleControlShape::propertyChange: where did this come from?" ); + } +#endif +} + +//-------------------------------------------------------------------- +Any SAL_CALL AccessibleControlShape::queryInterface( const Type& _rType ) throw (RuntimeException) +{ + Any aReturn = AccessibleShape::queryInterface( _rType ); + if ( !aReturn.hasValue() ) + { + aReturn = AccessibleControlShape_Base::queryInterface( _rType ); + if ( !aReturn.hasValue() && m_xControlContextProxy.is() ) + aReturn = m_xControlContextProxy->queryAggregation( _rType ); + } + return aReturn; +} + +//-------------------------------------------------------------------- +Sequence< Type > SAL_CALL AccessibleControlShape::getTypes() throw (RuntimeException) +{ + Sequence< Type > aShapeTypes = AccessibleShape::getTypes(); + Sequence< Type > aOwnTypes = AccessibleControlShape_Base::getTypes(); + + Sequence< Type > aAggregateTypes; + if ( m_xControlContextTypeAccess.is() ) + aAggregateTypes = m_xControlContextTypeAccess->getTypes(); + + Sequence< Type > aAllTypes = concatSequences( aShapeTypes, aOwnTypes, aAggregateTypes ); + + // remove duplicates + Type* pBegin = aAllTypes.getArray(); + Type* pEnd = pBegin + aAllTypes.getLength(); + while ( pBegin != pEnd ) + { + Type aThisRoundType = *pBegin; + if ( ++pBegin != pEnd ) + { + pEnd = ::std::remove( pBegin, pEnd, aThisRoundType ); + // now all types between begin and (the old) end which equal aThisRoundType + // are moved behind the new end + } + } + aAllTypes.realloc( pEnd - aAllTypes.getArray() ); + + return aAllTypes; +} + +//-------------------------------------------------------------------- +void SAL_CALL AccessibleControlShape::notifyEvent( const AccessibleEventObject& _rEvent ) throw (RuntimeException) +{ + if ( AccessibleEventId::STATE_CHANGED == _rEvent.EventId ) + { + // multiplex this change + sal_Int16 nLostState( 0 ), nGainedState( 0 ); + _rEvent.OldValue >>= nLostState; + _rEvent.NewValue >>= nGainedState; + + // don't multiplex states which the inner context is not resposible for + if ( isComposedState( nLostState ) ) + AccessibleShape::ResetState( nLostState ); + + if ( isComposedState( nGainedState ) ) + AccessibleShape::SetState( nGainedState ); + } + else + { + AccessibleEventObject aTranslatedEvent( _rEvent ); + + { + ::osl::MutexGuard aGuard( maMutex ); + + // let the child manager translate the event + aTranslatedEvent.Source = *this; + m_pChildManager->translateAccessibleEvent( _rEvent, aTranslatedEvent ); + + // see if any of these notifications affect our child manager + m_pChildManager->handleChildNotification( _rEvent ); + } + + FireEvent( aTranslatedEvent ); + } +} + +//-------------------------------------------------------------------- +void SAL_CALL AccessibleControlShape::modeChanged( const ModeChangeEvent& _rSource ) throw (RuntimeException) +{ + // did it come from our inner context (the real one, not it's proxy!)? + OSL_TRACE ("AccessibleControlShape::modeChanged"); + Reference< XControl > xSource( _rSource.Source, UNO_QUERY ); // for faster compare + if ( xSource.get() == m_xUnoControl.get() ) + { + // If our "pseudo-aggregated" inner context does not live anymore, + // we don't want to live, too. This is accomplished by asking our + // parent to replace this object with a new one. Disposing this + // object and sending notifications about the replacement are in + // the responsibility of our parent. + OSL_VERIFY( mpParent->ReplaceChild ( this, mxShape, mnIndex, maShapeTreeInfo ) ); + } +#if OSL_DEBUG_LEVEL > 0 + else + OSL_ENSURE( sal_False, "AccessibleControlShape::modeChanged: where did this come from?" ); +#endif +} + +//-------------------------------------------------------------------- +void SAL_CALL AccessibleControlShape::disposing (const EventObject& _rSource) throw (RuntimeException) +{ + AccessibleShape::disposing( _rSource ); +} + +//-------------------------------------------------------------------- +sal_Bool AccessibleControlShape::ensureListeningState( + const sal_Bool _bCurrentlyListening, const sal_Bool _bNeedNewListening, + const ::rtl::OUString& _rPropertyName ) +{ + if ( ( _bCurrentlyListening == _bNeedNewListening ) || !ensureControlModelAccess() ) + // nothing to do + return _bCurrentlyListening; + + try + { + if ( !m_xModelPropsMeta.is() || m_xModelPropsMeta->hasPropertyByName( _rPropertyName ) ) + { + // add or revoke as listener + if ( _bNeedNewListening ) + m_xControlModel->addPropertyChangeListener( _rPropertyName, static_cast< XPropertyChangeListener* >( this ) ); + else + m_xControlModel->removePropertyChangeListener( _rPropertyName, static_cast< XPropertyChangeListener* >( this ) ); + } + else + OSL_ENSURE( sal_False, "AccessibleControlShape::ensureListeningState: this property does not exist at this model!" ); + } + catch( const Exception& e ) + { + (void)e; // make compiler happy + OSL_ENSURE( sal_False, "AccessibleControlShape::ensureListeningState: could not change the listening state!" ); + } + + return _bNeedNewListening; +} + +//-------------------------------------------------------------------- +sal_Int32 SAL_CALL AccessibleControlShape::getAccessibleChildCount( ) throw(RuntimeException) +{ + if ( !m_xUnoControl.is() ) + return 0; + else if ( !isAliveMode( m_xUnoControl ) ) + // no special action required when in design mode + return AccessibleShape::getAccessibleChildCount( ); + else + { + // in alive mode, we have the full control over our children - they are determined by the children + // of the context of our UNO control + Reference< XAccessibleContext > xControlContext( m_aControlContext ); + OSL_ENSURE( xControlContext.is(), "AccessibleControlShape::getAccessibleChildCount: control context already dead! How this!" ); + return xControlContext.is() ? xControlContext->getAccessibleChildCount() : 0; + } +} + +//-------------------------------------------------------------------- +Reference< XAccessible > SAL_CALL AccessibleControlShape::getAccessibleChild( sal_Int32 i ) throw(IndexOutOfBoundsException, RuntimeException) +{ + Reference< XAccessible > xChild; + if ( !m_xUnoControl.is() ) + { + throw IndexOutOfBoundsException(); + } + if ( !isAliveMode( m_xUnoControl ) ) + { + // no special action required when in design mode - let the base class handle this + xChild = AccessibleShape::getAccessibleChild( i ); + } + else + { + // in alive mode, we have the full control over our children - they are determined by the children + // of the context of our UNO control + + Reference< XAccessibleContext > xControlContext( m_aControlContext ); + OSL_ENSURE( xControlContext.is(), "AccessibleControlShape::getAccessibleChildCount: control context already dead! How this!" ); + if ( xControlContext.is() ) + { + Reference< XAccessible > xInnerChild( xControlContext->getAccessibleChild( i ) ); + OSL_ENSURE( xInnerChild.is(), "AccessibleControlShape::getAccessibleChild: control context returned nonsense!" ); + if ( xInnerChild.is() ) + { + // we need to wrap this inner child into an own implementation + xChild = m_pChildManager->getAccessibleWrapperFor( xInnerChild ); + } + } + } + +#if OSL_DEBUG_LEVEL > 0 + sal_Int32 nChildIndex = -1; + Reference< XAccessibleContext > xContext; + if ( xChild.is() ) + xContext = xChild->getAccessibleContext( ); + if ( xContext.is() ) + nChildIndex = xContext->getAccessibleIndexInParent( ); + OSL_ENSURE( nChildIndex == i, "AccessibleControlShape::getAccessibleChild: index mismatch!" ); +#endif + return xChild; +} + +//-------------------------------------------------------------------- +Reference< XAccessibleRelationSet > SAL_CALL AccessibleControlShape::getAccessibleRelationSet( ) throw (RuntimeException) +{ + // TODO + return AccessibleShape::getAccessibleRelationSet( ); +} + +//-------------------------------------------------------------------- +::rtl::OUString AccessibleControlShape::CreateAccessibleName (void) throw (RuntimeException) +{ + ensureControlModelAccess(); + + // check if we can obtain the "Name" resp. "Label" property from the model + const ::rtl::OUString& rAccNameProperty = lcl_getPreferredAccNameProperty( m_xModelPropsMeta ); + + ::rtl::OUString sName( getControlModelStringProperty( rAccNameProperty ) ); + if ( !sName.getLength() ) + { // no -> use the default + sName = AccessibleShape::CreateAccessibleName(); + } + + // now that somebody first asked us for our name, ensure that we are listening to name changes on the model + m_bListeningForName = ensureListeningState( m_bListeningForName, sal_True, lcl_getPreferredAccNameProperty( m_xModelPropsMeta ) ); + + return sName; +} + +//-------------------------------------------------------------------- +void SAL_CALL AccessibleControlShape::disposing (void) +{ + // ensure we're not listening + m_bListeningForName = ensureListeningState( m_bListeningForName, sal_False, lcl_getPreferredAccNameProperty( m_xModelPropsMeta ) ); + m_bListeningForDesc = ensureListeningState( m_bListeningForDesc, sal_False, lcl_getDescPropertyName() ); + + if ( m_bMultiplexingStates ) + stopStateMultiplexing( ); + + // dispose the child cache/map + m_pChildManager->dispose(); + + // release the model + m_xControlModel.clear(); + m_xModelPropsMeta.clear(); + m_aControlContext = WeakReference< XAccessibleContext >(); + + // stop listening at the control container (should never be necessary here, but who knows ....) + if ( m_bWaitingForControl ) + { + OSL_ENSURE( sal_False, "AccessibleControlShape::disposing: this should never happen!" ); + Reference< XContainer > xContainer = lcl_getControlContainer( maShapeTreeInfo.GetWindow(), maShapeTreeInfo.GetSdrView() ); + if ( xContainer.is() ) + { + m_bWaitingForControl = sal_False; + xContainer->removeContainerListener( this ); + } + } + + // forward the disposel to our inner context + if ( m_bDisposeNativeContext ) + { + // don't listen for mode changes anymore + Reference< XModeChangeBroadcaster > xControlModes( m_xUnoControl, UNO_QUERY ); + OSL_ENSURE( xControlModes.is(), "AccessibleControlShape::disposing: don't have an mode broadcaster anymore!" ); + if ( xControlModes.is() ) + xControlModes->removeModeChangeListener( this ); + + if ( m_xControlContextComponent.is() ) + m_xControlContextComponent->dispose(); + // do _not_ clear m_xControlContextProxy! This has to be done in the dtor for correct ref-count handling + + // no need to dispose the proxy/inner context anymore + m_bDisposeNativeContext = sal_False; + } + + m_xUnoControl.clear(); + + // let the base do it's stuff + AccessibleShape::disposing(); +} + +//-------------------------------------------------------------------- +sal_Bool AccessibleControlShape::ensureControlModelAccess() SAL_THROW(()) +{ + if ( m_xControlModel.is() ) + return sal_True; + + try + { + Reference< XControlShape > xShape( mxShape, UNO_QUERY ); + if ( xShape.is() ) + m_xControlModel = m_xControlModel.query( xShape->getControl() ); + + if ( m_xControlModel.is() ) + m_xModelPropsMeta = m_xControlModel->getPropertySetInfo(); + } + catch( const Exception& e ) + { + (void)e; // make compiler happy + OSL_ENSURE( sal_False, "AccessibleControlShape::ensureControlModelAccess: caught an exception!" ); + } + + return m_xControlModel.is(); +} + +//-------------------------------------------------------------------- +void AccessibleControlShape::startStateMultiplexing() +{ + OSL_PRECOND( !m_bMultiplexingStates, "AccessibleControlShape::startStateMultiplexing: already multiplexing!" ); + +#if OSL_DEBUG_LEVEL > 0 + // we should have a control, and it should be in alive mode + OSL_PRECOND( isAliveMode( m_xUnoControl ), + "AccessibleControlShape::startStateMultiplexing: should be done in alive mode only!" ); +#endif + // we should have the native context of the control + Reference< XAccessibleEventBroadcaster > xBroadcaster( m_aControlContext.get(), UNO_QUERY ); + OSL_ENSURE( xBroadcaster.is(), "AccessibleControlShape::startStateMultiplexing: no AccessibleEventBroadcaster on the native context!" ); + + if ( xBroadcaster.is() ) + { + xBroadcaster->addEventListener( this ); + m_bMultiplexingStates = sal_True; + } +} + +//-------------------------------------------------------------------- +void AccessibleControlShape::stopStateMultiplexing() +{ + OSL_PRECOND( m_bMultiplexingStates, "AccessibleControlShape::stopStateMultiplexing: not multiplexing!" ); + + // we should have the native context of the control + Reference< XAccessibleEventBroadcaster > xBroadcaster( m_aControlContext.get(), UNO_QUERY ); + OSL_ENSURE( xBroadcaster.is(), "AccessibleControlShape::stopStateMultiplexing: no AccessibleEventBroadcaster on the native context!" ); + + if ( xBroadcaster.is() ) + { + xBroadcaster->removeEventListener( this ); + m_bMultiplexingStates = sal_False; + } +} + +//-------------------------------------------------------------------- +::rtl::OUString AccessibleControlShape::getControlModelStringProperty( const ::rtl::OUString& _rPropertyName ) const SAL_THROW(()) +{ + ::rtl::OUString sReturn; + try + { + if ( const_cast< AccessibleControlShape* >( this )->ensureControlModelAccess() ) + { + if ( !m_xModelPropsMeta.is() || m_xModelPropsMeta->hasPropertyByName( _rPropertyName ) ) + // ask only if a) the control does not have a PropertySetInfo object or b) it has, and the + // property in question is available + m_xControlModel->getPropertyValue( _rPropertyName ) >>= sReturn; + } + } + catch( const Exception& ) + { + OSL_ENSURE( sal_False, "OAccessibleControlContext::getModelStringProperty: caught an exception!" ); + } + return sReturn; +} + +//-------------------------------------------------------------------- +void AccessibleControlShape::adjustAccessibleRole( ) +{ + // if we're in design mode, we are a simple SHAPE, in alive mode, we use the role of our inner context + if ( !isAliveMode( m_xUnoControl ) ) + return; + + // we're in alive mode -> determine the role of the inner context + Reference< XAccessibleContext > xNativeContext( m_aControlContext ); + OSL_PRECOND( xNativeContext.is(), "AccessibleControlShape::adjustAccessibleRole: no inner context!" ); + if ( xNativeContext.is() ) + SetAccessibleRole( xNativeContext->getAccessibleRole( ) ); +} + +#ifdef DBG_UTIL +//-------------------------------------------------------------------- +sal_Bool AccessibleControlShape::SetState( sal_Int16 _nState ) +{ + OSL_ENSURE( !isAliveMode( m_xUnoControl ) || !isComposedState( _nState ), + "AccessibleControlShape::SetState: a state which should be determined by the control context is set from outside!" ); + return AccessibleShape::SetState( _nState ); +} +#endif // DBG_UTIL + +//-------------------------------------------------------------------- +void AccessibleControlShape::initializeComposedState() +{ + if ( !isAliveMode( m_xUnoControl ) ) + // no action necessary for design mode + return; + + // get our own state set implementation + ::utl::AccessibleStateSetHelper* pComposedStates = + static_cast< ::utl::AccessibleStateSetHelper* >( mxStateSet.get() ); + OSL_PRECOND( pComposedStates, + "AccessibleControlShape::initializeComposedState: no composed set!" ); + + // we need to reset some states of the composed set, because they either do not apply + // for controls in alive mode, or are in the responsibility of the UNO-control, anyway + pComposedStates->RemoveState( AccessibleStateType::ENABLED ); // this is controlled by the UNO-control + pComposedStates->RemoveState( AccessibleStateType::SENSITIVE ); // this is controlled by the UNO-control + pComposedStates->RemoveState( AccessibleStateType::FOCUSABLE ); // this is controlled by the UNO-control + pComposedStates->RemoveState( AccessibleStateType::SELECTABLE ); // this does not hold for an alive UNO-control +#if OSL_DEBUG_LEVEL > 0 + // now, only states which are not in the responsibility of the UNO control should be part of this state set + { + Sequence< sal_Int16 > aInitStates = pComposedStates->getStates(); + for ( sal_Int32 i=0; i<aInitStates.getLength(); ++i ) + OSL_ENSURE( !isComposedState( aInitStates.getConstArray()[i] ), + "AccessibleControlShape::initializeComposedState: invalid initial composed state (should be controlled by the UNO-control)!" ); + } +#endif + + // get my inner context + Reference< XAccessibleContext > xInnerContext( m_aControlContext ); + OSL_PRECOND( xInnerContext.is(), "AccessibleControlShape::initializeComposedState: no inner context!" ); + if ( xInnerContext.is() ) + { + // get all states of the inner context + Reference< XAccessibleStateSet > xInnerStates( xInnerContext->getAccessibleStateSet() ); + OSL_ENSURE( xInnerStates.is(), "AccessibleControlShape::initializeComposedState: no inner states!" ); + Sequence< sal_Int16 > aInnerStates; + if ( xInnerStates.is() ) + aInnerStates = xInnerStates->getStates(); + + // look which one are to be propagated to the composed context + const sal_Int16* pStates = aInnerStates.getConstArray(); + const sal_Int16* pStatesEnd = pStates + aInnerStates.getLength(); + for ( ; pStates != pStatesEnd; ++pStates ) + { + if ( isComposedState( *pStates ) && !pComposedStates->contains( *pStates ) ) + { + pComposedStates->AddState( *pStates ); + } + } + } +} + +void SAL_CALL AccessibleControlShape::elementInserted( const ::com::sun::star::container::ContainerEvent& _rEvent ) throw (::com::sun::star::uno::RuntimeException) +{ + Reference< XContainer > xContainer( _rEvent.Source, UNO_QUERY ); + Reference< XControl > xControl( _rEvent.Element, UNO_QUERY ); + + OSL_ENSURE( xContainer.is() && xControl.is(), + "AccessibleControlShape::elementInserted: invalid event description!" ); + + if ( !xControl.is() ) + return; + + ensureControlModelAccess(); + + Reference< XInterface > xNewNormalized( xControl->getModel(), UNO_QUERY ); + Reference< XInterface > xMyModelNormalized( m_xControlModel, UNO_QUERY ); + if ( xNewNormalized.get() && xMyModelNormalized.get() ) + { + // now finally the control for the model we're responsible for has been inserted into the container + Reference< XInterface > xKeepAlive( *this ); + + // first, we're not interested in any more container events + if ( xContainer.is() ) + { + xContainer->removeContainerListener( this ); + m_bWaitingForControl = sal_False; + } + + // second, we need to replace ourself with a new version, which now can be based on the + // control + OSL_VERIFY( mpParent->ReplaceChild ( this, mxShape, mnIndex, maShapeTreeInfo ) ); + } +} + +void SAL_CALL AccessibleControlShape::elementRemoved( const ::com::sun::star::container::ContainerEvent& ) throw (::com::sun::star::uno::RuntimeException) +{ + // not interested in +} + +void SAL_CALL AccessibleControlShape::elementReplaced( const ::com::sun::star::container::ContainerEvent& ) throw (::com::sun::star::uno::RuntimeException) +{ + // not interested in +} diff --git a/svx/source/accessibility/AccessibleEmptyEditSource.cxx b/svx/source/accessibility/AccessibleEmptyEditSource.cxx new file mode 100644 index 000000000000..1c342f5dbfba --- /dev/null +++ b/svx/source/accessibility/AccessibleEmptyEditSource.cxx @@ -0,0 +1,356 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svx.hxx" + +//------------------------------------------------------------------------ +// +// Global header +// +//------------------------------------------------------------------------ +#include <svl/itemset.hxx> +#include <editeng/editdata.hxx> +#include <editeng/outliner.hxx> +#include <svx/svdmodel.hxx> +#include <svx/svdobj.hxx> +#include <svx/svdpool.hxx> + +//------------------------------------------------------------------------ +// +// Project-local header +// +//------------------------------------------------------------------------ + +#include "AccessibleEmptyEditSource.hxx" +#include <svx/unoshtxt.hxx> + +namespace accessibility +{ + + /** This class simply wraps a SvxTextEditSource, forwarding all + methods except the GetBroadcaster() call + */ + class AccessibleProxyEditSource_Impl : public SvxEditSource + { + public: + /** Construct AccessibleEmptyEditSource_Impl + + @param rBrdCast + + Proxy broadcaster to allow seamless flipping of edit source implementations. ProxyEditSource and EmptyEditSource + */ + AccessibleProxyEditSource_Impl( SdrObject& rObj, + SdrView& rView, + const Window& rViewWindow ); + ~AccessibleProxyEditSource_Impl(); + + // from the SvxEditSource interface + SvxTextForwarder* GetTextForwarder(); + SvxViewForwarder* GetViewForwarder(); + SvxEditViewForwarder* GetEditViewForwarder( sal_Bool bCreate = sal_False ); + + SvxEditSource* Clone() const; + + void UpdateData(); + + SfxBroadcaster& GetBroadcaster() const; + + private: + SvxTextEditSource maEditSource; + + }; + + /** Dummy class, faking exactly one empty paragraph for EditEngine accessibility + */ + class AccessibleEmptyEditSource_Impl : public SvxEditSource, public SvxViewForwarder, public SvxTextForwarder, public SfxBroadcaster + { + public: + + AccessibleEmptyEditSource_Impl() {} + ~AccessibleEmptyEditSource_Impl() {} + + // from the SfxListener interface + void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ); + + // SvxEditSource + SvxTextForwarder* GetTextForwarder() { return this; } + SvxViewForwarder* GetViewForwarder() { return this; } + SvxEditSource* Clone() const { return NULL; } + void UpdateData() {} + SfxBroadcaster& GetBroadcaster() const { return *(const_cast<AccessibleEmptyEditSource_Impl*>(this)); } + + // SvxTextForwarder + USHORT GetParagraphCount() const { return 1; } + USHORT GetTextLen( USHORT /*nParagraph*/ ) const { return 0; } + String GetText( const ESelection& /*rSel*/ ) const { return String(); } + SfxItemSet GetAttribs( const ESelection& /*rSel*/, BOOL /*bOnlyHardAttrib*/ = 0 ) const + { + // AW: Very dangerous: The former implementation used a SfxItemPool created on the + // fly which of course was deleted again ASAP. Thus, the returned SfxItemSet was using + // a deleted Pool by design. + return SfxItemSet(SdrObject::GetGlobalDrawObjectItemPool()); + } + SfxItemSet GetParaAttribs( USHORT /*nPara*/ ) const { return GetAttribs(ESelection()); } + void SetParaAttribs( USHORT /*nPara*/, const SfxItemSet& /*rSet*/ ) {} + void RemoveAttribs( const ESelection& /*rSelection*/, sal_Bool /*bRemoveParaAttribs*/, sal_uInt16 /*nWhich*/ ){} + void GetPortions( USHORT /*nPara*/, SvUShorts& /*rList*/ ) const {} + + USHORT GetItemState( const ESelection& /*rSel*/, USHORT /*nWhich*/ ) const { return 0; } + USHORT GetItemState( USHORT /*nPara*/, USHORT /*nWhich*/ ) const { return 0; } + + SfxItemPool* GetPool() const { return NULL; } + + void QuickInsertText( const String& /*rText*/, const ESelection& /*rSel*/ ) {} + void QuickInsertField( const SvxFieldItem& /*rFld*/, const ESelection& /*rSel*/ ) {} + void QuickSetAttribs( const SfxItemSet& /*rSet*/, const ESelection& /*rSel*/ ) {} + void QuickInsertLineBreak( const ESelection& /*rSel*/ ) {} + + const SfxItemSet * GetEmptyItemSetPtr() { return 0; } + + void AppendParagraph() {} + xub_StrLen AppendTextPortion( USHORT /*nPara*/, const String & /*rText*/, const SfxItemSet & /*rSet*/ ) { return 0; } + + //XTextCopy + void CopyText(const SvxTextForwarder& ){} + + XubString CalcFieldValue( const SvxFieldItem& /*rField*/, USHORT /*nPara*/, USHORT /*nPos*/, Color*& /*rpTxtColor*/, Color*& /*rpFldColor*/ ) + { + return XubString(); + } + void FieldClicked( const SvxFieldItem&, USHORT, xub_StrLen ) {;} + + BOOL IsValid() const { return sal_True; } + + void SetNotifyHdl( const Link& ) {} + LanguageType GetLanguage( USHORT, USHORT ) const { return LANGUAGE_DONTKNOW; } + USHORT GetFieldCount( USHORT ) const { return 0; } + EFieldInfo GetFieldInfo( USHORT, USHORT ) const { return EFieldInfo(); } + EBulletInfo GetBulletInfo( USHORT ) const { return EBulletInfo(); } + Rectangle GetCharBounds( USHORT, USHORT ) const { return Rectangle(); } + Rectangle GetParaBounds( USHORT ) const { return Rectangle(); } + MapMode GetMapMode() const { return MapMode(); } + OutputDevice* GetRefDevice() const { return NULL; } + sal_Bool GetIndexAtPoint( const Point&, USHORT&, USHORT& ) const { return sal_False; } + sal_Bool GetWordIndices( USHORT, USHORT, USHORT&, USHORT& ) const { return sal_False; } + sal_Bool GetAttributeRun( USHORT&, USHORT&, USHORT, USHORT ) const { return sal_False; } + USHORT GetLineCount( USHORT nPara ) const { return nPara == 0 ? 1 : 0; } + USHORT GetLineLen( USHORT, USHORT ) const { return 0; } + void GetLineBoundaries( /*out*/USHORT & rStart, /*out*/USHORT & rEnd, USHORT /*nParagraph*/, USHORT /*nLine*/ ) const { rStart = rEnd = 0; } + USHORT GetLineNumberAtIndex( USHORT /*nPara*/, USHORT /*nIndex*/ ) const { return 0; } + + // the following two methods would, strictly speaking, require + // a switch to a real EditSource, too. Fortunately, the + // AccessibleEditableTextPara implementation currently always + // calls GetEditViewForwarder(true) before doing + // changes. Thus, we rely on this behabviour here (problem + // when that changes: via accessibility API, it would no + // longer be possible to enter text in previously empty + // shapes). + sal_Bool Delete( const ESelection& ) { return sal_False; } + sal_Bool InsertText( const String&, const ESelection& ) { return sal_False; } + sal_Bool QuickFormatDoc( BOOL ) { return sal_True; } + sal_Int16 GetDepth( USHORT ) const { return -1; } + sal_Bool SetDepth( USHORT, sal_Int16 ) { return sal_True; } + + Rectangle GetVisArea() const { return Rectangle(); } + Point LogicToPixel( const Point& rPoint, const MapMode& /*rMapMode*/ ) const { return rPoint; } + Point PixelToLogic( const Point& rPoint, const MapMode& /*rMapMode*/ ) const { return rPoint; } + + }; + + // ------------------------------------------------------------------------- + // Implementing AccessibleProxyEditSource_Impl + // ------------------------------------------------------------------------- + + AccessibleProxyEditSource_Impl::AccessibleProxyEditSource_Impl( SdrObject& rObj, + SdrView& rView, + const Window& rViewWindow ) : + maEditSource( rObj, 0, rView, rViewWindow ) + { + } + + AccessibleProxyEditSource_Impl::~AccessibleProxyEditSource_Impl() + { + } + + SvxTextForwarder* AccessibleProxyEditSource_Impl::GetTextForwarder() + { + return maEditSource.GetTextForwarder(); + } + + SvxViewForwarder* AccessibleProxyEditSource_Impl::GetViewForwarder() + { + return maEditSource.GetViewForwarder(); + } + + SvxEditViewForwarder* AccessibleProxyEditSource_Impl::GetEditViewForwarder( sal_Bool bCreate ) + { + return maEditSource.GetEditViewForwarder( bCreate ); + } + + SvxEditSource* AccessibleProxyEditSource_Impl::Clone() const + { + return maEditSource.Clone(); + } + + void AccessibleProxyEditSource_Impl::UpdateData() + { + maEditSource.UpdateData(); + } + + SfxBroadcaster& AccessibleProxyEditSource_Impl::GetBroadcaster() const + { + return maEditSource.GetBroadcaster(); + } + + + // ------------------------------------------------------------------------- + // Implementing AccessibleEmptyEditSource + // ------------------------------------------------------------------------- + + AccessibleEmptyEditSource::AccessibleEmptyEditSource( SdrObject& rObj, + SdrView& rView, + const Window& rViewWindow ) : + mpEditSource( new AccessibleEmptyEditSource_Impl() ), + mrObj(rObj), + mrView(rView), + mrViewWindow(rViewWindow), + mbEditSourceEmpty( true ) + { + if( mrObj.GetModel() ) + StartListening( *mrObj.GetModel() ); + } + + AccessibleEmptyEditSource::~AccessibleEmptyEditSource() + { + if( !mbEditSourceEmpty ) + { + // deregister as listener + if( mpEditSource.get() ) + EndListening( mpEditSource->GetBroadcaster() ); + } + else + { + if( mrObj.GetModel() ) + EndListening( *mrObj.GetModel() ); + } + } + + SvxTextForwarder* AccessibleEmptyEditSource::GetTextForwarder() + { + if( !mpEditSource.get() ) + return NULL; + + return mpEditSource->GetTextForwarder(); + } + + SvxViewForwarder* AccessibleEmptyEditSource::GetViewForwarder() + { + if( !mpEditSource.get() ) + return NULL; + + return mpEditSource->GetViewForwarder(); + } + + void AccessibleEmptyEditSource::Switch2ProxyEditSource() + { + // deregister EmptyEditSource model listener + if( mrObj.GetModel() ) + EndListening( *mrObj.GetModel() ); + + ::std::auto_ptr< SvxEditSource > pProxySource( new AccessibleProxyEditSource_Impl(mrObj, mrView, mrViewWindow) ); + ::std::auto_ptr< SvxEditSource > tmp = mpEditSource; + mpEditSource = pProxySource; + pProxySource = tmp; + + // register as listener + StartListening( mpEditSource->GetBroadcaster() ); + + // we've irrevocably a full EditSource now. + mbEditSourceEmpty = false; + } + + SvxEditViewForwarder* AccessibleEmptyEditSource::GetEditViewForwarder( sal_Bool bCreate ) + { + if( !mpEditSource.get() ) + return NULL; + + // switch edit source, if not yet done + if( mbEditSourceEmpty && bCreate ) + Switch2ProxyEditSource(); + + return mpEditSource->GetEditViewForwarder( bCreate ); + } + + SvxEditSource* AccessibleEmptyEditSource::Clone() const + { + if( !mpEditSource.get() ) + return NULL; + + return mpEditSource->Clone(); + } + + void AccessibleEmptyEditSource::UpdateData() + { + if( mpEditSource.get() ) + mpEditSource->UpdateData(); + } + + SfxBroadcaster& AccessibleEmptyEditSource::GetBroadcaster() const + { + return *(const_cast<AccessibleEmptyEditSource*>(this)); + } + + void AccessibleEmptyEditSource::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint ) + { + const SdrHint* pSdrHint = PTR_CAST( SdrHint, &rHint ); + + if( pSdrHint && pSdrHint->GetKind() == HINT_BEGEDIT && + &mrObj == pSdrHint->GetObject() && mpEditSource.get() ) + { + // switch edit source, if not yet done. This is necessary + // to become a full-fledged EditSource the first time a + // user start entering text in a previously empty object. + if( mbEditSourceEmpty ) + Switch2ProxyEditSource(); + } + else if (pSdrHint && pSdrHint->GetObject()!=NULL) + { + // When the SdrObject just got a para outliner object then + // switch the edit source. + if (pSdrHint->GetObject()->GetOutlinerParaObject() != NULL) + Switch2ProxyEditSource(); + } + + // forward messages + Broadcast( rHint ); + } + +} // end of namespace accessibility + +//------------------------------------------------------------------------ diff --git a/svx/source/accessibility/AccessibleEmptyEditSource.hxx b/svx/source/accessibility/AccessibleEmptyEditSource.hxx new file mode 100644 index 000000000000..93ff0383eab1 --- /dev/null +++ b/svx/source/accessibility/AccessibleEmptyEditSource.hxx @@ -0,0 +1,100 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SVX_ACCESSIBLEEMPTYEDITSOURCE_HXX +#define _SVX_ACCESSIBLEEMPTYEDITSOURCE_HXX + +#include <svl/brdcst.hxx> +#include <svl/lstner.hxx> + +#include <memory> +#include <editeng/unoedsrc.hxx> + +class SdrObject; +class SdrView; +class Window; + +namespace accessibility +{ + /** Proxy edit source for shapes without text + + Extracted from old SvxDummyEditSource + */ + class AccessibleEmptyEditSource : public SvxEditSource, public SfxListener, public SfxBroadcaster + { + public: + /** Create proxy edit source for shapes without text + + Since the views don't broadcast their dying, make sure that + this object gets destroyed if the view becomes invalid + + The window is necessary, since our views can display on multiple windows + + Make sure you only create such an object if the shape _really_ + does not contain text. + */ + AccessibleEmptyEditSource( SdrObject& rObj, SdrView& rView, const Window& rViewWindow ); + ~AccessibleEmptyEditSource(); + + // from the SvxEditSource interface + SvxTextForwarder* GetTextForwarder(); + SvxViewForwarder* GetViewForwarder(); + + SvxEditSource* Clone() const; + + // this method internally switches from empty to proxy mode, + // creating an SvxTextEditSource for the functionality. + SvxEditViewForwarder* GetEditViewForwarder( sal_Bool bCreate = sal_False ); + + void UpdateData(); + SfxBroadcaster& GetBroadcaster() const; + + // from the SfxListener interface + void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ); + + private: + void Switch2ProxyEditSource(); + + /** Pointer to edit source implementation. This is switched on + a GetEditViewForwarder( true ) call, to actually create a + SvxTextEditSource. + + @dyn + */ + std::auto_ptr< SvxEditSource > mpEditSource; + + SdrObject& mrObj; + SdrView& mrView; + const Window& mrViewWindow; + + bool mbEditSourceEmpty; + }; + +} // namespace accessibility + +#endif + diff --git a/svx/source/accessibility/AccessibleFrameSelector.cxx b/svx/source/accessibility/AccessibleFrameSelector.cxx new file mode 100644 index 000000000000..82c522e1b0ff --- /dev/null +++ b/svx/source/accessibility/AccessibleFrameSelector.cxx @@ -0,0 +1,740 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svx.hxx" +#include "AccessibleFrameSelector.hxx" +#include <com/sun/star/awt/KeyEvent.hpp> +#include <com/sun/star/awt/KeyModifier.hpp> +#include <com/sun/star/awt/Key.hpp> +#ifndef _COM_SUN_STAR_ACCESSIBILITY_ACCESSIBLESTATETYPE_HDL_ +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#endif +#ifndef _COM_SUN_STAR_ACCESSIBILITY_ACCESSIBLERELATIONTYPE_HDL_ +#include <com/sun/star/accessibility/AccessibleRelationType.hpp> +#endif +#ifndef _COM_SUN_STAR_ACCESSIBILITY_ACCESSIBLEROLE_HDL_ +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#endif +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <com/sun/star/awt/FocusChangeReason.hpp> +#include <unotools/accessiblestatesethelper.hxx> +#include <unotools/accessiblerelationsethelper.hxx> +#include <vos/mutex.hxx> +#include <vcl/svapp.hxx> +#include <svx/frmsel.hxx> +#include <svx/dialmgr.hxx> +#include "editeng/unolingu.hxx" + +#ifndef _SVX_DIALOGS_HRC +#include <svx/dialogs.hrc> +#endif +#ifndef SVX_FRMSEL_HRC +#include "frmsel.hrc" +#endif + +#ifndef MNEMONIC_CHAR +#define MNEMONIC_CHAR ((sal_Unicode)'~') +#endif + +namespace svx { +namespace a11y { + +using ::rtl::OUString; +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::RuntimeException; +using ::com::sun::star::uno::XInterface; +using ::com::sun::star::lang::Locale; +using ::com::sun::star::lang::EventObject; +using ::com::sun::star::beans::XPropertyChangeListener; +using ::com::sun::star::awt::XFocusListener; + +using namespace ::com::sun::star::accessibility; + +namespace AwtKey = ::com::sun::star::awt::Key; +namespace AwtKeyModifier = ::com::sun::star::awt::KeyModifier; +namespace AwtFocusChangeReason = ::com::sun::star::awt::FocusChangeReason; + +typedef ::com::sun::star::awt::Point AwtPoint; +typedef ::com::sun::star::awt::Size AwtSize; +typedef ::com::sun::star::awt::Rectangle AwtRectangle; +typedef ::com::sun::star::awt::KeyEvent AwtKeyEvent; +typedef ::com::sun::star::awt::FocusEvent AwtFocusEvent; + +// ============================================================================ + +AccFrameSelector::AccFrameSelector( FrameSelector& rFrameSel, FrameBorderType eBorder ) : + Resource( SVX_RES( RID_SVXSTR_BORDER_CONTROL ) ), + mpFrameSel( &rFrameSel ), + meBorder( eBorder ), + maFocusListeners( maFocusMutex ), + maPropertyListeners( maPropertyMutex ), + maNames( SVX_RES( ARR_TEXTS ) ), + maDescriptions( SVX_RES(ARR_DESCRIPTIONS ) ), + mnClientId( 0 ) +{ + FreeResource(); + + if ( mpFrameSel ) + { + mpFrameSel->AddEventListener( LINK( this, AccFrameSelector, WindowEventListener ) ); + } +} + +// ---------------------------------------------------------------------------- + +AccFrameSelector::~AccFrameSelector() +{ + if ( mpFrameSel ) + { + mpFrameSel->RemoveEventListener( LINK( this, AccFrameSelector, WindowEventListener ) ); + } +} + +// ---------------------------------------------------------------------------- + +Reference< XAccessibleContext > AccFrameSelector::getAccessibleContext( ) + throw (RuntimeException) +{ + return this; +} + +// ---------------------------------------------------------------------------- + +sal_Int32 AccFrameSelector::getAccessibleChildCount( ) throw (RuntimeException) +{ + vos::OGuard aGuard(Application::GetSolarMutex()); + IsValid(); + return (meBorder == FRAMEBORDER_NONE) ? mpFrameSel->GetEnabledBorderCount() : 0; +} + +// ---------------------------------------------------------------------------- + +Reference< XAccessible > AccFrameSelector::getAccessibleChild( sal_Int32 i ) + throw (RuntimeException) +{ + vos::OGuard aGuard(Application::GetSolarMutex()); + IsValid(); + Reference< XAccessible > xRet; + if( meBorder == FRAMEBORDER_NONE ) + xRet = mpFrameSel->GetChildAccessible( i ); + if( !xRet.is() ) + throw RuntimeException(); + return xRet; +} + +// ---------------------------------------------------------------------------- + +Reference< XAccessible > AccFrameSelector::getAccessibleParent( ) + throw (RuntimeException) +{ + vos::OGuard aGuard(Application::GetSolarMutex()); + IsValid(); + Reference< XAccessible > xRet; + if(meBorder == FRAMEBORDER_NONE) + xRet = mpFrameSel->GetParent()->GetAccessible( sal_True ); + else + xRet = mpFrameSel->CreateAccessible(); + return xRet; +} + +// ---------------------------------------------------------------------------- + +sal_Int32 AccFrameSelector::getAccessibleIndexInParent( ) + throw (RuntimeException) +{ + vos::OGuard aGuard(Application::GetSolarMutex()); + IsValid(); + + sal_Int32 nIdx = 0; + if( meBorder == FRAMEBORDER_NONE ) + { + Window* pTabPage = mpFrameSel->GetParent(); + sal_Int32 nChildren = pTabPage->GetChildCount(); + for( nIdx = 0; nIdx < nChildren; ++nIdx ) + if( pTabPage->GetChild( static_cast< USHORT >( nIdx ) ) == mpFrameSel ) + break; + } + else + nIdx = mpFrameSel->GetEnabledBorderIndex( meBorder ); + + if( nIdx < 0 ) + throw RuntimeException(); + return nIdx; +} + +// ---------------------------------------------------------------------------- + +sal_Int16 AccFrameSelector::getAccessibleRole( ) throw (RuntimeException) +{ + return AccessibleRole::OPTION_PANE; +} + +// ---------------------------------------------------------------------------- + +OUString AccFrameSelector::getAccessibleDescription( ) + throw (RuntimeException) +{ + vos::OGuard aGuard(Application::GetSolarMutex()); + IsValid(); + return maDescriptions.GetString(meBorder); +} + +// ---------------------------------------------------------------------------- + +OUString AccFrameSelector::getAccessibleName( ) + throw (RuntimeException) +{ + vos::OGuard aGuard(Application::GetSolarMutex()); + IsValid(); + return maNames.GetString(meBorder); +} + +// ---------------------------------------------------------------------------- + +Reference< XAccessibleRelationSet > AccFrameSelector::getAccessibleRelationSet( ) + throw (RuntimeException) +{ + vos::OGuard aGuard(Application::GetSolarMutex()); + IsValid(); + utl::AccessibleRelationSetHelper* pHelper; + Reference< XAccessibleRelationSet > xRet = pHelper = new utl::AccessibleRelationSetHelper; + if(meBorder == FRAMEBORDER_NONE) + { + //add the label relation + Window* pPrev = mpFrameSel->GetWindow( WINDOW_PREV ); + if(pPrev && WINDOW_FIXEDTEXT == pPrev->GetType()) + { + AccessibleRelation aLabelRelation; + aLabelRelation.RelationType = AccessibleRelationType::LABELED_BY; + aLabelRelation.TargetSet.realloc(1); + aLabelRelation.TargetSet.getArray()[0] = pPrev->GetAccessible(); + pHelper->AddRelation(aLabelRelation); + } + } + return xRet; +} + +// ---------------------------------------------------------------------------- + +Reference< XAccessibleStateSet > AccFrameSelector::getAccessibleStateSet( ) + throw (RuntimeException) +{ + vos::OGuard aGuard(Application::GetSolarMutex()); + utl::AccessibleStateSetHelper* pStateSetHelper = new utl::AccessibleStateSetHelper; + Reference< XAccessibleStateSet > xRet = pStateSetHelper; + + if(!mpFrameSel) + pStateSetHelper->AddState(AccessibleStateType::DEFUNC); + else + { + const sal_Int16 aStandardStates[] = + { + AccessibleStateType::EDITABLE, + AccessibleStateType::FOCUSABLE, + AccessibleStateType::MULTI_SELECTABLE, + AccessibleStateType::SELECTABLE, + AccessibleStateType::SHOWING, + AccessibleStateType::VISIBLE, + AccessibleStateType::OPAQUE, + 0}; + sal_Int16 nState = 0; + while(aStandardStates[nState]) + { + pStateSetHelper->AddState(aStandardStates[nState++]); + } + if(mpFrameSel->IsEnabled()) + { + pStateSetHelper->AddState(AccessibleStateType::ENABLED); + pStateSetHelper->AddState(AccessibleStateType::SENSITIVE); + } + + sal_Bool bIsParent = meBorder == FRAMEBORDER_NONE; + if(mpFrameSel->HasFocus() && + (bIsParent || mpFrameSel->IsBorderSelected(meBorder))) + { + pStateSetHelper->AddState(AccessibleStateType::ACTIVE); + pStateSetHelper->AddState(AccessibleStateType::FOCUSED); + pStateSetHelper->AddState(AccessibleStateType::SELECTED); + } + } + return xRet; +} + +// ---------------------------------------------------------------------------- + +Locale AccFrameSelector::getLocale( ) + throw (IllegalAccessibleComponentStateException, RuntimeException) +{ + Locale aRet; + SvxLanguageToLocale( aRet, Application::GetSettings().GetUILanguage() ); + return aRet; +} + +// ---------------------------------------------------------------------------- + +void AccFrameSelector::addPropertyChangeListener( + const Reference< XPropertyChangeListener >& xListener ) + throw (RuntimeException) +{ + maPropertyListeners.addInterface( xListener ); +} + +// ---------------------------------------------------------------------------- + +void AccFrameSelector::removePropertyChangeListener( const Reference< XPropertyChangeListener >& xListener ) + throw (RuntimeException) +{ + maPropertyListeners.removeInterface( xListener ); +} + +// ---------------------------------------------------------------------------- + +sal_Bool AccFrameSelector::containsPoint( const AwtPoint& aPt ) + throw (RuntimeException) +{ + vos::OGuard aGuard(Application::GetSolarMutex()); + IsValid(); + //aPt is relative to the frame selector + return mpFrameSel->ContainsClickPoint( Point( aPt.X, aPt.Y ) ); +} + +// ---------------------------------------------------------------------------- + +Reference< XAccessible > AccFrameSelector::getAccessibleAtPoint( + const AwtPoint& aPt ) + throw (RuntimeException) +{ + vos::OGuard aGuard(Application::GetSolarMutex()); + IsValid(); + //aPt is relative to the frame selector + return mpFrameSel->GetChildAccessible( Point( aPt.X, aPt.Y ) ); +} + +AwtRectangle AccFrameSelector::getBounds( ) throw (RuntimeException) +{ + vos::OGuard aGuard(Application::GetSolarMutex()); + IsValid(); + Size aSz; + Point aPos; + switch(meBorder) + { + case FRAMEBORDER_NONE: + aSz = mpFrameSel->GetSizePixel(); + aPos = mpFrameSel->GetPosPixel(); + break; + default: + const Rectangle aSpot = mpFrameSel->GetClickBoundRect( meBorder ); + aPos = aSpot.TopLeft(); + aSz = aSpot.GetSize(); + } + AwtRectangle aRet; + aRet.X = aPos.X(); + aRet.Y = aPos.Y(); + aRet.Width = aSz.Width(); + aRet.Height = aSz.Height(); + return aRet; +} + +// ---------------------------------------------------------------------------- + +AwtPoint AccFrameSelector::getLocation( ) throw (RuntimeException) +{ + vos::OGuard aGuard(Application::GetSolarMutex()); + IsValid(); + Point aPos; + switch(meBorder) + { + case FRAMEBORDER_NONE: + aPos = mpFrameSel->GetPosPixel(); + break; + default: + const Rectangle aSpot = mpFrameSel->GetClickBoundRect( meBorder ); + aPos = aSpot.TopLeft(); + } + AwtPoint aRet(aPos.X(), aPos.Y()); + return aRet; +} + +// ---------------------------------------------------------------------------- + +AwtPoint AccFrameSelector::getLocationOnScreen( ) throw (RuntimeException) +{ + vos::OGuard aGuard(Application::GetSolarMutex()); + IsValid(); + Point aPos; + switch(meBorder) + { + case FRAMEBORDER_NONE: + aPos = mpFrameSel->GetPosPixel(); + break; + default: + const Rectangle aSpot = mpFrameSel->GetClickBoundRect( meBorder ); + aPos = aSpot.TopLeft(); + } + aPos = mpFrameSel->OutputToAbsoluteScreenPixel( aPos ); + AwtPoint aRet(aPos.X(), aPos.Y()); + return aRet; +} + +// ---------------------------------------------------------------------------- + +AwtSize AccFrameSelector::getSize( ) throw (RuntimeException) +{ + vos::OGuard aGuard(Application::GetSolarMutex()); + IsValid(); + Size aSz; + switch(meBorder) + { + case FRAMEBORDER_NONE: + aSz = mpFrameSel->GetSizePixel(); + break; + default: + const Rectangle aSpot = mpFrameSel->GetClickBoundRect( meBorder ); + aSz = aSpot.GetSize(); + } + AwtSize aRet(aSz.Width(), aSz.Height()); + return aRet; +} + +// ---------------------------------------------------------------------------- + +sal_Bool AccFrameSelector::isShowing( ) throw (RuntimeException) +{ + vos::OGuard aGuard(Application::GetSolarMutex()); + IsValid(); + return sal_True; +} + +// ---------------------------------------------------------------------------- + +sal_Bool AccFrameSelector::isVisible( ) throw (RuntimeException) +{ + vos::OGuard aGuard(Application::GetSolarMutex()); + IsValid(); + return sal_True; +} + +// ---------------------------------------------------------------------------- + +sal_Bool AccFrameSelector::isFocusTraversable( ) throw (RuntimeException) +{ + vos::OGuard aGuard(Application::GetSolarMutex()); + IsValid(); + return sal_True; +} + +// ---------------------------------------------------------------------------- + +void AccFrameSelector::addFocusListener( const Reference< XFocusListener >& xListener ) throw (RuntimeException) +{ + maFocusListeners.addInterface( xListener ); +} + +// ---------------------------------------------------------------------------- + +void AccFrameSelector::removeFocusListener( const Reference< XFocusListener >& xListener ) throw (RuntimeException) +{ + maFocusListeners.removeInterface( xListener ); +} + +// ---------------------------------------------------------------------------- + +void AccFrameSelector::grabFocus( ) throw (RuntimeException) +{ + vos::OGuard aGuard(Application::GetSolarMutex()); + IsValid(); + mpFrameSel->GrabFocus(); +} + +// ---------------------------------------------------------------------------- + +Any AccFrameSelector::getAccessibleKeyBinding( ) throw (RuntimeException) +{ + Any aRet; + vos::OGuard aGuard(Application::GetSolarMutex()); + IsValid(); + utl::AccessibleRelationSetHelper* pHelper; + Reference< XAccessibleRelationSet > xRet = pHelper = new utl::AccessibleRelationSetHelper; + if(meBorder == FRAMEBORDER_NONE) + { + Window* pPrev = mpFrameSel->GetWindow( WINDOW_PREV ); + if(pPrev && WINDOW_FIXEDTEXT == pPrev->GetType()) + { + String sText = pPrev->GetText(); + xub_StrLen nFound = sText.Search( MNEMONIC_CHAR ); + if(STRING_NOTFOUND != nFound && ++nFound < sText.Len()) + { + sText.ToUpperAscii(); + sal_Unicode cChar = sText.GetChar(nFound); + AwtKeyEvent aEvent; + + aEvent.KeyCode = 0; + aEvent.KeyChar = cChar; + aEvent.KeyFunc = 0; + if(cChar >= 'A' && cChar <= 'Z') + { + aEvent.KeyCode = AwtKey::A + cChar - 'A'; + } + aEvent.Modifiers = AwtKeyModifier::MOD2; + aRet <<= aEvent; + } + } + } + return aRet; +} + +// ---------------------------------------------------------------------------- + +sal_Int32 AccFrameSelector::getForeground( ) + throw (RuntimeException) +{ + Any aRet; + vos::OGuard aGuard(Application::GetSolarMutex()); + IsValid(); + return mpFrameSel->GetControlForeground().GetColor(); +} + +// ---------------------------------------------------------------------------- + +sal_Int32 AccFrameSelector::getBackground( ) + throw (RuntimeException) +{ + Any aRet; + vos::OGuard aGuard(Application::GetSolarMutex()); + IsValid(); + return mpFrameSel->GetControlBackground().GetColor(); +} + +// ---------------------------------------------------------------------------- + +void AccFrameSelector::addEventListener( const Reference< XAccessibleEventListener >& xListener ) throw (RuntimeException) +{ + vos::OGuard aGuard( Application::GetSolarMutex() ); + + if ( xListener.is() ) + { + if ( !mnClientId ) + { + mnClientId = ::comphelper::AccessibleEventNotifier::registerClient(); + } + ::comphelper::AccessibleEventNotifier::addEventListener( mnClientId, xListener ); + } +} + +// ---------------------------------------------------------------------------- + +void AccFrameSelector::removeEventListener( const Reference< XAccessibleEventListener >& xListener ) throw (RuntimeException) +{ + vos::OGuard aGuard( Application::GetSolarMutex() ); + + if ( xListener.is() && mnClientId != 0 && + ::comphelper::AccessibleEventNotifier::removeEventListener( mnClientId, xListener ) == 0 ) + { + // no listeners anymore + // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client), + // and at least to us not firing any events anymore, in case somebody calls + // NotifyAccessibleEvent, again + ::comphelper::AccessibleEventNotifier::TClientId nId( mnClientId ); + mnClientId = 0; + ::comphelper::AccessibleEventNotifier::revokeClient( nId ); + } +} + +// ---------------------------------------------------------------------------- + +OUString AccFrameSelector::getImplementationName( ) throw (RuntimeException) +{ + return OUString::createFromAscii("AccFrameSelector"); +} + +// ---------------------------------------------------------------------------- + +const sal_Char sAccessible[] = "Accessible"; +const sal_Char sAccessibleContext[] = "AccessibleContext"; +const sal_Char sAccessibleComponent[] = "AccessibleComponent"; + +sal_Bool AccFrameSelector::supportsService( const OUString& rServiceName ) + throw (RuntimeException) +{ + return rServiceName.equalsAsciiL( sAccessible , sizeof(sAccessible )-1 ) || + rServiceName.equalsAsciiL( sAccessibleContext , sizeof(sAccessibleContext )-1 ) || + rServiceName.equalsAsciiL( sAccessibleComponent, sizeof(sAccessibleComponent)-1 ); +} + +// ---------------------------------------------------------------------------- + +Sequence< OUString > AccFrameSelector::getSupportedServiceNames( ) + throw (RuntimeException) +{ + Sequence< OUString > aRet(3); + OUString* pArray = aRet.getArray(); + pArray[0] = OUString( RTL_CONSTASCII_USTRINGPARAM(sAccessible ) ); + pArray[1] = OUString( RTL_CONSTASCII_USTRINGPARAM(sAccessibleContext ) ); + pArray[2] = OUString( RTL_CONSTASCII_USTRINGPARAM(sAccessibleComponent) ); + return aRet; +} + +// ---------------------------------------------------------------------------- + +void AccFrameSelector::IsValid() throw (RuntimeException) +{ + if(!mpFrameSel) + throw RuntimeException(); +} + +// ---------------------------------------------------------------------------- + +void AccFrameSelector::NotifyFocusListeners(sal_Bool bGetFocus) +{ + vos::OGuard aGuard(Application::GetSolarMutex()); + AwtFocusEvent aEvent; + aEvent.FocusFlags = 0; + if(bGetFocus) + { + USHORT nFocusFlags = mpFrameSel->GetGetFocusFlags(); + if(nFocusFlags&GETFOCUS_TAB) + aEvent.FocusFlags |= AwtFocusChangeReason::TAB; + if(nFocusFlags&GETFOCUS_CURSOR) + aEvent.FocusFlags |= AwtFocusChangeReason::CURSOR; + if(nFocusFlags&GETFOCUS_MNEMONIC) + aEvent.FocusFlags |= AwtFocusChangeReason::MNEMONIC; + if(nFocusFlags&GETFOCUS_FORWARD) + aEvent.FocusFlags |= AwtFocusChangeReason::FORWARD; + if(nFocusFlags&GETFOCUS_BACKWARD) + aEvent.FocusFlags |= AwtFocusChangeReason::BACKWARD; + if(nFocusFlags&GETFOCUS_AROUND) + aEvent.FocusFlags |= AwtFocusChangeReason::AROUND; + if(nFocusFlags&GETFOCUS_UNIQUEMNEMONIC) + aEvent.FocusFlags |= AwtFocusChangeReason::UNIQUEMNEMONIC; + // if(nFocusFlags&GETFOCUS_INIT) + // aEvent.FocusFlags |= AwtFocusChangeReason:: + } +// else + //how can I find the current focus window? +// aEvent.NextFocus = ; + aEvent.Temporary = sal_False; + + Reference < XAccessibleContext > xThis( this ); + aEvent.Source = xThis; + + ::cppu::OInterfaceIteratorHelper aIter( maFocusListeners ); + while( aIter.hasMoreElements() ) + { + Reference < XFocusListener > xListener( aIter.next(), UNO_QUERY ); + if(bGetFocus) + xListener->focusGained( aEvent ); + else + xListener->focusLost( aEvent ); + } +} + +// ---------------------------------------------------------------------------- + +IMPL_LINK( AccFrameSelector, WindowEventListener, VclSimpleEvent*, pEvent ) +{ + VclWindowEvent* pWinEvent = dynamic_cast< VclWindowEvent* >( pEvent ); + DBG_ASSERT( pWinEvent, "AccFrameSelector::WindowEventListener - unknown window event" ); + if ( pWinEvent ) + { + Window* pWindow = pWinEvent->GetWindow(); + DBG_ASSERT( pWindow, "AccFrameSelector::WindowEventListener: no window!" ); + if ( !pWindow->IsAccessibilityEventsSuppressed() || ( pWinEvent->GetId() == VCLEVENT_OBJECT_DYING ) ) + { + ProcessWindowEvent( *pWinEvent ); + } + } + + return 0; +} + +// ---------------------------------------------------------------------------- + +void AccFrameSelector::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) +{ + switch ( rVclWindowEvent.GetId() ) + { + case VCLEVENT_WINDOW_GETFOCUS: + { + if ( meBorder == FRAMEBORDER_NONE ) + { + Any aOldValue, aNewValue; + aNewValue <<= AccessibleStateType::FOCUSED; + NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + } + } + break; + case VCLEVENT_WINDOW_LOSEFOCUS: + { + if ( meBorder == FRAMEBORDER_NONE ) + { + Any aOldValue, aNewValue; + aOldValue <<= AccessibleStateType::FOCUSED; + NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + } + } + break; + default: + { + } + break; + } +} + +// ---------------------------------------------------------------------------- + +void AccFrameSelector::NotifyAccessibleEvent( const sal_Int16 _nEventId, + const Any& _rOldValue, const Any& _rNewValue ) +{ + if ( mnClientId ) + { + Reference< XInterface > xSource( *this ); + AccessibleEventObject aEvent( xSource, _nEventId, _rNewValue, _rOldValue ); + ::comphelper::AccessibleEventNotifier::addEvent( mnClientId, aEvent ); + } +} + +// ---------------------------------------------------------------------------- + +void AccFrameSelector::Invalidate() +{ + mpFrameSel = 0; + EventObject aEvent; + Reference < XAccessibleContext > xThis( this ); + aEvent.Source = xThis; + maFocusListeners.disposeAndClear( aEvent ); + maPropertyListeners.disposeAndClear( aEvent ); +} + +// ============================================================================ + +} // namespace a11y +} // namespace svx + diff --git a/svx/source/accessibility/AccessibleGraphicShape.cxx b/svx/source/accessibility/AccessibleGraphicShape.cxx new file mode 100644 index 000000000000..f4e7f7747dda --- /dev/null +++ b/svx/source/accessibility/AccessibleGraphicShape.cxx @@ -0,0 +1,212 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svx.hxx" + + +#include <svx/AccessibleGraphicShape.hxx> + +#include <svx/ShapeTypeHandler.hxx> +#include <svx/SvxShapeTypes.hxx> + +using namespace ::accessibility; +using namespace ::rtl; +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; + +//===== internal ============================================================ + +AccessibleGraphicShape::AccessibleGraphicShape ( + const AccessibleShapeInfo& rShapeInfo, + const AccessibleShapeTreeInfo& rShapeTreeInfo) + : AccessibleShape (rShapeInfo, rShapeTreeInfo) +{ +} + + + + +AccessibleGraphicShape::~AccessibleGraphicShape (void) +{ +} + + + + +//===== XAccessibleImage ==================================================== + +::rtl::OUString SAL_CALL AccessibleGraphicShape::getAccessibleImageDescription (void) + throw (::com::sun::star::uno::RuntimeException) +{ + return AccessibleShape::getAccessibleDescription (); +} + + + + +sal_Int32 SAL_CALL AccessibleGraphicShape::getAccessibleImageHeight (void) + throw (::com::sun::star::uno::RuntimeException) +{ + return AccessibleShape::getSize().Height; +} + + + + +sal_Int32 SAL_CALL AccessibleGraphicShape::getAccessibleImageWidth (void) + throw (::com::sun::star::uno::RuntimeException) +{ + return AccessibleShape::getSize().Width; +} + + + + +//===== XInterface ========================================================== + +com::sun::star::uno::Any SAL_CALL + AccessibleGraphicShape::queryInterface (const com::sun::star::uno::Type & rType) + throw (::com::sun::star::uno::RuntimeException) +{ + ::com::sun::star::uno::Any aReturn = AccessibleShape::queryInterface (rType); + if ( ! aReturn.hasValue()) + aReturn = ::cppu::queryInterface (rType, + static_cast<XAccessibleImage*>(this)); + return aReturn; +} + + + +void SAL_CALL + AccessibleGraphicShape::acquire (void) + throw () +{ + AccessibleShape::acquire (); +} + + + +void SAL_CALL + AccessibleGraphicShape::release (void) + throw () +{ + AccessibleShape::release (); +} + + + + +//===== XServiceInfo ======================================================== + +::rtl::OUString SAL_CALL + AccessibleGraphicShape::getImplementationName (void) + throw (::com::sun::star::uno::RuntimeException) +{ + return ::rtl::OUString( + RTL_CONSTASCII_USTRINGPARAM("AccessibleGraphicShape")); +} + + + + +::com::sun::star::uno::Sequence< ::rtl::OUString> SAL_CALL + AccessibleGraphicShape::getSupportedServiceNames (void) + throw (::com::sun::star::uno::RuntimeException) +{ + ThrowIfDisposed (); + // Get list of supported service names from base class... + uno::Sequence<OUString> aServiceNames = + AccessibleShape::getSupportedServiceNames(); + sal_Int32 nCount (aServiceNames.getLength()); + + // ...and add additional names. + aServiceNames.realloc (nCount + 1); + static const OUString sAdditionalServiceName (RTL_CONSTASCII_USTRINGPARAM( + "com.sun.star.drawing.AccessibleGraphicShape")); + aServiceNames[nCount] = sAdditionalServiceName; + + return aServiceNames; +} + + + + +//===== XTypeProvider =================================================== + +uno::Sequence<uno::Type> SAL_CALL + AccessibleGraphicShape::getTypes (void) + throw (uno::RuntimeException) +{ + // Get list of types from the context base implementation... + uno::Sequence<uno::Type> aTypeList (AccessibleShape::getTypes()); + // ...and add the additional type for the component. + long nTypeCount = aTypeList.getLength(); + aTypeList.realloc (nTypeCount + 1); + const uno::Type aImageType = + ::getCppuType((const uno::Reference<XAccessibleImage>*)0); + aTypeList[nTypeCount] = aImageType; + + return aTypeList; +} + + + + +/// Create the base name of this object, i.e. the name without appended number. +::rtl::OUString + AccessibleGraphicShape::CreateAccessibleBaseName (void) + throw (::com::sun::star::uno::RuntimeException) +{ + ::rtl::OUString sName; + + ShapeTypeId nShapeType = ShapeTypeHandler::Instance().GetTypeId (mxShape); + switch (nShapeType) + { + case DRAWING_GRAPHIC_OBJECT: + sName = ::rtl::OUString (RTL_CONSTASCII_USTRINGPARAM("GraphicObjectShape")); + break; + + default: + sName = ::rtl::OUString (RTL_CONSTASCII_USTRINGPARAM("UnknownAccessibleGraphicShape")); + uno::Reference<drawing::XShapeDescriptor> xDescriptor (mxShape, uno::UNO_QUERY); + if (xDescriptor.is()) + sName += ::rtl::OUString (RTL_CONSTASCII_USTRINGPARAM(": ")) + + xDescriptor->getShapeType(); + } + + return sName; +} + + + +::rtl::OUString + AccessibleGraphicShape::CreateAccessibleDescription (void) + throw (::com::sun::star::uno::RuntimeException) +{ + return CreateAccessibleName (); +} diff --git a/svx/source/accessibility/AccessibleOLEShape.cxx b/svx/source/accessibility/AccessibleOLEShape.cxx new file mode 100644 index 000000000000..8f7effb4d2b8 --- /dev/null +++ b/svx/source/accessibility/AccessibleOLEShape.cxx @@ -0,0 +1,232 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svx.hxx" + + +#include <svx/AccessibleOLEShape.hxx> + +#include <svx/ShapeTypeHandler.hxx> +#include <svx/SvxShapeTypes.hxx> + +using namespace accessibility; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::accessibility; + +//===== internal ============================================================ + +AccessibleOLEShape::AccessibleOLEShape ( + const AccessibleShapeInfo& rShapeInfo, + const AccessibleShapeTreeInfo& rShapeTreeInfo) + : AccessibleShape (rShapeInfo, rShapeTreeInfo) +{ +} + + + + +AccessibleOLEShape::~AccessibleOLEShape (void) +{ +} + + + + +//===== XAccessibleAction =================================================== + +sal_Int32 SAL_CALL AccessibleOLEShape::getAccessibleActionCount (void) + throw (::com::sun::star::uno::RuntimeException) +{ + return 0; +} + + + + +sal_Bool SAL_CALL AccessibleOLEShape::doAccessibleAction (sal_Int32 /*nIndex*/) + throw (lang::IndexOutOfBoundsException, uno::RuntimeException) +{ + throw lang::IndexOutOfBoundsException(); +} + + + + +::rtl::OUString SAL_CALL AccessibleOLEShape::getAccessibleActionDescription (sal_Int32 /*nIndex*/) + throw (::com::sun::star::lang::IndexOutOfBoundsException, + ::com::sun::star::uno::RuntimeException) +{ + throw lang::IndexOutOfBoundsException(); +} + + + + +Reference<XAccessibleKeyBinding> SAL_CALL AccessibleOLEShape::getAccessibleActionKeyBinding (sal_Int32 /*nIndex*/) + throw (::com::sun::star::lang::IndexOutOfBoundsException, + ::com::sun::star::uno::RuntimeException) +{ + throw lang::IndexOutOfBoundsException(); +} + + + + +//===== XInterface ========================================================== + +com::sun::star::uno::Any SAL_CALL + AccessibleOLEShape::queryInterface (const com::sun::star::uno::Type & rType) + throw (::com::sun::star::uno::RuntimeException) +{ + ::com::sun::star::uno::Any aReturn = AccessibleShape::queryInterface (rType); + if ( ! aReturn.hasValue()) + aReturn = ::cppu::queryInterface (rType, + static_cast<XAccessibleAction*>(this)); + return aReturn; +} + + + +void SAL_CALL + AccessibleOLEShape::acquire (void) + throw () +{ + AccessibleShape::acquire (); +} + + + +void SAL_CALL + AccessibleOLEShape::release (void) + throw () +{ + AccessibleShape::release (); +} + + + + +//===== XServiceInfo ======================================================== + +::rtl::OUString SAL_CALL + AccessibleOLEShape::getImplementationName (void) + throw (::com::sun::star::uno::RuntimeException) +{ + return ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("AccessibleOLEShape")); +} + + + + +::com::sun::star::uno::Sequence< ::rtl::OUString> SAL_CALL + AccessibleOLEShape::getSupportedServiceNames (void) + throw (::com::sun::star::uno::RuntimeException) +{ + ThrowIfDisposed(); + // Get list of supported service names from base class... + uno::Sequence< ::rtl::OUString > aServiceNames = + AccessibleShape::getSupportedServiceNames(); + sal_Int32 nCount (aServiceNames.getLength()); + + // ...and add additional names. + aServiceNames.realloc (nCount + 1); + static const ::rtl::OUString sAdditionalServiceName (RTL_CONSTASCII_USTRINGPARAM( + "com.sun.star.drawing.AccessibleOLEShape")); + aServiceNames[nCount] = sAdditionalServiceName; + + return aServiceNames; +} + + + + +//===== XTypeProvider =================================================== + +uno::Sequence<uno::Type> SAL_CALL + AccessibleOLEShape::getTypes (void) + throw (uno::RuntimeException) +{ + // Get list of types from the context base implementation... + uno::Sequence<uno::Type> aTypeList (AccessibleShape::getTypes()); + // ...and add the additional type for the component. + long nTypeCount = aTypeList.getLength(); + aTypeList.realloc (nTypeCount + 1); + const uno::Type aActionType = + ::getCppuType((const uno::Reference<XAccessibleAction>*)0); + aTypeList[nTypeCount] = aActionType; + + return aTypeList; +} + + + + +/// Set this object's name if is different to the current name. +::rtl::OUString + AccessibleOLEShape::CreateAccessibleBaseName (void) + throw (::com::sun::star::uno::RuntimeException) +{ + ::rtl::OUString sName; + + ShapeTypeId nShapeType = ShapeTypeHandler::Instance().GetTypeId (mxShape); + switch (nShapeType) + { + case DRAWING_APPLET: + sName = ::rtl::OUString (RTL_CONSTASCII_USTRINGPARAM("AppletOLEShape")); + break; + case DRAWING_FRAME: + sName = ::rtl::OUString (RTL_CONSTASCII_USTRINGPARAM("FrameOLEShape")); + break; + case DRAWING_OLE: + sName = ::rtl::OUString (RTL_CONSTASCII_USTRINGPARAM("OLEShape")); + break; + case DRAWING_PLUGIN: + sName = ::rtl::OUString (RTL_CONSTASCII_USTRINGPARAM("PluginOLEShape")); + break; + + default: + sName = ::rtl::OUString (RTL_CONSTASCII_USTRINGPARAM("UnknownAccessibleOLEShape")); + uno::Reference<drawing::XShapeDescriptor> xDescriptor (mxShape, uno::UNO_QUERY); + if (xDescriptor.is()) + sName += ::rtl::OUString (RTL_CONSTASCII_USTRINGPARAM(": ")) + + xDescriptor->getShapeType(); + } + + return sName; +} + + + +::rtl::OUString + AccessibleOLEShape::CreateAccessibleDescription (void) + throw (::com::sun::star::uno::RuntimeException) +{ + return CreateAccessibleName (); +} + diff --git a/svx/source/accessibility/AccessibleShape.cxx b/svx/source/accessibility/AccessibleShape.cxx new file mode 100755 index 000000000000..28cdb8b0882b --- /dev/null +++ b/svx/source/accessibility/AccessibleShape.cxx @@ -0,0 +1,1244 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svx.hxx" +#include <svx/AccessibleShape.hxx> +#include "DescriptionGenerator.hxx" +#include <svx/AccessibleShapeInfo.hxx> +#include <com/sun/star/view/XSelectionSupplier.hpp> +#include <rtl/uuid.h> +#ifndef _COM_SUN_STAR_ACCESSIBILITY_ACCESSIBLE_ROLE_HPP_ +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#endif +#ifndef _COM_SUN_STAR_ACCESSIBILITY_ACCESSIBLE_STATE_TYPE_HPP_ +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#endif +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/XChild.hpp> +#include <com/sun/star/drawing/XShapes.hpp> +#include <com/sun/star/drawing/XShapeDescriptor.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/drawing/FillStyle.hpp> +#include <com/sun/star/text/XText.hpp> +#include <editeng/outlobj.hxx> +#include <rtl/ref.hxx> +#include <editeng/unoedsrc.hxx> +#include <svx/unoshtxt.hxx> +#include <svx/svdobj.hxx> +#include <svx/svdmodel.hxx> +#include "unoapi.hxx" +#include <com/sun/star/uno/Exception.hpp> +#include <svx/ShapeTypeHandler.hxx> +#include <svx/SvxShapeTypes.hxx> + +#ifndef _SVX_ACCESSIBILITY_HRC +#include "accessibility.hrc" +#endif +#include "svdstr.hrc" +#include <svx/dialmgr.hxx> +#include <vcl/svapp.hxx> +#include <unotools/accessiblestatesethelper.hxx> +#include <svx/svdview.hxx> +#include "AccessibleEmptyEditSource.hxx" + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; +using ::com::sun::star::uno::Reference; +using ::rtl::OUString; + +namespace accessibility { + +namespace { + +OUString GetOptionalProperty ( + const Reference<beans::XPropertySet>& rxSet, + const OUString& rsPropertyName) +{ + OUString sValue; + + if (rxSet.is()) + { + const Reference<beans::XPropertySetInfo> xInfo (rxSet->getPropertySetInfo()); + if ( ! xInfo.is() || xInfo->hasPropertyByName(rsPropertyName)) + { + try + { + rxSet->getPropertyValue(rsPropertyName) >>= sValue; + } + catch (beans::UnknownPropertyException&) + { + // This exception should only be thrown when the property + // does not exits (of course) and the XPropertySetInfo is + // not available. + } + } + } + return sValue; +} + +} // end of anonymous namespace + + + + +//===== internal ============================================================ + +AccessibleShape::AccessibleShape ( + const AccessibleShapeInfo& rShapeInfo, + const AccessibleShapeTreeInfo& rShapeTreeInfo) + : AccessibleContextBase (rShapeInfo.mxParent,AccessibleRole::LIST_ITEM), + mpChildrenManager(NULL), + mxShape (rShapeInfo.mxShape), + maShapeTreeInfo (rShapeTreeInfo), + mnIndex (rShapeInfo.mnIndex), + m_nIndexInParent(-1), + mpText (NULL), + mpParent (rShapeInfo.mpChildrenManager) +{ + m_pShape = GetSdrObjectFromXShape(mxShape); + UpdateNameAndDescription(); +} + + + + +AccessibleShape::~AccessibleShape (void) +{ + if (mpChildrenManager != NULL) + delete mpChildrenManager; + if (mpText != NULL) + delete mpText; + OSL_TRACE ("~AccessibleShape"); + + // Unregistering from the various broadcasters should be unnecessary + // since this destructor would not have been called if one of the + // broadcasters would still hold a strong reference to this object. +} + + + + +void AccessibleShape::Init (void) +{ + // Update the OPAQUE and SELECTED shape. + UpdateStates (); + + // Create a children manager when this shape has children of its own. + Reference<drawing::XShapes> xShapes (mxShape, uno::UNO_QUERY); + if (xShapes.is() && xShapes->getCount() > 0) + mpChildrenManager = new ChildrenManager ( + this, xShapes, maShapeTreeInfo, *this); + if (mpChildrenManager != NULL) + mpChildrenManager->Update(); + + // Register at model as document::XEventListener. + if (maShapeTreeInfo.GetModelBroadcaster().is()) + maShapeTreeInfo.GetModelBroadcaster()->addEventListener ( + static_cast<document::XEventListener*>(this)); + + // Beware! Here we leave the paths of the UNO API and descend into the + // depths of the core. Necessary for makeing the edit engine + // accessible. + Reference<text::XText> xText (mxShape, uno::UNO_QUERY); + if (xText.is()) + { + SdrView* pView = maShapeTreeInfo.GetSdrView (); + const Window* pWindow = maShapeTreeInfo.GetWindow (); + if (pView != NULL && pWindow != NULL && mxShape.is()) + { + // #107948# Determine whether shape text is empty + SdrObject* pSdrObject = GetSdrObjectFromXShape(mxShape); + if( pSdrObject ) + { + SdrTextObj* pTextObj = PTR_CAST( SdrTextObj, pSdrObject ); + OutlinerParaObject* pOutlinerParaObject = NULL; + + if( pTextObj ) + pOutlinerParaObject = pTextObj->GetEditOutlinerParaObject(); // Get the OutlinerParaObject if text edit is active + + bool bOwnParaObj = pOutlinerParaObject != NULL; + + if( !pOutlinerParaObject && pSdrObject ) + pOutlinerParaObject = pSdrObject->GetOutlinerParaObject(); + + // create AccessibleTextHelper to handle this shape's text + if( !pOutlinerParaObject ) + { + // empty text -> use proxy edit source to delay creation of EditEngine + ::std::auto_ptr<SvxEditSource> pEditSource( new AccessibleEmptyEditSource ( *pSdrObject, *pView, *pWindow) ); + mpText = new AccessibleTextHelper( pEditSource ); + } + else + { + // non-empty text -> use full-fledged edit source right away + ::std::auto_ptr<SvxEditSource> pEditSource( new SvxTextEditSource ( *pSdrObject, 0, *pView, *pWindow) ); + mpText = new AccessibleTextHelper( pEditSource ); + } + + if( bOwnParaObj ) + delete pOutlinerParaObject; + + mpText->SetEventSource(this); + } + } + } +} + + + + +void AccessibleShape::UpdateStates (void) +{ + ::utl::AccessibleStateSetHelper* pStateSet = + static_cast< ::utl::AccessibleStateSetHelper*>(mxStateSet.get()); + if (pStateSet == NULL) + return; + + // Set the opaque state for certain shape types when their fill style is + // solid. + bool bShapeIsOpaque = false; + switch (ShapeTypeHandler::Instance().GetTypeId (mxShape)) + { + case DRAWING_PAGE: + case DRAWING_RECTANGLE: + case DRAWING_TEXT: + { + uno::Reference<beans::XPropertySet> xSet (mxShape, uno::UNO_QUERY); + if (xSet.is()) + { + try + { + drawing::FillStyle aFillStyle; + bShapeIsOpaque = ( xSet->getPropertyValue (::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("FillStyle"))) >>= aFillStyle) + && aFillStyle == drawing::FillStyle_SOLID; + } + catch (::com::sun::star::beans::UnknownPropertyException&) + { + // Ignore. + } + } + } + } + if (bShapeIsOpaque) + pStateSet->AddState (AccessibleStateType::OPAQUE); + else + pStateSet->RemoveState (AccessibleStateType::OPAQUE); + + // Set the selected state. + bool bShapeIsSelected = false; + // XXX fix_me this has to be done with an extra interface later on + if ( m_pShape && maShapeTreeInfo.GetSdrView() ) + { + bShapeIsSelected = maShapeTreeInfo.GetSdrView()->IsObjMarked(m_pShape) == TRUE; + } + + if (bShapeIsSelected) + pStateSet->AddState (AccessibleStateType::SELECTED); + else + pStateSet->RemoveState (AccessibleStateType::SELECTED); +} + + + + +bool AccessibleShape::operator== (const AccessibleShape& rShape) +{ + return this==&rShape; +} + + + + +sal_Bool AccessibleShape::SetState (sal_Int16 aState) +{ + sal_Bool bStateHasChanged = sal_False; + + if (aState == AccessibleStateType::FOCUSED && mpText != NULL) + { + // Offer FOCUSED state to edit engine and detect whether the state + // changes. + sal_Bool bIsFocused = mpText->HaveFocus (); + mpText->SetFocus (sal_True); + bStateHasChanged = (bIsFocused != mpText->HaveFocus ()); + } + else + bStateHasChanged = AccessibleContextBase::SetState (aState); + + return bStateHasChanged; +} + + + + +sal_Bool AccessibleShape::ResetState (sal_Int16 aState) +{ + sal_Bool bStateHasChanged = sal_False; + + if (aState == AccessibleStateType::FOCUSED && mpText != NULL) + { + // Try to remove FOCUSED state from the edit engine and detect + // whether the state changes. + sal_Bool bIsFocused = mpText->HaveFocus (); + mpText->SetFocus (sal_False); + bStateHasChanged = (bIsFocused != mpText->HaveFocus ()); + } + else + bStateHasChanged = AccessibleContextBase::ResetState (aState); + + return bStateHasChanged; +} + + + + +sal_Bool AccessibleShape::GetState (sal_Int16 aState) +{ + if (aState == AccessibleStateType::FOCUSED && mpText != NULL) + { + // Just delegate the call to the edit engine. The state is not + // merged into the state set. + return mpText->HaveFocus(); + } + else + return AccessibleContextBase::GetState (aState); +} + + + + +//===== XAccessibleContext ================================================== + +/** The children of this shape come from two sources: The children from + group or scene shapes and the paragraphs of text. +*/ +sal_Int32 SAL_CALL + AccessibleShape::getAccessibleChildCount () + throw (::com::sun::star::uno::RuntimeException) +{ + ThrowIfDisposed (); + sal_Int32 nChildCount = 0; + + // Add the number of shapes that are children of this shape. + if (mpChildrenManager != NULL) + nChildCount += mpChildrenManager->GetChildCount (); + // Add the number text paragraphs. + if (mpText != NULL) + nChildCount += mpText->GetChildCount (); + + return nChildCount; +} + + + + +/** Forward the request to the shape. Return the requested shape or throw + an exception for a wrong index. +*/ +uno::Reference<XAccessible> SAL_CALL + AccessibleShape::getAccessibleChild (sal_Int32 nIndex) + throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::uno::RuntimeException) +{ + ThrowIfDisposed (); + + uno::Reference<XAccessible> xChild; + + // Depending on the index decide whether to delegate this call to the + // children manager or the edit engine. + if ((mpChildrenManager != NULL) + && (nIndex < mpChildrenManager->GetChildCount())) + { + xChild = mpChildrenManager->GetChild (nIndex); + } + else if (mpText != NULL) + { + sal_Int32 nI = nIndex; + if (mpChildrenManager != NULL) + nI -= mpChildrenManager->GetChildCount(); + xChild = mpText->GetChild (nI); + } + else + throw lang::IndexOutOfBoundsException ( + ::rtl::OUString::createFromAscii ("shape has no child with index ") + + rtl::OUString::valueOf(nIndex), + static_cast<uno::XWeak*>(this)); + + return xChild; +} + + + + +/** Return a copy of the state set. + Possible states are: + ENABLED + SHOWING + VISIBLE +*/ +uno::Reference<XAccessibleStateSet> SAL_CALL + AccessibleShape::getAccessibleStateSet (void) + throw (::com::sun::star::uno::RuntimeException) +{ + ::osl::MutexGuard aGuard (maMutex); + Reference<XAccessibleStateSet> xStateSet; + + if (rBHelper.bDisposed || mpText == NULL) + // Return a minimal state set that only contains the DEFUNC state. + xStateSet = AccessibleContextBase::getAccessibleStateSet (); + else + { + ::utl::AccessibleStateSetHelper* pStateSet = + static_cast< ::utl::AccessibleStateSetHelper*>(mxStateSet.get()); + + if (pStateSet != NULL) + { + // Merge current FOCUSED state from edit engine. + if (mpText != NULL) + { + if (mpText->HaveFocus()) + pStateSet->AddState (AccessibleStateType::FOCUSED); + else + pStateSet->RemoveState (AccessibleStateType::FOCUSED); + } + + // Create a copy of the state set that may be modified by the + // caller without affecting the current state set. + xStateSet = Reference<XAccessibleStateSet>( + new ::utl::AccessibleStateSetHelper (*pStateSet)); + } + } + + return xStateSet; +} + + + + +//===== XAccessibleComponent ================================================ + +/** The implementation below is at the moment straightforward. It iterates + over all children (and thereby instances all children which have not + been already instatiated) until a child covering the specifed point is + found. + This leaves room for improvement. For instance, first iterate only over + the already instantiated children and only if no match is found + instantiate the remaining ones. +*/ +uno::Reference<XAccessible > SAL_CALL + AccessibleShape::getAccessibleAtPoint ( + const awt::Point& aPoint) + throw (uno::RuntimeException) +{ + ::osl::MutexGuard aGuard (maMutex); + + sal_Int32 nChildCount = getAccessibleChildCount (); + for (sal_Int32 i=0; i<nChildCount; ++i) + { + Reference<XAccessible> xChild (getAccessibleChild (i)); + if (xChild.is()) + { + Reference<XAccessibleComponent> xChildComponent ( + xChild->getAccessibleContext(), uno::UNO_QUERY); + if (xChildComponent.is()) + { + awt::Rectangle aBBox (xChildComponent->getBounds()); + if ( (aPoint.X >= aBBox.X) + && (aPoint.Y >= aBBox.Y) + && (aPoint.X < aBBox.X+aBBox.Width) + && (aPoint.Y < aBBox.Y+aBBox.Height) ) + return xChild; + } + } + } + + // Have not found a child under the given point. Returning empty + // reference to indicate this. + return uno::Reference<XAccessible>(); +} + + + + +awt::Rectangle SAL_CALL AccessibleShape::getBounds (void) + throw (::com::sun::star::uno::RuntimeException) +{ + ::vos::OGuard aSolarGuard (::Application::GetSolarMutex()); + ::osl::MutexGuard aGuard (maMutex); + + ThrowIfDisposed (); + awt::Rectangle aBoundingBox; + if ( mxShape.is() ) + { + + static const OUString sBoundRectName ( + RTL_CONSTASCII_USTRINGPARAM("BoundRect")); + static const OUString sAnchorPositionName ( + RTL_CONSTASCII_USTRINGPARAM("AnchorPosition")); + + // Get the shape's bounding box in internal coordinates (in 100th of + // mm). Use the property BoundRect. Only if that is not supported ask + // the shape for its position and size directly. + Reference<beans::XPropertySet> xSet (mxShape, uno::UNO_QUERY); + Reference<beans::XPropertySetInfo> xSetInfo; + bool bFoundBoundRect = false; + if (xSet.is()) + { + xSetInfo = xSet->getPropertySetInfo (); + if (xSetInfo.is()) + { + if (xSetInfo->hasPropertyByName (sBoundRectName)) + { + try + { + uno::Any aValue = xSet->getPropertyValue (sBoundRectName); + aValue >>= aBoundingBox; + bFoundBoundRect = true; + } + catch (beans::UnknownPropertyException e) + { + // Handled below (bFoundBoundRect stays false). + } + } + else + OSL_TRACE (" no property BoundRect"); + } + } + + // Fallback when there is no BoundRect Property. + if ( ! bFoundBoundRect ) + { + awt::Point aPosition (mxShape->getPosition()); + awt::Size aSize (mxShape->getSize()); + aBoundingBox = awt::Rectangle ( + aPosition.X, aPosition.Y, + aSize.Width, aSize.Height); + + // While BoundRects have absolute positions, the position returned + // by XPosition::getPosition is relative. Get the anchor position + // (usually not (0,0) for Writer shapes). + if (xSetInfo.is()) + { + if (xSetInfo->hasPropertyByName (sAnchorPositionName)) + { + uno::Any aPos = xSet->getPropertyValue (sAnchorPositionName); + awt::Point aAnchorPosition; + aPos >>= aAnchorPosition; + aBoundingBox.X += aAnchorPosition.X; + aBoundingBox.Y += aAnchorPosition.Y; + } + } + } + + // Transform coordinates from internal to pixel. + if (maShapeTreeInfo.GetViewForwarder() == NULL) + throw uno::RuntimeException (::rtl::OUString ( + RTL_CONSTASCII_USTRINGPARAM( + "AccessibleShape has no valid view forwarder")), + static_cast<uno::XWeak*>(this)); + ::Size aPixelSize = maShapeTreeInfo.GetViewForwarder()->LogicToPixel ( + ::Size (aBoundingBox.Width, aBoundingBox.Height)); + ::Point aPixelPosition = maShapeTreeInfo.GetViewForwarder()->LogicToPixel ( + ::Point (aBoundingBox.X, aBoundingBox.Y)); + + // Clip the shape's bounding box with the bounding box of its parent. + Reference<XAccessibleComponent> xParentComponent ( + getAccessibleParent(), uno::UNO_QUERY); + if (xParentComponent.is()) + { + // Make the coordinates relative to the parent. + awt::Point aParentLocation (xParentComponent->getLocationOnScreen()); + int x = aPixelPosition.getX() - aParentLocation.X; + int y = aPixelPosition.getY() - aParentLocation.Y; + + /* // The following block is a workarround for bug #99889# (property + // BoundRect returnes coordinates relative to document window + // instead of absolute coordinates for shapes in Writer). Has to + // be removed as soon as bug is fixed. + + // Use a non-null anchor position as flag that the shape is in a + // Writer document. + if (xSetInfo.is()) + if (xSetInfo->hasPropertyByName (sAnchorPositionName)) + { + uno::Any aPos = xSet->getPropertyValue (sAnchorPositionName); + awt::Point aAnchorPosition; + aPos >>= aAnchorPosition; + if (aAnchorPosition.X > 0) + { + x = aPixelPosition.getX(); + y = aPixelPosition.getY(); + } + } + // End of workarround. + */ + // Clip with parent (with coordinates relative to itself). + ::Rectangle aBBox ( + x, y, x + aPixelSize.getWidth(), y + aPixelSize.getHeight()); + awt::Size aParentSize (xParentComponent->getSize()); + ::Rectangle aParentBBox (0,0, aParentSize.Width, aParentSize.Height); + aBBox = aBBox.GetIntersection (aParentBBox); + aBoundingBox = awt::Rectangle ( + aBBox.getX(), + aBBox.getY(), + aBBox.getWidth(), + aBBox.getHeight()); + } + else + { + OSL_TRACE ("parent does not support component"); + aBoundingBox = awt::Rectangle ( + aPixelPosition.getX(), aPixelPosition.getY(), + aPixelSize.getWidth(), aPixelSize.getHeight()); + } + } + + return aBoundingBox; +} + + + + +awt::Point SAL_CALL AccessibleShape::getLocation (void) + throw (::com::sun::star::uno::RuntimeException) +{ + ThrowIfDisposed (); + awt::Rectangle aBoundingBox (getBounds()); + return awt::Point (aBoundingBox.X, aBoundingBox.Y); +} + + + + +awt::Point SAL_CALL AccessibleShape::getLocationOnScreen (void) + throw (::com::sun::star::uno::RuntimeException) +{ + ThrowIfDisposed (); + + // Get relative position... + awt::Point aLocation (getLocation ()); + + // ... and add absolute position of the parent. + uno::Reference<XAccessibleComponent> xParentComponent ( + getAccessibleParent(), uno::UNO_QUERY); + if (xParentComponent.is()) + { + awt::Point aParentLocation (xParentComponent->getLocationOnScreen()); + aLocation.X += aParentLocation.X; + aLocation.Y += aParentLocation.Y; + } + else + OSL_TRACE ("getLocation: parent does not support XAccessibleComponent"); + return aLocation; +} + + + + +awt::Size SAL_CALL AccessibleShape::getSize (void) + throw (uno::RuntimeException) +{ + ThrowIfDisposed (); + awt::Rectangle aBoundingBox (getBounds()); + return awt::Size (aBoundingBox.Width, aBoundingBox.Height); +} + + + + +sal_Int32 SAL_CALL AccessibleShape::getForeground (void) + throw (::com::sun::star::uno::RuntimeException) +{ + ThrowIfDisposed (); + sal_Int32 nColor (0x0ffffffL); + + try + { + uno::Reference<beans::XPropertySet> aSet (mxShape, uno::UNO_QUERY); + if (aSet.is()) + { + uno::Any aColor; + aColor = aSet->getPropertyValue (OUString::createFromAscii ("LineColor")); + aColor >>= nColor; + } + } + catch (::com::sun::star::beans::UnknownPropertyException) + { + // Ignore exception and return default color. + } + return nColor; +} + + + + +sal_Int32 SAL_CALL AccessibleShape::getBackground (void) + throw (::com::sun::star::uno::RuntimeException) +{ + ThrowIfDisposed (); + sal_Int32 nColor (0L); + + try + { + uno::Reference<beans::XPropertySet> aSet (mxShape, uno::UNO_QUERY); + if (aSet.is()) + { + uno::Any aColor; + aColor = aSet->getPropertyValue (OUString::createFromAscii ("FillColor")); + aColor >>= nColor; + } + } + catch (::com::sun::star::beans::UnknownPropertyException) + { + // Ignore exception and return default color. + } + return nColor; +} + + + + +//===== XAccessibleEventBroadcaster ========================================= + +void SAL_CALL AccessibleShape::addEventListener ( + const Reference<XAccessibleEventListener >& rxListener) + throw (uno::RuntimeException) +{ + if (rBHelper.bDisposed || rBHelper.bInDispose) + { + uno::Reference<uno::XInterface> xThis ( + (lang::XComponent *)this, uno::UNO_QUERY); + rxListener->disposing (lang::EventObject (xThis)); + } + else + { + AccessibleContextBase::addEventListener (rxListener); + if (mpText != NULL) + mpText->AddEventListener (rxListener); + } +} + + + + +void SAL_CALL AccessibleShape::removeEventListener ( + const Reference<XAccessibleEventListener >& rxListener) + throw (uno::RuntimeException) +{ + AccessibleContextBase::removeEventListener (rxListener); + if (mpText != NULL) + mpText->RemoveEventListener (rxListener); +} + + + + +//===== XInterface ========================================================== + +com::sun::star::uno::Any SAL_CALL + AccessibleShape::queryInterface (const com::sun::star::uno::Type & rType) + throw (::com::sun::star::uno::RuntimeException) +{ + ::com::sun::star::uno::Any aReturn = AccessibleContextBase::queryInterface (rType); + if ( ! aReturn.hasValue()) + aReturn = ::cppu::queryInterface (rType, + static_cast<XAccessibleComponent*>(this), + static_cast<XAccessibleExtendedComponent*>(this), + static_cast<lang::XEventListener*>(this), + static_cast<document::XEventListener*>(this), + static_cast<lang::XUnoTunnel*>(this) + ); + return aReturn; +} + + + + +void SAL_CALL + AccessibleShape::acquire (void) + throw () +{ + AccessibleContextBase::acquire (); +} + + + + +void SAL_CALL + AccessibleShape::release (void) + throw () +{ + AccessibleContextBase::release (); +} + + + + +//===== XServiceInfo ======================================================== + +::rtl::OUString SAL_CALL + AccessibleShape::getImplementationName (void) + throw (::com::sun::star::uno::RuntimeException) +{ + return ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("AccessibleShape")); +} + + + + +uno::Sequence<OUString> SAL_CALL + AccessibleShape::getSupportedServiceNames (void) + throw (::com::sun::star::uno::RuntimeException) +{ + ThrowIfDisposed (); + // Get list of supported service names from base class... + uno::Sequence<OUString> aServiceNames = + AccessibleContextBase::getSupportedServiceNames(); + sal_Int32 nCount (aServiceNames.getLength()); + + // ...and add additional names. + aServiceNames.realloc (nCount + 1); + static const OUString sAdditionalServiceName (RTL_CONSTASCII_USTRINGPARAM( + "com.sun.star.drawing.AccessibleShape")); + aServiceNames[nCount] = sAdditionalServiceName; + + return aServiceNames; +} + + + + + +//===== XTypeProvider =================================================== + +uno::Sequence<uno::Type> SAL_CALL + AccessibleShape::getTypes (void) + throw (uno::RuntimeException) +{ + ThrowIfDisposed (); + // Get list of types from the context base implementation, ... + uno::Sequence<uno::Type> aTypeList (AccessibleContextBase::getTypes()); + // ... get list of types from component base implementation, ... + uno::Sequence<uno::Type> aComponentTypeList (AccessibleComponentBase::getTypes()); + // ... define local types, ... + const uno::Type aLangEventListenerType = + ::getCppuType((const uno::Reference<lang::XEventListener>*)0); + const uno::Type aDocumentEventListenerType = + ::getCppuType((const uno::Reference<document::XEventListener>*)0); + const uno::Type aUnoTunnelType = + ::getCppuType((const uno::Reference<lang::XUnoTunnel>*)0); + // const uno::Type aStateSetType = + // ::getCppuType((const uno::Reference<XAccessibleStateSet>*)0); + + // ... and merge them all into one list. + sal_Int32 nTypeCount (aTypeList.getLength()), + nComponentTypeCount (aComponentTypeList.getLength()); + int i; + + aTypeList.realloc (nTypeCount + nComponentTypeCount + 3); + + for (i=0; i<nComponentTypeCount; i++) + aTypeList[nTypeCount + i] = aComponentTypeList[i]; + + aTypeList[nTypeCount + i++ ] = aLangEventListenerType; + aTypeList[nTypeCount + i++ ] = aDocumentEventListenerType; + aTypeList[nTypeCount + i ] = aUnoTunnelType; + + return aTypeList; +} + + + + +//===== lang::XEventListener ================================================ + +/** Disposing calls are accepted only from the model: Just reset the + reference to the model in the shape tree info. Otherwise this object + remains functional. +*/ +void SAL_CALL + AccessibleShape::disposing (const lang::EventObject& aEvent) + throw (uno::RuntimeException) +{ + ::vos::OGuard aSolarGuard (::Application::GetSolarMutex()); + ::osl::MutexGuard aGuard (maMutex); + + try + { + if (aEvent.Source == maShapeTreeInfo.GetModelBroadcaster()) + { + // Remove reference to model broadcaster to allow it to pass + // away. + maShapeTreeInfo.SetModelBroadcaster(NULL); + } + + } + catch (uno::RuntimeException e) + { + OSL_TRACE ("caught exception while disposing"); + } +} + + + + +//===== document::XEventListener ============================================ + +void SAL_CALL + AccessibleShape::notifyEvent (const document::EventObject& rEventObject) + throw (uno::RuntimeException) +{ + static const OUString sShapeModified ( + RTL_CONSTASCII_USTRINGPARAM("ShapeModified")); + + // First check if the event is for us. + uno::Reference<drawing::XShape> xShape ( + rEventObject.Source, uno::UNO_QUERY); + if ( xShape.get() == mxShape.get() ) + { + if (rEventObject.EventName.equals (sShapeModified)) + { + // Some property of a shape has been modified. Send an event + // that indicates a change of the visible data to all listeners. + CommitChange ( + AccessibleEventId::VISIBLE_DATA_CHANGED, + uno::Any(), + uno::Any()); + + // Name and Description may have changed. Update the local + // values accordingly. + UpdateNameAndDescription(); + } + } +} + + + + +//===== lang::XUnoTunnel ================================================ + +const uno::Sequence< sal_Int8 >& + AccessibleShape::getUnoTunnelImplementationId() + throw() +{ + static uno::Sequence< sal_Int8 >* pSeq = 0; + + if( !pSeq ) + { + ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() ); + + if( !pSeq ) + { + static uno::Sequence< sal_Int8 > aSeq( 16 ); + rtl_createUuid( (sal_uInt8*) aSeq.getArray(), 0, sal_True ); + pSeq = &aSeq; + } + } + + return( *pSeq ); +} + +//------------------------------------------------------------------------------ +AccessibleShape* + AccessibleShape::getImplementation( const uno::Reference< uno::XInterface >& rxIFace ) + throw() +{ + uno::Reference< lang::XUnoTunnel > xTunnel( rxIFace, uno::UNO_QUERY ); + AccessibleShape* pReturn = NULL; + + if( xTunnel.is() ) + pReturn = reinterpret_cast< AccessibleShape* >( xTunnel->getSomething( getUnoTunnelImplementationId() ) ); + + return( pReturn ); +} + +//------------------------------------------------------------------------------ +sal_Int64 SAL_CALL + AccessibleShape::getSomething( const uno::Sequence< sal_Int8 >& rIdentifier ) + throw(uno::RuntimeException) +{ + sal_Int64 nReturn( 0 ); + + if( ( rIdentifier.getLength() == 16 ) && ( 0 == rtl_compareMemory( getUnoTunnelImplementationId().getConstArray(), rIdentifier.getConstArray(), 16 ) ) ) + nReturn = reinterpret_cast< sal_Int64 >( this ); + + return( nReturn ); +} + +//===== IAccessibleViewForwarderListener ==================================== + +void AccessibleShape::ViewForwarderChanged (ChangeType aChangeType, + const IAccessibleViewForwarder* pViewForwarder) +{ + // Inform all listeners that the graphical representation (i.e. size + // and/or position) of the shape has changed. + CommitChange (AccessibleEventId::VISIBLE_DATA_CHANGED, + uno::Any(), + uno::Any()); + + // Tell children manager of the modified view forwarder. + if (mpChildrenManager != NULL) + mpChildrenManager->ViewForwarderChanged (aChangeType, pViewForwarder); + + // update our children that our screen position might have changed + if( mpText ) + mpText->UpdateChildren(); +} + + + + +//===== protected internal ================================================== +/// Set this object's name if is different to the current name. +::rtl::OUString + AccessibleShape::CreateAccessibleBaseName (void) + throw (::com::sun::star::uno::RuntimeException) +{ + return ShapeTypeHandler::CreateAccessibleBaseName( mxShape ); +} + + +::rtl::OUString + AccessibleShape::CreateAccessibleName (void) + throw (::com::sun::star::uno::RuntimeException) +{ + OUString sName (CreateAccessibleBaseName()); + + // Append the shape's index to the name to disambiguate between shapes + // of the same type. If such an index where not given to the + // constructor then use the z-order instead. If even that does not exist + // we throw an exception. + long nIndex = mnIndex; + if (nIndex == -1) + { + try + { + uno::Reference<beans::XPropertySet> xSet (mxShape, uno::UNO_QUERY); + if (xSet.is()) + { + uno::Any aZOrder (xSet->getPropertyValue (::rtl::OUString::createFromAscii ("ZOrder"))); + aZOrder >>= nIndex; + + // Add one to be not zero based. + nIndex += 1; + } + } + catch (beans::UnknownPropertyException) + { + // We throw our own exception that is a bit more informative. + throw uno::RuntimeException (::rtl::OUString ( + RTL_CONSTASCII_USTRINGPARAM("AccessibleShape has invalid index and no ZOrder property")), + static_cast<uno::XWeak*>(this)); + } + + } + + // Put a space between name and index because of Gnopernicus othewise + // spells the name. + sName += OUString (RTL_CONSTASCII_USTRINGPARAM(" ")) + OUString::valueOf (nIndex); + + return sName; +} + + + + +::rtl::OUString + AccessibleShape::CreateAccessibleDescription (void) + throw (::com::sun::star::uno::RuntimeException) +{ + DescriptionGenerator aDG (mxShape); + aDG.Initialize (CreateAccessibleBaseName()); + switch (ShapeTypeHandler::Instance().GetTypeId (mxShape)) + { + case DRAWING_3D_CUBE: + case DRAWING_3D_EXTRUDE: + case DRAWING_3D_LATHE: + case DRAWING_3D_SPHERE: + aDG.Add3DProperties (); + break; + + case DRAWING_3D_SCENE: + case DRAWING_GROUP: + case DRAWING_PAGE: + // No further information is appended. + break; + + case DRAWING_CAPTION: + case DRAWING_CLOSED_BEZIER: + case DRAWING_CLOSED_FREEHAND: + case DRAWING_ELLIPSE: + case DRAWING_POLY_POLYGON: + case DRAWING_POLY_POLYGON_PATH: + case DRAWING_RECTANGLE: + aDG.AddLineProperties (); + aDG.AddFillProperties (); + break; + + case DRAWING_CONNECTOR: + case DRAWING_LINE: + case DRAWING_MEASURE: + case DRAWING_OPEN_BEZIER: + case DRAWING_OPEN_FREEHAND: + case DRAWING_POLY_LINE: + case DRAWING_POLY_LINE_PATH: + aDG.AddLineProperties (); + break; + + case DRAWING_CONTROL: + aDG.AddProperty (OUString::createFromAscii ("ControlBackground"), + DescriptionGenerator::COLOR, + OUString()); + aDG.AddProperty (OUString::createFromAscii ("ControlBorder"), + DescriptionGenerator::INTEGER, + OUString()); + break; + + case DRAWING_TEXT: + aDG.AddTextProperties (); + break; + + default: + aDG.Initialize (::rtl::OUString ( + RTL_CONSTASCII_USTRINGPARAM("Unknown accessible shape"))); + uno::Reference<drawing::XShapeDescriptor> xDescriptor (mxShape, uno::UNO_QUERY); + if (xDescriptor.is()) + { + aDG.AppendString (::rtl::OUString (RTL_CONSTASCII_USTRINGPARAM("service name="))); + aDG.AppendString (xDescriptor->getShapeType()); + } + } + + return aDG(); +} + + + + +uno::Reference< drawing::XShape > AccessibleShape::GetXShape() +{ + return( mxShape ); +} + + + +// protected +void AccessibleShape::disposing (void) +{ + ::vos::OGuard aSolarGuard (::Application::GetSolarMutex()); + ::osl::MutexGuard aGuard (maMutex); + + // Make sure to send an event that this object looses the focus in the + // case that it has the focus. + ::utl::AccessibleStateSetHelper* pStateSet = + static_cast< ::utl::AccessibleStateSetHelper*>(mxStateSet.get()); + if (pStateSet != NULL) + pStateSet->RemoveState (AccessibleStateType::FOCUSED); + + // Unregister from broadcasters. + Reference<lang::XComponent> xComponent (mxShape, uno::UNO_QUERY); + if (xComponent.is()) + xComponent->removeEventListener (this); + + // Unregister from model. + if (maShapeTreeInfo.GetModelBroadcaster().is()) + maShapeTreeInfo.GetModelBroadcaster()->removeEventListener ( + static_cast<document::XEventListener*>(this)); + + // Release the child containers. + if (mpChildrenManager != NULL) + { + delete mpChildrenManager; + mpChildrenManager = NULL; + } + if (mpText != NULL) + { + mpText->Dispose(); + delete mpText; + mpText = NULL; + } + + // Cleanup. Remove references to objects to allow them to be + // destroyed. + mxShape = NULL; + maShapeTreeInfo = AccessibleShapeTreeInfo(); + + // Call base classes. + AccessibleContextBase::dispose (); +} + +sal_Int32 SAL_CALL + AccessibleShape::getAccessibleIndexInParent (void) + throw (::com::sun::star::uno::RuntimeException) +{ + ThrowIfDisposed (); + // Use a simple but slow solution for now. Optimize later. + + sal_Int32 nIndex = m_nIndexInParent; + if ( -1 == nIndex ) + nIndex = AccessibleContextBase::getAccessibleIndexInParent(); + return nIndex; +} + + + + +void AccessibleShape::UpdateNameAndDescription (void) +{ + // Ignore missing title, name, or description. There are fallbacks for + // them. + try + { + Reference<beans::XPropertySet> xSet (mxShape, uno::UNO_QUERY_THROW); + OUString sString; + + // Get the accessible name. + sString = GetOptionalProperty(xSet, OUString(RTL_CONSTASCII_USTRINGPARAM("Title"))); + if (sString.getLength() > 0) + { + SetAccessibleName(sString, AccessibleContextBase::FromShape); + } + else + { + sString = GetOptionalProperty(xSet, OUString(RTL_CONSTASCII_USTRINGPARAM("Name"))); + if (sString.getLength() > 0) + SetAccessibleName(sString, AccessibleContextBase::FromShape); + } + + // Get the accessible description. + sString = GetOptionalProperty(xSet, OUString(RTL_CONSTASCII_USTRINGPARAM("Description"))); + if (sString.getLength() > 0) + SetAccessibleDescription(sString, AccessibleContextBase::FromShape); + } + catch (uno::RuntimeException&) + { + } +} + + + + +} // end of namespace accessibility diff --git a/svx/source/accessibility/AccessibleShapeInfo.cxx b/svx/source/accessibility/AccessibleShapeInfo.cxx new file mode 100644 index 000000000000..7d6eb8f4c50f --- /dev/null +++ b/svx/source/accessibility/AccessibleShapeInfo.cxx @@ -0,0 +1,84 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svx.hxx" + + +#include <svx/AccessibleShapeInfo.hxx> + + +namespace accessibility { + +AccessibleShapeInfo::AccessibleShapeInfo ( + const ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XShape>& rxShape, + const ::com::sun::star::uno::Reference< + ::com::sun::star::accessibility::XAccessible>& rxParent, + IAccessibleParent* pChildrenManager, + sal_Int32 nIndex) + : mxShape (rxShape), + mxParent (rxParent), + mpChildrenManager (pChildrenManager), + mnIndex (nIndex) +{ + // empty. +} + + + + +AccessibleShapeInfo::AccessibleShapeInfo ( + const ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XShape>& rxShape, + const ::com::sun::star::uno::Reference< + ::com::sun::star::accessibility::XAccessible>& rxParent, + sal_Int32 nIndex) + : mxShape (rxShape), + mxParent (rxParent), + mpChildrenManager (NULL), + mnIndex (nIndex) +{ + // empty. +} + +AccessibleShapeInfo::AccessibleShapeInfo (const AccessibleShapeInfo &rOther) + : mxShape (rOther.mxShape), + mxParent (rOther.mxParent), + mpChildrenManager (rOther.mpChildrenManager), + mnIndex (rOther.mnIndex) +{ + // empty. +} + + +AccessibleShapeInfo::~AccessibleShapeInfo (void) +{ + // empty. +} + +} // end of namespace accessibility. diff --git a/svx/source/accessibility/AccessibleShapeTreeInfo.cxx b/svx/source/accessibility/AccessibleShapeTreeInfo.cxx new file mode 100644 index 000000000000..5241fa5abbf1 --- /dev/null +++ b/svx/source/accessibility/AccessibleShapeTreeInfo.cxx @@ -0,0 +1,223 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svx.hxx" + +#include <svx/AccessibleShapeTreeInfo.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; +using ::com::sun::star::uno::Reference; + +namespace accessibility { + +AccessibleShapeTreeInfo::AccessibleShapeTreeInfo ( + const Reference<XAccessibleComponent>& rxDocumentWindow, + const Reference<document::XEventBroadcaster>& rxModelBroadcaster) + : mxDocumentWindow (rxDocumentWindow), + mxModelBroadcaster (rxModelBroadcaster), + mpView (NULL), + mpWindow (NULL), + mpViewForwarder (NULL) +{ + // Empty. +} + + + + +AccessibleShapeTreeInfo::AccessibleShapeTreeInfo (void) + : mpView (NULL), + mpWindow (NULL), + mpViewForwarder (NULL) +{ + // Empty. +} + + + + +AccessibleShapeTreeInfo::AccessibleShapeTreeInfo (const AccessibleShapeTreeInfo& rInfo) + : mxDocumentWindow (rInfo.mxDocumentWindow), + mxModelBroadcaster (rInfo.mxModelBroadcaster), + mpView (rInfo.mpView), + mxController (rInfo.mxController), + mpWindow (rInfo.mpWindow), + mpViewForwarder (rInfo.mpViewForwarder) +{ + // Empty. +} + + + + +AccessibleShapeTreeInfo& AccessibleShapeTreeInfo::operator= (const AccessibleShapeTreeInfo& rInfo) +{ + if ( this != &rInfo ) + { + mxDocumentWindow = rInfo.mxDocumentWindow; + mxModelBroadcaster = rInfo.mxModelBroadcaster; + mpView = rInfo.mpView; + mxController = rInfo.mxController, + mpWindow = rInfo.mpWindow; + mpViewForwarder = rInfo.mpViewForwarder; + } + return *this; +} + + + + +AccessibleShapeTreeInfo::~AccessibleShapeTreeInfo (void) +{ + //empty +} + + + + +void AccessibleShapeTreeInfo::SetDocumentWindow ( + const Reference<XAccessibleComponent>& rxDocumentWindow) +{ + if (mxDocumentWindow != rxDocumentWindow) + mxDocumentWindow = rxDocumentWindow; +} + + + + +uno::Reference<XAccessibleComponent> + AccessibleShapeTreeInfo::GetDocumentWindow (void) const +{ + return mxDocumentWindow; +} + + + + +void AccessibleShapeTreeInfo::SetControllerBroadcaster ( + const uno::Reference<document::XEventBroadcaster>& rxControllerBroadcaster) +{ + mxModelBroadcaster = rxControllerBroadcaster; +} + + + + +uno::Reference<document::XEventBroadcaster> + AccessibleShapeTreeInfo::GetControllerBroadcaster (void) const +{ + return mxModelBroadcaster; +} + + + + +void AccessibleShapeTreeInfo::SetModelBroadcaster ( + const Reference<document::XEventBroadcaster>& rxModelBroadcaster) +{ + mxModelBroadcaster = rxModelBroadcaster; +} + + + + +Reference<document::XEventBroadcaster> + AccessibleShapeTreeInfo::GetModelBroadcaster (void) const +{ + return mxModelBroadcaster; +} + + + + +void AccessibleShapeTreeInfo::SetSdrView (SdrView* pView) +{ + mpView = pView; +} + + + + +SdrView* AccessibleShapeTreeInfo::GetSdrView (void) const +{ + return mpView; +} + + + + +void AccessibleShapeTreeInfo::SetController ( + const Reference<frame::XController>& rxController) +{ + mxController = rxController; +} + + + + +Reference<frame::XController> + AccessibleShapeTreeInfo::GetController (void) const +{ + return mxController; +} + + + + +void AccessibleShapeTreeInfo::SetWindow (Window* pWindow) +{ + mpWindow = pWindow; +} + + + + +Window* AccessibleShapeTreeInfo::GetWindow (void) const +{ + return mpWindow; +} + + + + +void AccessibleShapeTreeInfo::SetViewForwarder (const IAccessibleViewForwarder* pViewForwarder) +{ + mpViewForwarder = pViewForwarder; +} + + + + +const IAccessibleViewForwarder* AccessibleShapeTreeInfo::GetViewForwarder (void) const +{ + return mpViewForwarder; +} + + +} // end of namespace accessibility diff --git a/svx/source/accessibility/AccessibleTextEventQueue.cxx b/svx/source/accessibility/AccessibleTextEventQueue.cxx new file mode 100644 index 000000000000..fa34e6d0cc17 --- /dev/null +++ b/svx/source/accessibility/AccessibleTextEventQueue.cxx @@ -0,0 +1,111 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svx.hxx" +#include "AccessibleTextEventQueue.hxx" +#include <svx/unoshape.hxx> +#include "editeng/unolingu.hxx" +#include <editeng/unotext.hxx> + +#include "editeng/unoedhlp.hxx" +#include "editeng/unopracc.hxx" +#include <svx/svdmodel.hxx> +#include <svx/svdpntv.hxx> +#include <editeng/editdata.hxx> +#include <editeng/editeng.hxx> +#include <editeng/editview.hxx> + +namespace accessibility +{ + //------------------------------------------------------------------------ + // + // EventQueue implementation + // + //------------------------------------------------------------------------ + + AccessibleTextEventQueue::AccessibleTextEventQueue() + { + } + + AccessibleTextEventQueue::~AccessibleTextEventQueue() + { + Clear(); + } + + void AccessibleTextEventQueue::Append( const SfxHint& rHint ) + { + maEventQueue.push_back( new SfxHint( rHint ) ); + } + + void AccessibleTextEventQueue::Append( const SdrHint& rHint ) + { + maEventQueue.push_back( new SdrHint( rHint ) ); + } + + void AccessibleTextEventQueue::Append( const SfxSimpleHint& rHint ) + { + maEventQueue.push_back( new SfxSimpleHint( rHint ) ); + } + + void AccessibleTextEventQueue::Append( const TextHint& rHint ) + { + maEventQueue.push_back( new TextHint( rHint ) ); + } + + void AccessibleTextEventQueue::Append( const SvxViewHint& rHint ) + { + maEventQueue.push_back( new SvxViewHint( rHint ) ); + } + + void AccessibleTextEventQueue::Append( const SvxEditSourceHint& rHint ) + { + maEventQueue.push_back( new SvxEditSourceHint( rHint ) ); + } + + ::std::auto_ptr< SfxHint > AccessibleTextEventQueue::PopFront() + { + ::std::auto_ptr< SfxHint > aRes( *(maEventQueue.begin()) ); + maEventQueue.pop_front(); + return aRes; + } + + bool AccessibleTextEventQueue::IsEmpty() const + { + return maEventQueue.empty(); + } + + void AccessibleTextEventQueue::Clear() + { + // clear queue + while( !IsEmpty() ) + PopFront(); + } + +} // end of namespace accessibility + +//------------------------------------------------------------------------ diff --git a/svx/source/accessibility/AccessibleTextEventQueue.hxx b/svx/source/accessibility/AccessibleTextEventQueue.hxx new file mode 100644 index 000000000000..8f508858108a --- /dev/null +++ b/svx/source/accessibility/AccessibleTextEventQueue.hxx @@ -0,0 +1,103 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SVX_TEXT_CHANGED_QUEUE_HXX +#define _SVX_TEXT_CHANGED_QUEUE_HXX + +#include <memory> +#include <list> +#include <algorithm> +#include <tools/solar.h> +#include <tools/rtti.hxx> + +class SfxHint; +class SdrHint; +class SfxSimpleHint; +class TextHint; +class SvxViewHint; +class SvxEditSourceHint; + +namespace accessibility +{ + /** This class handles the notification events for the + AccessibleTextHelper class. + + For various reasons, we cannot process EditEngine events as + they arrive, but have to queue and handle them in a batch. + */ + class AccessibleTextEventQueue + { + public: + typedef ::std::list< SfxHint* > EventQueue; + + AccessibleTextEventQueue(); + ~AccessibleTextEventQueue(); + + /// Append event to end of queue + void Append( const SfxHint& rHint ); + /// Append event to end of queue + void Append( const SdrHint& rHint ); + /// Append event to end of queue + void Append( const SfxSimpleHint& rHint ); + /// Append event to end of queue + void Append( const TextHint& rHint ); + /// Append event to end of queue + void Append( const SvxViewHint& rHint ); + /// Append event to end of queue + void Append( const SvxEditSourceHint& rHint ); + + /** Pop first queue element + + return first queue element, ownership transfers to caller + */ + ::std::auto_ptr< SfxHint > PopFront(); + + /** Apply functor to every queue member + + @param rFunctor + Functor to apply. Functor receives queue element as + parameter: void func( const SfxHint* ); + */ + template < typename Functor > void ForEach( Functor& rFunctor ) const + { + // #109864# Make sure results are put back into rFunctor + rFunctor = ::std::for_each( maEventQueue.begin(), maEventQueue.end(), rFunctor ); + } + + /// Query whether queue is empty + bool IsEmpty() const; + + /// Clear event queue + void Clear(); + + private: + EventQueue maEventQueue; + }; + +} // end of namespace accessibility + +#endif /* _SVX_TEXT_CHANGED_QUEUE_HXX */ diff --git a/svx/source/accessibility/AccessibleTextHelper.cxx b/svx/source/accessibility/AccessibleTextHelper.cxx new file mode 100644 index 000000000000..25bed87a23ad --- /dev/null +++ b/svx/source/accessibility/AccessibleTextHelper.cxx @@ -0,0 +1,2077 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svx.hxx" + +//------------------------------------------------------------------------ +// +// Global header +// +//------------------------------------------------------------------------ + +#include <limits.h> +#include <memory> +#include <algorithm> +#include <deque> +#include <vos/mutex.hxx> +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/Reference.hxx> +#include <cppuhelper/weakref.hxx> +#include <com/sun/star/awt/Point.hpp> +#include <com/sun/star/awt/Rectangle.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <com/sun/star/accessibility/XAccessible.hpp> +#include <com/sun/star/accessibility/XAccessibleContext.hpp> +#include <com/sun/star/accessibility/XAccessibleComponent.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <comphelper/accessibleeventnotifier.hxx> +#include <unotools/accessiblestatesethelper.hxx> +#include <vcl/unohelp.hxx> +#include <vcl/svapp.hxx> + +//------------------------------------------------------------------------ +// +// Project-local header +// +//------------------------------------------------------------------------ +#include "AccessibleTextEventQueue.hxx" +#include <svx/AccessibleTextHelper.hxx> +#include <svx/unoshape.hxx> +#include "editeng/unolingu.hxx" +#include <editeng/unotext.hxx> + +#include "editeng/unoedhlp.hxx" +#include "editeng/unopracc.hxx" +#include "editeng/AccessibleParaManager.hxx" +#include "editeng/AccessibleEditableTextPara.hxx" +#include <svx/svdmodel.hxx> +#include <svx/svdpntv.hxx> +#include <editeng/editdata.hxx> +#include <editeng/editeng.hxx> +#include <editeng/editview.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; + +namespace accessibility +{ + +//------------------------------------------------------------------------ +// +// AccessibleTextHelper_Impl declaration +// +//------------------------------------------------------------------------ + + DBG_NAME( AccessibleTextHelper_Impl ) + + template < typename first_type, typename second_type > + ::std::pair< first_type, second_type > makeSortedPair( first_type first, + second_type second ) + { + if( first > second ) + return ::std::make_pair( second, first ); + else + return ::std::make_pair( first, second ); + } + + class AccessibleTextHelper_Impl : public SfxListener + { + + public: + typedef ::std::vector< sal_Int16 > VectorOfStates; + + // receive pointer to our frontend class and view window + AccessibleTextHelper_Impl(); + ~AccessibleTextHelper_Impl(); + + // XAccessibleContext child handling methods + sal_Int32 SAL_CALL getAccessibleChildCount() SAL_THROW((uno::RuntimeException)); + uno::Reference< XAccessible > SAL_CALL getAccessibleChild( sal_Int32 i ) SAL_THROW((lang::IndexOutOfBoundsException, uno::RuntimeException)); + + // XAccessibleEventBroadcaster child related methods + void SAL_CALL addEventListener( const uno::Reference< XAccessibleEventListener >& xListener ) SAL_THROW((uno::RuntimeException)); + void SAL_CALL removeEventListener( const uno::Reference< XAccessibleEventListener >& xListener ) SAL_THROW((uno::RuntimeException)); + + // XAccessibleComponent child related methods + uno::Reference< XAccessible > SAL_CALL getAccessibleAtPoint( const awt::Point& aPoint ) SAL_THROW((uno::RuntimeException)); + + SvxEditSourceAdapter& GetEditSource() const SAL_THROW((uno::RuntimeException)); + void SetEditSource( ::std::auto_ptr< SvxEditSource > pEditSource ) SAL_THROW((uno::RuntimeException)); + + void SetEventSource( const uno::Reference< XAccessible >& rInterface ) + { + DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); + mxFrontEnd = rInterface; + } + uno::Reference< XAccessible > GetEventSource() const + { + DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); + return mxFrontEnd; + } + + void SetOffset( const Point& ); + Point GetOffset() const + { + DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); + ::osl::MutexGuard aGuard( maMutex ); Point aPoint( maOffset ); + return aPoint; + } + + void SetStartIndex( sal_Int32 nOffset ); + sal_Int32 GetStartIndex() const + { + DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); + // Strictly correct only with locked solar mutex, // but + // here we rely on the fact that sal_Int32 access is + // atomic + return mnStartIndex; + } + + void SetAdditionalChildStates( const VectorOfStates& rChildStates ); + const VectorOfStates& GetAdditionalChildStates() const; + + sal_Bool IsSelected() const; + + void Dispose(); + + // do NOT hold object mutex when calling this! Danger of deadlock + void FireEvent( const sal_Int16 nEventId, const uno::Any& rNewValue = uno::Any(), const uno::Any& rOldValue = uno::Any() ) const; + void FireEvent( const AccessibleEventObject& rEvent ) const; + + void SetFocus( sal_Bool bHaveFocus ) SAL_THROW((::com::sun::star::uno::RuntimeException)); + sal_Bool HaveFocus() SAL_THROW((::com::sun::star::uno::RuntimeException)); + void SetChildFocus( sal_Int32 nChild, sal_Bool bHaveFocus ) SAL_THROW((::com::sun::star::uno::RuntimeException)); + void SetShapeFocus( sal_Bool bHaveFocus ) SAL_THROW((::com::sun::star::uno::RuntimeException)); + void ChangeChildFocus( sal_Int32 nNewChild ) SAL_THROW((::com::sun::star::uno::RuntimeException)); + +#ifdef DBG_UTIL + void CheckInvariants() const; +#endif + + // checks all children for visibility, throws away invisible ones + void UpdateVisibleChildren( bool bBroadcastEvents=true ); + + // check all children for changes in positíon and size + void UpdateBoundRect(); + + // calls SetSelection on the forwarder and updates maLastSelection + // cache. + void UpdateSelection(); + + private: + + // Process event queue + void ProcessQueue(); + + // syntactic sugar for FireEvent + void GotPropertyEvent( const uno::Any& rNewValue, const sal_Int16 nEventId ) const { FireEvent( nEventId, rNewValue ); } + void LostPropertyEvent( const uno::Any& rOldValue, const sal_Int16 nEventId ) const { FireEvent( nEventId, uno::Any(), rOldValue ); } + + // shutdown usage of current edit source on myself and the children. + void ShutdownEditSource() SAL_THROW((uno::RuntimeException)); + + void ParagraphsMoved( sal_Int32 nFirst, sal_Int32 nMiddle, sal_Int32 nLast ); + + virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ); + + int getNotifierClientId() const { return mnNotifierClientId; } + + // lock solar mutex before + SvxTextForwarder& GetTextForwarder() const SAL_THROW((uno::RuntimeException)); + // lock solar mutex before + SvxViewForwarder& GetViewForwarder() const SAL_THROW((uno::RuntimeException)); + // lock solar mutex before + SvxEditViewForwarder& GetEditViewForwarder( sal_Bool bCreate = sal_False ) const SAL_THROW((uno::RuntimeException)); + + // are we in edit mode? + sal_Bool IsActive() const SAL_THROW((uno::RuntimeException)); + + // our frontend class (the one implementing the actual + // interface). That's not necessarily the one containing the impl + // pointer! + uno::Reference< XAccessible > mxFrontEnd; + + // a wrapper for the text forwarders (guarded by solar mutex) + mutable SvxEditSourceAdapter maEditSource; + + // store last selection (to correctly report selection changes, guarded by solar mutex) + ESelection maLastSelection; + + // cache range of visible children (guarded by solar mutex) + sal_Int32 mnFirstVisibleChild; + sal_Int32 mnLastVisibleChild; + + // offset to add to all our children (unguarded, relying on + // the fact that sal_Int32 access is atomic) + sal_Int32 mnStartIndex; + + // the object handling our children (guarded by solar mutex) + ::accessibility::AccessibleParaManager maParaManager; + + // number of not-yet-closed event frames (BEGIN/END sequences) (guarded by solar mutex) + sal_Int32 maEventOpenFrames; + + // Queued events from Notify() (guarded by solar mutex) + AccessibleTextEventQueue maEventQueue; + + // spin lock to prevent notify in notify (guarded by solar mutex) + sal_Bool mbInNotify; + + // whether the object or it's children has the focus set (guarded by solar mutex) + sal_Bool mbGroupHasFocus; + + // whether we (this object) has the focus set (guarded by solar mutex) + sal_Bool mbThisHasFocus; + + mutable ::osl::Mutex maMutex; + + /// our current offset to the containing shape/cell (guarded by maMutex) + Point maOffset; + + /// client Id from AccessibleEventNotifier + int mnNotifierClientId; + }; + + //------------------------------------------------------------------------ + // + // AccessibleTextHelper_Impl implementation + // + //------------------------------------------------------------------------ + + AccessibleTextHelper_Impl::AccessibleTextHelper_Impl() : + mxFrontEnd( NULL ), + maLastSelection( EE_PARA_NOT_FOUND,EE_PARA_NOT_FOUND,EE_PARA_NOT_FOUND,EE_PARA_NOT_FOUND ), + mnFirstVisibleChild( -1 ), + mnLastVisibleChild( -2 ), + mnStartIndex( 0 ), + maEventOpenFrames( 0 ), + mbInNotify( sal_False ), + mbGroupHasFocus( sal_False ), + mbThisHasFocus( sal_False ), + maOffset(0,0), + // well, that's strictly exception safe, though not really + // robust. We rely on the fact that this member is constructed + // last, and that the constructor body is empty, thus no + // chance for exceptions once the Id is fetched. Nevertheless, + // normally should employ RAII here... + mnNotifierClientId(::comphelper::AccessibleEventNotifier::registerClient()) + { + DBG_CTOR( AccessibleTextHelper_Impl, NULL ); + +#ifdef DBG_UTIL + OSL_TRACE( "AccessibleTextHelper_Impl received ID: %d", mnNotifierClientId ); +#endif + } + + AccessibleTextHelper_Impl::~AccessibleTextHelper_Impl() + { + DBG_DTOR( AccessibleTextHelper_Impl, NULL ); + + ::vos::OGuard aGuard( Application::GetSolarMutex() ); + + try + { + // call Dispose here, too, since we've some resources not + // automatically freed otherwise + Dispose(); + } + catch( const uno::Exception& ) {} + } + + SvxTextForwarder& AccessibleTextHelper_Impl::GetTextForwarder() const SAL_THROW((uno::RuntimeException)) + { + DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); + + if( !maEditSource.IsValid() ) + throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Unknown edit source")), mxFrontEnd); + + SvxTextForwarder* pTextForwarder = maEditSource.GetTextForwarder(); + + if( !pTextForwarder ) + throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Unable to fetch text forwarder, model might be dead")), mxFrontEnd); + + if( pTextForwarder->IsValid() ) + return *pTextForwarder; + else + throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Text forwarder is invalid, model might be dead")), mxFrontEnd); + } + + SvxViewForwarder& AccessibleTextHelper_Impl::GetViewForwarder() const SAL_THROW((uno::RuntimeException)) + { + DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); + + if( !maEditSource.IsValid() ) + throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Unknown edit source")), mxFrontEnd); + + SvxViewForwarder* pViewForwarder = maEditSource.GetViewForwarder(); + + if( !pViewForwarder ) + throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Unable to fetch view forwarder, model might be dead")), mxFrontEnd); + + if( pViewForwarder->IsValid() ) + return *pViewForwarder; + else + throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("View forwarder is invalid, model might be dead")), mxFrontEnd); + } + + SvxEditViewForwarder& AccessibleTextHelper_Impl::GetEditViewForwarder( sal_Bool bCreate ) const SAL_THROW((uno::RuntimeException)) + { + DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); + + if( !maEditSource.IsValid() ) + throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Unknown edit source")), mxFrontEnd); + + SvxEditViewForwarder* pViewForwarder = maEditSource.GetEditViewForwarder( bCreate ); + + if( !pViewForwarder ) + { + if( bCreate ) + throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Unable to fetch edit view forwarder, model might be dead")), mxFrontEnd); + else + throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("No edit view forwarder, object not in edit mode")), mxFrontEnd); + } + + if( pViewForwarder->IsValid() ) + return *pViewForwarder; + else + { + if( bCreate ) + throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("View forwarder is invalid, model might be dead")), mxFrontEnd); + else + throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("View forwarder is invalid, object not in edit mode")), mxFrontEnd); + } + } + + SvxEditSourceAdapter& AccessibleTextHelper_Impl::GetEditSource() const SAL_THROW((uno::RuntimeException)) + { + DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); + + if( maEditSource.IsValid() ) + return maEditSource; + else + throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("AccessibleTextHelper_Impl::GetEditSource: no edit source")), mxFrontEnd ); + } + + sal_Bool AccessibleTextHelper_Impl::IsSelected() const + { + DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); + + sal_Bool bRet = sal_False; + + try + { + ESelection aSelection; + bRet = GetEditViewForwarder().GetSelection( aSelection ); + } + catch( const uno::Exception& ) {} + + return bRet; + } + + // functor for sending child events (no stand-alone function, they are maybe not inlined) + class AccessibleTextHelper_OffsetChildIndex : public ::std::unary_function< ::accessibility::AccessibleEditableTextPara&, void > + { + public: + AccessibleTextHelper_OffsetChildIndex( sal_Int32 nDifference ) : mnDifference(nDifference) {} + void operator()( ::accessibility::AccessibleEditableTextPara& rPara ) + { + rPara.SetIndexInParent( rPara.GetIndexInParent() + mnDifference ); + } + + private: + const sal_Int32 mnDifference; + }; + + void AccessibleTextHelper_Impl::SetStartIndex( sal_Int32 nOffset ) + { + DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); + + sal_Int32 nOldOffset( mnStartIndex ); + + mnStartIndex = nOffset; + + if( nOldOffset != nOffset ) + { + // update children + AccessibleTextHelper_OffsetChildIndex aFunctor( nOffset - nOldOffset ); + + ::std::for_each( maParaManager.begin(), maParaManager.end(), + AccessibleParaManager::WeakChildAdapter< AccessibleTextHelper_OffsetChildIndex > (aFunctor) ); + } + } + + void AccessibleTextHelper_Impl::SetAdditionalChildStates( const VectorOfStates& rChildStates ) + { + maParaManager.SetAdditionalChildStates( rChildStates ); + } + + const AccessibleTextHelper_Impl::VectorOfStates& AccessibleTextHelper_Impl::GetAdditionalChildStates() const + { + return maParaManager.GetAdditionalChildStates(); + } + + void AccessibleTextHelper_Impl::SetChildFocus( sal_Int32 nChild, sal_Bool bHaveFocus ) SAL_THROW((::com::sun::star::uno::RuntimeException)) + { + DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); + + if( bHaveFocus ) + { + if( mbThisHasFocus ) + SetShapeFocus( sal_False ); + + maParaManager.SetFocus( nChild ); + + // we just received the focus, also send caret event then + UpdateSelection(); + + DBG_TRACE1("AccessibleTextHelper_Impl::SetChildFocus(): Paragraph %d received focus", nChild ); + } + else + { + maParaManager.SetFocus( -1 ); + + DBG_TRACE1("AccessibleTextHelper_Impl::SetChildFocus(): Paragraph %d lost focus", nChild ); + + if( mbGroupHasFocus ) + SetShapeFocus( sal_True ); + } + } + + void AccessibleTextHelper_Impl::ChangeChildFocus( sal_Int32 nNewChild ) SAL_THROW((::com::sun::star::uno::RuntimeException)) + { + DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); + + if( mbThisHasFocus ) + SetShapeFocus( sal_False ); + + mbGroupHasFocus = sal_True; + maParaManager.SetFocus( nNewChild ); + + DBG_TRACE1("AccessibleTextHelper_Impl::ChangeChildFocus(): Paragraph %d received focus", nNewChild ); + } + + void AccessibleTextHelper_Impl::SetShapeFocus( sal_Bool bHaveFocus ) SAL_THROW((::com::sun::star::uno::RuntimeException)) + { + DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); + + sal_Bool bOldFocus( mbThisHasFocus ); + + mbThisHasFocus = bHaveFocus; + + if( bOldFocus != bHaveFocus ) + { + if( bHaveFocus ) + { + GotPropertyEvent( uno::makeAny(AccessibleStateType::FOCUSED), AccessibleEventId::STATE_CHANGED ); + DBG_TRACE("AccessibleTextHelper_Impl::SetShapeFocus(): Parent object received focus" ); + } + else + { + LostPropertyEvent( uno::makeAny(AccessibleStateType::FOCUSED), AccessibleEventId::STATE_CHANGED ); + DBG_TRACE("AccessibleTextHelper_Impl::SetShapeFocus(): Parent object lost focus" ); + } + } + } + + void AccessibleTextHelper_Impl::SetFocus( sal_Bool bHaveFocus ) SAL_THROW((::com::sun::star::uno::RuntimeException)) + { + DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); + + sal_Bool bOldFocus( mbGroupHasFocus ); + + mbGroupHasFocus = bHaveFocus; + + if( IsActive() ) + { + try + { + // find the one with the cursor and get/set focus accordingly + ESelection aSelection; + if( GetEditViewForwarder().GetSelection( aSelection ) ) + SetChildFocus( aSelection.nEndPara, bHaveFocus ); + } + catch( const uno::Exception& ) {} + } + else if( bOldFocus != bHaveFocus ) + { + SetShapeFocus( bHaveFocus ); + } + + DBG_TRACE2("AccessibleTextHelper_Impl::SetFocus: focus changed, Object %d, state: %s", this, bHaveFocus ? "focused" : "not focused"); + } + + sal_Bool AccessibleTextHelper_Impl::HaveFocus() SAL_THROW((::com::sun::star::uno::RuntimeException)) + { + DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); + + // No locking of solar mutex here, since we rely on the fact + // that sal_Bool access is atomic + return mbThisHasFocus; + } + + sal_Bool AccessibleTextHelper_Impl::IsActive() const SAL_THROW((uno::RuntimeException)) + { + DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); + + try + { + SvxEditSource& rEditSource = GetEditSource(); + SvxEditViewForwarder* pViewForwarder = rEditSource.GetEditViewForwarder(); + + if( !pViewForwarder ) + return sal_False; + + if( pViewForwarder->IsValid() ) + return sal_True; + else + return sal_False; + } + catch( const uno::RuntimeException& ) + { + return sal_False; + } + } + + void AccessibleTextHelper_Impl::UpdateSelection() + { + DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); + + try + { + ESelection aSelection; + if( GetEditViewForwarder().GetSelection( aSelection ) ) + { + if( !maLastSelection.IsEqual( aSelection ) && + aSelection.nEndPara < maParaManager.GetNum() ) + { + // #103998# Not that important, changed from assertion to trace + if( mbThisHasFocus ) + { + DBG_TRACE("AccessibleTextHelper_Impl::UpdateSelection(): Parent has focus!"); + } + + USHORT nMaxValidParaIndex( static_cast< USHORT >( GetTextForwarder().GetParagraphCount() ) - 1 ); + + // notify all affected paragraphs (TODO: may be suboptimal, + // since some paragraphs might stay selected) + if( maLastSelection.nStartPara != EE_PARA_NOT_FOUND ) + { + // Did the caret move from one paragraph to another? + // #100530# no caret events if not focused. + if( mbGroupHasFocus && + maLastSelection.nEndPara != aSelection.nEndPara ) + { + if( maLastSelection.nEndPara < maParaManager.GetNum() ) + { + maParaManager.FireEvent( ::std::min( maLastSelection.nEndPara, nMaxValidParaIndex ), + ::std::min( maLastSelection.nEndPara, nMaxValidParaIndex )+1, + AccessibleEventId::CARET_CHANGED, + uno::makeAny(static_cast<sal_Int32>(-1)), + uno::makeAny(static_cast<sal_Int32>(maLastSelection.nEndPos)) ); + } + + ChangeChildFocus( aSelection.nEndPara ); + + DBG_TRACE3("AccessibleTextHelper_Impl::UpdateSelection(): focus changed, Object: %d, Paragraph: %d, Last paragraph: %d", + this, aSelection.nEndPara, maLastSelection.nEndPara); + } + } + + // #100530# no caret events if not focused. + if( mbGroupHasFocus ) + { + uno::Any aOldCursor; + + // #i13705# The old cursor can only contain valid + // values if it's the same paragraph! + if( maLastSelection.nStartPara != EE_PARA_NOT_FOUND && + maLastSelection.nEndPara == aSelection.nEndPara ) + { + aOldCursor <<= static_cast<sal_Int32>(maLastSelection.nEndPos); + } + else + { + aOldCursor <<= static_cast<sal_Int32>(-1); + } + + maParaManager.FireEvent( aSelection.nEndPara, + aSelection.nEndPara+1, + AccessibleEventId::CARET_CHANGED, + uno::makeAny(static_cast<sal_Int32>(aSelection.nEndPos)), + aOldCursor ); + } + + DBG_TRACE5("AccessibleTextHelper_Impl::UpdateSelection(): caret changed, Object: %d, New pos: %d, Old pos: %d, New para: %d, Old para: %d", + this, aSelection.nEndPos, maLastSelection.nEndPos, aSelection.nEndPara, maLastSelection.nEndPara); + + // #108947# Sort new range before calling FireEvent + ::std::pair< xub_StrLen, xub_StrLen > sortedSelection( + makeSortedPair(::std::min( aSelection.nStartPara, nMaxValidParaIndex ), + ::std::min( aSelection.nEndPara, nMaxValidParaIndex ) ) ); + + // #108947# Sort last range before calling FireEvent + ::std::pair< xub_StrLen, xub_StrLen > sortedLastSelection( + makeSortedPair(::std::min( maLastSelection.nStartPara, nMaxValidParaIndex ), + ::std::min( maLastSelection.nEndPara, nMaxValidParaIndex ) ) ); + + // --> OD 2005-12-15 #i27299# + // event TEXT_SELECTION_CHANGED has to be submitted. + const sal_Int16 nTextSelChgEventId = + AccessibleEventId::TEXT_SELECTION_CHANGED; + // <-- + // #107037# notify selection change + if( maLastSelection.nStartPara == EE_PARA_NOT_FOUND ) + { + // last selection is undefined + // --> OD 2005-12-15 #i27299# - use method <ESelection::HasRange()> + if ( aSelection.HasRange() ) + // <-- + { + // selection was undefined, now is on + maParaManager.FireEvent( sortedSelection.first, + sortedSelection.second+1, + nTextSelChgEventId ); + } + } + else + { + // last selection is valid + // --> OD 2005-12-15 #i27299# - use method <ESelection::HasRange()> + if ( maLastSelection.HasRange() && + !aSelection.HasRange() ) + // <-- + { + // selection was on, now is empty + maParaManager.FireEvent( sortedLastSelection.first, + sortedLastSelection.second+1, + nTextSelChgEventId ); + } + // --> OD 2005-12-15 #i27299# - use method <ESelection::HasRange()> + else if( !maLastSelection.HasRange() && + aSelection.HasRange() ) + // <-- + { + // selection was empty, now is on + maParaManager.FireEvent( sortedSelection.first, + sortedSelection.second+1, + nTextSelChgEventId ); + } + // --> OD 2005-12-15 #i27299# + // - no event TEXT_SELECTION_CHANGED event, if new and + // last selection are empty. + else if ( maLastSelection.HasRange() && + aSelection.HasRange() ) + // <-- + { + // --> OD 2005-12-16 #i27299# + // - send event TEXT_SELECTION_CHANGED for difference + // between last and new selection. +// // selection was on, now is different: take union of ranges +// maParaManager.FireEvent( ::std::min(sortedSelection.first, +// sortedLastSelection.second), +// ::std::max(sortedSelection.first, +// sortedLastSelection.second)+1, +// nTextSelChgEventId ); + // use sorted last and new selection + ESelection aTmpLastSel( maLastSelection ); + aTmpLastSel.Adjust(); + ESelection aTmpSel( aSelection ); + aTmpSel.Adjust(); + // first submit event for new and changed selection + sal_uInt32 nPara = aTmpSel.nStartPara; + for ( ; nPara <= aTmpSel.nEndPara; ++nPara ) + { + if ( nPara < aTmpLastSel.nStartPara || + nPara > aTmpLastSel.nEndPara ) + { + // new selection on paragraph <nPara> + maParaManager.FireEvent( nPara, + nTextSelChgEventId ); + } + else + { + // check for changed selection on paragraph <nPara> + const xub_StrLen nParaStartPos = + nPara == aTmpSel.nStartPara + ? aTmpSel.nStartPos : 0; + const xub_StrLen nParaEndPos = + nPara == aTmpSel.nEndPara + ? aTmpSel.nEndPos : STRING_LEN; + const xub_StrLen nLastParaStartPos = + nPara == aTmpLastSel.nStartPara + ? aTmpLastSel.nStartPos : 0; + const xub_StrLen nLastParaEndPos = + nPara == aTmpLastSel.nEndPara + ? aTmpLastSel.nEndPos : STRING_LEN; + if ( nParaStartPos != nLastParaStartPos || + nParaEndPos != nLastParaEndPos ) + { + maParaManager.FireEvent( + nPara, nTextSelChgEventId ); + } + } + } + // second submit event for 'old' selections + nPara = aTmpLastSel.nStartPara; + for ( ; nPara <= aTmpLastSel.nEndPara; ++nPara ) + { + if ( nPara < aTmpSel.nStartPara || + nPara > aTmpSel.nEndPara ) + { + maParaManager.FireEvent( nPara, + nTextSelChgEventId ); + } + } + } + } + + maLastSelection = aSelection; + } + } + } + // no selection? no update actions + catch( const uno::RuntimeException& ) {} + } + + void AccessibleTextHelper_Impl::ShutdownEditSource() SAL_THROW((uno::RuntimeException)) + { + DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); + + // This should only be called with solar mutex locked, i.e. from the main office thread + + // This here is somewhat clumsy: As soon as our children have + // a NULL EditSource (maParaManager.SetEditSource()), they + // enter the disposed state and cannot be reanimated. Thus, it + // is unavoidable and a hard requirement to let go and create + // from scratch each and every child. + + // invalidate children + maParaManager.Dispose(); + maParaManager.SetNum(0); + + // lost all children + if( mxFrontEnd.is() ) + FireEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN); + + // quit listen on stale edit source + if( maEditSource.IsValid() ) + EndListening( maEditSource.GetBroadcaster() ); + + maEditSource.SetEditSource( ::std::auto_ptr< SvxEditSource >(NULL) ); + } + + void AccessibleTextHelper_Impl::SetEditSource( ::std::auto_ptr< SvxEditSource > pEditSource ) SAL_THROW((uno::RuntimeException)) + { + DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); + + // This should only be called with solar mutex locked, i.e. from the main office thread + + // shutdown old edit source + ShutdownEditSource(); + + // set new edit source + maEditSource.SetEditSource( pEditSource ); + + // init child vector to the current child count + if( maEditSource.IsValid() ) + { + maParaManager.SetNum( GetTextForwarder().GetParagraphCount() ); + + // listen on new edit source + StartListening( maEditSource.GetBroadcaster() ); + + UpdateVisibleChildren(); + } + } + + void AccessibleTextHelper_Impl::SetOffset( const Point& rPoint ) + { + DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); + + // guard against non-atomic access to maOffset data structure + { + ::osl::MutexGuard aGuard( maMutex ); + maOffset = rPoint; + } + + maParaManager.SetEEOffset( rPoint ); + + // in all cases, check visibility afterwards. + UpdateVisibleChildren(); + UpdateBoundRect(); + } + + void AccessibleTextHelper_Impl::UpdateVisibleChildren( bool bBroadcastEvents ) + { + DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); + + try + { + SvxTextForwarder& rCacheTF = GetTextForwarder(); + SvxViewForwarder& rCacheVF = GetViewForwarder(); + + Rectangle aViewArea = rCacheVF.GetVisArea(); + + if( IsActive() ) + { + // maybe the edit view scrolls, adapt aViewArea + Rectangle aEditViewArea = GetEditViewForwarder().GetVisArea(); + aViewArea += aEditViewArea.TopLeft(); + + // now determine intersection + aViewArea.Intersection( aEditViewArea ); + } + + Rectangle aTmpBB, aParaBB; + sal_Bool bFirstChild = sal_True; + sal_Int32 nCurrPara; + sal_Int32 nParas=rCacheTF.GetParagraphCount(); + + mnFirstVisibleChild = -1; + mnLastVisibleChild = -2; + + for( nCurrPara=0; nCurrPara<nParas; ++nCurrPara ) + { + DBG_ASSERT(nCurrPara >= 0 && nCurrPara <= USHRT_MAX, + "AccessibleTextHelper_Impl::UpdateVisibleChildren: index value overflow"); + + aTmpBB = rCacheTF.GetParaBounds( static_cast< USHORT >( nCurrPara ) ); + + // convert to screen coordinates + aParaBB = ::accessibility::AccessibleEditableTextPara::LogicToPixel( aTmpBB, rCacheTF.GetMapMode(), rCacheVF ); + + if( aParaBB.IsOver( aViewArea ) ) + { + // at least partially visible + if( bFirstChild ) + { + bFirstChild = sal_False; + mnFirstVisibleChild = nCurrPara; + } + + mnLastVisibleChild = nCurrPara; + + // child not yet created? + ::accessibility::AccessibleParaManager::WeakChild aChild( maParaManager.GetChild(nCurrPara) ); + if( aChild.second.Width == 0 && + aChild.second.Height == 0 && + mxFrontEnd.is() && + bBroadcastEvents ) + { + GotPropertyEvent( uno::makeAny( maParaManager.CreateChild( nCurrPara - mnFirstVisibleChild, + mxFrontEnd, GetEditSource(), nCurrPara ).first ), + AccessibleEventId::CHILD ); + } + } + else + { + // not or no longer visible + if( maParaManager.IsReferencable( nCurrPara ) ) + { + if( bBroadcastEvents ) + LostPropertyEvent( uno::makeAny( maParaManager.GetChild( nCurrPara ).first.get().getRef() ), + AccessibleEventId::CHILD ); + + // clear reference + maParaManager.Release( nCurrPara ); + } + } + } + } + catch( const uno::Exception& ) + { + DBG_ERROR("AccessibleTextHelper_Impl::UpdateVisibleChildren error while determining visible children"); + + // something failed - currently no children + mnFirstVisibleChild = -1; + mnLastVisibleChild = -2; + maParaManager.SetNum(0); + + // lost all children + if( bBroadcastEvents ) + FireEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN); + } + } + + // functor for checking changes in paragraph bounding boxes (no stand-alone function, maybe not inlined) + class AccessibleTextHelper_UpdateChildBounds : public ::std::unary_function< const ::accessibility::AccessibleParaManager::WeakChild&, + ::accessibility::AccessibleParaManager::WeakChild > + { + public: + AccessibleTextHelper_UpdateChildBounds( AccessibleTextHelper_Impl& rImpl ) : mrImpl(rImpl) {} + ::accessibility::AccessibleParaManager::WeakChild operator()( const ::accessibility::AccessibleParaManager::WeakChild& rChild ) + { + // retrieve hard reference from weak one + ::accessibility::AccessibleParaManager::WeakPara::HardRefType aHardRef( rChild.first.get() ); + + if( aHardRef.is() ) + { + awt::Rectangle aNewRect = aHardRef->getBounds(); + const awt::Rectangle& aOldRect = rChild.second; + + if( aNewRect.X != aOldRect.X || + aNewRect.Y != aOldRect.Y || + aNewRect.Width != aOldRect.Width || + aNewRect.Height != aOldRect.Height ) + { + // visible data changed + aHardRef->FireEvent( AccessibleEventId::BOUNDRECT_CHANGED ); + + // update internal bounds + return ::accessibility::AccessibleParaManager::WeakChild( rChild.first, aNewRect ); + } + } + + // identity transform + return rChild; + } + + private: + AccessibleTextHelper_Impl& mrImpl; + }; + + void AccessibleTextHelper_Impl::UpdateBoundRect() + { + DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); + + // send BOUNDRECT_CHANGED to affected children + AccessibleTextHelper_UpdateChildBounds aFunctor( *this ); + ::std::transform( maParaManager.begin(), maParaManager.end(), maParaManager.begin(), aFunctor ); + } + +#ifdef DBG_UTIL + void AccessibleTextHelper_Impl::CheckInvariants() const + { + if( mnFirstVisibleChild >= 0 && + mnFirstVisibleChild > mnLastVisibleChild ) + { + DBG_ERROR( "AccessibleTextHelper: range invalid" ); + } + } +#endif + + // functor for sending child events (no stand-alone function, they are maybe not inlined) + class AccessibleTextHelper_LostChildEvent : public ::std::unary_function< const ::accessibility::AccessibleParaManager::WeakChild&, void > + { + public: + AccessibleTextHelper_LostChildEvent( AccessibleTextHelper_Impl& rImpl ) : mrImpl(rImpl) {} + void operator()( const ::accessibility::AccessibleParaManager::WeakChild& rPara ) + { + // retrieve hard reference from weak one + ::accessibility::AccessibleParaManager::WeakPara::HardRefType aHardRef( rPara.first.get() ); + + if( aHardRef.is() ) + mrImpl.FireEvent(AccessibleEventId::CHILD, uno::Any(), uno::makeAny( aHardRef.getRef() ) ); + } + + private: + AccessibleTextHelper_Impl& mrImpl; + }; + + void AccessibleTextHelper_Impl::ParagraphsMoved( sal_Int32 nFirst, sal_Int32 nMiddle, sal_Int32 nLast ) + { + DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); + + const sal_Int32 nParas = GetTextForwarder().GetParagraphCount(); + + /* rotate paragraphs + * ================= + * + * Three cases: + * + * 1. + * ... nParagraph ... nParam1 ... nParam2 ... + * |______________[xxxxxxxxxxx] + * becomes + * [xxxxxxxxxxx]|______________ + * + * tail is 0 + * + * 2. + * ... nParam1 ... nParagraph ... nParam2 ... + * [xxxxxxxxxxx|xxxxxxxxxxxxxx]____________ + * becomes + * ____________[xxxxxxxxxxx|xxxxxxxxxxxxxx] + * + * tail is nParagraph - nParam1 + * + * 3. + * ... nParam1 ... nParam2 ... nParagraph ... + * [xxxxxxxxxxx]___________|____________ + * becomes + * ___________|____________[xxxxxxxxxxx] + * + * tail is nParam2 - nParam1 + */ + + // sort nParagraph, nParam1 and nParam2 in ascending order, calc range + if( nMiddle < nFirst ) + { + ::std::swap(nFirst, nMiddle); + } + else if( nMiddle < nLast ) + { + nLast = nLast + nMiddle - nFirst; + } + else + { + ::std::swap(nMiddle, nLast); + nLast = nLast + nMiddle - nFirst; + } + + if( nFirst < nParas && nMiddle < nParas && nLast < nParas ) + { + // since we have no "paragraph index + // changed" event on UAA, remove + // [first,last] and insert again later (in + // UpdateVisibleChildren) + + // maParaManager.Rotate( nFirst, nMiddle, nLast ); + + // send CHILD_EVENT to affected children + ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator begin = maParaManager.begin(); + ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator end = begin; + + ::std::advance( begin, nFirst ); + ::std::advance( end, nLast+1 ); + + // TODO: maybe optimize here in the following way. If the + // number of removed children exceeds a certain threshold, + // use INVALIDATE_CHILDREN + AccessibleTextHelper_LostChildEvent aFunctor( *this ); + + ::std::for_each( begin, end, aFunctor ); + + maParaManager.Release(nFirst, nLast+1); + // should be no need for UpdateBoundRect, since all affected children are cleared. + } + } + + // functor for sending child events (no stand-alone function, they are maybe not inlined) + class AccessibleTextHelper_ChildrenTextChanged : public ::std::unary_function< ::accessibility::AccessibleEditableTextPara&, void > + { + public: + void operator()( ::accessibility::AccessibleEditableTextPara& rPara ) + { + rPara.TextChanged(); + } + }; + + /** functor processing queue events + + Reacts on TEXT_HINT_PARAINSERTED/REMOVED events and stores + their content + */ + class AccessibleTextHelper_QueueFunctor : public ::std::unary_function< const SfxHint*, void > + { + public: + AccessibleTextHelper_QueueFunctor() : + mnParasChanged( 0 ), + mnParaIndex(-1), + mnHintId(-1) + {} + void operator()( const SfxHint* pEvent ) + { + if( pEvent && + mnParasChanged != -1 ) + { + // determine hint type + const TextHint* pTextHint = PTR_CAST( TextHint, pEvent ); + const SvxEditSourceHint* pEditSourceHint = PTR_CAST( SvxEditSourceHint, pEvent ); + + if( !pEditSourceHint && pTextHint && + (pTextHint->GetId() == TEXT_HINT_PARAINSERTED || + pTextHint->GetId() == TEXT_HINT_PARAREMOVED ) ) + { + if( pTextHint->GetValue() == EE_PARA_ALL ) + { + mnParasChanged = -1; + } + else + { + mnHintId = pTextHint->GetId(); + mnParaIndex = pTextHint->GetValue(); + ++mnParasChanged; + } + } + } + } + + /** Query number of paragraphs changed during queue processing. + + @return number of changed paragraphs, -1 for + "every paragraph changed" + */ + int GetNumberOfParasChanged() { return mnParasChanged; } + /** Query index of last added/removed paragraph + + @return index of lastly added paragraphs, -1 for none + added so far. + */ + int GetParaIndex() { return mnParaIndex; } + /** Query hint id of last interesting event + + @return hint id of last interesting event (REMOVED/INSERTED). + */ + int GetHintId() { return mnHintId; } + + private: + /** number of paragraphs changed during queue processing. -1 for + "every paragraph changed" + */ + int mnParasChanged; + /// index of paragraph added/removed last + int mnParaIndex; + /// TextHint ID (removed/inserted) of last interesting event + int mnHintId; + }; + + void AccessibleTextHelper_Impl::ProcessQueue() + { + DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); + + // inspect queue for paragraph insert/remove events. If there + // is exactly _one_ of those in the queue, and the number of + // paragraphs has changed by exactly one, use that event to + // determine a priori which paragraph was added/removed. This + // is necessary, since I must sync right here with the + // EditEngine state (number of paragraphs etc.), since I'm + // potentially sending listener events right away. + AccessibleTextHelper_QueueFunctor aFunctor; + maEventQueue.ForEach( aFunctor ); + + const sal_Int32 nNewParas( GetTextForwarder().GetParagraphCount() ); + const sal_Int32 nCurrParas( maParaManager.GetNum() ); + + // whether every paragraph already is updated (no need to + // repeat that later on, e.g. for PARA_MOVED events) + bool bEverythingUpdated( false ); + + if( labs( nNewParas - nCurrParas ) == 1 && + aFunctor.GetNumberOfParasChanged() == 1 ) + { + // #103483# Exactly one paragraph added/removed. This is + // the normal case, optimize event handling here. + + if( aFunctor.GetHintId() == TEXT_HINT_PARAINSERTED ) + { + // update num of paras + maParaManager.SetNum( nNewParas ); + + // release everything from the insertion position until the end + maParaManager.Release(aFunctor.GetParaIndex(), nCurrParas); + + // TODO: Clarify whether this behaviour _really_ saves + // anybody anything! + // update children, _don't_ broadcast + UpdateVisibleChildren( false ); + UpdateBoundRect(); + + // send insert event + // #109864# Enforce creation of this paragraph + try + { + GotPropertyEvent( uno::makeAny( getAccessibleChild( aFunctor.GetParaIndex() - + mnFirstVisibleChild + GetStartIndex() ) ), + AccessibleEventId::CHILD ); + } + catch( const uno::Exception& ) + { + DBG_ERROR("AccessibleTextHelper_Impl::ProcessQueue: could not create new paragraph"); + } + } + else if( aFunctor.GetHintId() == TEXT_HINT_PARAREMOVED ) + { + ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator begin = maParaManager.begin(); + ::std::advance( begin, aFunctor.GetParaIndex() ); + ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator end = begin; + ::std::advance( end, 1 ); + + // #i61812# remember para to be removed for later notification + // AFTER the new state is applied (that after the para got removed) + ::uno::Reference< XAccessible > xPara; + ::accessibility::AccessibleParaManager::WeakPara::HardRefType aHardRef( begin->first.get() ); + if( aHardRef.is() ) + xPara = ::uno::Reference< XAccessible >( aHardRef.getRef(), ::uno::UNO_QUERY ); + + // release everything from the remove position until the end + maParaManager.Release(aFunctor.GetParaIndex(), nCurrParas); + + // update num of paras + maParaManager.SetNum( nNewParas ); + + // TODO: Clarify whether this behaviour _really_ saves + // anybody anything! + // update children, _don't_ broadcast + UpdateVisibleChildren( false ); + UpdateBoundRect(); + + // #i61812# notification for removed para + if (xPara.is()) + FireEvent(AccessibleEventId::CHILD, uno::Any(), uno::makeAny( xPara) ); + } +#ifdef DBG_UTIL + else + DBG_ERROR("AccessibleTextHelper_Impl::ProcessQueue() invalid hint id"); +#endif + } + else if( nNewParas != nCurrParas ) + { + // release all paras + maParaManager.Release(0, nCurrParas); + + // update num of paras + maParaManager.SetNum( nNewParas ); + + // #109864# create from scratch, don't broadcast + UpdateVisibleChildren( false ); + UpdateBoundRect(); + + // number of paragraphs somehow changed - but we have no + // chance determining how. Thus, throw away everything and + // create from scratch. + // (child events should be broadcast after the changes are done...) + FireEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN); + + // no need for further updates later on + bEverythingUpdated = true; + } + + while( !maEventQueue.IsEmpty() ) + { + ::std::auto_ptr< SfxHint > pHint( maEventQueue.PopFront() ); + if( pHint.get() ) + { + const SfxHint& rHint = *(pHint.get()); + + // determine hint type + const SdrHint* pSdrHint = PTR_CAST( SdrHint, &rHint ); + const SfxSimpleHint* pSimpleHint = PTR_CAST( SfxSimpleHint, &rHint ); + const TextHint* pTextHint = PTR_CAST( TextHint, &rHint ); + const SvxViewHint* pViewHint = PTR_CAST( SvxViewHint, &rHint ); + const SvxEditSourceHint* pEditSourceHint = PTR_CAST( SvxEditSourceHint, &rHint ); + + try + { + const sal_Int32 nParas = GetTextForwarder().GetParagraphCount(); + + if( pEditSourceHint ) + { + switch( pEditSourceHint->GetId() ) + { + case EDITSOURCE_HINT_PARASMOVED: + { + DBG_ASSERT( pEditSourceHint->GetStartValue() < GetTextForwarder().GetParagraphCount() && + pEditSourceHint->GetEndValue() < GetTextForwarder().GetParagraphCount(), + "AccessibleTextHelper_Impl::NotifyHdl: Invalid notification"); + + if( !bEverythingUpdated ) + { + ParagraphsMoved(pEditSourceHint->GetStartValue(), + pEditSourceHint->GetValue(), + pEditSourceHint->GetEndValue()); + + // in all cases, check visibility afterwards. + UpdateVisibleChildren(); + } + break; + } + + case EDITSOURCE_HINT_SELECTIONCHANGED: + // notify listeners + try + { + UpdateSelection(); + } + // maybe we're not in edit mode (this is not an error) + catch( const uno::Exception& ) {} + break; + } + } + else if( pTextHint ) + { + switch( pTextHint->GetId() ) + { + case TEXT_HINT_MODIFIED: + { + // notify listeners + sal_Int32 nPara( pTextHint->GetValue() ); + + // #108900# Delegate change event to children + AccessibleTextHelper_ChildrenTextChanged aNotifyChildrenFunctor; + + if( nPara == static_cast<sal_Int32>(EE_PARA_ALL) ) + { + // #108900# Call every child + ::std::for_each( maParaManager.begin(), maParaManager.end(), + AccessibleParaManager::WeakChildAdapter< AccessibleTextHelper_ChildrenTextChanged > (aNotifyChildrenFunctor) ); + } + else + if( nPara < nParas ) + { + // #108900# Call child at index nPara + ::std::for_each( maParaManager.begin()+nPara, maParaManager.begin()+nPara+1, + AccessibleParaManager::WeakChildAdapter< AccessibleTextHelper_ChildrenTextChanged > (aNotifyChildrenFunctor) ); + } + break; + } + + case TEXT_HINT_PARAINSERTED: + // already happened above + break; + + case TEXT_HINT_PARAREMOVED: + // already happened above + break; + + case TEXT_HINT_TEXTHEIGHTCHANGED: + // visibility changed, done below + break; + + case TEXT_HINT_VIEWSCROLLED: + // visibility changed, done below + break; + } + + // in all cases, check visibility afterwards. + UpdateVisibleChildren(); + UpdateBoundRect(); + } + else if( pViewHint ) + { + switch( pViewHint->GetHintType() ) + { + case SvxViewHint::SVX_HINT_VIEWCHANGED: + // just check visibility + UpdateVisibleChildren(); + UpdateBoundRect(); + break; + } + } + else if( pSdrHint ) + { + switch( pSdrHint->GetKind() ) + { + case HINT_BEGEDIT: + { + // change children state + maParaManager.SetActive(); + + // per definition, edit mode text has the focus + SetFocus( sal_True ); + break; + } + + case HINT_ENDEDIT: + { + // focused child now looses focus + ESelection aSelection; + if( GetEditViewForwarder().GetSelection( aSelection ) ) + SetChildFocus( aSelection.nEndPara, sal_False ); + + // change children state + maParaManager.SetActive( sal_False ); + + maLastSelection = ESelection( EE_PARA_NOT_FOUND, EE_PARA_NOT_FOUND, + EE_PARA_NOT_FOUND, EE_PARA_NOT_FOUND); + break; + } + default: + break; + } + } + // it's VITAL to keep the SfxSimpleHint last! It's the base of some classes above! + else if( pSimpleHint ) + { + switch( pSimpleHint->GetId() ) + { + case SFX_HINT_DYING: + // edit source is dying under us, become defunc then + try + { + // make edit source inaccessible + // Note: cannot destroy it here, since we're called from there! + ShutdownEditSource(); + } + catch( const uno::Exception& ) {} + + break; + } + } + } + catch( const uno::Exception& ) + { +#ifdef DBG_UTIL + OSL_TRACE("AccessibleTextHelper_Impl::ProcessQueue: Unhandled exception."); +#endif + } + } + } + } + + void AccessibleTextHelper_Impl::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint ) + { + DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); + + // precondition: solar mutex locked + DBG_TESTSOLARMUTEX(); + + // precondition: not in a recursion + if( mbInNotify ) + return; + + mbInNotify = sal_True; + + // determine hint type + const SdrHint* pSdrHint = PTR_CAST( SdrHint, &rHint ); + const SfxSimpleHint* pSimpleHint = PTR_CAST( SfxSimpleHint, &rHint ); + const TextHint* pTextHint = PTR_CAST( TextHint, &rHint ); + const SvxViewHint* pViewHint = PTR_CAST( SvxViewHint, &rHint ); + const SvxEditSourceHint* pEditSourceHint = PTR_CAST( SvxEditSourceHint, &rHint ); + + try + { + // Process notification event + if( pEditSourceHint ) + { + maEventQueue.Append( *pEditSourceHint ); + // --> OD 2005-12-19 #i27299# + if( maEventOpenFrames == 0 ) + ProcessQueue(); + // <-- + } + else if( pTextHint ) + { + switch( pTextHint->GetId() ) + { + case TEXT_HINT_BLOCKNOTIFICATION_END: + case TEXT_HINT_INPUT_END: + --maEventOpenFrames; + + if( maEventOpenFrames == 0 ) + { + // #103483# + /* All information should have arrived + * now, process queue. As stated in the + * above bug, we can often avoid throwing + * away all paragraphs by looking forward + * in the event queue (searching for + * PARAINSERT/REMOVE events). Furthermore, + * processing the event queue only at the + * end of an interaction cycle, ensures + * that the EditEngine state and the + * AccessibleText state are the same + * (well, mostly. If there are _multiple_ + * interaction cycles in the EE queues, it + * can still happen that EE state is + * different. That's so to say broken by + * design with that delayed EE event + * concept). + */ + ProcessQueue(); + } + break; + + case TEXT_HINT_BLOCKNOTIFICATION_START: + case TEXT_HINT_INPUT_START: + ++maEventOpenFrames; + // --> OD 2005-12-19 #i27299# - no FALLTROUGH + // reason: event will not be processes, thus appending + // the event isn't necessary. + break; + // <-- + default: + maEventQueue.Append( *pTextHint ); + // --> OD 2005-12-19 #i27299# + if( maEventOpenFrames == 0 ) + ProcessQueue(); + // <-- + break; + } + } + else if( pViewHint ) + { + maEventQueue.Append( *pViewHint ); + + // process visibility right away, if not within an + // open EE notification frame. Otherwise, event + // processing would be delayed until next EE + // notification sequence. + if( maEventOpenFrames == 0 ) + ProcessQueue(); + } + else if( pSdrHint ) + { + maEventQueue.Append( *pSdrHint ); + + // process drawing layer events right away, if not + // within an open EE notification frame. Otherwise, + // event processing would be delayed until next EE + // notification sequence. + if( maEventOpenFrames == 0 ) + ProcessQueue(); + } + // it's VITAL to keep the SfxSimpleHint last! It's the base of some classes above! + else if( pSimpleHint ) + { + // handle this event _at once_, because after that, objects are invalid + switch( pSimpleHint->GetId() ) + { + case SFX_HINT_DYING: + // edit source is dying under us, become defunc then + maEventQueue.Clear(); + try + { + // make edit source inaccessible + // Note: cannot destroy it here, since we're called from there! + ShutdownEditSource(); + } + catch( const uno::Exception& ) {} + + break; + } + } + } + catch( const uno::Exception& ) + { +#ifdef DBG_UTIL + OSL_TRACE("AccessibleTextHelper_Impl::Notify: Unhandled exception."); +#endif + mbInNotify = sal_False; + } + + mbInNotify = sal_False; + } + + void AccessibleTextHelper_Impl::Dispose() + { + DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); + + if( getNotifierClientId() != -1 ) + { + try + { + // #106234# Unregister from EventNotifier + ::comphelper::AccessibleEventNotifier::revokeClient( getNotifierClientId() ); +#ifdef DBG_UTIL + OSL_TRACE( "AccessibleTextHelper_Impl disposed ID: %d", mnNotifierClientId ); +#endif + } + catch( const uno::Exception& ) {} + + mnNotifierClientId = -1; + } + + try + { + // dispose children + maParaManager.Dispose(); + } + catch( const uno::Exception& ) {} + + // quit listen on stale edit source + if( maEditSource.IsValid() ) + EndListening( maEditSource.GetBroadcaster() ); + + // clear references + maEditSource.SetEditSource( ::std::auto_ptr< SvxEditSource >(NULL) ); + mxFrontEnd = NULL; + } + + void AccessibleTextHelper_Impl::FireEvent( const sal_Int16 nEventId, const uno::Any& rNewValue, const uno::Any& rOldValue ) const + { + DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); + + // -- object locked -- + ::osl::ClearableMutexGuard aGuard( maMutex ); + + AccessibleEventObject aEvent; + + DBG_ASSERT(mxFrontEnd.is(), "AccessibleTextHelper::FireEvent: no event source set" ); + + if( mxFrontEnd.is() ) + aEvent = AccessibleEventObject(mxFrontEnd->getAccessibleContext(), nEventId, rNewValue, rOldValue); + else + aEvent = AccessibleEventObject(uno::Reference< uno::XInterface >(), nEventId, rNewValue, rOldValue); + + // no locking necessary, FireEvent internally copies listeners + // if someone removes/adds in between Further locking, + // actually, might lead to deadlocks, since we're calling out + // of this object + aGuard.clear(); + // -- until here -- + + FireEvent(aEvent); + } + + void AccessibleTextHelper_Impl::FireEvent( const AccessibleEventObject& rEvent ) const + { + DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); + + // #102261# Call global queue for focus events + if( rEvent.EventId == AccessibleStateType::FOCUSED ) + vcl::unohelper::NotifyAccessibleStateEventGlobally( rEvent ); + + // #106234# Delegate to EventNotifier + ::comphelper::AccessibleEventNotifier::addEvent( getNotifierClientId(), + rEvent ); + } + + // XAccessibleContext + sal_Int32 SAL_CALL AccessibleTextHelper_Impl::getAccessibleChildCount() SAL_THROW((uno::RuntimeException)) + { + DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); + + return mnLastVisibleChild - mnFirstVisibleChild + 1; + } + + uno::Reference< XAccessible > SAL_CALL AccessibleTextHelper_Impl::getAccessibleChild( sal_Int32 i ) SAL_THROW((lang::IndexOutOfBoundsException, uno::RuntimeException)) + { + DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); + + i -= GetStartIndex(); + + if( 0 > i || i >= getAccessibleChildCount() || + GetTextForwarder().GetParagraphCount() <= i ) + { + throw lang::IndexOutOfBoundsException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Invalid child index")), mxFrontEnd); + } + + DBG_ASSERT(mxFrontEnd.is(), "AccessibleTextHelper_Impl::UpdateVisibleChildren: no frontend set"); + + if( mxFrontEnd.is() ) + return maParaManager.CreateChild( i, mxFrontEnd, GetEditSource(), mnFirstVisibleChild + i ).first; + else + return NULL; + } + + void SAL_CALL AccessibleTextHelper_Impl::addEventListener( const uno::Reference< XAccessibleEventListener >& xListener ) SAL_THROW((uno::RuntimeException)) + { + DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); + + if( getNotifierClientId() != -1 ) + ::comphelper::AccessibleEventNotifier::addEventListener( getNotifierClientId(), xListener ); + } + + void SAL_CALL AccessibleTextHelper_Impl::removeEventListener( const uno::Reference< XAccessibleEventListener >& xListener ) SAL_THROW((uno::RuntimeException)) + { + DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); + + if( getNotifierClientId() != -1 ) + ::comphelper::AccessibleEventNotifier::removeEventListener( getNotifierClientId(), xListener ); + } + + uno::Reference< XAccessible > SAL_CALL AccessibleTextHelper_Impl::getAccessibleAtPoint( const awt::Point& _aPoint ) SAL_THROW((uno::RuntimeException)) + { + DBG_CHKTHIS( AccessibleTextHelper_Impl, NULL ); + + // make given position relative + if( !mxFrontEnd.is() ) + throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("AccessibleTextHelper_Impl::getAccessibleAt: frontend invalid")), mxFrontEnd ); + + uno::Reference< XAccessibleContext > xFrontEndContext = mxFrontEnd->getAccessibleContext(); + + if( !xFrontEndContext.is() ) + throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("AccessibleTextHelper_Impl::getAccessibleAt: frontend invalid")), mxFrontEnd ); + + uno::Reference< XAccessibleComponent > xFrontEndComponent( xFrontEndContext, uno::UNO_QUERY ); + + if( !xFrontEndComponent.is() ) + throw uno::RuntimeException(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("AccessibleTextHelper_Impl::getAccessibleAt: frontend is no XAccessibleComponent")), + mxFrontEnd ); + + // #103862# No longer need to make given position relative + Point aPoint( _aPoint.X, _aPoint.Y ); + + // respect EditEngine offset to surrounding shape/cell + aPoint -= GetOffset(); + + // convert to EditEngine coordinate system + SvxTextForwarder& rCacheTF = GetTextForwarder(); + Point aLogPoint( GetViewForwarder().PixelToLogic( aPoint, rCacheTF.GetMapMode() ) ); + + // iterate over all visible children (including those not yet created) + sal_Int32 nChild; + for( nChild=mnFirstVisibleChild; nChild <= mnLastVisibleChild; ++nChild ) + { + DBG_ASSERT(nChild >= 0 && nChild <= USHRT_MAX, + "AccessibleTextHelper_Impl::getAccessibleAt: index value overflow"); + + Rectangle aParaBounds( rCacheTF.GetParaBounds( static_cast< USHORT > (nChild) ) ); + + if( aParaBounds.IsInside( aLogPoint ) ) + return getAccessibleChild( nChild - mnFirstVisibleChild + GetStartIndex() ); + } + + // found none + return NULL; + } + + //------------------------------------------------------------------------ + // + // AccessibleTextHelper implementation (simply forwards to impl) + // + //------------------------------------------------------------------------ + + AccessibleTextHelper::AccessibleTextHelper( ::std::auto_ptr< SvxEditSource > pEditSource ) : + mpImpl( new AccessibleTextHelper_Impl() ) + { + ::vos::OGuard aGuard( Application::GetSolarMutex() ); + + SetEditSource( pEditSource ); + } + + AccessibleTextHelper::~AccessibleTextHelper() + { + } + + const SvxEditSource& AccessibleTextHelper::GetEditSource() const SAL_THROW((uno::RuntimeException)) + { +#ifdef DBG_UTIL + mpImpl->CheckInvariants(); + + const SvxEditSource& aEditSource = mpImpl->GetEditSource(); + + mpImpl->CheckInvariants(); + + return aEditSource; +#else + return mpImpl->GetEditSource(); +#endif + } + + void AccessibleTextHelper::SetEditSource( ::std::auto_ptr< SvxEditSource > pEditSource ) SAL_THROW((uno::RuntimeException)) + { +#ifdef DBG_UTIL + // precondition: solar mutex locked + DBG_TESTSOLARMUTEX(); + + mpImpl->CheckInvariants(); +#endif + + mpImpl->SetEditSource( pEditSource ); + +#ifdef DBG_UTIL + mpImpl->CheckInvariants(); +#endif + } + + void AccessibleTextHelper::SetEventSource( const uno::Reference< XAccessible >& rInterface ) + { +#ifdef DBG_UTIL + mpImpl->CheckInvariants(); +#endif + + mpImpl->SetEventSource( rInterface ); + +#ifdef DBG_UTIL + mpImpl->CheckInvariants(); +#endif + } + + uno::Reference< XAccessible > AccessibleTextHelper::GetEventSource() const + { +#ifdef DBG_UTIL + mpImpl->CheckInvariants(); + + uno::Reference< XAccessible > xRet( mpImpl->GetEventSource() ); + + mpImpl->CheckInvariants(); + + return xRet; +#else + return mpImpl->GetEventSource(); +#endif + } + + void AccessibleTextHelper::SetFocus( sal_Bool bHaveFocus ) SAL_THROW((::com::sun::star::uno::RuntimeException)) + { +#ifdef DBG_UTIL + // precondition: solar mutex locked + DBG_TESTSOLARMUTEX(); + + mpImpl->CheckInvariants(); +#endif + + mpImpl->SetFocus( bHaveFocus ); + +#ifdef DBG_UTIL + mpImpl->CheckInvariants(); +#endif + } + + sal_Bool AccessibleTextHelper::HaveFocus() SAL_THROW((::com::sun::star::uno::RuntimeException)) + { +#ifdef DBG_UTIL + mpImpl->CheckInvariants(); + + sal_Bool bRet( mpImpl->HaveFocus() ); + + mpImpl->CheckInvariants(); + + return bRet; +#else + return mpImpl->HaveFocus(); +#endif + } + + void AccessibleTextHelper::FireEvent( const sal_Int16 nEventId, const uno::Any& rNewValue, const uno::Any& rOldValue ) const + { +#ifdef DBG_UTIL + mpImpl->CheckInvariants(); +#endif + + mpImpl->FireEvent( nEventId, rNewValue, rOldValue ); + +#ifdef DBG_UTIL + mpImpl->CheckInvariants(); +#endif + } + + void AccessibleTextHelper::FireEvent( const AccessibleEventObject& rEvent ) const + { +#ifdef DBG_UTIL + mpImpl->CheckInvariants(); +#endif + + mpImpl->FireEvent( rEvent ); + +#ifdef DBG_UTIL + mpImpl->CheckInvariants(); +#endif + } + + void AccessibleTextHelper::SetOffset( const Point& rPoint ) + { +#ifdef DBG_UTIL + // precondition: solar mutex locked + DBG_TESTSOLARMUTEX(); + + mpImpl->CheckInvariants(); +#endif + + mpImpl->SetOffset( rPoint ); + +#ifdef DBG_UTIL + mpImpl->CheckInvariants(); +#endif + } + + Point AccessibleTextHelper::GetOffset() const + { +#ifdef DBG_UTIL + mpImpl->CheckInvariants(); + + Point aPoint( mpImpl->GetOffset() ); + + mpImpl->CheckInvariants(); + + return aPoint; +#else + return mpImpl->GetOffset(); +#endif + } + + void AccessibleTextHelper::SetStartIndex( sal_Int32 nOffset ) + { +#ifdef DBG_UTIL + // precondition: solar mutex locked + DBG_TESTSOLARMUTEX(); + + mpImpl->CheckInvariants(); +#endif + + mpImpl->SetStartIndex( nOffset ); + +#ifdef DBG_UTIL + mpImpl->CheckInvariants(); +#endif + } + + sal_Int32 AccessibleTextHelper::GetStartIndex() const + { +#ifdef DBG_UTIL + mpImpl->CheckInvariants(); + + sal_Int32 nOffset = mpImpl->GetStartIndex(); + + mpImpl->CheckInvariants(); + + return nOffset; +#else + return mpImpl->GetStartIndex(); +#endif + } + + void AccessibleTextHelper::SetAdditionalChildStates( const VectorOfStates& rChildStates ) + { + mpImpl->SetAdditionalChildStates( rChildStates ); + } + + const AccessibleTextHelper::VectorOfStates& AccessibleTextHelper::GetAdditionalChildStates() const + { + return mpImpl->GetAdditionalChildStates(); + } + + void AccessibleTextHelper::UpdateChildren() SAL_THROW((::com::sun::star::uno::RuntimeException)) + { +#ifdef DBG_UTIL + // precondition: solar mutex locked + DBG_TESTSOLARMUTEX(); + + mpImpl->CheckInvariants(); +#endif + + mpImpl->UpdateVisibleChildren(); + mpImpl->UpdateBoundRect(); + + mpImpl->UpdateSelection(); + +#ifdef DBG_UTIL + mpImpl->CheckInvariants(); +#endif + } + + void AccessibleTextHelper::Dispose() + { + // As Dispose calls ShutdownEditSource, which in turn + // deregisters as listener on the edit source, have to lock + // here + ::vos::OGuard aGuard( Application::GetSolarMutex() ); + +#ifdef DBG_UTIL + mpImpl->CheckInvariants(); +#endif + + mpImpl->Dispose(); + +#ifdef DBG_UTIL + mpImpl->CheckInvariants(); +#endif + } + + sal_Bool AccessibleTextHelper::IsSelected() const + { + ::vos::OGuard aGuard( Application::GetSolarMutex() ); + +#ifdef DBG_UTIL + mpImpl->CheckInvariants(); + + sal_Bool aRet = mpImpl->IsSelected(); + + mpImpl->CheckInvariants(); + + return aRet; +#else + return mpImpl->IsSelected(); +#endif + } + + // XAccessibleContext + sal_Int32 AccessibleTextHelper::GetChildCount() SAL_THROW((uno::RuntimeException)) + { + ::vos::OGuard aGuard( Application::GetSolarMutex() ); + +#ifdef DBG_UTIL + mpImpl->CheckInvariants(); + + sal_Int32 nRet = mpImpl->getAccessibleChildCount(); + + mpImpl->CheckInvariants(); + + return nRet; +#else + return mpImpl->getAccessibleChildCount(); +#endif + } + + uno::Reference< XAccessible > AccessibleTextHelper::GetChild( sal_Int32 i ) SAL_THROW((lang::IndexOutOfBoundsException, uno::RuntimeException)) + { + ::vos::OGuard aGuard( Application::GetSolarMutex() ); + +#ifdef DBG_UTIL + mpImpl->CheckInvariants(); + + uno::Reference< XAccessible > xRet = mpImpl->getAccessibleChild( i ); + + mpImpl->CheckInvariants(); + + return xRet; +#else + return mpImpl->getAccessibleChild( i ); +#endif + } + + void AccessibleTextHelper::AddEventListener( const uno::Reference< XAccessibleEventListener >& xListener ) SAL_THROW((uno::RuntimeException)) + { +#ifdef DBG_UTIL + mpImpl->CheckInvariants(); + + mpImpl->addEventListener( xListener ); + + mpImpl->CheckInvariants(); +#else + mpImpl->addEventListener( xListener ); +#endif + } + + void AccessibleTextHelper::RemoveEventListener( const uno::Reference< XAccessibleEventListener >& xListener ) SAL_THROW((uno::RuntimeException)) + { +#ifdef DBG_UTIL + mpImpl->CheckInvariants(); + + mpImpl->removeEventListener( xListener ); + + mpImpl->CheckInvariants(); +#else + mpImpl->removeEventListener( xListener ); +#endif + } + + // XAccessibleComponent + uno::Reference< XAccessible > AccessibleTextHelper::GetAt( const awt::Point& aPoint ) SAL_THROW((uno::RuntimeException)) + { + ::vos::OGuard aGuard( Application::GetSolarMutex() ); + +#ifdef DBG_UTIL + mpImpl->CheckInvariants(); + + uno::Reference< XAccessible > xChild = mpImpl->getAccessibleAtPoint( aPoint ); + + mpImpl->CheckInvariants(); + + return xChild; +#else + return mpImpl->getAccessibleAtPoint( aPoint ); +#endif + } + +} // end of namespace accessibility + +//------------------------------------------------------------------------ diff --git a/svx/source/accessibility/ChildrenManager.cxx b/svx/source/accessibility/ChildrenManager.cxx new file mode 100644 index 000000000000..89afa1ae073f --- /dev/null +++ b/svx/source/accessibility/ChildrenManager.cxx @@ -0,0 +1,180 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svx.hxx" +#include <svx/ChildrenManager.hxx> +#ifndef _SVX_ACCESSIBILITY_CHILDREN_MANAGER_IMPL_HXX +#include "ChildrenManagerImpl.hxx" +#endif +#include <svx/AccessibleShape.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; +using ::com::sun::star::uno::Reference; + +namespace accessibility { + + +//===== AccessibleChildrenManager =========================================== + +ChildrenManager::ChildrenManager ( + const ::com::sun::star::uno::Reference<XAccessible>& rxParent, + const ::com::sun::star::uno::Reference<drawing::XShapes>& rxShapeList, + const AccessibleShapeTreeInfo& rShapeTreeInfo, + AccessibleContextBase& rContext) + : mpImpl (NULL) +{ + mpImpl = new ChildrenManagerImpl (rxParent, rxShapeList, rShapeTreeInfo, rContext); + if (mpImpl != NULL) + mpImpl->Init (); + else + throw uno::RuntimeException( + ::rtl::OUString::createFromAscii( + "ChildrenManager::ChildrenManager can't create implementation object"), NULL); +} + + + + +ChildrenManager::~ChildrenManager (void) +{ + if (mpImpl != NULL) + mpImpl->dispose(); + + // emtpy + OSL_TRACE ("~ChildrenManager"); +} + + + + +long ChildrenManager::GetChildCount (void) const throw () +{ + OSL_ASSERT (mpImpl != NULL); + return mpImpl->GetChildCount(); +} + + + + +::com::sun::star::uno::Reference<XAccessible> ChildrenManager::GetChild (long nIndex) + throw (::com::sun::star::uno::RuntimeException, + ::com::sun::star::lang::IndexOutOfBoundsException) +{ + OSL_ASSERT (mpImpl != NULL); + return mpImpl->GetChild (nIndex); +} + + + + +void ChildrenManager::Update (bool bCreateNewObjectsOnDemand) +{ + OSL_ASSERT (mpImpl != NULL); + mpImpl->Update (bCreateNewObjectsOnDemand); +} + + + + +void ChildrenManager::SetShapeList (const ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XShapes>& xShapeList) +{ + OSL_ASSERT (mpImpl != NULL); + mpImpl->SetShapeList (xShapeList); +} + + + + +void ChildrenManager::AddAccessibleShape (std::auto_ptr<AccessibleShape> pShape) +{ + OSL_ASSERT (mpImpl != NULL); + mpImpl->AddAccessibleShape (pShape); +} + + + + +void ChildrenManager::ClearAccessibleShapeList (void) +{ + OSL_ASSERT (mpImpl != NULL); + mpImpl->ClearAccessibleShapeList (); +} + + + + +void ChildrenManager::SetInfo (AccessibleShapeTreeInfo& rShapeTreeInfo) +{ + OSL_ASSERT (mpImpl != NULL); + mpImpl->SetInfo (rShapeTreeInfo); +} + + + + +void ChildrenManager::UpdateSelection (void) +{ + OSL_ASSERT (mpImpl != NULL); + mpImpl->UpdateSelection (); +} + + + + +bool ChildrenManager::HasFocus (void) +{ + OSL_ASSERT (mpImpl != NULL); + return mpImpl->HasFocus (); +} + + + + +void ChildrenManager::RemoveFocus (void) +{ + OSL_ASSERT (mpImpl != NULL); + mpImpl->RemoveFocus (); +} + + + + +//===== IAccessibleViewForwarderListener ==================================== +void ChildrenManager::ViewForwarderChanged (ChangeType aChangeType, + const IAccessibleViewForwarder* pViewForwarder) +{ + OSL_ASSERT (mpImpl != NULL); + mpImpl->ViewForwarderChanged (aChangeType, pViewForwarder); +} + + + +} // end of namespace accessibility + diff --git a/svx/source/accessibility/ChildrenManagerImpl.cxx b/svx/source/accessibility/ChildrenManagerImpl.cxx new file mode 100644 index 000000000000..803388414451 --- /dev/null +++ b/svx/source/accessibility/ChildrenManagerImpl.cxx @@ -0,0 +1,1098 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svx.hxx" + +#include "ChildrenManagerImpl.hxx" +#include <svx/ShapeTypeHandler.hxx> +#include <svx/AccessibleShapeInfo.hxx> +#ifndef _COM_SUN_STAR_ACCESSIBLE_ACCESSIBLESTATETYPE_HPP_ +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#endif +#include <com/sun/star/view/XSelectionSupplier.hpp> +#include <com/sun/star/container/XChild.hpp> +#include <comphelper/uno3.hxx> +#include <com/sun/star/container/XChild.hpp> + +#include <rtl/ustring.hxx> +#include <tools/debug.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; +using ::com::sun::star::uno::Reference; + + +namespace accessibility { + +namespace +{ +void adjustIndexInParentOfShapes(ChildDescriptorListType& _rList) +{ + ChildDescriptorListType::iterator aEnd = _rList.end(); + sal_Int32 i=0; + for ( ChildDescriptorListType::iterator aIter = _rList.begin(); aIter != aEnd; ++aIter,++i) + aIter->setIndexAtAccessibleShape(i); +} +} + +//===== AccessibleChildrenManager =========================================== + +ChildrenManagerImpl::ChildrenManagerImpl ( + const uno::Reference<XAccessible>& rxParent, + const uno::Reference<drawing::XShapes>& rxShapeList, + const AccessibleShapeTreeInfo& rShapeTreeInfo, + AccessibleContextBase& rContext) + : ::cppu::WeakComponentImplHelper2< + ::com::sun::star::document::XEventListener, + ::com::sun::star::view::XSelectionChangeListener>(maMutex), + mxShapeList (rxShapeList), + mxParent (rxParent), + maShapeTreeInfo (rShapeTreeInfo), + mrContext (rContext), + mnNewNameIndex(1), + mpFocusedShape(NULL) +{ +} + + + + +ChildrenManagerImpl::~ChildrenManagerImpl (void) +{ + DBG_ASSERT (rBHelper.bDisposed || rBHelper.bInDispose, + "~AccessibleDrawDocumentView: object has not been disposed"); +} + + + + +void ChildrenManagerImpl::Init (void) +{ + // Register as view::XSelectionChangeListener. + Reference<frame::XController> xController(maShapeTreeInfo.GetController()); + Reference<view::XSelectionSupplier> xSelectionSupplier ( + xController, uno::UNO_QUERY); + if (xSelectionSupplier.is()) + { + xController->addEventListener( + static_cast<document::XEventListener*>(this)); + + xSelectionSupplier->addSelectionChangeListener ( + static_cast<view::XSelectionChangeListener*>(this)); + } + + // Register at model as document::XEventListener. + if (maShapeTreeInfo.GetModelBroadcaster().is()) + maShapeTreeInfo.GetModelBroadcaster()->addEventListener ( + static_cast<document::XEventListener*>(this)); +} + + + + +long ChildrenManagerImpl::GetChildCount (void) const throw () +{ + return maVisibleChildren.size(); +} + + + + +/** Return the requested accessible child object. Create it if it is not + yet in the cache. +*/ +uno::Reference<XAccessible> + ChildrenManagerImpl::GetChild (long nIndex) + throw (::com::sun::star::uno::RuntimeException, + ::com::sun::star::lang::IndexOutOfBoundsException) +{ + // Check wether the given index is valid. + if (nIndex < 0 || (unsigned long)nIndex >= maVisibleChildren.size()) + throw lang::IndexOutOfBoundsException ( + ::rtl::OUString::createFromAscii( + "no accessible child with index ") + nIndex, + mxParent); + + return GetChild (maVisibleChildren[nIndex],nIndex); +} + + + + +/** Return the requested accessible child object. Create it if it is not + yet in the cache. +*/ +uno::Reference<XAccessible> + ChildrenManagerImpl::GetChild (ChildDescriptor& rChildDescriptor,sal_Int32 _nIndex) + throw (::com::sun::star::uno::RuntimeException) +{ + if ( ! rChildDescriptor.mxAccessibleShape.is()) + { + ::osl::MutexGuard aGuard (maMutex); + // Make sure that the requested accessible object has not been + // created while locking the global mutex. + if ( ! rChildDescriptor.mxAccessibleShape.is()) + { + AccessibleShapeInfo aShapeInfo( + rChildDescriptor.mxShape, + mxParent, + this, + mnNewNameIndex++); + // Create accessible object that corresponds to the descriptor's + // shape. + AccessibleShape* pShape = + ShapeTypeHandler::Instance().CreateAccessibleObject ( + aShapeInfo, + maShapeTreeInfo); + rChildDescriptor.mxAccessibleShape = uno::Reference<XAccessible> ( + static_cast<uno::XWeak*>(pShape), + uno::UNO_QUERY); + // Now that there is a reference to the new accessible shape we + // can safely call its Init() method. + if ( pShape != NULL ) + { + pShape->Init(); + pShape->setIndexInParent(_nIndex); + } + } + } + + return rChildDescriptor.mxAccessibleShape; +} + + + + +uno::Reference<XAccessible> + ChildrenManagerImpl::GetChild (const uno::Reference<drawing::XShape>& xShape) + throw (uno::RuntimeException) +{ + ChildDescriptorListType::iterator I, aEnd = maVisibleChildren.end(); + for (I = maVisibleChildren.begin(); I != aEnd; ++I) + { + if ( I->mxShape.get() == xShape.get() ) + return I->mxAccessibleShape; + } + return uno::Reference<XAccessible> (); +} + + + + +/** Find all shapes among the specified shapes that lie fully or partially + inside the visible area. Put those shapes into the cleared cache. The + corresponding accessible objects will be created on demand. + + At the moment, first all accessible objects are removed from the cache + and the appropriate listeners are informed of this. Next, the list is + created again. This should be optimized in the future to not remove and + create objects that will be in the list before and after the update + method. +*/ +void ChildrenManagerImpl::Update (bool bCreateNewObjectsOnDemand) +{ + if (maShapeTreeInfo.GetViewForwarder() == NULL) + return; + Rectangle aVisibleArea = maShapeTreeInfo.GetViewForwarder()->GetVisibleArea(); + + // 1. Create a local list of visible shapes. + ChildDescriptorListType aChildList; + CreateListOfVisibleShapes (aChildList); + + // 2. Merge the information that is already known about the visible + // shapes from the current list into the new list. + MergeAccessibilityInformation (aChildList); + + // 3. Replace the current list of visible shapes with the new one. Do + // the same with the visible area. + { + ::osl::MutexGuard aGuard (maMutex); + adjustIndexInParentOfShapes(aChildList); + + // Use swap to copy the contents of the new list in constant time. + maVisibleChildren.swap (aChildList); + + // aChildList now contains all the old children, while maVisibleChildren + // contains all the current children + + // 4. Find all shapes in the old list that are not in the current list, + // send appropriate events and remove the accessible shape. + // + // Do this *after* we have set our new list of children, because + // removing a child may cause + // + // ChildDescriptor::disposeAccessibleObject --> + // AccessibleContextBase::CommitChange --> + // AtkListener::notifyEvent -> + // AtkListener::handleChildRemoved -> + // AtkListener::updateChildList + // AccessibleDrawDocumentView::getAccessibleChildCount -> + // ChildrenManagerImpl::GetChildCount -> + // maVisibleChildren.size() + // + // to be fired, and so the operations will take place on + // the list we are trying to replace + // + RemoveNonVisibleChildren (maVisibleChildren, aChildList); + + aChildList.clear(); + + maVisibleArea = aVisibleArea; + } + + // 5. If the visible area has changed then send events that signal a + // change of their bounding boxes for all shapes that are members of + // both the current and the new list of visible shapes. + if (maVisibleArea != aVisibleArea) + SendVisibleAreaEvents (maVisibleChildren); + + // 6. If children have to be created immediately and not on demand then + // create the missing accessible objects now. + if ( ! bCreateNewObjectsOnDemand) + CreateAccessibilityObjects (maVisibleChildren); +} + + + + +void ChildrenManagerImpl::CreateListOfVisibleShapes ( + ChildDescriptorListType& raDescriptorList) +{ + ::osl::MutexGuard aGuard (maMutex); + + OSL_ASSERT (maShapeTreeInfo.GetViewForwarder() != NULL); + + Rectangle aVisibleArea = maShapeTreeInfo.GetViewForwarder()->GetVisibleArea(); + + // Add the visible shapes for wich the accessible objects already exist. + AccessibleShapeList::iterator I,aEnd = maAccessibleShapes.end(); + for (I=maAccessibleShapes.begin(); I != aEnd; ++I) + { + if (I->is()) + { + uno::Reference<XAccessibleComponent> xComponent ( + (*I)->getAccessibleContext(), uno::UNO_QUERY); + if (xComponent.is()) + { + // The bounding box of the object already is clipped to the + // visible area. The object is therefore visible if the + // bounding box has non-zero extensions. + awt::Rectangle aPixelBBox (xComponent->getBounds()); + if ((aPixelBBox.Width > 0) && (aPixelBBox.Height > 0)) + raDescriptorList.push_back (ChildDescriptor (*I)); + } + } + } + + // Add the visible shapes for which only the XShapes exist. + uno::Reference<container::XIndexAccess> xShapeAccess (mxShapeList, uno::UNO_QUERY); + if (xShapeAccess.is()) + { + sal_Int32 nShapeCount = xShapeAccess->getCount(); + raDescriptorList.reserve( nShapeCount ); + awt::Point aPos; + awt::Size aSize; + Rectangle aBoundingBox; + uno::Reference<drawing::XShape> xShape; + for (sal_Int32 i=0; i<nShapeCount; ++i) + { + xShapeAccess->getByIndex(i) >>= xShape; + aPos = xShape->getPosition(); + aSize = xShape->getSize(); + + aBoundingBox.nLeft = aPos.X; + aBoundingBox.nTop = aPos.Y; + aBoundingBox.nRight = aPos.X + aSize.Width; + aBoundingBox.nBottom = aPos.Y + aSize.Height; + + // Insert shape if it is visible, i.e. its bounding box overlaps + // the visible area. + if ( aBoundingBox.IsOver (aVisibleArea) ) + raDescriptorList.push_back (ChildDescriptor (xShape)); + } + } +} + + + + +void ChildrenManagerImpl::RemoveNonVisibleChildren ( + const ChildDescriptorListType& rNewChildList, + ChildDescriptorListType& rOldChildList) +{ + // Iterate over list of formerly visible children and remove those that + // are not visible anymore, i.e. member of the new list of visible + // children. + ChildDescriptorListType::iterator I, aEnd = rOldChildList.end(); + for (I=rOldChildList.begin(); I != aEnd; ++I) + { + if (::std::find(rNewChildList.begin(), rNewChildList.end(), *I) == rNewChildList.end()) + { + // The child is disposed when there is a UNO shape from which + // the accessible shape can be created when the shape becomes + // visible again. When there is no such UNO shape then simply + // reset the descriptor but keep the accessibility object. + if (I->mxShape.is()) + { + UnregisterAsDisposeListener (I->mxShape); + I->disposeAccessibleObject (mrContext); + } + else + { + AccessibleShape* pAccessibleShape = I->GetAccessibleShape(); + pAccessibleShape->ResetState (AccessibleStateType::VISIBLE); + I->mxAccessibleShape = NULL; + } + } + } +} + + + + +void ChildrenManagerImpl::MergeAccessibilityInformation ( + ChildDescriptorListType& raNewChildList) +{ + ChildDescriptorListType::iterator aOldChildDescriptor; + ChildDescriptorListType::iterator I, aEnd = raNewChildList.end(); + for (I=raNewChildList.begin(); I != aEnd; ++I) + { + aOldChildDescriptor = ::std::find (maVisibleChildren.begin(), maVisibleChildren.end(), *I); + + // Copy accessible shape if that exists in the old descriptor. + bool bRegistrationIsNecessary = true; + if (aOldChildDescriptor != maVisibleChildren.end()) + if (aOldChildDescriptor->mxAccessibleShape.is()) + { + I->mxAccessibleShape = aOldChildDescriptor->mxAccessibleShape; + I->mbCreateEventPending = false; + bRegistrationIsNecessary = false; + } + if (bRegistrationIsNecessary) + RegisterAsDisposeListener (I->mxShape); + } +} + + + + +void ChildrenManagerImpl::SendVisibleAreaEvents ( + ChildDescriptorListType& raNewChildList) +{ + ChildDescriptorListType::iterator I,aEnd = raNewChildList.end(); + for (I=raNewChildList.begin(); I != aEnd; ++I) + { + // Tell shape of changed visible area. To do this, fake a + // change of the view forwarder. (Actually we usually get here + // as a result of a change of the view forwarder). + AccessibleShape* pShape = I->GetAccessibleShape (); + if (pShape != NULL) + pShape->ViewForwarderChanged ( + IAccessibleViewForwarderListener::VISIBLE_AREA, + maShapeTreeInfo.GetViewForwarder()); + } +} + + + + +void ChildrenManagerImpl::CreateAccessibilityObjects ( + ChildDescriptorListType& raNewChildList) +{ + ChildDescriptorListType::iterator I, aEnd = raNewChildList.end(); + sal_Int32 nPos = 0; + for ( I = raNewChildList.begin(); I != aEnd; ++I,++nPos) + { + // Create the associated accessible object when the flag says so and + // it does not yet exist. + if ( ! I->mxAccessibleShape.is() ) + GetChild (*I,nPos); + if (I->mxAccessibleShape.is() && I->mbCreateEventPending) + { + I->mbCreateEventPending = false; + mrContext.CommitChange ( + AccessibleEventId::CHILD, + uno::makeAny(I->mxAccessibleShape), + uno::Any()); + } + } +} + + + + +void ChildrenManagerImpl::AddShape (const Reference<drawing::XShape>& rxShape) +{ + if (rxShape.is()) + { + ::osl::ClearableMutexGuard aGuard (maMutex); + + // Test visibility of the shape. + Rectangle aVisibleArea = maShapeTreeInfo.GetViewForwarder()->GetVisibleArea(); + awt::Point aPos = rxShape->getPosition(); + awt::Size aSize = rxShape->getSize(); + + Rectangle aBoundingBox ( + aPos.X, + aPos.Y, + aPos.X + aSize.Width, + aPos.Y + aSize.Height); + + // Add the shape only when it belongs to the list of shapes stored + // in mxShapeList (which is either a page or a group shape). + Reference<container::XChild> xChild (rxShape, uno::UNO_QUERY); + if (xChild.is()) + { + Reference<drawing::XShapes> xParent (xChild->getParent(), uno::UNO_QUERY); + if (xParent == mxShapeList) + if (aBoundingBox.IsOver (aVisibleArea)) + { + // Add shape to list of visible shapes. + maVisibleChildren.push_back (ChildDescriptor (rxShape)); + + // Create accessibility object. + ChildDescriptor& rDescriptor = maVisibleChildren.back(); + GetChild (rDescriptor, maVisibleChildren.size()-1); + + // Inform listeners about new child. + uno::Any aNewShape; + aNewShape <<= rDescriptor.mxAccessibleShape; + aGuard.clear(); + mrContext.CommitChange ( + AccessibleEventId::CHILD, + aNewShape, + uno::Any()); + RegisterAsDisposeListener (rDescriptor.mxShape); + } + } + } +} + + + + +void ChildrenManagerImpl::RemoveShape (const Reference<drawing::XShape>& rxShape) +{ + if (rxShape.is()) + { + ::osl::ClearableMutexGuard aGuard (maMutex); + + // Search shape in list of visible children. + ChildDescriptorListType::iterator I ( + ::std::find (maVisibleChildren.begin(), maVisibleChildren.end(), + ChildDescriptor (rxShape))); + if (I != maVisibleChildren.end()) + { + // Remove descriptor from that list. + Reference<XAccessible> xAccessibleShape (I->mxAccessibleShape); + + UnregisterAsDisposeListener (I->mxShape); + // Dispose the accessible object. + I->disposeAccessibleObject (mrContext); + + // Now we can safely remove the child descriptor and thus + // invalidate the iterator. + maVisibleChildren.erase (I); + + adjustIndexInParentOfShapes(maVisibleChildren); + } + } +} + + + + +void ChildrenManagerImpl::SetShapeList (const ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XShapes>& xShapeList) +{ + mxShapeList = xShapeList; +} + + + + +void ChildrenManagerImpl::AddAccessibleShape (std::auto_ptr<AccessibleShape> pShape) +{ + if (pShape.get() != NULL) + maAccessibleShapes.push_back (pShape.release()); +} + + + + +void ChildrenManagerImpl::ClearAccessibleShapeList (void) +{ + // Copy the list of (visible) shapes to local lists and clear the + // originals. + ChildDescriptorListType aLocalVisibleChildren; + aLocalVisibleChildren.swap(maVisibleChildren); + AccessibleShapeList aLocalAccessibleShapes; + aLocalAccessibleShapes.swap(maAccessibleShapes); + + // Tell the listeners that all children are gone. + mrContext.CommitChange ( + AccessibleEventId::INVALIDATE_ALL_CHILDREN, + uno::Any(), + uno::Any()); + + // There are no accessible shapes left so the index assigned to new + // accessible shapes can be reset. + mnNewNameIndex = 1; + + // Now the objects in the local lists can be safely disposed without + // having problems with callers that want to update their child lists. + + // Clear the list of visible accessible objects. Objects not created on + // demand for XShapes are treated below. + ChildDescriptorListType::iterator I,aEnd = aLocalVisibleChildren.end(); + for (I=aLocalVisibleChildren.begin(); I != aEnd; ++I) + if ( I->mxAccessibleShape.is() && I->mxShape.is() ) + { + ::comphelper::disposeComponent(I->mxAccessibleShape); + I->mxAccessibleShape = NULL; + } + + // Dispose all objects in the accessible shape list. + AccessibleShapeList::iterator J,aEnd2 = aLocalAccessibleShapes.end(); + for (J=aLocalAccessibleShapes.begin(); J != aEnd2; ++J) + if (J->is()) + { + // Dispose the object. + ::comphelper::disposeComponent(*J); + *J = NULL; + } +} + + + + +/** If the broadcasters change at which this object is registered then + unregister at old and register at new broadcasters. +*/ +void ChildrenManagerImpl::SetInfo (const AccessibleShapeTreeInfo& rShapeTreeInfo) +{ + // Remember the current broadcasters and exchange the shape tree info. + Reference<document::XEventBroadcaster> xCurrentBroadcaster; + Reference<frame::XController> xCurrentController; + Reference<view::XSelectionSupplier> xCurrentSelectionSupplier; + { + ::osl::MutexGuard aGuard (maMutex); + xCurrentBroadcaster = maShapeTreeInfo.GetModelBroadcaster(); + xCurrentController = maShapeTreeInfo.GetController(); + xCurrentSelectionSupplier = Reference<view::XSelectionSupplier> ( + xCurrentController, uno::UNO_QUERY); + maShapeTreeInfo = rShapeTreeInfo; + } + + // Move registration to new model. + if (maShapeTreeInfo.GetModelBroadcaster() != xCurrentBroadcaster) + { + // Register at new broadcaster. + if (maShapeTreeInfo.GetModelBroadcaster().is()) + maShapeTreeInfo.GetModelBroadcaster()->addEventListener ( + static_cast<document::XEventListener*>(this)); + + // Unregister at old broadcaster. + if (xCurrentBroadcaster.is()) + xCurrentBroadcaster->removeEventListener ( + static_cast<document::XEventListener*>(this)); + } + + // Move registration to new selection supplier. + Reference<frame::XController> xNewController(maShapeTreeInfo.GetController()); + Reference<view::XSelectionSupplier> xNewSelectionSupplier ( + xNewController, uno::UNO_QUERY); + if (xNewSelectionSupplier != xCurrentSelectionSupplier) + { + // Register at new broadcaster. + if (xNewSelectionSupplier.is()) + { + xNewController->addEventListener( + static_cast<document::XEventListener*>(this)); + + xNewSelectionSupplier->addSelectionChangeListener ( + static_cast<view::XSelectionChangeListener*>(this)); + } + + // Unregister at old broadcaster. + if (xCurrentSelectionSupplier.is()) + { + xCurrentSelectionSupplier->removeSelectionChangeListener ( + static_cast<view::XSelectionChangeListener*>(this)); + + xCurrentController->removeEventListener( + static_cast<document::XEventListener*>(this)); + } + } +} + + + + +//===== lang::XEventListener ================================================ + +void SAL_CALL + ChildrenManagerImpl::disposing (const lang::EventObject& rEventObject) + throw (uno::RuntimeException) +{ + if (rEventObject.Source == maShapeTreeInfo.GetModelBroadcaster() + || rEventObject.Source == maShapeTreeInfo.GetController()) + { + impl_dispose(); + } + + // Handle disposing UNO shapes. + else + { + Reference<drawing::XShape> xShape (rEventObject.Source, uno::UNO_QUERY); + + // Find the descriptor for the given shape. + ChildDescriptorListType::iterator I ( + ::std::find (maVisibleChildren.begin(), maVisibleChildren.end(), + ChildDescriptor (xShape))); + if (I != maVisibleChildren.end()) + { + // Clear the descriptor. + I->disposeAccessibleObject (mrContext); + I->mxShape = NULL; + } + } +} + + + + +//===== document::XEventListener ============================================ + +/** Listen for new and removed shapes. +*/ +void SAL_CALL + ChildrenManagerImpl::notifyEvent ( + const document::EventObject& rEventObject) + throw (uno::RuntimeException) +{ + static const ::rtl::OUString sShapeInserted ( + RTL_CONSTASCII_USTRINGPARAM("ShapeInserted")); + static const ::rtl::OUString sShapeRemoved ( + RTL_CONSTASCII_USTRINGPARAM("ShapeRemoved")); + + + if (rEventObject.EventName.equals (sShapeInserted)) + AddShape (Reference<drawing::XShape>(rEventObject.Source, uno::UNO_QUERY)); + else if (rEventObject.EventName.equals (sShapeRemoved)) + RemoveShape (Reference<drawing::XShape>(rEventObject.Source, uno::UNO_QUERY)); + // else ignore unknown event. +} + + + + +//===== view::XSelectionChangeListener ====================================== + +void SAL_CALL + ChildrenManagerImpl::selectionChanged (const lang::EventObject& /*rEvent*/) + throw (uno::RuntimeException) +{ + UpdateSelection (); +} + + + + +void ChildrenManagerImpl::impl_dispose (void) +{ + Reference<frame::XController> xController(maShapeTreeInfo.GetController()); + // Remove from broadcasters. + try + { + Reference<view::XSelectionSupplier> xSelectionSupplier ( + xController, uno::UNO_QUERY); + if (xSelectionSupplier.is()) + { + xSelectionSupplier->removeSelectionChangeListener ( + static_cast<view::XSelectionChangeListener*>(this)); + } + } + catch( uno::RuntimeException&) + {} + + try + { + if (xController.is()) + xController->removeEventListener( + static_cast<document::XEventListener*>(this)); + } + catch( uno::RuntimeException&) + {} + + maShapeTreeInfo.SetController (NULL); + + try + { + // Remove from broadcaster. + if (maShapeTreeInfo.GetModelBroadcaster().is()) + maShapeTreeInfo.GetModelBroadcaster()->removeEventListener ( + static_cast<document::XEventListener*>(this)); + maShapeTreeInfo.SetModelBroadcaster (NULL); + } + catch( uno::RuntimeException& ) + {} + + ClearAccessibleShapeList (); + SetShapeList (NULL); +} + + + +void SAL_CALL ChildrenManagerImpl::disposing (void) +{ + impl_dispose(); +} + + + + +// This method is experimental. Use with care. +long int ChildrenManagerImpl::GetChildIndex (const ::com::sun::star::uno::Reference< + ::com::sun::star::accessibility::XAccessible>& xChild) const + throw (::com::sun::star::uno::RuntimeException) +{ + ::osl::MutexGuard aGuard (maMutex); + sal_Int32 nCount = maVisibleChildren.size(); + for (sal_Int32 i=0; i < nCount; ++i) + { + // Is this equality comparison valid? + if (maVisibleChildren[i].mxAccessibleShape == xChild) + return i; + } + + return -1; +} + + + + +//===== IAccessibleViewForwarderListener ==================================== + +void ChildrenManagerImpl::ViewForwarderChanged (ChangeType aChangeType, + const IAccessibleViewForwarder* pViewForwarder) +{ + if (aChangeType == IAccessibleViewForwarderListener::VISIBLE_AREA) + Update (false); + else + { + ::osl::MutexGuard aGuard (maMutex); + ChildDescriptorListType::iterator I, aEnd = maVisibleChildren.end(); + for (I=maVisibleChildren.begin(); I != aEnd; ++I) + { + AccessibleShape* pShape = I->GetAccessibleShape(); + if (pShape != NULL) + pShape->ViewForwarderChanged (aChangeType, pViewForwarder); + } + } +} + + + + +//===== IAccessibleParent =================================================== + +sal_Bool ChildrenManagerImpl::ReplaceChild ( + AccessibleShape* pCurrentChild, + const ::com::sun::star::uno::Reference< ::com::sun::star::drawing::XShape >& _rxShape, + const long _nIndex, + const AccessibleShapeTreeInfo& _rShapeTreeInfo) + throw (uno::RuntimeException) +{ + AccessibleShapeInfo aShapeInfo( _rxShape, pCurrentChild->getAccessibleParent(), this, _nIndex ); + // create the new child + AccessibleShape* pNewChild = ShapeTypeHandler::Instance().CreateAccessibleObject ( + aShapeInfo, + _rShapeTreeInfo + ); + Reference< XAccessible > xNewChild( pNewChild ); // keep this alive (do this before calling Init!) + if ( pNewChild ) + pNewChild->Init(); + + sal_Bool bResult = sal_False; + + // Iterate over the visible children. If one of them has an already + // created accessible object that matches pCurrentChild then replace + // it. Otherwise the child to replace is either not in the list or has + // not ye been created (and is therefore not in the list, too) and a + // replacement is not necessary. + ChildDescriptorListType::iterator I,aEnd = maVisibleChildren.end(); + for (I=maVisibleChildren.begin(); I != aEnd; ++I) + { + if (I->GetAccessibleShape() == pCurrentChild) + { + // Dispose the current child and send an event about its deletion. + pCurrentChild->dispose(); + mrContext.CommitChange ( + AccessibleEventId::CHILD, + uno::Any(), + uno::makeAny (I->mxAccessibleShape)); + + // Replace with replacement and send an event about existance + // of the new child. + I->mxAccessibleShape = pNewChild; + mrContext.CommitChange ( + AccessibleEventId::CHILD, + uno::makeAny (I->mxAccessibleShape), + uno::Any()); + bResult = sal_True; + break; + } + } + + // When not found among the visible children we have to search the list + // of accessible shapes. This is not yet implemented. + + return bResult; +} + + + + +/** Update the <const>SELECTED</const> and the <const>FOCUSED</const> state + of all visible children. Maybe this should be changed to all children. + + Iterate over all descriptors of visible accessible shapes and look them + up in the selection. + + If there is no valid controller then all shapes are deselected and + unfocused. If the controller's frame is not active then all shapes are + unfocused. +*/ +void ChildrenManagerImpl::UpdateSelection (void) +{ + Reference<frame::XController> xController(maShapeTreeInfo.GetController()); + Reference<view::XSelectionSupplier> xSelectionSupplier ( + xController, uno::UNO_QUERY); + + // Try to cast the selection both to a multi selection and to a single + // selection. + Reference<container::XIndexAccess> xSelectedShapeAccess; + Reference<drawing::XShape> xSelectedShape; + if (xSelectionSupplier.is()) + { + xSelectedShapeAccess = Reference<container::XIndexAccess> ( + xSelectionSupplier->getSelection(), uno::UNO_QUERY); + xSelectedShape = Reference<drawing::XShape> ( + xSelectionSupplier->getSelection(), uno::UNO_QUERY); + } + + // Remember the current and new focused shape. + AccessibleShape* pCurrentlyFocusedShape = NULL; + AccessibleShape* pNewFocusedShape = NULL; + + ChildDescriptorListType::iterator I, aEnd = maVisibleChildren.end(); + for (I=maVisibleChildren.begin(); I != aEnd; ++I) + { + AccessibleShape* pAccessibleShape = I->GetAccessibleShape(); + if (I->mxAccessibleShape.is() && I->mxShape.is() && pAccessibleShape!=NULL) + { + bool bShapeIsSelected = false; + + // Look up the shape in the (single or multi-) selection. + if (xSelectedShape.is()) + { + if (I->mxShape == xSelectedShape) + { + bShapeIsSelected = true; + pNewFocusedShape = pAccessibleShape; + } + } + else if (xSelectedShapeAccess.is()) + { + sal_Int32 nCount=xSelectedShapeAccess->getCount(); + for (sal_Int32 i=0; i<nCount&&!bShapeIsSelected; i++) + if (xSelectedShapeAccess->getByIndex(i) == I->mxShape) + { + bShapeIsSelected = true; + // In a multi-selection no shape has the focus. + if (nCount == 1) + pNewFocusedShape = pAccessibleShape; + } + } + + // Set or reset the SELECTED state. + if (bShapeIsSelected) + pAccessibleShape->SetState (AccessibleStateType::SELECTED); + else + pAccessibleShape->ResetState (AccessibleStateType::SELECTED); + + // Does the shape have the current selection? + if (pAccessibleShape->GetState (AccessibleStateType::FOCUSED)) + pCurrentlyFocusedShape = pAccessibleShape; + } + } + + // Check if the frame we are in is currently active. If not then make + // sure to not send a FOCUSED state change. + if (xController.is()) + { + Reference<frame::XFrame> xFrame (xController->getFrame()); + if (xFrame.is()) + if ( ! xFrame->isActive()) + pNewFocusedShape = NULL; + } + + // Move focus from current to newly focused shape. + if (pCurrentlyFocusedShape != pNewFocusedShape) + { + if (pCurrentlyFocusedShape != NULL) + pCurrentlyFocusedShape->ResetState (AccessibleStateType::FOCUSED); + if (pNewFocusedShape != NULL) + pNewFocusedShape->SetState (AccessibleStateType::FOCUSED); + } + + // Remember whether there is a shape that now has the focus. + mpFocusedShape = pNewFocusedShape; +} + + + + +bool ChildrenManagerImpl::HasFocus (void) +{ + return mpFocusedShape != NULL; +} + + + + +void ChildrenManagerImpl::RemoveFocus (void) +{ + if (mpFocusedShape != NULL) + { + mpFocusedShape->ResetState (AccessibleStateType::FOCUSED); + mpFocusedShape = NULL; + } +} + + + +void ChildrenManagerImpl::RegisterAsDisposeListener ( + const Reference<drawing::XShape>& xShape) +{ + Reference<lang::XComponent> xComponent (xShape, uno::UNO_QUERY); + if (xComponent.is()) + xComponent->addEventListener ( + static_cast<document::XEventListener*>(this)); +} + + + + +void ChildrenManagerImpl::UnregisterAsDisposeListener ( + const Reference<drawing::XShape>& xShape) +{ + Reference<lang::XComponent> xComponent (xShape, uno::UNO_QUERY); + if (xComponent.is()) + xComponent->removeEventListener ( + static_cast<document::XEventListener*>(this)); +} + + + + +//===== AccessibleChildDescriptor =========================================== + +ChildDescriptor::ChildDescriptor (const Reference<drawing::XShape>& xShape) + : mxShape (xShape), + mxAccessibleShape (NULL), + mbCreateEventPending (true) +{ + // Empty. +} + + + + +ChildDescriptor::ChildDescriptor (const Reference<XAccessible>& rxAccessibleShape) + : mxShape (NULL), + mxAccessibleShape (rxAccessibleShape), + mbCreateEventPending (true) +{ + // Make sure that the accessible object has the <const>VISIBLE</const> + // state set. + AccessibleShape* pAccessibleShape = GetAccessibleShape(); + pAccessibleShape->SetState (AccessibleStateType::VISIBLE); +} + + + + +ChildDescriptor::~ChildDescriptor (void) +{ +} + + + + +AccessibleShape* ChildDescriptor::GetAccessibleShape (void) const +{ + return static_cast<AccessibleShape*> (mxAccessibleShape.get()); +} +// ----------------------------------------------------------------------------- +void ChildDescriptor::setIndexAtAccessibleShape(sal_Int32 _nIndex) +{ + AccessibleShape* pShape = GetAccessibleShape(); + if ( pShape ) + pShape->setIndexInParent(_nIndex); +} +// ----------------------------------------------------------------------------- + + + + +void ChildDescriptor::disposeAccessibleObject (AccessibleContextBase& rParent) +{ + if (mxAccessibleShape.is()) + { + // Send event that the shape has been removed. + uno::Any aOldValue; + aOldValue <<= mxAccessibleShape; + rParent.CommitChange ( + AccessibleEventId::CHILD, + uno::Any(), + aOldValue); + + // Dispose and remove the object. + Reference<lang::XComponent> xComponent (mxAccessibleShape, uno::UNO_QUERY); + if (xComponent.is()) + xComponent->dispose (); + + mxAccessibleShape = NULL; + } +} + + +} // end of namespace accessibility + diff --git a/svx/source/accessibility/ChildrenManagerImpl.hxx b/svx/source/accessibility/ChildrenManagerImpl.hxx new file mode 100644 index 000000000000..9e82a96434db --- /dev/null +++ b/svx/source/accessibility/ChildrenManagerImpl.hxx @@ -0,0 +1,577 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SVX_ACCESSIBILITY_CHILDREN_MANAGER_IMPL_HXX + +#include <svx/IAccessibleViewForwarderListener.hxx> +#include <svx/IAccessibleParent.hxx> +#include <svx/AccessibleShapeTreeInfo.hxx> +#include <editeng/AccessibleContextBase.hxx> +#include <cppuhelper/compbase2.hxx> +#include <vos/mutex.hxx> +#include <vector> +#include <memory> +#include <com/sun/star/drawing/XShape.hpp> +#include <com/sun/star/drawing/XShapes.hpp> +#include <com/sun/star/document/XEventListener.hpp> +#include <com/sun/star/view/XSelectionChangeListener.hpp> +#include <com/sun/star/accessibility/XAccessible.hpp> + +using namespace ::com::sun::star; + +namespace accessibility { + +class AccessibleShape; + +class ChildDescriptor; // See below for declaration. +typedef ::std::vector<ChildDescriptor> ChildDescriptorListType; + +// Re-using MutexOwner class defined in AccessibleContextBase.hxx + +/** This class contains the actual implementation of the children manager. + + <p>It maintains a set of visible accessible shapes in + <member>maVisibleChildren</member>. The objects in this list stem from + two sources. The first is a list of UNO shapes like the list of shapes + in a draw page. A reference to this list is held in + <member>maShapeList</member>. Accessible objects for these shapes are + created on demand. The list can be replaced by calls to the + <member>SetShapeList</member> method. The second source is a list of + already accessible objects. It can be modified by calls to the + <member>AddAccessibleShape</member> and + <member>ClearAccessibleShapeList</member> methods.</p> + + <p>Each call of the <member>Update</member> method leads to a + re-calculation of the visible shapes which then can be queried with the + <member>GetChildCount</member> and <member>GetChild</member> methods. + Events are send informing all listeners about the removed shapes which are + not visible anymore and about the added shapes.</p> + + <p> The visible area which is used to determine the visibility of the + shapes is taken from the view forwarder. Thus, to signal a change of + the visible area call <member>ViewForwarderChanged</member>.</p> + + <p>The children manager adds itself as disposing() listener at every UNO + shape it creates an accessible object for so that when the UNO shape + passes away it can dispose() the associated accessible object.</p> + + @see ChildrenManager +*/ +class ChildrenManagerImpl + : public MutexOwner, + public cppu::WeakComponentImplHelper2< + ::com::sun::star::document::XEventListener, + ::com::sun::star::view::XSelectionChangeListener>, + public IAccessibleViewForwarderListener, + public IAccessibleParent +{ +public: + /** Create a children manager, which manages the children of the given + parent. The parent is used for creating accessible objects. The + list of shapes for which to create those objects is not derived from + the parent and has to be provided seperately by calling one of the + update methods. + @param rxParent + The parent of the accessible objects which will be created + on demand at some point of time in the future. + @param rxShapeList + List of UNO shapes to manage. + @param rShapeTreeInfo + Bundel of information passed down the shape tree. + @param rContext + An accessible context object that is called for fireing events + for new and deleted children, i.e. that holds a list of + listeners to be informed. + */ + ChildrenManagerImpl (const ::com::sun::star::uno::Reference< + ::com::sun::star::accessibility::XAccessible>& rxParent, + const ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XShapes>& rxShapeList, + const AccessibleShapeTreeInfo& rShapeTreeInfo, + AccessibleContextBase& rContext); + + /** If there still are managed children these are disposed and + released. + */ + ~ChildrenManagerImpl (void); + + /** Do that part of the initialization that you can not or should not do + in the constructor like registering at broadcasters. + */ + void Init (void); + + /** Return the number of currently visible accessible children. + @return + If there are no children a 0 is returned. + */ + long GetChildCount (void) const throw (); + + /** Return the requested accessible child or throw and + IndexOutOfBoundsException if the given index is invalid. + @param nIndex + Index of the requested child. Call getChildCount for obtaining + the number of children. + @return + In case of a valid index this method returns a reference to the + requested accessible child. This reference is empty if it has + not been possible to create the accessible object of the + corresponding shape. + @raises + Throws an IndexOutOfBoundsException if the index is not valid. + */ + ::com::sun::star::uno::Reference< + ::com::sun::star::accessibility::XAccessible> + GetChild (long nIndex) + throw (::com::sun::star::uno::RuntimeException, + ::com::sun::star::lang::IndexOutOfBoundsException); + + /** Return the requested accessible child. + @param aChildDescriptor + This object contains references to the original shape and its + associated accessible object. + @param _nIndex + The index which will be used in getAccessibleIndexInParent of the accessible shape. + @return + Returns a reference to the requested accessible child. This + reference is empty if it has not been possible to create the + accessible object of the corresponding shape. + */ + ::com::sun::star::uno::Reference< + ::com::sun::star::accessibility::XAccessible> + GetChild (ChildDescriptor& aChildDescriptor,sal_Int32 _nIndex) + throw (::com::sun::star::uno::RuntimeException); + + /** Return the requested accessible child given a shape. This method + searches the list of descriptors for the one that holds the + association of the given shape to the requested accessible object + and returns that. If no such descriptor is found that is + interpreted so that the specified shape is not visible at the moment. + @param xShape + The shape for which to return the associated accessible object. + @return + Returns a reference to the requested accessible child. The + reference is empty if there is no shape descriptor that + associates the shape with an accessible object. + */ + ::com::sun::star::uno::Reference< + ::com::sun::star::accessibility::XAccessible> + GetChild (const ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XShape>& xShape) + throw (::com::sun::star::uno::RuntimeException); + + /** Update the child manager. Take care of a modified set of children + and modified visible area. This method can optimize the update + process with respect seperate updates of a modified children list + and visible area. + @param bCreateNewObjectsOnDemand + If </true> then accessible objects associated with the visible + shapes are created only when asked for. No event is sent on + creation. If </false> then the accessible objects are created + before this method returns and events are sent to inform the + listeners of the new object. + */ + void Update (bool bCreateNewObjectsOnDemand = true); + + /** Set the list of UNO shapes to the given list. This removes the old + list and does not add to it. The list of accessible shapes that is + build up by calls to <member>AddAccessibleShape</member> is not + modified. Neither is the list of visible children. Accessible + objects are created on demand. + @param xShapeList + The list of UNO shapes that replaces the old list. + */ + void SetShapeList (const ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XShapes>& xShapeList); + + /** Add a accessible shape. This does not modify the list of UNO shapes + or the list of visible shapes. Accessible shapes are, at the + moment, not tested against the visible area but are always appended + to the list of visible children. + @param pShape + The new shape that is added to the list of accessible shapes. + */ + void AddAccessibleShape (std::auto_ptr<AccessibleShape> pShape); + + /** Clear the lists of accessible shapes and that of visible accessible + shapes. The list of UNO shapes is not modified. + */ + void ClearAccessibleShapeList (void); + + /** Set a new event shape tree info. Call this method to inform the + children manager of a change of the info bundle. + @param rShapeTreeInfo + The new info that replaces the current one. + */ + void SetInfo (const AccessibleShapeTreeInfo& rShapeTreeInfo); + + /** Update the SELECTED and FOCUSED states of all visible children + according to the given selection. This includes setting + <em>and</em> resetting the states. + */ + void UpdateSelection (void); + + /** Return whether one of the shapes managed by this object has + currently the focus. + @return + Returns <true/> when there is a shape that has the focus and + <false/> when there is no such shape. + */ + bool HasFocus (void); + + /** When there is a shape that currently has the focus, + i.e. <member>HasFocus()</member> returns <true/> then remove the + focus from that shape. Otherwise nothing changes. + */ + void RemoveFocus (void); + + //===== lang::XEventListener ============================================ + + virtual void SAL_CALL + disposing (const ::com::sun::star::lang::EventObject& rEventObject) + throw (::com::sun::star::uno::RuntimeException); + + + //===== document::XEventListener ======================================== + + virtual void SAL_CALL + notifyEvent (const ::com::sun::star::document::EventObject& rEventObject) + throw (::com::sun::star::uno::RuntimeException); + + + //===== view::XSelectionChangeListener ================================== + + virtual void SAL_CALL + selectionChanged (const ::com::sun::star::lang::EventObject& rEvent) + throw (::com::sun::star::uno::RuntimeException); + + + //===== IAccessibleViewForwarderListener ================================ + + /** Informs this children manager and its children about a change of one + (or more) aspect of the view forwarder. + @param aChangeType + A change type of <const>VISIBLE_AREA</const> leads to a call to + the <member>Update</memeber> which creates accessible objects of + new shapes immediately. Other change types are passed to the + visible accessible children without calling + <member>Update</memeber>. + @param pViewForwarder + The modified view forwarder. Use this one from now on. + */ + virtual void ViewForwarderChanged (ChangeType aChangeType, + const IAccessibleViewForwarder* pViewForwarder); + + //===== IAccessibleParent =============================================== + + /** Replace the specified child with a replacement. + @param pCurrentChild + This child is to be replaced. + @param pReplacement + The replacement for the current child. + @return + The returned value indicates wether the replacement has been + finished successfully. + */ + virtual sal_Bool ReplaceChild ( + AccessibleShape* pCurrentChild, + const ::com::sun::star::uno::Reference< ::com::sun::star::drawing::XShape >& _rxShape, + const long _nIndex, + const AccessibleShapeTreeInfo& _rShapeTreeInfo + ) throw (::com::sun::star::uno::RuntimeException); + + +protected: + /** This list holds the descriptors of all currently visible shapes and + associated accessible object. + + <p>With the descriptors it maintains a mapping of shapes to + accessible objects. It acts as a cache in that accessible objects + are only created on demand and released with every update (where the + latter may be optimized by the update methods).<p> + + <p>The list is realized as a vector because it remains unchanged + between updates (i.e. complete rebuilds of the list) and allows a + fast (constant time) access to its elements for given indices.</p> + */ + ChildDescriptorListType maVisibleChildren; + + /** The original list of UNO shapes. The visible shapes are inserted + into the list of visible children + <member>maVisibleChildren</member>. + */ + ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XShapes> mxShapeList; + + /** This list of additional accessible shapes that can or shall not be + created by the shape factory. + */ + typedef std::vector< ::com::sun::star::uno::Reference< + ::com::sun::star::accessibility::XAccessible> > AccessibleShapeList; + AccessibleShapeList maAccessibleShapes; + + /** Rectangle that describes the visible area in which a shape has to lie + at least partly, to be accessible through this class. Used to + detect changes of the visible area after changes of the view forwarder. + */ + Rectangle maVisibleArea; + + /** The parent of the shapes. It is used for creating accessible + objects for given shapes. + */ + ::com::sun::star::uno::Reference< + ::com::sun::star::accessibility::XAccessible> mxParent; + + /** Bundel of information passed down the shape tree. + */ + AccessibleShapeTreeInfo maShapeTreeInfo; + + /** Reference to an accessible context object that is used to inform its + listeners of new and remved children. + */ + AccessibleContextBase& mrContext; + + /** This method is called from the component helper base class while + disposing. + */ + virtual void SAL_CALL disposing (void); + + /** Experimental: Get the index of the specified accessible object with + respect to the list of children maintained by this object. + + @return + Return the index of the given child or -1 to indicate that the + child is unknown. + */ + long GetChildIndex (const ::com::sun::star::uno::Reference< + ::com::sun::star::accessibility::XAccessible>& xChild) const + throw (::com::sun::star::uno::RuntimeException); + + void impl_dispose (void); + +private: + /** Names of new accessible objects are disambiguated with this index. + It gets increased every time a new object is created and (at the + moment) never reset. + */ + sal_Int32 mnNewNameIndex; + + // Don't use the copy constructor or the assignment operator. They are + // not implemented (and are not intended to be). + ChildrenManagerImpl (const ChildrenManagerImpl&); + ChildrenManagerImpl& operator= (const ChildrenManagerImpl&); + + /** This member points to the currently focused shape. It is NULL when + there is no focused shape. + */ + AccessibleShape* mpFocusedShape; + + /** Three helper functions for the <member>Update</member> method. + */ + + /** Create a list of visible shapes from the list of UNO shapes + <member>maShapeList</member> and the list of accessible objects. + @param raChildList + For every visible shape from the two sources mentioned above one + descriptor is added to this list. + */ + void CreateListOfVisibleShapes (ChildDescriptorListType& raChildList); + + /** From the old list of (former) visible shapes remove those that + are not member of the new list. Send appropriate events for every + such shape. + @param raNewChildList + The new list of visible children against which the old one + is compared. + @param raOldChildList + The old list of visible children against which the new one + is compared. + */ + void RemoveNonVisibleChildren ( + const ChildDescriptorListType& raNewChildList, + ChildDescriptorListType& raOldChildList); + + /** Merge the information that is already known about the visible shapes + from the current list into the new list. + @param raChildList + Information is merged from the current list of visible children + to this list. + */ + void MergeAccessibilityInformation (ChildDescriptorListType& raChildList); + + /** If the visible area has changed then send events that signal a + change of their bounding boxes for all shapes that are members of + both the current and the new list of visible shapes. + @param raChildList + Events are sent to all entries of this list that already contain + an accessible object. + */ + void SendVisibleAreaEvents (ChildDescriptorListType& raChildList); + + /** If children have to be created immediately and not on demand the + create the missing accessible objects now. + @param raDescriptorList + Create an accessible object for every member of this list where + that obejct does not already exist. + */ + void CreateAccessibilityObjects (ChildDescriptorListType& raChildList); + + /** Add a single shape. Update all relevant data structures + accordingly. Use this method instead of <member>Update()</member> + when only a single shape has been added. + */ + void AddShape (const ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XShape>& xShape); + + /** Remove a single shape. Update all relevant data structures + accordingly. Use this method instead of <member>Update()</member> + when only a single shape has been removed. + */ + void RemoveShape (const ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XShape>& xShape); + + /** Add the children manager as dispose listener at the given shape so + that the associated accessible object can be disposed when the shape + is disposed. + @param xShape + Register at this shape as dispose listener. + */ + void RegisterAsDisposeListener (const ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XShape>& xShape); + + /** Remove the children manager as dispose listener at the given shape + @param xShape + Unregister at this shape as dispose listener. + */ + void UnregisterAsDisposeListener (const ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XShape>& xShape); +}; + + + + +/** A child descriptor holds a reference to a UNO shape and the + corresponding accessible object. There are two use cases: + <ol><li>The accessible object is only created on demand and is then + initially empty.</li> + <li>There is no UNO shape. The accessible object is given as argument + to the constructor.</li> + </ol> + In both cases the child descriptor assumes ownership over the accessible + object. +*/ +class ChildDescriptor +{ +public: + /** Reference to a (partially) visible shape. + */ + ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XShape> mxShape; + + /** The corresponding accessible object. This reference is initially + empty and only replaced by a reference to a new object when that is + requested from the outside. + */ + ::com::sun::star::uno::Reference< + ::com::sun::star::accessibility::XAccessible> mxAccessibleShape; + + /** Return a pointer to the implementation object of the accessible + shape of this descriptor. + @return + The result is NULL if either the UNO reference to the accessible + shape is empty or it can not be transformed into a pointer to + the desired class. + */ + AccessibleShape* GetAccessibleShape (void) const; + + /** set the index _nIndex at the accessible shape + @param _nIndex + The new index in parent. + */ + void setIndexAtAccessibleShape(sal_Int32 _nIndex); + + /** This flag is set during the visibility calculation and indicates + that at one time in this process an event is sent that informs the + listners of the creation of a new accessible object. This flags is + not reset afterwards. Don't use it unless you know exactly what you + are doing. + */ + bool mbCreateEventPending; + + /** Create a new descriptor for the specified shape with empty reference + to accessible object. + */ + explicit ChildDescriptor (const ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XShape>& xShape); + + /** Create a new descriptor for the specified shape with empty reference + to the original shape. + */ + explicit ChildDescriptor (const ::com::sun::star::uno::Reference< + ::com::sun::star::accessibility::XAccessible>& rxAccessibleShape); + + ~ChildDescriptor (void); + + /** Dispose the accessible object of this descriptor. If that object + does not exist then do nothing. + @param rParent + The parent of the accessible object to dispose. A child event + is sent in its name. + */ + void disposeAccessibleObject (AccessibleContextBase& rParent); + + /** Compare two child descriptors. Take into account that a child + descriptor may be based on a UNO shape or, already, on an accessible + shape. + */ + inline bool operator == (const ChildDescriptor& aDescriptor) const + { + return ( + this == &aDescriptor || + ( + (mxShape.get() == aDescriptor.mxShape.get() ) && + (mxShape.is() || mxAccessibleShape.get() == aDescriptor.mxAccessibleShape.get()) + ) + ); + } + + /** The ordering defined by this operator is only used in order to be able + to put child descriptors in some STL containers. The ordering itself is + not so important, its 'features' are not used. + */ + inline bool operator < (const ChildDescriptor& aDescriptor) const + { + return (mxShape.get() < aDescriptor.mxShape.get()); + } + +}; + + + +} // end of namespace accessibility + +#endif + diff --git a/svx/source/accessibility/DGColorNameLookUp.cxx b/svx/source/accessibility/DGColorNameLookUp.cxx new file mode 100644 index 000000000000..8c8e0e403654 --- /dev/null +++ b/svx/source/accessibility/DGColorNameLookUp.cxx @@ -0,0 +1,148 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svx.hxx" +#include "DGColorNameLookUp.hxx" +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <comphelper/processfactory.hxx> +#include <vos/mutex.hxx> +#include <vcl/svapp.hxx> + + +using ::rtl::OUString; +using namespace ::com::sun::star; + +namespace accessibility { + +// Initialize the class instance with NULL. A true instance is created only +// when the static <member>Instance</member> is called for the first time. +DGColorNameLookUp* DGColorNameLookUp::mpInstance = NULL; + +DGColorNameLookUp& DGColorNameLookUp::Instance (void) +{ + // Using double check pattern to make sure that exactly one instance of + // the shape type handler is instantiated. + if (mpInstance == NULL) + { + ::vos::OGuard aGuard (::Application::GetSolarMutex()); + if (mpInstance == NULL) + { + // Create the single instance of the color name look up. + mpInstance = new DGColorNameLookUp(); + } + } + + return *mpInstance; +} + + + + +OUString DGColorNameLookUp::LookUpColor (long int nColor) const +{ + OUString sColorName; + tColorValueToNameMap::const_iterator I; + I = maColorValueToNameMap.find (nColor); + if (I != maColorValueToNameMap.end()) + // Found the color value. Return the associated name. + sColorName = I->second; + else + { + // Did not find the given color. Append its rgb tuple to the + // description. + ::rtl::OUStringBuffer sNameBuffer; + sNameBuffer.append (sal_Unicode('#')); + sNameBuffer.append (nColor, 16); + sColorName = sNameBuffer.makeStringAndClear(); + } + return sColorName; +} + + + + +DGColorNameLookUp::DGColorNameLookUp (void) +{ + uno::Sequence<OUString> aNames; + uno::Reference<container::XNameAccess> xNA; + + try + { + // Create color table in which to look up the given color. + uno::Reference<container::XNameContainer> xColorTable ( + ::comphelper::getProcessServiceFactory()->createInstance( + OUString::createFromAscii("com.sun.star.drawing.ColorTable")), + uno::UNO_QUERY); + + // Get list of color names in order to iterate over the color table. + xNA = uno::Reference<container::XNameAccess>(xColorTable, uno::UNO_QUERY); + if (xNA.is()) + { + // Look the solar mutex here as workarround for missing lock in + // called function. + ::vos::OGuard aGuard (::Application::GetSolarMutex()); + aNames = xNA->getElementNames(); + } + } + catch (uno::RuntimeException e) + { + // When an excpetion occured then whe have an empty name sequence + // and the loop below is not entered. + } + + // Fill the map to convert from numerical color values to names. + if (xNA.is()) + for (long int i=0; i<aNames.getLength(); i++) + { + // Get the numerical value for the i-th color name. + try + { + uno::Any aColor (xNA->getByName (aNames[i])); + long nColor = 0; + aColor >>= nColor; + maColorValueToNameMap[nColor] = aNames[i]; + } + catch (uno::RuntimeException e) + { + // Ignore the exception: the color who lead to the excpetion + // is not included into the map. + } + } +} + + + + +DGColorNameLookUp::~DGColorNameLookUp (void) +{ + maColorValueToNameMap.clear(); +} + +} // end of namespace accessibility diff --git a/svx/source/accessibility/DescriptionGenerator.cxx b/svx/source/accessibility/DescriptionGenerator.cxx new file mode 100644 index 000000000000..010bc5decfeb --- /dev/null +++ b/svx/source/accessibility/DescriptionGenerator.cxx @@ -0,0 +1,484 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svx.hxx" + +#include "DescriptionGenerator.hxx" +#include <com/sun/star/beans/PropertyState.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/XPropertyState.hpp> +#include <com/sun/star/container/XChild.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/container/XNamed.hpp> +#include <com/sun/star/drawing/FillStyle.hpp> +#include <com/sun/star/drawing/XShapes.hpp> +#include <com/sun/star/drawing/XShapeDescriptor.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/style/XStyle.hpp> +#include <comphelper/processfactory.hxx> +#include <vos/mutex.hxx> +#include <vcl/svapp.hxx> + +#include <com/sun/star/uno/Exception.hpp> + +// Includes for string resources. +#ifndef _SVX_ACCESSIBILITY_HRC +#include "accessibility.hrc" +#endif +#include "svdstr.hrc" +#include <svx/dialmgr.hxx> +#include <tools/string.hxx> + +#include <svx/xdef.hxx> +#include "unoapi.hxx" +#include "accessibility.hrc" +#include "DGColorNameLookUp.hxx" + +using namespace ::rtl; +using namespace ::com::sun::star; + + +void SvxUnogetInternalNameForItem( const sal_Int16 nWhich, const rtl::OUString& rApiName, String& rInternalName ) throw(); + +namespace accessibility { + + +DescriptionGenerator::DescriptionGenerator ( + const uno::Reference<drawing::XShape>& xShape) + : mxShape (xShape), + mxSet (mxShape, uno::UNO_QUERY), + mbIsFirstProperty (true) +{ +} + + + + +DescriptionGenerator::~DescriptionGenerator (void) +{ +} + + + + +void DescriptionGenerator::Initialize (sal_Int32 nResourceId) +{ + // Get the string from the resource for the specified id. + OUString sPrefix; + { + ::vos::OGuard aGuard (::Application::GetSolarMutex()); + sPrefix = OUString (SVX_RESSTR (nResourceId)); + } + + // Forward the call with the resulting string. + Initialize (sPrefix); +} + + + + +void DescriptionGenerator::Initialize (::rtl::OUString sPrefix) +{ + msDescription = sPrefix; + if (mxSet.is()) + { + { + ::vos::OGuard aGuard (::Application::GetSolarMutex()); + + msDescription.append (sal_Unicode (' ')); + msDescription.append (OUString (SVX_RESSTR(RID_SVXSTR_A11Y_WITH))); + msDescription.append (sal_Unicode (' ')); + + msDescription.append (OUString (SVX_RESSTR (RID_SVXSTR_A11Y_STYLE))); + msDescription.append (sal_Unicode ('=')); + } + + try + { + if (mxSet.is()) + { + uno::Any aValue = mxSet->getPropertyValue (OUString::createFromAscii ("Style")); + uno::Reference<container::XNamed> xStyle (aValue, uno::UNO_QUERY); + if (xStyle.is()) + msDescription.append (xStyle->getName()); + } + else + msDescription.append ( + OUString::createFromAscii("<no style>")); + } + catch (::com::sun::star::beans::UnknownPropertyException) + { + msDescription.append ( + OUString::createFromAscii("<unknown>")); + } + } +} + + + + +::rtl::OUString DescriptionGenerator::operator() (void) +{ + msDescription.append (sal_Unicode ('.')); + return msDescription.makeStringAndClear(); +} + + + + +void DescriptionGenerator::AddProperty ( + const OUString& sPropertyName, + PropertyType aType, + const sal_Int32 nLocalizedNameId, + long nWhichId) +{ + OUString sLocalizedName; + { + ::vos::OGuard aGuard (::Application::GetSolarMutex()); + sLocalizedName = SVX_RESSTR (nLocalizedNameId); + } + AddProperty (sPropertyName, aType, sLocalizedName, nWhichId); +} + + + + +void DescriptionGenerator::AddProperty (const OUString& sPropertyName, + PropertyType aType, const OUString& sLocalizedName, long nWhichId) +{ + uno::Reference<beans::XPropertyState> xState (mxShape, uno::UNO_QUERY); + if (xState.is() + && xState->getPropertyState(sPropertyName)!=beans::PropertyState_DEFAULT_VALUE) + if (mxSet.is()) + { + // Append a seperator from previous Properties. + if ( ! mbIsFirstProperty) + msDescription.append (sal_Unicode (',')); + else + { + ::vos::OGuard aGuard (::Application::GetSolarMutex()); + + msDescription.append (sal_Unicode (' ')); + msDescription.append (OUString (SVX_RESSTR(RID_SVXSTR_A11Y_AND))); + msDescription.append (sal_Unicode (' ')); + mbIsFirstProperty = false; + } + + // Delegate to type specific property handling. + switch (aType) + { + case COLOR: + AddColor (sPropertyName, sLocalizedName); + break; + case INTEGER: + AddInteger (sPropertyName, sLocalizedName); + break; + case STRING: + AddString (sPropertyName, sLocalizedName, nWhichId); + break; + case FILL_STYLE: + AddFillStyle (sPropertyName, sLocalizedName); + break; + } + } +} + + + + +void DescriptionGenerator::AppendString (const ::rtl::OUString& sString) +{ + msDescription.append (sString); +} + + + + +void DescriptionGenerator::AddLineProperties (void) +{ + AddProperty (OUString::createFromAscii ("LineColor"), + DescriptionGenerator::COLOR, + SIP_XA_LINECOLOR); + AddProperty (OUString::createFromAscii ("LineDashName"), + DescriptionGenerator::STRING, + SIP_XA_LINEDASH, + XATTR_LINEDASH); + AddProperty (OUString::createFromAscii ("LineWidth"), + DescriptionGenerator::INTEGER, + SIP_XA_LINEWIDTH); +} + + + + +/** The fill style is described by the property "FillStyle". Depending on + its value a hatch-, gradient-, or bitmap name is appended. +*/ +void DescriptionGenerator::AddFillProperties (void) +{ + AddProperty (OUString::createFromAscii ("FillStyle"), + DescriptionGenerator::FILL_STYLE, + SIP_XA_FILLSTYLE); +} + + + + +void DescriptionGenerator::Add3DProperties (void) +{ + AddProperty (OUString::createFromAscii ("D3DMaterialColor"), + DescriptionGenerator::COLOR, + RID_SVXSTR_A11Y_3D_MATERIAL_COLOR); + AddLineProperties (); + AddFillProperties (); +} + + + + +void DescriptionGenerator::AddTextProperties (void) +{ + AddProperty (OUString::createFromAscii ("CharColor"), + DescriptionGenerator::COLOR); + AddFillProperties (); +} + + + + +/** Search for the given color in the global color table. If found append + its name to the description. Otherwise append its RGB tuple. +*/ +void DescriptionGenerator::AddColor (const OUString& sPropertyName, + const OUString& sLocalizedName) +{ + msDescription.append (sLocalizedName); + msDescription.append (sal_Unicode('=')); + + try + { + + long nValue(0); + if (mxSet.is()) + { + uno::Any aValue = mxSet->getPropertyValue (sPropertyName); + aValue >>= nValue; + } + + msDescription.append (DGColorNameLookUp::Instance().LookUpColor (nValue)); + } + catch (::com::sun::star::beans::UnknownPropertyException) + { + msDescription.append ( + OUString::createFromAscii("<unknown>")); + } +} + + + + +void DescriptionGenerator::AddUnknown (const OUString& /*sPropertyName*/, + const OUString& sLocalizedName) +{ + // uno::Any aValue = mxSet->getPropertyValue (sPropertyName); + msDescription.append (sLocalizedName); +} + + + + +void DescriptionGenerator::AddInteger (const OUString& sPropertyName, + const OUString& sLocalizedName) +{ + msDescription.append (sLocalizedName); + msDescription.append (sal_Unicode('=')); + + try + { + if (mxSet.is()) + { + uno::Any aValue = mxSet->getPropertyValue (sPropertyName); + long nValue = 0; + aValue >>= nValue; + msDescription.append (nValue); + } + } + catch (::com::sun::star::beans::UnknownPropertyException) + { + msDescription.append ( + OUString::createFromAscii("<unknown>")); + } +} + + + + +void DescriptionGenerator::AddString (const OUString& sPropertyName, + const OUString& sLocalizedName, long nWhichId) +{ + msDescription.append (sLocalizedName); + msDescription.append (sal_Unicode('=')); + + try + { + if (mxSet.is()) + { + uno::Any aValue = mxSet->getPropertyValue (sPropertyName); + OUString sValue; + aValue >>= sValue; + + if (nWhichId >= 0) + { + ::vos::OGuard aGuard (::Application::GetSolarMutex()); + String sLocalizedValue; + SvxUnogetInternalNameForItem (sal::static_int_cast<sal_Int16>(nWhichId), + sValue, sLocalizedValue); + msDescription.append (sLocalizedValue); + } + else + msDescription.append (sValue); + } + } + catch (::com::sun::star::beans::UnknownPropertyException) + { + msDescription.append ( + OUString::createFromAscii("<unknown>")); + } +} + + + + +void DescriptionGenerator::AddFillStyle (const OUString& sPropertyName, + const OUString& sLocalizedName) +{ + msDescription.append (sLocalizedName); + msDescription.append (sal_Unicode('=')); + + try + { + if (mxSet.is()) + { + uno::Any aValue = mxSet->getPropertyValue (sPropertyName); + drawing::FillStyle aFillStyle; + aValue >>= aFillStyle; + + // Get the fill style name from the resource. + OUString sFillStyleName; + { + ::vos::OGuard aGuard (::Application::GetSolarMutex()); + switch (aFillStyle) + { + case drawing::FillStyle_NONE: + sFillStyleName = SVX_RESSTR(RID_SVXSTR_A11Y_FILLSTYLE_NONE); + break; + case drawing::FillStyle_SOLID: + sFillStyleName = SVX_RESSTR(RID_SVXSTR_A11Y_FILLSTYLE_SOLID); + break; + case drawing::FillStyle_GRADIENT: + sFillStyleName = SVX_RESSTR(RID_SVXSTR_A11Y_FILLSTYLE_GRADIENT); + break; + case drawing::FillStyle_HATCH: + sFillStyleName = SVX_RESSTR(RID_SVXSTR_A11Y_FILLSTYLE_HATCH); + break; + case drawing::FillStyle_BITMAP: + sFillStyleName = SVX_RESSTR(RID_SVXSTR_A11Y_FILLSTYLE_BITMAP); + break; + case drawing::FillStyle_MAKE_FIXED_SIZE: + break; + } + } + msDescription.append (sFillStyleName); + + // Append the appropriate properties. + switch (aFillStyle) + { + case drawing::FillStyle_NONE: + break; + case drawing::FillStyle_SOLID: + AddProperty (OUString::createFromAscii ("FillColor"), + COLOR, + SIP_XA_FILLCOLOR); + break; + case drawing::FillStyle_GRADIENT: + AddProperty (OUString::createFromAscii ("FillGradientName"), + STRING, + SIP_XA_FILLGRADIENT, + XATTR_FILLGRADIENT); + break; + case drawing::FillStyle_HATCH: + AddProperty (OUString::createFromAscii ("FillColor"), + COLOR, + SIP_XA_FILLCOLOR); + AddProperty (OUString::createFromAscii ("FillHatchName"), + STRING, + SIP_XA_FILLHATCH, + XATTR_FILLHATCH); + break; + case drawing::FillStyle_BITMAP: + AddProperty (OUString::createFromAscii ("FillBitmapName"), + STRING, + SIP_XA_FILLBITMAP, + XATTR_FILLBITMAP); + break; + case drawing::FillStyle_MAKE_FIXED_SIZE: + break; + } + } + } + catch (::com::sun::star::beans::UnknownPropertyException) + { + msDescription.append ( + OUString::createFromAscii("<unknown>")); + } +} + + + + +void DescriptionGenerator::AddPropertyNames (void) +{ + if (mxSet.is()) + { + uno::Reference<beans::XPropertySetInfo> xInfo (mxSet->getPropertySetInfo()); + if (xInfo.is()) + { + uno::Sequence<beans::Property> aPropertyList (xInfo->getProperties ()); + for (int i=0; i<aPropertyList.getLength(); i++) + { + msDescription.append (aPropertyList[i].Name); + msDescription.append (sal_Unicode(',')); + } + } + } +} + + +} // end of namespace accessibility diff --git a/svx/source/accessibility/GraphCtlAccessibleContext.cxx b/svx/source/accessibility/GraphCtlAccessibleContext.cxx new file mode 100644 index 000000000000..b0523bda67e1 --- /dev/null +++ b/svx/source/accessibility/GraphCtlAccessibleContext.cxx @@ -0,0 +1,1027 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svx.hxx" +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/beans/PropertyChangeEvent.hpp> +#include <com/sun/star/awt/XWindow.hpp> +#include <unotools/accessiblestatesethelper.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <vcl/svapp.hxx> +#include <osl/mutex.hxx> +#include <rtl/uuid.h> +#include <tools/debug.hxx> +#include <tools/gen.hxx> +#include <svl/smplhint.hxx> +#include <toolkit/helper/convert.hxx> +#include <svtools/colorcfg.hxx> +#include <comphelper/accessibleeventnotifier.hxx> +#include <sdrpaintwindow.hxx> + +//===== local includes ======================================================== +#include <svx/ShapeTypeHandler.hxx> +#include <svx/AccessibleShapeInfo.hxx> +#include "GraphCtlAccessibleContext.hxx" +#include <svx/graphctl.hxx> +#ifndef _SVX_DIALOGS_HRC +#include <svx/dialogs.hrc> +#endif +#ifndef _SVX_ACCESSIBILITY_HRC +#include "accessibility.hrc" +#endif +#include <svx/svdpage.hxx> +#include <svx/unomod.hxx> +#include <svx/dialmgr.hxx> +#include <svx/svdetc.hxx> +#include <svx/sdrhittesthelper.hxx> + +//===== namespaces =========================================================== + +using namespace ::vos; +using namespace ::cppu; +using namespace ::osl; +using ::rtl::OUString; +using namespace ::accessibility; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::accessibility; + +//===== internal ============================================================ + +/** initialize this component and set default values */ +SvxGraphCtrlAccessibleContext::SvxGraphCtrlAccessibleContext( + const Reference< XAccessible >& rxParent, + GraphCtrl& rRepr, + const OUString* pName, + const OUString* pDesc ) : + + SvxGraphCtrlAccessibleContext_Base( m_aMutex ), + mxParent( rxParent ), + mpControl( &rRepr ), + mpModel (NULL), + mpPage (NULL), + mpView (NULL), + mnClientId( 0 ), + mbDisposed( sal_False ) +{ + if (mpControl != NULL) + { + mpModel = mpControl->GetSdrModel(); + if (mpModel != NULL) + mpPage = (SdrPage*)mpModel->GetPage( 0 ); + mpView = mpControl->GetSdrView(); + + if( mpModel == NULL || mpPage == NULL || mpView == NULL ) + { + mbDisposed = true; + // Set all the pointers to NULL just in case they are used as + // a disposed flag. + mpModel = NULL; + mpPage = NULL; + mpView = NULL; + } + } + + if( pName ) + { + msName = *pName; + } + else + { + ::vos::OGuard aSolarGuard( Application::GetSolarMutex() ); + msName = SVX_RESSTR( RID_SVXSTR_GRAPHCTRL_ACC_NAME ); + } + + if( pDesc ) + { + msDescription = *pDesc; + } + else + { + ::vos::OGuard aSolarGuard( Application::GetSolarMutex() ); + msDescription = SVX_RESSTR( RID_SVXSTR_GRAPHCTRL_ACC_DESCRIPTION ); + } + + maTreeInfo.SetSdrView( mpView ); + maTreeInfo.SetWindow( mpControl ); + maTreeInfo.SetViewForwarder( const_cast<SvxGraphCtrlAccessibleContext*>(this) ); +} + +//----------------------------------------------------------------------------- + +/** on destruction, this component is disposed and all dispose listeners + are called, except if this component was already disposed */ +SvxGraphCtrlAccessibleContext::~SvxGraphCtrlAccessibleContext() +{ + disposing(); +} + +//----------------------------------------------------------------------------- + +/** returns the XAccessible interface for a given SdrObject. + Multiple calls for the same SdrObject return the same XAccessible. +*/ +Reference< XAccessible > SAL_CALL SvxGraphCtrlAccessibleContext::getAccessible( const SdrObject* pObj ) +{ + Reference<XAccessible> xAccessibleShape; + + if( pObj ) + { + // see if we already created an XAccessible for the given SdrObject + ShapesMapType::iterator iter = mxShapes.find( pObj ); + + if( iter != mxShapes.end() ) + { + // if we already have one, return it + xAccessibleShape = (*iter).second; + } + else + { + // create a new one and remember in our internal map + Reference< XShape > xShape( Reference< XShape >::query( (const_cast<SdrObject*>(pObj))->getUnoShape() ) ); + + AccessibleShapeInfo aShapeInfo (xShape,mxParent); + // Create accessible object that corresponds to the descriptor's shape. + AccessibleShape* pAcc = ShapeTypeHandler::Instance().CreateAccessibleObject( + aShapeInfo, maTreeInfo); + xAccessibleShape = pAcc; + if (pAcc != NULL) + { + pAcc->acquire(); + // Now that we acquired the new accessible shape we can + // safely call its Init() method. + pAcc->Init (); + } + mxShapes[pObj] = pAcc; + + // Create event and inform listeners of the object creation. + CommitChange( AccessibleEventId::CHILD, makeAny( xAccessibleShape ), makeAny( Reference<XAccessible>() ) ); + } + } + + return xAccessibleShape; +} + +//===== XAccessible ========================================================= + +Reference< XAccessibleContext > SAL_CALL SvxGraphCtrlAccessibleContext::getAccessibleContext( void ) throw( RuntimeException ) +{ + return this; +} + +//===== XAccessibleComponent ================================================ + +sal_Bool SAL_CALL SvxGraphCtrlAccessibleContext::containsPoint( const awt::Point& rPoint ) throw( RuntimeException ) +{ + // no guard -> done in getSize() + awt::Size aSize (getSize()); + return (rPoint.X >= 0) + && (rPoint.X < aSize.Width) + && (rPoint.Y >= 0) + && (rPoint.Y < aSize.Height); +} + +//----------------------------------------------------------------------------- + +Reference< XAccessible > SAL_CALL SvxGraphCtrlAccessibleContext::getAccessibleAtPoint( const awt::Point& rPoint ) throw( RuntimeException ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + Reference< XAccessible > xAccessible; + + if( mpControl ) + { + Point aPnt( rPoint.X, rPoint.Y ); + mpControl->PixelToLogic( aPnt ); + + SdrObject* pObj = 0; + + if(mpView && mpView->GetSdrPageView()) + { + pObj = SdrObjListPrimitiveHit(*mpPage, aPnt, 1, *mpView->GetSdrPageView(), 0, false); + } + + if( pObj ) + xAccessible = getAccessible( pObj ); + } + else + { + throw DisposedException(); + } + + return xAccessible; +} + +//----------------------------------------------------------------------------- + +awt::Rectangle SAL_CALL SvxGraphCtrlAccessibleContext::getBounds() throw( RuntimeException ) +{ + // no guard -> done in GetBoundingBox() + Rectangle aCoreBounds( GetBoundingBox() ); + awt::Rectangle aBounds; + aBounds.X = aCoreBounds.getX(); + aBounds.Y = aCoreBounds.getY(); + aBounds.Width = aCoreBounds.getWidth(); + aBounds.Height = aCoreBounds.getHeight(); + return aBounds; +} + +//----------------------------------------------------------------------------- + +awt::Point SAL_CALL SvxGraphCtrlAccessibleContext::getLocation() throw( RuntimeException ) +{ + // no guard -> done in GetBoundingBox() + Rectangle aRect( GetBoundingBox() ); + return awt::Point( aRect.getX(), aRect.getY() ); +} + +//----------------------------------------------------------------------------- + +awt::Point SAL_CALL SvxGraphCtrlAccessibleContext::getLocationOnScreen() throw( RuntimeException ) +{ + // no guard -> done in GetBoundingBoxOnScreen() + Rectangle aRect( GetBoundingBoxOnScreen() ); + return awt::Point( aRect.getX(), aRect.getY() ); +} + +//----------------------------------------------------------------------------- + +awt::Size SAL_CALL SvxGraphCtrlAccessibleContext::getSize() throw( RuntimeException ) +{ + // no guard -> done in GetBoundingBox() + Rectangle aRect( GetBoundingBox() ); + return awt::Size( aRect.getWidth(), aRect.getHeight() ); +} + + +//===== XAccessibleContext ================================================== + +sal_Int32 SAL_CALL SvxGraphCtrlAccessibleContext::getAccessibleChildCount( void ) throw( RuntimeException ) +{ + OGuard aGuard( Application::GetSolarMutex() ); + + if( NULL == mpPage ) + throw DisposedException(); + + return mpPage->GetObjCount(); +} + +//----------------------------------------------------------------------------- + +/** returns the SdrObject at index nIndex from the model of this graph */ +SdrObject* SvxGraphCtrlAccessibleContext::getSdrObject( sal_Int32 nIndex ) + throw( RuntimeException, lang::IndexOutOfBoundsException ) +{ + OGuard aGuard( Application::GetSolarMutex() ); + + if( NULL == mpPage ) + throw DisposedException(); + + if( (nIndex < 0) || ( static_cast<sal_uInt32>(nIndex) >= mpPage->GetObjCount() ) ) + throw lang::IndexOutOfBoundsException(); + + return mpPage->GetObj( nIndex ); +} + +//----------------------------------------------------------------------------- + +/** sends an AccessibleEventObject to all added XAccessibleEventListeners */ +void SvxGraphCtrlAccessibleContext::CommitChange ( + sal_Int16 nEventId, + const uno::Any& rNewValue, + const uno::Any& rOldValue) +{ + AccessibleEventObject aEvent ( + static_cast<uno::XWeak*>(this), + nEventId, + rNewValue, + rOldValue); + + FireEvent (aEvent); +} + +/** sends an AccessibleEventObject to all added XAccessibleEventListeners */ +void SvxGraphCtrlAccessibleContext::FireEvent (const AccessibleEventObject& aEvent) +{ + if (mnClientId) + comphelper::AccessibleEventNotifier::addEvent( mnClientId, aEvent ); +} + +//----------------------------------------------------------------------------- + +Reference< XAccessible > SAL_CALL SvxGraphCtrlAccessibleContext::getAccessibleChild( sal_Int32 nIndex ) + throw( RuntimeException, lang::IndexOutOfBoundsException ) +{ + OGuard aGuard( Application::GetSolarMutex() ); + + return getAccessible( getSdrObject( nIndex ) ); +} + +//----------------------------------------------------------------------------- + +Reference< XAccessible > SAL_CALL SvxGraphCtrlAccessibleContext::getAccessibleParent( void ) throw( RuntimeException ) +{ + return mxParent; +} + +//----------------------------------------------------------------------------- + +sal_Int32 SAL_CALL SvxGraphCtrlAccessibleContext::getAccessibleIndexInParent( void ) throw( RuntimeException ) +{ + OGuard aGuard( Application::GetSolarMutex() ); + // Use a simple but slow solution for now. Optimize later. + + // Iterate over all the parent's children and search for this object. + if( mxParent.is() ) + { + Reference< XAccessibleContext > xParentContext( mxParent->getAccessibleContext() ); + if( xParentContext.is() ) + { + sal_Int32 nChildCount = xParentContext->getAccessibleChildCount(); + for( sal_Int32 i = 0 ; i < nChildCount ; ++i ) + { + Reference< XAccessible > xChild( xParentContext->getAccessibleChild( i ) ); + if( xChild.is() ) + { + Reference< XAccessibleContext > xChildContext = xChild->getAccessibleContext(); + if( xChildContext == ( XAccessibleContext* ) this ) + return i; + } + } + } + } + + // Return -1 to indicate that this object's parent does not know about the + // object. + return -1; +} + +//----------------------------------------------------------------------------- + +sal_Int16 SAL_CALL SvxGraphCtrlAccessibleContext::getAccessibleRole( void ) throw( RuntimeException ) +{ + return AccessibleRole::PANEL; +} + +//----------------------------------------------------------------------------- + +OUString SAL_CALL SvxGraphCtrlAccessibleContext::getAccessibleDescription( void ) throw( RuntimeException ) +{ + OGuard aGuard( Application::GetSolarMutex() ); + return msDescription; +} + +//----------------------------------------------------------------------------- + +OUString SAL_CALL SvxGraphCtrlAccessibleContext::getAccessibleName( void ) throw( RuntimeException ) +{ + OGuard aGuard( Application::GetSolarMutex() ); + return msName; +} + +//----------------------------------------------------------------------------- + +/** Return empty reference to indicate that the relation set is not + supported. +*/ +Reference< XAccessibleRelationSet > SAL_CALL SvxGraphCtrlAccessibleContext::getAccessibleRelationSet( void ) throw( RuntimeException ) +{ + return Reference< XAccessibleRelationSet >(); +} + +//----------------------------------------------------------------------------- + +Reference< XAccessibleStateSet > SAL_CALL SvxGraphCtrlAccessibleContext::getAccessibleStateSet( void ) throw( RuntimeException ) +{ + OGuard aGuard( Application::GetSolarMutex() ); + + utl::AccessibleStateSetHelper* pStateSetHelper = new utl::AccessibleStateSetHelper; + + if ( rBHelper.bDisposed || mbDisposed ) + { + pStateSetHelper->AddState( AccessibleStateType::DEFUNC ); + } + else + { + // pStateSetHelper->AddState( AccessibleStateType::ENABLED ); + // pStateSetHelper->AddState( AccessibleStateType::SENSITIVE ); + pStateSetHelper->AddState( AccessibleStateType::FOCUSABLE ); + if( mpControl->HasFocus() ) + pStateSetHelper->AddState( AccessibleStateType::FOCUSED ); + pStateSetHelper->AddState( AccessibleStateType::OPAQUE ); + pStateSetHelper->AddState( AccessibleStateType::SHOWING ); + pStateSetHelper->AddState( AccessibleStateType::VISIBLE ); + } + + return pStateSetHelper; +} + +//----------------------------------------------------------------------------- + +lang::Locale SAL_CALL SvxGraphCtrlAccessibleContext::getLocale( void ) throw( IllegalAccessibleComponentStateException, RuntimeException ) +{ + OGuard aGuard( Application::GetSolarMutex() ); + + if( mxParent.is() ) + { + Reference< XAccessibleContext > xParentContext( mxParent->getAccessibleContext() ); + if( xParentContext.is() ) + return xParentContext->getLocale(); + } + + // No parent. Therefore throw exception to indicate this cluelessness. + throw IllegalAccessibleComponentStateException(); +} + +//===== XAccessibleEventListener ============================================ + +void SAL_CALL SvxGraphCtrlAccessibleContext::addEventListener( const Reference< XAccessibleEventListener >& xListener ) + throw( RuntimeException ) +{ + if (xListener.is()) + { + OGuard aGuard( Application::GetSolarMutex() ); + if (!mnClientId) + mnClientId = comphelper::AccessibleEventNotifier::registerClient( ); + comphelper::AccessibleEventNotifier::addEventListener( mnClientId, xListener ); + } +} + +//----------------------------------------------------------------------------- + +void SAL_CALL SvxGraphCtrlAccessibleContext::removeEventListener( const Reference< XAccessibleEventListener >& xListener ) + throw( RuntimeException ) +{ + if (xListener.is()) + { + OGuard aGuard( Application::GetSolarMutex() ); + + sal_Int32 nListenerCount = comphelper::AccessibleEventNotifier::removeEventListener( mnClientId, xListener ); + if ( !nListenerCount ) + { + // no listeners anymore + // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client), + // and at least to us not firing any events anymore, in case somebody calls + // NotifyAccessibleEvent, again + comphelper::AccessibleEventNotifier::revokeClient( mnClientId ); + mnClientId = 0; + } + } +} + +//----------------------------------------------------------------------------- + +void SAL_CALL SvxGraphCtrlAccessibleContext::addFocusListener( const Reference< awt::XFocusListener >& xListener ) + throw( RuntimeException ) +{ + OGuard aGuard( Application::GetSolarMutex() ); + + if( xListener.is() ) + { + Reference< ::com::sun::star::awt::XWindow > xWindow( VCLUnoHelper::GetInterface( mpControl ) ); + if( xWindow.is() ) + xWindow->addFocusListener( xListener ); + } +} + +//----------------------------------------------------------------------------- + +void SAL_CALL SvxGraphCtrlAccessibleContext::removeFocusListener( const Reference< awt::XFocusListener >& xListener ) + throw (RuntimeException) +{ + OGuard aGuard( Application::GetSolarMutex() ); + + if( xListener.is() ) + { + Reference< ::com::sun::star::awt::XWindow > xWindow = VCLUnoHelper::GetInterface( mpControl ); + if( xWindow.is() ) + xWindow->removeFocusListener( xListener ); + } +} + +//----------------------------------------------------------------------------- + +void SAL_CALL SvxGraphCtrlAccessibleContext::grabFocus() throw( RuntimeException ) +{ + OGuard aGuard( Application::GetSolarMutex() ); + + if( NULL == mpControl ) + throw DisposedException(); + + mpControl->GrabFocus(); +} + +//----------------------------------------------------------------------------- + +Any SAL_CALL SvxGraphCtrlAccessibleContext::getAccessibleKeyBinding() throw( RuntimeException ) +{ + // here is no implementation, because here are no KeyBindings for every object + return Any(); +} + + + + + +sal_Int32 SAL_CALL SvxGraphCtrlAccessibleContext::getForeground (void) + throw (::com::sun::star::uno::RuntimeException) +{ + svtools::ColorConfig aColorConfig; + UINT32 nColor = aColorConfig.GetColorValue( svtools::FONTCOLOR ).nColor; + return static_cast<sal_Int32>(nColor); +} + + + + +sal_Int32 SAL_CALL SvxGraphCtrlAccessibleContext::getBackground (void) + throw (::com::sun::star::uno::RuntimeException) +{ + UINT32 nColor = Application::GetSettings().GetStyleSettings().GetWindowColor().GetColor(); + return static_cast<sal_Int32>(nColor); +} + + +//===== XServiceInfo ======================================================== + +OUString SAL_CALL SvxGraphCtrlAccessibleContext::getImplementationName( void ) throw( RuntimeException ) +{ + return OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.comp.ui.SvxGraphCtrlAccessibleContext" ) ); +} + +//----------------------------------------------------------------------------- + +sal_Bool SAL_CALL SvxGraphCtrlAccessibleContext::supportsService( const OUString& sServiceName ) throw( RuntimeException ) +{ + OGuard aGuard( Application::GetSolarMutex() ); + // Iterate over all supported service names and return true if on of them + // matches the given name. + Sequence< OUString > aSupportedServices( getSupportedServiceNames() ); + int nLenght = aSupportedServices.getLength(); + + for( int i = 0 ; i < nLenght ; ++i ) + { + if( sServiceName == aSupportedServices[ i ] ) + return sal_True; + } + + return sal_False; +} + +//----------------------------------------------------------------------------- + +Sequence< OUString > SAL_CALL SvxGraphCtrlAccessibleContext::getSupportedServiceNames( void ) throw( RuntimeException ) +{ + Sequence< OUString > aSNs( 3 ); + + aSNs[0] = OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.accessibility.Accessible" ) ); + aSNs[1] = OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.accessibility.AccessibleContext" ) ); + aSNs[2] = OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.drawing.AccessibleGraphControl" ) ); + + return aSNs; +} + +//===== XTypeProvider ======================================================= + +Sequence<sal_Int8> SAL_CALL SvxGraphCtrlAccessibleContext::getImplementationId( void ) throw( RuntimeException ) +{ + OGuard aGuard( Application::GetSolarMutex() ); + return getUniqueId(); +} + +//===== XServiceName ======================================================== + +OUString SvxGraphCtrlAccessibleContext::getServiceName( void ) throw( RuntimeException ) +{ + return OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.accessibility.AccessibleContext" ) ); +} + +//===== XAccessibleSelection ============================================= + +void SAL_CALL SvxGraphCtrlAccessibleContext::selectAccessibleChild( sal_Int32 nIndex ) throw( lang::IndexOutOfBoundsException, RuntimeException ) +{ + OGuard aGuard( Application::GetSolarMutex() ); + + if( NULL == mpView ) + throw DisposedException(); + + SdrObject* pObj = getSdrObject( nIndex ); + + if( pObj ) + mpView->MarkObj( pObj, mpView->GetSdrPageView()); +} + +//----------------------------------------------------------------------------- + +sal_Bool SAL_CALL SvxGraphCtrlAccessibleContext::isAccessibleChildSelected( sal_Int32 nIndex ) throw( lang::IndexOutOfBoundsException, RuntimeException ) +{ + OGuard aGuard( Application::GetSolarMutex() ); + + if( NULL == mpView ) + throw DisposedException(); + + return mpView->IsObjMarked( getSdrObject( nIndex ) ); +} + +//----------------------------------------------------------------------------- + +void SAL_CALL SvxGraphCtrlAccessibleContext::clearAccessibleSelection() throw( RuntimeException ) +{ + OGuard aGuard( Application::GetSolarMutex() ); + + if( NULL == mpView ) + throw DisposedException(); + + mpView->UnmarkAllObj(); +} + +//----------------------------------------------------------------------------- + +void SAL_CALL SvxGraphCtrlAccessibleContext::selectAllAccessibleChildren() throw( RuntimeException ) +{ + OGuard aGuard( Application::GetSolarMutex() ); + + if( NULL == mpView ) + throw DisposedException(); + + mpView->MarkAllObj(); +} + +//----------------------------------------------------------------------------- + +sal_Int32 SAL_CALL SvxGraphCtrlAccessibleContext::getSelectedAccessibleChildCount() throw( RuntimeException ) +{ + OGuard aGuard( Application::GetSolarMutex() ); + + if( NULL == mpView ) + throw DisposedException(); + + const SdrMarkList& rList = mpView->GetMarkedObjectList(); + return rList.GetMarkCount(); +} + +//----------------------------------------------------------------------------- + +Reference< XAccessible > SAL_CALL SvxGraphCtrlAccessibleContext::getSelectedAccessibleChild( sal_Int32 nIndex ) + throw( lang::IndexOutOfBoundsException, RuntimeException ) +{ + OGuard aGuard( Application::GetSolarMutex() ); + + checkChildIndexOnSelection( nIndex ); + + Reference< XAccessible > xAccessible; + + const SdrMarkList& rList = mpView->GetMarkedObjectList(); + SdrObject* pObj = rList.GetMark(nIndex)->GetMarkedSdrObj(); + if( pObj ) + xAccessible = getAccessible( pObj ); + + return xAccessible; +} + +//----------------------------------------------------------------------------- + +void SAL_CALL SvxGraphCtrlAccessibleContext::deselectAccessibleChild( sal_Int32 nIndex ) throw( lang::IndexOutOfBoundsException, RuntimeException ) +{ + OGuard aGuard( Application::GetSolarMutex() ); + + checkChildIndexOnSelection( nIndex ); + + if( mpView ) + { + const SdrMarkList& rList = mpView->GetMarkedObjectList(); + + SdrObject* pObj = getSdrObject( nIndex ); + if( pObj ) + { + SdrMarkList aRefList( rList ); + + SdrPageView* pPV = mpView->GetSdrPageView(); + mpView->UnmarkAllObj( pPV ); + + sal_uInt32 nCount = aRefList.GetMarkCount(); + sal_uInt32 nMark; + for( nMark = 0; nMark < nCount; nMark++ ) + { + if( aRefList.GetMark(nMark)->GetMarkedSdrObj() != pObj ) + mpView->MarkObj( aRefList.GetMark(nMark)->GetMarkedSdrObj(), pPV ); + } + } + } +} + +//===== internals ======================================================== + +void SvxGraphCtrlAccessibleContext::checkChildIndex( long nIndex ) throw( lang::IndexOutOfBoundsException ) +{ + if( nIndex < 0 || nIndex >= getAccessibleChildCount() ) + throw lang::IndexOutOfBoundsException(); +} + +//----------------------------------------------------------------------------- + +void SvxGraphCtrlAccessibleContext::checkChildIndexOnSelection( long nIndex ) throw( lang::IndexOutOfBoundsException ) +{ + if( nIndex < 0 || nIndex >= getSelectedAccessibleChildCount() ) + throw lang::IndexOutOfBoundsException(); +} + +//----------------------------------------------------------------------------- + +void SvxGraphCtrlAccessibleContext::setName( const OUString& rName ) +{ + OGuard aGuard( Application::GetSolarMutex() ); + + msName = rName; +} + +//----------------------------------------------------------------------------- + +void SvxGraphCtrlAccessibleContext::setDescription( const OUString& rDescr ) +{ + OGuard aGuard( Application::GetSolarMutex() ); + + msDescription = rDescr; +} + + + + +/** Replace the model, page, and view pointers by the ones provided + (explicitly and implicitly). +*/ +void SvxGraphCtrlAccessibleContext::setModelAndView ( + SdrModel* pModel, + SdrView* pView) +{ + OGuard aGuard (Application::GetSolarMutex()); + + mpModel = pModel; + if (mpModel != NULL) + mpPage = (SdrPage*)mpModel->GetPage( 0 ); + mpView = pView; + + if (mpModel == NULL || mpPage == NULL || mpView == NULL) + { + mbDisposed = true; + + // Set all the pointers to NULL just in case they are used as + // a disposed flag. + mpModel = NULL; + mpPage = NULL; + mpView = NULL; + } + + maTreeInfo.SetSdrView (mpView); +} + + + +//----------------------------------------------------------------------------- + +void SAL_CALL SvxGraphCtrlAccessibleContext::disposing() +{ + OGuard aGuard( Application::GetSolarMutex() ); + + if( mbDisposed ) + return; + + mbDisposed = sal_True; + + mpControl = NULL; // object dies with representation + mpView = NULL; + mpPage = NULL; + + { + ShapesMapType::iterator I; + + for (I=mxShapes.begin(); I!=mxShapes.end(); I++) + { + XAccessible* pAcc = (*I).second; + Reference< XComponent > xComp( pAcc, UNO_QUERY ); + if( xComp.is() ) + xComp->dispose(); + + (*I).second->release(); + } + + mxShapes.clear(); + } + + // Send a disposing to all listeners. + if ( mnClientId ) + { + comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing( mnClientId, *this ); + mnClientId = 0; + } +} + +//----------------------------------------------------------------------------- + +Rectangle SvxGraphCtrlAccessibleContext::GetBoundingBoxOnScreen( void ) throw( RuntimeException ) +{ + OGuard aGuard( Application::GetSolarMutex() ); + + if( NULL == mpControl ) + throw DisposedException(); + + return Rectangle( + mpControl->GetAccessibleParentWindow()->OutputToAbsoluteScreenPixel( + mpControl->GetPosPixel() ), + mpControl->GetSizePixel() ); +} + +//----------------------------------------------------------------------------- + +/** Calculate the relative coordinates of the bounding box as difference + between the absolute coordinates of the bounding boxes of this control + and its parent in the accessibility tree. +*/ +Rectangle SvxGraphCtrlAccessibleContext::GetBoundingBox( void ) throw( RuntimeException ) +{ + OGuard aGuard( Application::GetSolarMutex() ); + + Rectangle aBounds ( 0, 0, 0, 0 ); + + Window* pWindow = mpControl; + if (pWindow != NULL) + { + aBounds = pWindow->GetWindowExtentsRelative (NULL); + Window* pParent = pWindow->GetAccessibleParentWindow(); + if (pParent != NULL) + { + Rectangle aParentRect = pParent->GetWindowExtentsRelative (NULL); + aBounds -= aParentRect.TopLeft(); + } + } + else + throw DisposedException(); + + return aBounds; +} + +//----------------------------------------------------------------------------- + +Sequence< sal_Int8 > SvxGraphCtrlAccessibleContext::getUniqueId( void ) +{ + // no guard because it's private -> has to guarded when using it! + static OImplementationId* pId = 0; + if( !pId ) + { + OGuard aGuard( Application::GetSolarMutex() ); + if( !pId) + { + static OImplementationId aId; + pId = &aId; + } + } + return pId->getImplementationId(); +} + +//----------------------------------------------------------------------------- + +void SvxGraphCtrlAccessibleContext::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint ) +{ + const SdrHint* pSdrHint = PTR_CAST( SdrHint, &rHint ); + + if( pSdrHint ) + { + switch( pSdrHint->GetKind() ) + { + case HINT_OBJCHG: + { + ShapesMapType::iterator iter = mxShapes.find( pSdrHint->GetObject() ); + + if( iter != mxShapes.end() ) + { + // if we already have one, return it + AccessibleShape* pShape = (*iter).second; + + if( NULL != pShape ) + pShape->CommitChange( AccessibleEventId::VISIBLE_DATA_CHANGED, uno::Any(), uno::Any() ); + } + } + break; + + case HINT_OBJINSERTED: + CommitChange( AccessibleEventId::CHILD, makeAny( getAccessible( pSdrHint->GetObject() ) ) , uno::Any()); + break; + case HINT_OBJREMOVED: + CommitChange( AccessibleEventId::CHILD, uno::Any(), makeAny( getAccessible( pSdrHint->GetObject() ) ) ); + break; + case HINT_MODELCLEARED: + dispose(); + break; + default: + break; + } + } + else + { + const SfxSimpleHint* pSfxHint = PTR_CAST(SfxSimpleHint, &rHint ); + + // ist unser SdDrawDocument gerade gestorben? + if(pSfxHint && pSfxHint->GetId() == SFX_HINT_DYING) + { + dispose(); + } + } +} + +//===== IAccessibleViewforwarder ======================================== + +sal_Bool SvxGraphCtrlAccessibleContext::IsValid (void) const +{ + return sal_True; +} + +//----------------------------------------------------------------------------- + +Rectangle SvxGraphCtrlAccessibleContext::GetVisibleArea (void) const +{ + Rectangle aVisArea; + + if( mpView && mpView->PaintWindowCount()) + { + SdrPaintWindow* pPaintWindow = mpView->GetPaintWindow(0L); + aVisArea = pPaintWindow->GetVisibleArea(); + } + + return aVisArea; +} + +//----------------------------------------------------------------------------- + +Point SvxGraphCtrlAccessibleContext::LogicToPixel (const Point& rPoint) const +{ + if( mpControl ) + { + Rectangle aBBox(mpControl->GetWindowExtentsRelative(NULL)); + return mpControl->LogicToPixel (rPoint) + aBBox.TopLeft(); + } + else + { + return rPoint; + } +} + +//----------------------------------------------------------------------------- + +Size SvxGraphCtrlAccessibleContext::LogicToPixel (const Size& rSize) const +{ + if( mpControl ) + return mpControl->LogicToPixel (rSize); + else + return rSize; +} + +//----------------------------------------------------------------------------- + +Point SvxGraphCtrlAccessibleContext::PixelToLogic (const Point& rPoint) const +{ + if( mpControl ) + return mpControl->PixelToLogic (rPoint); + else + return rPoint; +} + +//----------------------------------------------------------------------------- + +Size SvxGraphCtrlAccessibleContext::PixelToLogic (const Size& rSize) const +{ + if( mpControl ) + return mpControl->PixelToLogic (rSize); + else + return rSize; +} diff --git a/svx/source/accessibility/ShapeTypeHandler.cxx b/svx/source/accessibility/ShapeTypeHandler.cxx new file mode 100755 index 000000000000..228f2ed50492 --- /dev/null +++ b/svx/source/accessibility/ShapeTypeHandler.cxx @@ -0,0 +1,338 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svx.hxx" + +#include <svx/ShapeTypeHandler.hxx> +#include <svx/SvxShapeTypes.hxx> +#include <svx/AccessibleShapeInfo.hxx> +#include <com/sun/star/drawing/XShapeDescriptor.hpp> +#include <vos/mutex.hxx> +#include <vcl/svapp.hxx> +#include <svx/dialmgr.hxx> +#include "svdstr.hrc" + + +using namespace ::rtl; +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; + +namespace accessibility { + +// Pointer to the shape type handler singleton. +ShapeTypeHandler* ShapeTypeHandler::instance = NULL; + + +// Create an empty reference to an accessible object. +AccessibleShape* + CreateEmptyShapeReference ( + const AccessibleShapeInfo& /*rShapeInfo*/, + const AccessibleShapeTreeInfo& /*rShapeTreeInfo*/, + ShapeTypeId /*nId*/) +{ + return NULL; +} + + + + +ShapeTypeHandler& ShapeTypeHandler::Instance (void) +{ + // Using double check pattern to make sure that exactly one instance of + // the shape type handler is instantiated. + if (instance == NULL) + { + ::vos::OGuard aGuard (::Application::GetSolarMutex()); + if (instance == NULL) + { + // Create the single instance of the shape type handler. + instance = new ShapeTypeHandler; + + // Register the basic SVX shape types. + RegisterDrawShapeTypes (); + } + } + + return *instance; +} + + + + +/** The given service name is first transformed into a slot id that + identifies the place of the type descriptor. From that descriptor the + shape type id is returned. +*/ +ShapeTypeId ShapeTypeHandler::GetTypeId (const OUString& aServiceName) const +{ + tServiceNameToSlotId::iterator I (maServiceNameToSlotId.find (aServiceName)); + if (I != maServiceNameToSlotId.end()) + { + // long nSlotId = maServiceNameToSlotId[aServiceName]; + return maShapeTypeDescriptorList[I->second].mnShapeTypeId; + } + else + return -1; +} + + + +/** Extract the specified shape's service name and forward the request to + the appropriate method. +*/ +ShapeTypeId ShapeTypeHandler::GetTypeId (const uno::Reference<drawing::XShape>& rxShape) const +{ + uno::Reference<drawing::XShapeDescriptor> xDescriptor (rxShape, uno::UNO_QUERY); + if (xDescriptor.is()) + return GetTypeId (xDescriptor->getShapeType()); + else + return -1; +} + + + + +const OUString& ShapeTypeHandler::GetServiceName (ShapeTypeId aTypeId) const +{ + return maShapeTypeDescriptorList[aTypeId].msServiceName; +} + + + + +/** This factory method determines the type descriptor for the type of the + given shape, then calls the descriptor's create function, and finally + initializes the new object. +*/ +AccessibleShape* + ShapeTypeHandler::CreateAccessibleObject ( + const AccessibleShapeInfo& rShapeInfo, + const AccessibleShapeTreeInfo& rShapeTreeInfo) const +{ + ShapeTypeId nSlotId (GetSlotId (rShapeInfo.mxShape)); + AccessibleShape* pShape = + maShapeTypeDescriptorList[nSlotId].maCreateFunction ( + rShapeInfo, + rShapeTreeInfo, + maShapeTypeDescriptorList[nSlotId].mnShapeTypeId); + return pShape; +} + + + + +/** Create the single instance of this class and initialize its list of + type descriptors with an entry of an unknown type. +*/ +ShapeTypeHandler::ShapeTypeHandler (void) + : maShapeTypeDescriptorList (1) +{ + // Make sure that at least the UNKNOWN entry is present. + // Resize the list, if necessary, so that the new type can be inserted. + maShapeTypeDescriptorList[0].mnShapeTypeId = UNKNOWN_SHAPE_TYPE; + maShapeTypeDescriptorList[0].msServiceName = + OUString::createFromAscii ("UNKNOWN_SHAPE_TYPE"); + maShapeTypeDescriptorList[0].maCreateFunction = CreateEmptyShapeReference; + maServiceNameToSlotId[maShapeTypeDescriptorList[0].msServiceName] = 0; +} + + + + +ShapeTypeHandler::~ShapeTypeHandler (void) +{ + // Because this class is a singleton and the only instance, whose + // destructor has just been called, is pointed to from instance, + // we reset the static variable instance, so that further calls to + // getInstance do not return an undefined object but create a new + // singleton. + instance = NULL; +} + + + + +bool ShapeTypeHandler::AddShapeTypeList (int nDescriptorCount, + ShapeTypeDescriptor aDescriptorList[]) +{ + ::vos::OGuard aGuard (::Application::GetSolarMutex()); + + // Determine first id of new type descriptor(s). + int nFirstId = maShapeTypeDescriptorList.size(); + + // Resize the list, if necessary, so that the types can be inserted. + maShapeTypeDescriptorList.resize (nFirstId + nDescriptorCount); + + for (int i=0; i<nDescriptorCount; i++) + { + #if OSL_DEBUG_LEVEL > 0 + ShapeTypeId nId (aDescriptorList[i].mnShapeTypeId); + (void)nId; + #endif + + // Fill Type descriptor. + maShapeTypeDescriptorList[nFirstId+i].mnShapeTypeId = aDescriptorList[i].mnShapeTypeId; + maShapeTypeDescriptorList[nFirstId+i].msServiceName = aDescriptorList[i].msServiceName; + maShapeTypeDescriptorList[nFirstId+i].maCreateFunction = aDescriptorList[i].maCreateFunction; + + // Update inverse mapping from service name to the descriptor's position. + maServiceNameToSlotId[aDescriptorList[i].msServiceName] = nFirstId+i; + } + + return true; +} + + + + +#include <tools/string.hxx> +long ShapeTypeHandler::GetSlotId (const OUString& aServiceName) const +{ + tServiceNameToSlotId::iterator I (maServiceNameToSlotId.find (aServiceName)); + if (I != maServiceNameToSlotId.end()) + return I->second; + else + return 0; +} + + + + +// Extract the given shape's service name and forward request to appropriate +// method. +long ShapeTypeHandler::GetSlotId (const uno::Reference<drawing::XShape>& rxShape) const +{ + uno::Reference<drawing::XShapeDescriptor> xDescriptor (rxShape, uno::UNO_QUERY); + if (xDescriptor.is()) + return GetSlotId (xDescriptor->getShapeType()); + else + return 0; +} + +/// get the accessible base name for an object +::rtl::OUString + ShapeTypeHandler::CreateAccessibleBaseName (const uno::Reference<drawing::XShape>& rxShape) + throw (::com::sun::star::uno::RuntimeException) +{ + sal_Int32 nResourceId; + OUString sName; + + switch (ShapeTypeHandler::Instance().GetTypeId (rxShape)) + { + // case DRAWING_3D_POLYGON: was removed in original code in + // AccessibleShape::CreateAccessibleBaseName. See issue 11190 for details. + // Id can be removed from SvxShapeTypes.hxx as well. + case DRAWING_3D_CUBE: + nResourceId = STR_ObjNameSingulCube3d; + break; + case DRAWING_3D_EXTRUDE: + nResourceId = STR_ObjNameSingulExtrude3d; + break; + case DRAWING_3D_LATHE: + nResourceId = STR_ObjNameSingulLathe3d; + break; + case DRAWING_3D_SCENE: + nResourceId = STR_ObjNameSingulScene3d; + break; + case DRAWING_3D_SPHERE: + nResourceId = STR_ObjNameSingulSphere3d; + break; + case DRAWING_CAPTION: + nResourceId = STR_ObjNameSingulCAPTION; + break; + case DRAWING_CLOSED_BEZIER: + nResourceId = STR_ObjNameSingulPATHFILL; + break; + case DRAWING_CLOSED_FREEHAND: + nResourceId = STR_ObjNameSingulFREEFILL; + break; + case DRAWING_CONNECTOR: + nResourceId = STR_ObjNameSingulEDGE; + break; + case DRAWING_CONTROL: + nResourceId = STR_ObjNameSingulUno; + break; + case DRAWING_ELLIPSE: + nResourceId = STR_ObjNameSingulCIRCE; + break; + case DRAWING_GROUP: + nResourceId = STR_ObjNameSingulGRUP; + break; + case DRAWING_LINE: + nResourceId = STR_ObjNameSingulLINE; + break; + case DRAWING_MEASURE: + nResourceId = STR_ObjNameSingulMEASURE; + break; + case DRAWING_OPEN_BEZIER: + nResourceId = STR_ObjNameSingulPATHLINE; + break; + case DRAWING_OPEN_FREEHAND: + nResourceId = STR_ObjNameSingulFREELINE; + break; + case DRAWING_PAGE: + nResourceId = STR_ObjNameSingulPAGE; + break; + case DRAWING_POLY_LINE: + nResourceId = STR_ObjNameSingulPLIN; + break; + case DRAWING_POLY_LINE_PATH: + nResourceId = STR_ObjNameSingulPLIN; + break; + case DRAWING_POLY_POLYGON: + nResourceId = STR_ObjNameSingulPOLY; + break; + case DRAWING_POLY_POLYGON_PATH: + nResourceId = STR_ObjNameSingulPOLY; + break; + case DRAWING_RECTANGLE: + nResourceId = STR_ObjNameSingulRECT; + break; + case DRAWING_TEXT: + nResourceId = STR_ObjNameSingulTEXT; + break; + default: + nResourceId = -1; + sName = ::rtl::OUString (RTL_CONSTASCII_USTRINGPARAM("UnknownAccessibleShape")); + uno::Reference<drawing::XShapeDescriptor> xDescriptor (rxShape, uno::UNO_QUERY); + if (xDescriptor.is()) + sName += ::rtl::OUString (RTL_CONSTASCII_USTRINGPARAM(": ")) + + xDescriptor->getShapeType(); + break; + } + + if (nResourceId != -1) + { + ::vos::OGuard aGuard (::Application::GetSolarMutex()); + sName = OUString (SVX_RESSTR((unsigned short)nResourceId)); + } + + return sName; +} + +} // end of namespace accessibility diff --git a/svx/source/accessibility/SvxShapeTypes.cxx b/svx/source/accessibility/SvxShapeTypes.cxx new file mode 100644 index 000000000000..afdec80872b1 --- /dev/null +++ b/svx/source/accessibility/SvxShapeTypes.cxx @@ -0,0 +1,207 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svx.hxx" + +#include <svx/SvxShapeTypes.hxx> +#include <svx/AccessibleShape.hxx> +#include <svx/AccessibleGraphicShape.hxx> +#include <svx/AccessibleOLEShape.hxx> +#include <svx/AccessibleControlShape.hxx> +#include <svx/AccessibleTableShape.hxx> + +namespace accessibility { + +AccessibleShape* CreateSvxAccessibleShape ( + const AccessibleShapeInfo& rShapeInfo, + const AccessibleShapeTreeInfo& rShapeTreeInfo, + ShapeTypeId nId) +{ + switch (nId) + { + case DRAWING_3D_CUBE: + case DRAWING_3D_EXTRUDE: + case DRAWING_3D_LATHE: + case DRAWING_3D_SCENE: + case DRAWING_3D_SPHERE: + case DRAWING_CAPTION: + case DRAWING_CLOSED_BEZIER: + case DRAWING_CLOSED_FREEHAND: + case DRAWING_CONNECTOR: + case DRAWING_ELLIPSE: + case DRAWING_GROUP: + case DRAWING_LINE: + case DRAWING_MEASURE: + case DRAWING_OPEN_BEZIER: + case DRAWING_OPEN_FREEHAND: + case DRAWING_PAGE: + case DRAWING_POLY_POLYGON: + case DRAWING_POLY_LINE: + case DRAWING_POLY_POLYGON_PATH: + case DRAWING_POLY_LINE_PATH: + case DRAWING_RECTANGLE: + case DRAWING_TEXT: + // --> OD 2004-11-29 #i37790# - default accessiblility shape for + // com::sun::star::drawing::CustomShape + case DRAWING_CUSTOM: + // <-- + // --> OD 2008-05-19 #i85429# - default accessiblility shape for + // com::sun::star::drawing::MediaShape + case DRAWING_MEDIA: + // <-- + return new AccessibleShape (rShapeInfo, rShapeTreeInfo); + + case DRAWING_CONTROL: + return new AccessibleControlShape (rShapeInfo, rShapeTreeInfo); + + case DRAWING_GRAPHIC_OBJECT: + return new AccessibleGraphicShape (rShapeInfo, rShapeTreeInfo); + + case DRAWING_APPLET: + case DRAWING_FRAME: + case DRAWING_OLE: + case DRAWING_PLUGIN: + return new AccessibleOLEShape (rShapeInfo, rShapeTreeInfo); + + case DRAWING_TABLE: + return new AccessibleTableShape( rShapeInfo, rShapeTreeInfo ); + + default: + return NULL; + } +} + + + +ShapeTypeDescriptor aSvxShapeTypeList[] = { + ShapeTypeDescriptor (DRAWING_TEXT, + ::rtl::OUString::createFromAscii ("com.sun.star.drawing.TextShape"), + CreateSvxAccessibleShape), + ShapeTypeDescriptor (DRAWING_RECTANGLE, + ::rtl::OUString::createFromAscii ("com.sun.star.drawing.RectangleShape"), + CreateSvxAccessibleShape), + ShapeTypeDescriptor ( DRAWING_ELLIPSE, + ::rtl::OUString::createFromAscii ("com.sun.star.drawing.EllipseShape"), + CreateSvxAccessibleShape ), + ShapeTypeDescriptor ( DRAWING_CONTROL, + ::rtl::OUString::createFromAscii ("com.sun.star.drawing.ControlShape"), + CreateSvxAccessibleShape ), + ShapeTypeDescriptor ( DRAWING_CONNECTOR, + ::rtl::OUString::createFromAscii ("com.sun.star.drawing.ConnectorShape"), + CreateSvxAccessibleShape ), + ShapeTypeDescriptor ( DRAWING_MEASURE, + ::rtl::OUString::createFromAscii ("com.sun.star.drawing.MeasureShape"), + CreateSvxAccessibleShape ), + ShapeTypeDescriptor ( DRAWING_LINE, + ::rtl::OUString::createFromAscii ("com.sun.star.drawing.LineShape"), + CreateSvxAccessibleShape ), + ShapeTypeDescriptor ( DRAWING_POLY_POLYGON, + ::rtl::OUString::createFromAscii ("com.sun.star.drawing.PolyPolygonShape"), + CreateSvxAccessibleShape ), + ShapeTypeDescriptor ( DRAWING_POLY_LINE, + ::rtl::OUString::createFromAscii ("com.sun.star.drawing.PolyLineShape"), + CreateSvxAccessibleShape ), + ShapeTypeDescriptor ( DRAWING_OPEN_BEZIER, + ::rtl::OUString::createFromAscii ("com.sun.star.drawing.OpenBezierShape"), + CreateSvxAccessibleShape ), + ShapeTypeDescriptor ( DRAWING_CLOSED_BEZIER, + ::rtl::OUString::createFromAscii ("com.sun.star.drawing.ClosedBezierShape"), + CreateSvxAccessibleShape ), + ShapeTypeDescriptor ( DRAWING_OPEN_FREEHAND, + ::rtl::OUString::createFromAscii ("com.sun.star.drawing.OpenFreeHandShape"), + CreateSvxAccessibleShape ), + ShapeTypeDescriptor ( DRAWING_CLOSED_FREEHAND, + ::rtl::OUString::createFromAscii ("com.sun.star.drawing.ClosedFreeHandShape"), + CreateSvxAccessibleShape ), + ShapeTypeDescriptor ( DRAWING_POLY_POLYGON_PATH, + ::rtl::OUString::createFromAscii ("com.sun.star.drawing.PolyPolygonPathShape"), + CreateSvxAccessibleShape ), + ShapeTypeDescriptor ( DRAWING_POLY_LINE_PATH, + ::rtl::OUString::createFromAscii ("com.sun.star.drawing.PolyLinePathShape"), + CreateSvxAccessibleShape ), + ShapeTypeDescriptor ( DRAWING_GRAPHIC_OBJECT, + ::rtl::OUString::createFromAscii ("com.sun.star.drawing.GraphicObjectShape"), + CreateSvxAccessibleShape ), + ShapeTypeDescriptor ( DRAWING_GROUP, + ::rtl::OUString::createFromAscii ("com.sun.star.drawing.GroupShape"), + CreateSvxAccessibleShape ), + ShapeTypeDescriptor ( DRAWING_OLE, + ::rtl::OUString::createFromAscii ("com.sun.star.drawing.OLE2Shape"), + CreateSvxAccessibleShape ), + ShapeTypeDescriptor ( DRAWING_PAGE, + ::rtl::OUString::createFromAscii ("com.sun.star.drawing.PageShape"), + CreateSvxAccessibleShape ), + ShapeTypeDescriptor ( DRAWING_CAPTION, + ::rtl::OUString::createFromAscii ("com.sun.star.drawing.CaptionShape"), + CreateSvxAccessibleShape ), + ShapeTypeDescriptor ( DRAWING_FRAME, + ::rtl::OUString::createFromAscii ("com.sun.star.drawing.FrameShape"), + CreateSvxAccessibleShape ), + ShapeTypeDescriptor ( DRAWING_PLUGIN, + ::rtl::OUString::createFromAscii ("com.sun.star.drawing.PluginShape"), + CreateSvxAccessibleShape ), + ShapeTypeDescriptor ( DRAWING_APPLET, + ::rtl::OUString::createFromAscii ("com.sun.star.drawing.AppletShape"), + CreateSvxAccessibleShape ), + ShapeTypeDescriptor ( DRAWING_3D_SCENE, + ::rtl::OUString::createFromAscii ("com.sun.star.drawing.Shape3DSceneObject"), + CreateSvxAccessibleShape ), + ShapeTypeDescriptor ( DRAWING_3D_CUBE, + ::rtl::OUString::createFromAscii ("com.sun.star.drawing.Shape3DCubeObject"), + CreateSvxAccessibleShape ), + ShapeTypeDescriptor ( DRAWING_3D_SPHERE, + ::rtl::OUString::createFromAscii ("com.sun.star.drawing.Shape3DSphereObject"), + CreateSvxAccessibleShape ), + ShapeTypeDescriptor ( DRAWING_3D_LATHE, + ::rtl::OUString::createFromAscii ("com.sun.star.drawing.Shape3DLatheObject"), + CreateSvxAccessibleShape ), + ShapeTypeDescriptor ( DRAWING_3D_EXTRUDE, + ::rtl::OUString::createFromAscii ("com.sun.star.drawing.Shape3DExtrudeObject"), + CreateSvxAccessibleShape ), + ShapeTypeDescriptor ( DRAWING_CUSTOM, + ::rtl::OUString::createFromAscii ("com.sun.star.drawing.CustomShape"), + CreateSvxAccessibleShape ), + ShapeTypeDescriptor ( DRAWING_TABLE, + rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.drawing.TableShape" ) ), + CreateSvxAccessibleShape ), + ShapeTypeDescriptor ( DRAWING_MEDIA, + rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.drawing.MediaShape" ) ), + CreateSvxAccessibleShape ), + +}; + + +void RegisterDrawShapeTypes (void) +{ + // --> OD 2004-11-26 #i37790# + ShapeTypeHandler::Instance().AddShapeTypeList ( DRAWING_END, aSvxShapeTypeList); + // <-- +} + + +} // end of namespace accessibility diff --git a/svx/source/accessibility/accessibility.src b/svx/source/accessibility/accessibility.src new file mode 100644 index 000000000000..f9784833cc3b --- /dev/null +++ b/svx/source/accessibility/accessibility.src @@ -0,0 +1,262 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include "accessibility.hrc" + +String RID_SVXSTR_A11Y_3D_MATERIAL_COLOR +{ + Text [ en-US ] = "3D material color" ; +}; +String RID_SVXSTR_A11Y_TEXT_COLOR +{ + Text [ en-US ] = "Font color" ; +}; +String RID_SVXSTR_A11Y_BACKGROUND_COLOR +{ + Text [ en-US ] = "Background color" ; +}; +String RID_SVXSTR_A11Y_FILLSTYLE_NONE +{ + Text [ en-US ] = "None" ; +}; +String RID_SVXSTR_A11Y_FILLSTYLE_SOLID +{ + Text [ en-US ] = "Solid" ; +}; +String RID_SVXSTR_A11Y_FILLSTYLE_HATCH +{ + Text [ en-US ] = "With hatching" ; +}; +String RID_SVXSTR_A11Y_FILLSTYLE_GRADIENT +{ + Text [ en-US ] = "Gradient" ; +}; +String RID_SVXSTR_A11Y_FILLSTYLE_BITMAP +{ + Text [ en-US ] = "Bitmap" ; +}; +String RID_SVXSTR_A11Y_WITH +{ + Text [ en-US ] = "with" ; +}; +String RID_SVXSTR_A11Y_STYLE +{ + Text [ en-US ] = "Style" ; +}; +String RID_SVXSTR_A11Y_AND +{ + Text [ en-US ] = "and" ; +}; + + +// SvxRectCtl + +String RID_SVXSTR_RECTCTL_ACC_CORN_NAME +{ + Text [ en-US ] = "Corner control" ; +}; + +String RID_SVXSTR_RECTCTL_ACC_CORN_DESCR +{ + Text [ en-US ] = "Selection of a corner point." ; +}; + +String RID_SVXSTR_RECTCTL_ACC_ANGL_NAME +{ + Text [ en-US ] = "Angle control" ; +}; + +String RID_SVXSTR_RECTCTL_ACC_ANGL_DESCR +{ + Text [ en-US ] = "Selection of a major angle." ; +}; + + +String RID_SVXSTR_RECTCTL_ACC_CHLD_LT +{ + Text [ en-US ] = "Top left" ; +}; + +String RID_SVXSTR_RECTCTL_ACC_CHLD_MT +{ + Text [ en-US ] = "Top middle" ; +}; + +String RID_SVXSTR_RECTCTL_ACC_CHLD_RT +{ + Text [ en-US ] = "Top right" ; +}; + +String RID_SVXSTR_RECTCTL_ACC_CHLD_LM +{ + Text [ en-US ] = "Left center" ; +}; + +String RID_SVXSTR_RECTCTL_ACC_CHLD_MM +{ + Text [ en-US ] = "Center" ; +}; + +String RID_SVXSTR_RECTCTL_ACC_CHLD_RM +{ + Text [ en-US ] = "Right center" ; +}; + +String RID_SVXSTR_RECTCTL_ACC_CHLD_LB +{ + Text [ en-US ] = "Bottom left" ; +}; + +String RID_SVXSTR_RECTCTL_ACC_CHLD_MB +{ + Text [ en-US ] = "Bottom middle" ; +}; + +String RID_SVXSTR_RECTCTL_ACC_CHLD_RB +{ + Text [ en-US ] = "Bottom right" ; +}; + + +String RID_SVXSTR_RECTCTL_ACC_CHLD_A000 +{ + Text [ en-US ] = "0 degrees" ; +}; + +String RID_SVXSTR_RECTCTL_ACC_CHLD_A045 +{ + Text [ en-US ] = "45 degrees" ; +}; + +String RID_SVXSTR_RECTCTL_ACC_CHLD_A090 +{ + Text [ en-US ] = "90 degrees" ; +}; + +String RID_SVXSTR_RECTCTL_ACC_CHLD_A135 +{ + Text [ en-US ] = "135 degrees" ; +}; + +String RID_SVXSTR_RECTCTL_ACC_CHLD_A180 +{ + Text [ en-US ] = "180 degrees" ; +}; + +String RID_SVXSTR_RECTCTL_ACC_CHLD_A225 +{ + Text [ en-US ] = "225 degrees" ; +}; + +String RID_SVXSTR_RECTCTL_ACC_CHLD_A270 +{ + Text [ en-US ] = "270 degrees" ; +}; + +String RID_SVXSTR_RECTCTL_ACC_CHLD_A315 +{ + Text [ en-US ] = "315 degrees" ; +}; + +// SvxGraphCtrlAccessibleContext +String RID_SVXSTR_GRAPHCTRL_ACC_NAME +{ + Text [ en-US ] = "Contour control" ; +}; + +String RID_SVXSTR_GRAPHCTRL_ACC_DESCRIPTION +{ + Text [ en-US ] = "This is where you can edit the contour." ; +}; + +String RID_SVXSTR_CHARACTER_SELECTION +{ + Text [ en-US ] = "Special character selection"; +}; + +String RID_SVXSTR_CHAR_SEL_DESC +{ + Text [ en-US ] = "Select special characters in this area."; +}; + +String RID_SVXSTR_CHARACTER_CODE +{ + // The space behind is a must. + Text [ en-US ] = "Character code "; +}; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/svx/source/accessibility/charmapacc.cxx b/svx/source/accessibility/charmapacc.cxx new file mode 100644 index 000000000000..c1509eacc843 --- /dev/null +++ b/svx/source/accessibility/charmapacc.cxx @@ -0,0 +1,891 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svx.hxx" + +#define _SVX_CHARMAP_CXX_ +#include <unotools/accessiblestatesethelper.hxx> +#include <vcl/svapp.hxx> +#include <stdio.h> +#include <svx/charmap.hxx> +#include "charmapacc.hxx" +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <toolkit/helper/externallock.hxx> +#include <toolkit/helper/convert.hxx> +#include <osl/interlck.h> +#include <svx/dialmgr.hxx> +#include "accessibility.hrc" +#include <comphelper/types.hxx> + +namespace svx +{ + using namespace comphelper; + using namespace ::com::sun::star; + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::accessibility; + +// ---------------- +// - SvxShowCharSetVirtualAcc - +// ---------------- +SvxShowCharSetVirtualAcc::SvxShowCharSetVirtualAcc( SvxShowCharSet* pParent ) : OAccessibleComponentHelper(new VCLExternalSolarLock()) +,mpParent( pParent ) +,m_pTable(NULL) +{ + osl_incrementInterlockedCount(&m_refCount); + { // #b6211265 # + lateInit(this); + } + osl_decrementInterlockedCount(&m_refCount); +} + +// ----------------------------------------------------------------------------- + +SvxShowCharSetVirtualAcc::~SvxShowCharSetVirtualAcc() +{ + ensureDisposed(); + delete getExternalLock(); +} +// ----------------------------------------------------------------------------- +IMPLEMENT_FORWARD_XINTERFACE2( SvxShowCharSetVirtualAcc, OAccessibleComponentHelper, OAccessibleHelper_Base_2 ) +IMPLEMENT_FORWARD_XTYPEPROVIDER2( SvxShowCharSetVirtualAcc, OAccessibleComponentHelper, OAccessibleHelper_Base_2 ) + +void SAL_CALL SvxShowCharSetVirtualAcc::fireEvent( + const sal_Int16 _nEventId, + const ::com::sun::star::uno::Any& _rOldValue, + const ::com::sun::star::uno::Any& _rNewValue + ) +{ + if ( m_pTable ) + m_pTable->fireEvent(_nEventId,_rOldValue,_rNewValue); +} +// ----------------------------------------------------------------------------- +sal_Int32 SAL_CALL SvxShowCharSetVirtualAcc::getAccessibleChildCount( ) throw (RuntimeException) +{ + OExternalLockGuard aGuard( this ); + ensureAlive(); + return ( mpParent->getScrollBar()->IsVisible() ) ? 2 : 1; +} +// ----------------------------------------------------------------------------- +uno::Reference< accessibility::XAccessible > SAL_CALL SvxShowCharSetVirtualAcc::getAccessibleAtPoint( const awt::Point& aPoint ) + throw (uno::RuntimeException) +{ + OExternalLockGuard aGuard( this ); + ensureAlive(); + + uno::Reference< accessibility::XAccessible > xRet; + const USHORT nItemId = sal::static_int_cast<USHORT>(mpParent->PixelToMapIndex( Point( aPoint.X, aPoint.Y ) )); + + if( USHORT(-1) != nItemId ) + { + if ( !m_pTable ) + m_pTable = new SvxShowCharSetAcc(this); + xRet = m_pTable; + } + else if ( mpParent->getScrollBar()->IsVisible() ) + { + const Point aOutPos( mpParent->getScrollBar()->GetPosPixel() ); + const Size aScrollBar = mpParent->getScrollBar()->GetOutputSizePixel(); + Rectangle aRect(aOutPos,aScrollBar); + + if ( aRect.IsInside(VCLPoint(aPoint)) ) + xRet = mpParent->getScrollBar()->GetAccessible(); + } + return xRet; +} +// ----------------------------------------------------------------------------- +uno::Any SAL_CALL SvxShowCharSetVirtualAcc::getAccessibleKeyBinding() + throw (uno::RuntimeException) +{ + return uno::Any(); +} +// ----------------------------------------------------------------------------- +void SAL_CALL SvxShowCharSetVirtualAcc::grabFocus() + throw (uno::RuntimeException) +{ + OExternalLockGuard aGuard( this ); + ensureAlive(); + mpParent->GrabFocus(); +} + + +// ----------------------------------------------------------------------------- +Reference< XAccessible > SAL_CALL SvxShowCharSetVirtualAcc::getAccessibleChild( sal_Int32 i ) throw (IndexOutOfBoundsException, RuntimeException) +{ + OExternalLockGuard aGuard( this ); + ensureAlive(); + if ( mpParent->getScrollBar()->IsVisible() && i == 0 ) + return mpParent->getScrollBar()->GetAccessible(); + else if ( i == 1 ) + { + if ( !m_xAcc.is() ) + { + m_pTable = new SvxShowCharSetAcc(this); + m_xAcc = m_pTable; + } + } + else + throw IndexOutOfBoundsException(); + return m_xAcc; +} +// ----------------------------------------------------------------------------- +Reference< XAccessible > SAL_CALL SvxShowCharSetVirtualAcc::getAccessibleParent( ) throw (RuntimeException) +{ + OExternalLockGuard aGuard( this ); + ensureAlive(); + Window* pParent = mpParent->GetParent(); + uno::Reference< accessibility::XAccessible > xRet; + + if ( pParent ) + xRet = pParent->GetAccessible(); + + return xRet; +} +// ----------------------------------------------------------------------------- +::com::sun::star::awt::Rectangle SAL_CALL SvxShowCharSetVirtualAcc::implGetBounds( ) throw (RuntimeException) +{ + const Point aOutPos( mpParent->GetPosPixel() ); + Size aOutSize( mpParent->GetOutputSizePixel() ); + if ( mpParent->getScrollBar()->IsVisible() ) + { + const Size aScrollBar = mpParent->getScrollBar()->GetOutputSizePixel(); + aOutSize.Width() -= aScrollBar.Width(); + aOutSize.Height() -= aScrollBar.Height(); + } + + awt::Rectangle aRet; + + aRet.X = aOutPos.X(); + aRet.Y = aOutPos.Y(); + aRet.Width = aOutSize.Width(); + aRet.Height = aOutSize.Height(); + + return aRet; +} +// ----------------------------------------------------------------------------- +sal_Int16 SAL_CALL SvxShowCharSetVirtualAcc::getAccessibleRole( ) throw (RuntimeException) +{ + return accessibility::AccessibleRole::SCROLL_PANE; +} +// ----------------------------------------------------------------------------- +::rtl::OUString SAL_CALL SvxShowCharSetVirtualAcc::getAccessibleDescription( ) throw (RuntimeException) +{ + OExternalLockGuard aGuard( this ); + return SVX_RESSTR( RID_SVXSTR_CHARACTER_SELECTION); +} +// ----------------------------------------------------------------------------- +::rtl::OUString SAL_CALL SvxShowCharSetVirtualAcc::getAccessibleName( ) throw (RuntimeException) +{ + OExternalLockGuard aGuard( this ); + return SVX_RESSTR( RID_SVXSTR_CHAR_SEL_DESC); +} +// ----------------------------------------------------------------------------- +Reference< XAccessibleRelationSet > SAL_CALL SvxShowCharSetVirtualAcc::getAccessibleRelationSet( ) throw (RuntimeException) +{ + return Reference< XAccessibleRelationSet >(); +} +// ----------------------------------------------------------------------------- +Reference< XAccessibleStateSet > SAL_CALL SvxShowCharSetVirtualAcc::getAccessibleStateSet( ) throw (RuntimeException) +{ + OExternalLockGuard aGuard( this ); + + ::utl::AccessibleStateSetHelper* pStateSet = new ::utl::AccessibleStateSetHelper; + + if( mpParent ) + { + // SELECTABLE + pStateSet->AddState( AccessibleStateType::FOCUSABLE ); + if ( mpParent->HasFocus() ) + pStateSet->AddState( AccessibleStateType::FOCUSED ); + if ( mpParent->IsActive() ) + pStateSet->AddState( AccessibleStateType::ACTIVE ); + if ( mpParent->IsEnabled() ) + { + pStateSet->AddState( AccessibleStateType::ENABLED ); + pStateSet->AddState( AccessibleStateType::SENSITIVE ); + } + if ( mpParent->IsReallyVisible() ) + pStateSet->AddState( AccessibleStateType::VISIBLE ); + } + + return pStateSet; +} +// ----------------------------------------------------------------------------- +void SAL_CALL SvxShowCharSetVirtualAcc::disposing() +{ + OAccessibleContextHelper::disposing(); + if ( m_pTable ) + m_pTable->dispose(); + m_pTable = NULL; +} +// ----------------------------------------------------------------------------- +// ---------------- +// - SvxShowCharSetItem - +// ---------------- + +SvxShowCharSetItem::SvxShowCharSetItem( SvxShowCharSet& rParent,SvxShowCharSetAcc* _pParent,USHORT _nPos ) : + mrParent( rParent ) + ,mnId( _nPos ) + ,m_pItem(NULL) + ,m_pParent(_pParent) +{ +} +// ----------------------------------------------------------------------- + +SvxShowCharSetItem::~SvxShowCharSetItem() +{ + if ( m_xAcc.is() ) + { + m_pItem->ParentDestroyed(); + ClearAccessible(); + } +} + +// ----------------------------------------------------------------------- + +uno::Reference< accessibility::XAccessible > SvxShowCharSetItem::GetAccessible() +{ + if( !m_xAcc.is() ) + { + m_pItem = new SvxShowCharSetItemAcc( this ); + m_xAcc = m_pItem; + } + + return m_xAcc; +} + +// ----------------------------------------------------------------------- + +void SvxShowCharSetItem::ClearAccessible() +{ + if ( m_xAcc.is() ) + { + m_pItem = NULL; + m_xAcc = NULL; + } +} + + +// --------------- +// - SvxShowCharSetAcc - +// --------------- + +SvxShowCharSetAcc::SvxShowCharSetAcc( SvxShowCharSetVirtualAcc* _pParent ) : OAccessibleSelectionHelper(new VCLExternalSolarLock()) + ,m_pParent( _pParent ) +{ + osl_incrementInterlockedCount(&m_refCount); + { // #b6211265 # + lateInit(this); + } + osl_decrementInterlockedCount(&m_refCount); +} + +// ----------------------------------------------------------------------------- + +SvxShowCharSetAcc::~SvxShowCharSetAcc() +{ + ensureDisposed(); + delete getExternalLock(); +} +// ----------------------------------------------------------------------------- +void SAL_CALL SvxShowCharSetAcc::disposing() +{ + OAccessibleSelectionHelper::disposing(); + ::std::vector< Reference< XAccessible > >::iterator aIter = m_aChildren.begin(); + ::std::vector< Reference< XAccessible > >::iterator aEnd = m_aChildren.end(); + for (;aIter != aEnd ; ++aIter) + ::comphelper::disposeComponent(*aIter); + + m_aChildren.clear(); + m_pParent = NULL; +} + +// ----------------------------------------------------------------------------- +IMPLEMENT_FORWARD_XINTERFACE2( SvxShowCharSetAcc, OAccessibleSelectionHelper, OAccessibleHelper_Base ) +IMPLEMENT_FORWARD_XTYPEPROVIDER2( SvxShowCharSetAcc, OAccessibleSelectionHelper, OAccessibleHelper_Base ) +// ----------------------------------------------------------------------- +sal_Bool SvxShowCharSetAcc::implIsSelected( sal_Int32 nAccessibleChildIndex ) throw (RuntimeException) +{ + return m_pParent && m_pParent->getCharSetControl()->IsSelected( + sal::static_int_cast<USHORT>(nAccessibleChildIndex)); +} +// ----------------------------------------------------------------------------- + // select the specified child => watch for special ChildIndexes (ACCESSIBLE_SELECTION_CHILD_xxx) +void SvxShowCharSetAcc::implSelect( sal_Int32 nAccessibleChildIndex, sal_Bool bSelect ) throw (IndexOutOfBoundsException, RuntimeException) +{ + if ( m_pParent ) + { + if ( bSelect ) + m_pParent->getCharSetControl()->SelectIndex(nAccessibleChildIndex,sal_True); + else + m_pParent->getCharSetControl()->DeSelect(); + } +} +// ----------------------------------------------------------------------------- +::com::sun::star::awt::Rectangle SAL_CALL SvxShowCharSetAcc::implGetBounds( ) throw (RuntimeException) +{ + const Point aOutPos( m_pParent->getCharSetControl()->GetPosPixel() ); + Size aOutSize( m_pParent->getCharSetControl()->GetOutputSizePixel()); + if ( m_pParent->getCharSetControl()->getScrollBar()->IsVisible() ) + { + const Size aScrollBar = m_pParent->getCharSetControl()->getScrollBar()->GetOutputSizePixel(); + aOutSize.Width() -= aScrollBar.Width(); + aOutSize.Height() -= aScrollBar.Height(); + } + + awt::Rectangle aRet; + + aRet.X = aOutPos.X(); + aRet.Y = aOutPos.Y(); + aRet.Width = aOutSize.Width(); + aRet.Height = aOutSize.Height(); + + return aRet; +} +// ----------------------------------------------------------------------------- +sal_Int32 SAL_CALL SvxShowCharSetAcc::getAccessibleChildCount() + throw (uno::RuntimeException) +{ + OExternalLockGuard aGuard( this ); + ensureAlive(); + return m_pParent->getCharSetControl()->getMaxCharCount(); +} + +// ----------------------------------------------------------------------------- + +uno::Reference< accessibility::XAccessible > SAL_CALL SvxShowCharSetAcc::getAccessibleChild( sal_Int32 i ) + throw (lang::IndexOutOfBoundsException, uno::RuntimeException) +{ + OExternalLockGuard aGuard( this ); + ensureAlive(); + uno::Reference< accessibility::XAccessible > xRet; + SvxShowCharSetItem* pItem = m_pParent->getCharSetControl()->ImplGetItem( static_cast< USHORT >( i ) ); + + if( pItem ) + { + pItem->m_pParent = this; + xRet = pItem->GetAccessible(); + m_aChildren.push_back(xRet); + } + else + throw lang::IndexOutOfBoundsException(); + + return xRet; +} + +// ----------------------------------------------------------------------------- + +uno::Reference< accessibility::XAccessible > SAL_CALL SvxShowCharSetAcc::getAccessibleParent() + throw (uno::RuntimeException) +{ + OExternalLockGuard aGuard( this ); + ensureAlive(); + return m_pParent; +} + +// ----------------------------------------------------------------------------- + +sal_Int16 SAL_CALL SvxShowCharSetAcc::getAccessibleRole() + throw (uno::RuntimeException) +{ + return accessibility::AccessibleRole::TABLE; +} + +// ----------------------------------------------------------------------------- + +::rtl::OUString SAL_CALL SvxShowCharSetAcc::getAccessibleDescription() + throw (uno::RuntimeException) +{ + OExternalLockGuard aGuard( this ); + return SVX_RESSTR( RID_SVXSTR_CHARACTER_SELECTION ); +} + +// ----------------------------------------------------------------------------- + +::rtl::OUString SAL_CALL SvxShowCharSetAcc::getAccessibleName() + throw (uno::RuntimeException) +{ + OExternalLockGuard aGuard( this ); + ensureAlive(); + return SVX_RESSTR( RID_SVXSTR_CHAR_SEL_DESC ); +} + +// ----------------------------------------------------------------------------- + +uno::Reference< accessibility::XAccessibleRelationSet > SAL_CALL SvxShowCharSetAcc::getAccessibleRelationSet() + throw (uno::RuntimeException) +{ + return uno::Reference< accessibility::XAccessibleRelationSet >(); +} + +// ----------------------------------------------------------------------------- + +uno::Reference< accessibility::XAccessibleStateSet > SAL_CALL SvxShowCharSetAcc::getAccessibleStateSet() + throw (uno::RuntimeException) +{ + OExternalLockGuard aGuard( this ); + + ::utl::AccessibleStateSetHelper* pStateSet = new ::utl::AccessibleStateSetHelper; + + if( m_pParent->getCharSetControl() ) + { + // SELECTABLE + pStateSet->AddState( AccessibleStateType::FOCUSABLE ); + if ( m_pParent->getCharSetControl()->HasFocus() ) + pStateSet->AddState( AccessibleStateType::FOCUSED ); + if ( m_pParent->getCharSetControl()->IsActive() ) + pStateSet->AddState( AccessibleStateType::ACTIVE ); + if ( m_pParent->getCharSetControl()->IsEnabled() ) + { + pStateSet->AddState( AccessibleStateType::ENABLED ); + pStateSet->AddState( AccessibleStateType::SENSITIVE ); + } + if ( m_pParent->getCharSetControl()->IsReallyVisible() ) + pStateSet->AddState( AccessibleStateType::VISIBLE ); + + pStateSet->AddState( AccessibleStateType::MANAGES_DESCENDANTS ); + } + + return pStateSet; +} +// ----------------------------------------------------------------------------- + +uno::Reference< accessibility::XAccessible > SAL_CALL SvxShowCharSetAcc::getAccessibleAtPoint( const awt::Point& aPoint ) + throw (uno::RuntimeException) +{ + OExternalLockGuard aGuard( this ); + ensureAlive(); + + uno::Reference< accessibility::XAccessible > xRet; + const USHORT nItemId = sal::static_int_cast<USHORT>( + m_pParent->getCharSetControl()->PixelToMapIndex( Point( aPoint.X, aPoint.Y ) )); + + if( USHORT(-1) != nItemId ) + { + SvxShowCharSetItem* pItem = m_pParent->getCharSetControl()->ImplGetItem( nItemId ); + xRet = pItem->GetAccessible(); + } + return xRet; +} + +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- + +void SAL_CALL SvxShowCharSetAcc::grabFocus() + throw (uno::RuntimeException) +{ + OExternalLockGuard aGuard( this ); + ensureAlive(); + m_pParent->getCharSetControl()->GrabFocus(); +} + +// ----------------------------------------------------------------------------- + +uno::Any SAL_CALL SvxShowCharSetAcc::getAccessibleKeyBinding() + throw (uno::RuntimeException) +{ + return uno::Any(); +} +// ----------------------------------------------------------------------------- +sal_Int32 SAL_CALL SvxShowCharSetAcc::getAccessibleRowCount( ) throw (RuntimeException) +{ + return ((getAccessibleChildCount()-1) / COLUMN_COUNT) + 1; +} +// ----------------------------------------------------------------------------- +sal_Int32 SAL_CALL SvxShowCharSetAcc::getAccessibleColumnCount( ) throw (RuntimeException) +{ + return COLUMN_COUNT; +} +// ----------------------------------------------------------------------------- +::rtl::OUString SAL_CALL SvxShowCharSetAcc::getAccessibleRowDescription( sal_Int32 /*nRow*/ ) throw (IndexOutOfBoundsException, RuntimeException) +{ + return ::rtl::OUString(); +} +// ----------------------------------------------------------------------------- +::rtl::OUString SAL_CALL SvxShowCharSetAcc::getAccessibleColumnDescription( sal_Int32 /*nColumn*/ ) throw (IndexOutOfBoundsException, RuntimeException) +{ + return ::rtl::OUString(); +} +// ----------------------------------------------------------------------------- +sal_Int32 SAL_CALL SvxShowCharSetAcc::getAccessibleRowExtentAt( sal_Int32 /*nRow*/, sal_Int32 /*nColumn*/ ) throw (IndexOutOfBoundsException, RuntimeException) +{ + return 1; +} +// ----------------------------------------------------------------------------- +sal_Int32 SAL_CALL SvxShowCharSetAcc::getAccessibleColumnExtentAt( sal_Int32 /*nRow*/, sal_Int32 /*nColumn*/ ) throw (IndexOutOfBoundsException, RuntimeException) +{ + return 1; +} +// ----------------------------------------------------------------------------- +Reference< XAccessibleTable > SAL_CALL SvxShowCharSetAcc::getAccessibleRowHeaders( ) throw (RuntimeException) +{ + return Reference< XAccessibleTable >(); +} +// ----------------------------------------------------------------------------- +Reference< XAccessibleTable > SAL_CALL SvxShowCharSetAcc::getAccessibleColumnHeaders( ) throw (RuntimeException) +{ + return Reference< XAccessibleTable >(); +} +// ----------------------------------------------------------------------------- +Sequence< sal_Int32 > SAL_CALL SvxShowCharSetAcc::getSelectedAccessibleRows( ) throw (RuntimeException) +{ + OExternalLockGuard aGuard( this ); + ensureAlive(); + Sequence< sal_Int32 > aSel(1); + aSel[0] = m_pParent->getCharSetControl()->GetRowPos(m_pParent->getCharSetControl()->GetSelectIndexId()); + return aSel; +} +// ----------------------------------------------------------------------------- +Sequence< sal_Int32 > SAL_CALL SvxShowCharSetAcc::getSelectedAccessibleColumns( ) throw (RuntimeException) +{ + OExternalLockGuard aGuard( this ); + ensureAlive(); + Sequence< sal_Int32 > aSel(1); + aSel[0] = m_pParent->getCharSetControl()->GetColumnPos(m_pParent->getCharSetControl()->GetSelectIndexId()); + return aSel; +} +// ----------------------------------------------------------------------------- +sal_Bool SAL_CALL SvxShowCharSetAcc::isAccessibleRowSelected( sal_Int32 nRow ) throw (IndexOutOfBoundsException, RuntimeException) +{ + OExternalLockGuard aGuard( this ); + ensureAlive(); + return m_pParent->getCharSetControl()->GetRowPos(m_pParent->getCharSetControl()->GetSelectIndexId()) == nRow; +} +// ----------------------------------------------------------------------------- +sal_Bool SAL_CALL SvxShowCharSetAcc::isAccessibleColumnSelected( sal_Int32 nColumn ) throw (IndexOutOfBoundsException, RuntimeException) +{ + OExternalLockGuard aGuard( this ); + ensureAlive(); + return m_pParent->getCharSetControl()->GetColumnPos(m_pParent->getCharSetControl()->GetSelectIndexId()) == nColumn; +} +// ----------------------------------------------------------------------------- +Reference< XAccessible > SAL_CALL SvxShowCharSetAcc::getAccessibleCellAt( sal_Int32 nRow, sal_Int32 nColumn ) throw (IndexOutOfBoundsException, RuntimeException) +{ + OExternalLockGuard aGuard( this ); + ensureAlive(); + ::svx::SvxShowCharSetItem* pItem = m_pParent->getCharSetControl()->ImplGetItem( + sal::static_int_cast<USHORT>(getAccessibleIndex(nRow,nColumn) )); + if ( !pItem ) + throw IndexOutOfBoundsException(); + return pItem->GetAccessible(); +} +// ----------------------------------------------------------------------------- +Reference< XAccessible > SAL_CALL SvxShowCharSetAcc::getAccessibleCaption( ) throw (RuntimeException) +{ + return Reference< XAccessible >(); +} +// ----------------------------------------------------------------------------- +Reference< XAccessible > SAL_CALL SvxShowCharSetAcc::getAccessibleSummary( ) throw (RuntimeException) +{ + return Reference< XAccessible >(); +} +// ----------------------------------------------------------------------------- +sal_Bool SAL_CALL SvxShowCharSetAcc::isAccessibleSelected( sal_Int32 nRow, sal_Int32 nColumn ) throw (IndexOutOfBoundsException, RuntimeException) +{ + OExternalLockGuard aGuard( this ); + ensureAlive(); + return m_pParent->getCharSetControl()->GetSelectIndexId() == getAccessibleIndex(nRow,nColumn); +} +// ----------------------------------------------------------------------------- +sal_Int32 SAL_CALL SvxShowCharSetAcc::getAccessibleIndex( sal_Int32 nRow, sal_Int32 nColumn ) throw (IndexOutOfBoundsException, RuntimeException) +{ + return (nRow*COLUMN_COUNT) + nColumn; +} +// ----------------------------------------------------------------------------- +sal_Int32 SAL_CALL SvxShowCharSetAcc::getAccessibleRow( sal_Int32 nChildIndex ) throw (IndexOutOfBoundsException, RuntimeException) +{ + OExternalLockGuard aGuard( this ); + ensureAlive(); + return m_pParent->getCharSetControl()->GetRowPos(sal::static_int_cast<USHORT>(nChildIndex)); +} +// ----------------------------------------------------------------------------- +sal_Int32 SAL_CALL SvxShowCharSetAcc::getAccessibleColumn( sal_Int32 nChildIndex ) throw (IndexOutOfBoundsException, RuntimeException) +{ + OExternalLockGuard aGuard( this ); + ensureAlive(); + return m_pParent->getCharSetControl()->GetColumnPos(sal::static_int_cast<USHORT>(nChildIndex)); +} +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// ---------------- +// - SvxShowCharSetItemAcc - +// ---------------- + +SvxShowCharSetItemAcc::SvxShowCharSetItemAcc( SvxShowCharSetItem* pParent ) : OAccessibleComponentHelper(new VCLExternalSolarLock()) +,mpParent( pParent ) +{ + OSL_ENSURE(pParent,"NO parent supplied!"); + osl_incrementInterlockedCount(&m_refCount); + { // #b6211265 # + lateInit(this); + } + osl_decrementInterlockedCount(&m_refCount); +} + +// ----------------------------------------------------------------------------- + +SvxShowCharSetItemAcc::~SvxShowCharSetItemAcc() +{ + ensureDisposed(); + delete getExternalLock(); +} +// ----------------------------------------------------------------------------- +IMPLEMENT_FORWARD_XINTERFACE2( SvxShowCharSetItemAcc, OAccessibleComponentHelper, OAccessibleHelper_Base_2 ) +IMPLEMENT_FORWARD_XTYPEPROVIDER2( SvxShowCharSetItemAcc, OAccessibleComponentHelper, OAccessibleHelper_Base_2 ) +// ----------------------------------------------------------------------------- + +void SvxShowCharSetItemAcc::ParentDestroyed() +{ + const ::osl::MutexGuard aGuard( GetMutex() ); + mpParent = NULL; +} + +// ----------------------------------------------------------------------------- + +sal_Int32 SAL_CALL SvxShowCharSetItemAcc::getAccessibleChildCount() + throw (uno::RuntimeException) +{ + return 0; +} + +// ----------------------------------------------------------------------------- + +uno::Reference< accessibility::XAccessible > SAL_CALL SvxShowCharSetItemAcc::getAccessibleChild( sal_Int32 /*i*/ ) + throw (lang::IndexOutOfBoundsException, uno::RuntimeException) +{ + throw lang::IndexOutOfBoundsException(); +} + +// ----------------------------------------------------------------------------- + +uno::Reference< accessibility::XAccessible > SAL_CALL SvxShowCharSetItemAcc::getAccessibleParent() + throw (uno::RuntimeException) +{ + OExternalLockGuard aGuard( this ); + ensureAlive(); + return mpParent->m_pParent; +} + +// ----------------------------------------------------------------------------- + +sal_Int16 SAL_CALL SvxShowCharSetItemAcc::getAccessibleRole() + throw (uno::RuntimeException) +{ + return accessibility::AccessibleRole::LABEL; +} + +// ----------------------------------------------------------------------------- + +::rtl::OUString SAL_CALL SvxShowCharSetItemAcc::getAccessibleDescription() + throw (uno::RuntimeException) +{ + OExternalLockGuard aGuard( this ); + ensureAlive(); + String sDescription = SVX_RESSTR( RID_SVXSTR_CHARACTER_CODE ); + + sal_Unicode c = mpParent->maText.GetChar(0); + char buf[16] = "0x0000"; + sal_Unicode c_Shifted = c; + for( int i = 0; i < 4; ++i ) + { + char h = (char)(c_Shifted & 0x0F); + buf[5-i] = (h > 9) ? (h - 10 + 'A') : (h + '0'); + c_Shifted >>= 4; + } + if( c < 256 ) + snprintf( buf+6, 10, " (%d)", c ); + sDescription.AppendAscii(buf); + + return sDescription; +} + +// ----------------------------------------------------------------------------- + +::rtl::OUString SAL_CALL SvxShowCharSetItemAcc::getAccessibleName() + throw (uno::RuntimeException) +{ + OExternalLockGuard aGuard( this ); + ensureAlive(); + String aRet; + + if( mpParent ) + { + aRet = mpParent->maText; + + if( !aRet.Len() ) + aRet = getAccessibleDescription(); + } + + return aRet; +} + +// ----------------------------------------------------------------------------- + +uno::Reference< accessibility::XAccessibleRelationSet > SAL_CALL SvxShowCharSetItemAcc::getAccessibleRelationSet() + throw (uno::RuntimeException) +{ + return uno::Reference< accessibility::XAccessibleRelationSet >(); +} + +// ----------------------------------------------------------------------------- + +uno::Reference< accessibility::XAccessibleStateSet > SAL_CALL SvxShowCharSetItemAcc::getAccessibleStateSet() + throw (uno::RuntimeException) +{ + OExternalLockGuard aGuard( this ); + ensureAlive(); + + ::utl::AccessibleStateSetHelper* pStateSet = new ::utl::AccessibleStateSetHelper; + + if( mpParent ) + { + // SELECTABLE + pStateSet->AddState( accessibility::AccessibleStateType::SELECTABLE ); + pStateSet->AddState( accessibility::AccessibleStateType::FOCUSABLE ); + + // SELECTED + if( mpParent->mrParent.GetSelectIndexId() == mpParent->mnId ) + { + pStateSet->AddState( accessibility::AccessibleStateType::SELECTED ); + pStateSet->AddState( accessibility::AccessibleStateType::FOCUSED ); + } + if ( mpParent->mnId >= mpParent->mrParent.FirstInView() && mpParent->mnId <= mpParent->mrParent.LastInView() ) + pStateSet->AddState( AccessibleStateType::VISIBLE ); + pStateSet->AddState( AccessibleStateType::TRANSIENT ); + } + + return pStateSet; +} + +// ----------------------------------------------------------------------------- +void SAL_CALL SvxShowCharSetItemAcc::grabFocus() + throw (uno::RuntimeException) +{ + // nothing to do +} + +// ----------------------------------------------------------------------------- + +uno::Any SAL_CALL SvxShowCharSetItemAcc::getAccessibleKeyBinding() + throw (uno::RuntimeException) +{ + return uno::Any(); +} +// ----------------------------------------------------------------------------- +awt::Rectangle SAL_CALL SvxShowCharSetItemAcc::implGetBounds( ) throw (RuntimeException) +{ + awt::Rectangle aRet; + + if( mpParent ) + { + Rectangle aRect( mpParent->maRect ); + Point aOrigin; + Rectangle aParentRect( aOrigin, mpParent->mrParent.GetOutputSizePixel() ); + + aRect.Intersection( aParentRect ); + + aRet.X = aRect.Left(); + aRet.Y = aRect.Top(); + aRet.Width = aRect.GetWidth(); + aRet.Height = aRect.GetHeight(); + } + + return aRet; +} +// ----------------------------------------------------------------------------- +uno::Reference< accessibility::XAccessible > SAL_CALL SvxShowCharSetItemAcc::getAccessibleAtPoint( const awt::Point& /*aPoint*/ ) + throw (uno::RuntimeException) +{ + return uno::Reference< accessibility::XAccessible >(); +} +// ----------------------------------------------------------------------------- +sal_Int32 SAL_CALL SvxShowCharSetVirtualAcc::getForeground( ) throw (RuntimeException) +{ + OExternalLockGuard aGuard( this ); + + sal_Int32 nColor = 0; + if ( mpParent ) + { + if ( mpParent->IsControlForeground() ) + nColor = mpParent->GetControlForeground().GetColor(); + else + { + Font aFont; + if ( mpParent->IsControlFont() ) + aFont = mpParent->GetControlFont(); + else + aFont = mpParent->GetFont(); + nColor = aFont.GetColor().GetColor(); + } + } + + return nColor; +} +// ----------------------------------------------------------------------------- +sal_Int32 SAL_CALL SvxShowCharSetVirtualAcc::getBackground( ) throw (RuntimeException) +{ + OExternalLockGuard aGuard( this ); + sal_Int32 nColor = 0; + if ( mpParent ) + { + if ( mpParent->IsControlBackground() ) + nColor = mpParent->GetControlBackground().GetColor(); + else + nColor = mpParent->GetBackground().GetColor().GetColor(); + } + + return nColor; +} +// ----------------------------------------------------------------------------- +sal_Int32 SAL_CALL SvxShowCharSetAcc::getForeground( ) throw (RuntimeException) +{ + OExternalLockGuard aGuard( this ); + + sal_Int32 nColor = 0; + if ( m_pParent ) + nColor = m_pParent->getForeground(); + return nColor; +} +// ----------------------------------------------------------------------------- +sal_Int32 SAL_CALL SvxShowCharSetAcc::getBackground( ) throw (RuntimeException) +{ + OExternalLockGuard aGuard( this ); + sal_Int32 nColor = 0; + if ( m_pParent ) + nColor = m_pParent->getBackground(); + return nColor; +} +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +} // namespace svx +// ----------------------------------------------------------------------------- + + diff --git a/svx/source/accessibility/makefile.mk b/svx/source/accessibility/makefile.mk new file mode 100755 index 000000000000..9ac9cff08a1d --- /dev/null +++ b/svx/source/accessibility/makefile.mk @@ -0,0 +1,68 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2000, 2010 Oracle and/or its affiliates. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +PRJ=..$/.. + +PRJNAME=svx +TARGET=accessibility +ENABLE_EXCEPTIONS=TRUE + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk +.INCLUDE : $(PRJ)$/util$/makefile.pmk + +# --- Files -------------------------------------------------------- + +SLOFILES= \ + $(SLO)$/charmapacc.obj \ + $(SLO)$/svxrectctaccessiblecontext.obj \ + $(SLO)$/GraphCtlAccessibleContext.obj \ + $(SLO)$/ChildrenManager.obj \ + $(SLO)$/ChildrenManagerImpl.obj \ + $(SLO)$/DescriptionGenerator.obj \ + $(SLO)$/AccessibleShape.obj \ + $(SLO)$/AccessibleGraphicShape.obj \ + $(SLO)$/AccessibleOLEShape.obj \ + $(SLO)$/AccessibleShapeInfo.obj \ + $(SLO)$/AccessibleShapeTreeInfo.obj \ + $(SLO)$/AccessibleTextHelper.obj \ + $(SLO)$/AccessibleEmptyEditSource.obj \ + $(SLO)$/AccessibleTextEventQueue.obj \ + $(SLO)$/ShapeTypeHandler.obj \ + $(SLO)$/SvxShapeTypes.obj \ + $(SLO)$/AccessibleControlShape.obj \ + $(SLO)$/DGColorNameLookUp.obj \ + $(SLO)$/AccessibleFrameSelector.obj + +SRS2NAME = accessibility +SRC2FILES = accessibility.src + +# --- Tagets ------------------------------------------------------- + +.INCLUDE : target.mk + diff --git a/svx/source/accessibility/svxrectctaccessiblecontext.cxx b/svx/source/accessibility/svxrectctaccessiblecontext.cxx new file mode 100644 index 000000000000..9dfb97dff4ac --- /dev/null +++ b/svx/source/accessibility/svxrectctaccessiblecontext.cxx @@ -0,0 +1,1206 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svx.hxx" + + +#include "svxrectctaccessiblecontext.hxx" +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <unotools/accessiblestatesethelper.hxx> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/beans/PropertyChangeEvent.hpp> +#include <com/sun/star/awt/XWindow.hpp> +#include <cppuhelper/typeprovider.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <toolkit/helper/convert.hxx> +#include <vcl/svapp.hxx> +#include <osl/mutex.hxx> +#include <rtl/uuid.h> +#include <tools/debug.hxx> +#include <tools/gen.hxx> + +#include <svx/dialogs.hrc> +#include "accessibility.hrc" +#include <svx/dlgctrl.hxx> +#include <svx/dialmgr.hxx> +#include <comphelper/accessibleeventnotifier.hxx> + + +using namespace ::cppu; +using namespace ::osl; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::accessibility; + + +#define MAX_NUM_OF_CHILDS 9 +#define NOCHILDSELECTED -1 + + +DBG_NAME( SvxRectCtlAccessibleContext ) + + +//===== internal ============================================================ + +namespace +{ + struct ChildIndexToPointData + { + short nResIdName; + short nResIdDescr; + RECT_POINT ePoint; + }; +} + + +static const ChildIndexToPointData* IndexToPoint( long nIndex, sal_Bool bAngleControl ) +{ + DBG_ASSERT( nIndex < ( bAngleControl? 8 : 9 ) && nIndex >= 0, "-IndexToPoint(): invalid child index! You have been warned..." ); + + // angles are counted reverse counter clock wise + static const ChildIndexToPointData pAngleData[] = + { // index + { RID_SVXSTR_RECTCTL_ACC_CHLD_A000, RID_SVXSTR_RECTCTL_ACC_CHLD_A000, RP_RM }, // 0 + { RID_SVXSTR_RECTCTL_ACC_CHLD_A045, RID_SVXSTR_RECTCTL_ACC_CHLD_A045, RP_RT }, // 1 + { RID_SVXSTR_RECTCTL_ACC_CHLD_A090, RID_SVXSTR_RECTCTL_ACC_CHLD_A090, RP_MT }, // 2 + { RID_SVXSTR_RECTCTL_ACC_CHLD_A135, RID_SVXSTR_RECTCTL_ACC_CHLD_A135, RP_LT }, // 3 + { RID_SVXSTR_RECTCTL_ACC_CHLD_A180, RID_SVXSTR_RECTCTL_ACC_CHLD_A180, RP_LM }, // 4 + { RID_SVXSTR_RECTCTL_ACC_CHLD_A225, RID_SVXSTR_RECTCTL_ACC_CHLD_A225, RP_LB }, // 5 + { RID_SVXSTR_RECTCTL_ACC_CHLD_A270, RID_SVXSTR_RECTCTL_ACC_CHLD_A270, RP_MB }, // 6 + { RID_SVXSTR_RECTCTL_ACC_CHLD_A315, RID_SVXSTR_RECTCTL_ACC_CHLD_A315, RP_RB } // 7 + }; + + // corners are counted from left to right and top to bottom + static const ChildIndexToPointData pCornerData[] = + { // index + { RID_SVXSTR_RECTCTL_ACC_CHLD_LT, RID_SVXSTR_RECTCTL_ACC_CHLD_LT, RP_LT }, // 0 + { RID_SVXSTR_RECTCTL_ACC_CHLD_MT, RID_SVXSTR_RECTCTL_ACC_CHLD_MT, RP_MT }, // 1 + { RID_SVXSTR_RECTCTL_ACC_CHLD_RT, RID_SVXSTR_RECTCTL_ACC_CHLD_RT, RP_RT }, // 2 + { RID_SVXSTR_RECTCTL_ACC_CHLD_LM, RID_SVXSTR_RECTCTL_ACC_CHLD_LM, RP_LM }, // 3 + { RID_SVXSTR_RECTCTL_ACC_CHLD_MM, RID_SVXSTR_RECTCTL_ACC_CHLD_MM, RP_MM }, // 4 + { RID_SVXSTR_RECTCTL_ACC_CHLD_RM, RID_SVXSTR_RECTCTL_ACC_CHLD_RM, RP_RM }, // 5 + { RID_SVXSTR_RECTCTL_ACC_CHLD_LB, RID_SVXSTR_RECTCTL_ACC_CHLD_LB, RP_LB }, // 6 + { RID_SVXSTR_RECTCTL_ACC_CHLD_MB, RID_SVXSTR_RECTCTL_ACC_CHLD_MB, RP_MB }, // 7 + { RID_SVXSTR_RECTCTL_ACC_CHLD_RB, RID_SVXSTR_RECTCTL_ACC_CHLD_RB, RP_RB } // 8 + }; + + return ( bAngleControl? pAngleData : pCornerData ) + nIndex; +} + + +static long PointToIndex( RECT_POINT ePoint, sal_Bool bAngleControl ) +{ + long nRet( (long) ePoint ); + if( bAngleControl ) + { // angle control + // angles are counted reverse counter clock wise + switch( ePoint ) + { + case RP_LT: nRet = 3; break; + case RP_MT: nRet = 2; break; + case RP_RT: nRet = 1; break; + case RP_LM: nRet = 4; break; + case RP_MM: nRet = NOCHILDSELECTED; break; + case RP_RM: nRet = 0; break; + case RP_LB: nRet = 5; break; + case RP_MB: nRet = 6; break; + case RP_RB: nRet = 7; break; + } + } + else + { // corner control + // corners are counted from left to right and top to bottom + DBG_ASSERT( RP_LT == 0 && RP_MT == 1 && RP_RT == 2 && RP_LM == 3 && RP_MM == 4 && RP_RM == 5 && + RP_LB == 6 && RP_MB == 7 && RP_RB == 8, "*PointToIndex(): unexpected enum value!" ); + + nRet = ( long ) ePoint; + } + + return nRet; +} + + +SvxRectCtlAccessibleContext::SvxRectCtlAccessibleContext( + const Reference< XAccessible >& rxParent, + SvxRectCtl& rRepr, + const ::rtl::OUString* pName, + const ::rtl::OUString* pDesc ) : + + SvxRectCtlAccessibleContext_Base( m_aMutex ), + mxParent( rxParent ), + mpRepr( &rRepr ), + mpChilds( NULL ), + mnClientId( 0 ), + mnSelectedChild( NOCHILDSELECTED ), + mbAngleMode( rRepr.GetNumOfChilds() == 8 ) +{ + DBG_CTOR( SvxRectCtlAccessibleContext, NULL ); + + if( pName ) + msName = *pName; + else + { + ::vos::OGuard aSolarGuard( Application::GetSolarMutex() ); + msName = SVX_RESSTR( mbAngleMode? RID_SVXSTR_RECTCTL_ACC_ANGL_NAME : RID_SVXSTR_RECTCTL_ACC_CORN_NAME ); + } + + if( pDesc ) + msDescription = *pDesc; + else + { + ::vos::OGuard aSolarGuard( Application::GetSolarMutex() ); + msDescription = SVX_RESSTR( mbAngleMode? RID_SVXSTR_RECTCTL_ACC_ANGL_DESCR : RID_SVXSTR_RECTCTL_ACC_CORN_DESCR ); + } + + mpChilds = new SvxRectCtlChildAccessibleContext*[ MAX_NUM_OF_CHILDS ]; + + SvxRectCtlChildAccessibleContext** p = mpChilds; + for( int i = MAX_NUM_OF_CHILDS ; i ; --i, ++p ) + *p = NULL; +} + + +SvxRectCtlAccessibleContext::~SvxRectCtlAccessibleContext() +{ + DBG_DTOR( SvxRectCtlAccessibleContext, NULL ); + + if( IsAlive() ) + { + osl_incrementInterlockedCount( &m_refCount ); + dispose(); // set mpRepr = NULL & release all childs + } +} + +//===== XAccessible ========================================================= + +Reference< XAccessibleContext > SAL_CALL SvxRectCtlAccessibleContext::getAccessibleContext( void ) throw( RuntimeException ) +{ + return this; +} + +//===== XAccessibleComponent ================================================ + +sal_Bool SAL_CALL SvxRectCtlAccessibleContext::containsPoint( const awt::Point& rPoint ) throw( RuntimeException ) +{ + // no guard -> done in getBounds() +// return GetBoundingBox().IsInside( VCLPoint( rPoint ) ); + return Rectangle( Point( 0, 0 ), GetBoundingBox().GetSize() ).IsInside( VCLPoint( rPoint ) ); +} + +Reference< XAccessible > SAL_CALL SvxRectCtlAccessibleContext::getAccessibleAtPoint( const awt::Point& rPoint ) throw( RuntimeException ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + ThrowExceptionIfNotAlive(); + + Reference< XAccessible > xRet; + + long nChild = PointToIndex( mpRepr->GetApproxRPFromPixPt( rPoint ), mbAngleMode ); + + if( nChild != NOCHILDSELECTED ) + xRet = getAccessibleChild( nChild ); + + return xRet; +} + +awt::Rectangle SAL_CALL SvxRectCtlAccessibleContext::getBounds() throw( RuntimeException ) +{ + // no guard -> done in GetBoundingBox() + return AWTRectangle( GetBoundingBox() ); +} + +awt::Point SAL_CALL SvxRectCtlAccessibleContext::getLocation() throw( RuntimeException ) +{ + // no guard -> done in GetBoundingBox() + return AWTPoint( GetBoundingBox().TopLeft() ); +} + +awt::Point SAL_CALL SvxRectCtlAccessibleContext::getLocationOnScreen() throw( RuntimeException ) +{ + // no guard -> done in GetBoundingBoxOnScreen() + return AWTPoint( GetBoundingBoxOnScreen().TopLeft() ); +} + +awt::Size SAL_CALL SvxRectCtlAccessibleContext::getSize() throw( RuntimeException ) +{ + // no guard -> done in GetBoundingBox() + return AWTSize( GetBoundingBox().GetSize() ); +} + +sal_Bool SAL_CALL SvxRectCtlAccessibleContext::isShowing() throw( RuntimeException ) +{ + return sal_True; +} + +sal_Bool SAL_CALL SvxRectCtlAccessibleContext::isVisible() throw( RuntimeException ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + ThrowExceptionIfNotAlive(); + + return mpRepr->IsVisible(); +} + +sal_Bool SAL_CALL SvxRectCtlAccessibleContext::isFocusTraversable() throw( RuntimeException ) +{ + return sal_True; +} + +//===== XAccessibleContext ================================================== + +sal_Int32 SAL_CALL SvxRectCtlAccessibleContext::getAccessibleChildCount( void ) throw( RuntimeException ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + ThrowExceptionIfNotAlive(); + + return mpRepr->GetNumOfChilds(); +} + +Reference< XAccessible > SAL_CALL SvxRectCtlAccessibleContext::getAccessibleChild( sal_Int32 nIndex ) + throw( RuntimeException, lang::IndexOutOfBoundsException ) +{ + checkChildIndex( nIndex ); + + Reference< XAccessible > xChild = mpChilds[ nIndex ]; + if( !xChild.is() ) + { + ::vos::OGuard aSolarGuard( Application::GetSolarMutex() ); + + ::osl::MutexGuard aGuard( m_aMutex ); + + ThrowExceptionIfNotAlive(); + + xChild = mpChilds[ nIndex ]; + + if( !xChild.is() ) + { + const ChildIndexToPointData* p = IndexToPoint( nIndex, mbAngleMode ); + UniString tmp = SVX_RESSTR( p->nResIdName ); + ::rtl::OUString aName( tmp ); + tmp = SVX_RESSTR( p->nResIdDescr ); + ::rtl::OUString aDescr( tmp ); + + Rectangle aFocusRect( mpRepr->CalculateFocusRectangle( p->ePoint ) ); + + Rectangle aBoundingBoxOnScreen( mpRepr->OutputToScreenPixel( aFocusRect.TopLeft() ), aFocusRect.GetSize() ); + + SvxRectCtlChildAccessibleContext* pChild = new SvxRectCtlChildAccessibleContext( + this, *mpRepr, aName, aDescr, aFocusRect, nIndex ); + xChild = mpChilds[ nIndex ] = pChild; + pChild->acquire(); + + // set actual state + if( mnSelectedChild == nIndex ) + pChild->setStateChecked( sal_True ); + } + } + + return xChild; +} + +Reference< XAccessible > SAL_CALL SvxRectCtlAccessibleContext::getAccessibleParent( void ) throw( RuntimeException ) +{ + return mxParent; +} + +sal_Int32 SAL_CALL SvxRectCtlAccessibleContext::getAccessibleIndexInParent( void ) throw( RuntimeException ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + // Use a simple but slow solution for now. Optimize later. + + // Iterate over all the parent's children and search for this object. + if( mxParent.is() ) + { + Reference< XAccessibleContext > xParentContext( mxParent->getAccessibleContext() ); + if( xParentContext.is() ) + { + sal_Int32 nChildCount = xParentContext->getAccessibleChildCount(); + for( sal_Int32 i = 0 ; i < nChildCount ; ++i ) + { + Reference< XAccessible > xChild( xParentContext->getAccessibleChild( i ) ); + if( xChild.get() == ( XAccessible* ) this ) + return i; + } + } + } + + // Return -1 to indicate that this object's parent does not know about the + // object. + return -1; +} + +sal_Int16 SAL_CALL SvxRectCtlAccessibleContext::getAccessibleRole( void ) throw( RuntimeException ) +{ + return AccessibleRole::PANEL; +} + +::rtl::OUString SAL_CALL SvxRectCtlAccessibleContext::getAccessibleDescription( void ) throw( RuntimeException ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + return msDescription; +} + +::rtl::OUString SAL_CALL SvxRectCtlAccessibleContext::getAccessibleName( void ) throw( RuntimeException ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + return msName; +} + +/** Return empty reference to indicate that the relation set is not + supported. +*/ +Reference< XAccessibleRelationSet > SAL_CALL SvxRectCtlAccessibleContext::getAccessibleRelationSet( void ) throw( RuntimeException ) +{ + return Reference< XAccessibleRelationSet >(); +} + +Reference< XAccessibleStateSet > SAL_CALL SvxRectCtlAccessibleContext::getAccessibleStateSet( void ) throw( RuntimeException ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + utl::AccessibleStateSetHelper* pStateSetHelper = new utl::AccessibleStateSetHelper; + + if( IsAlive() ) + { + // pStateSetHelper->AddState( AccessibleStateType::ENABLED ); + // pStateSetHelper->AddState( AccessibleStateType::SENSITIVE ); + pStateSetHelper->AddState( AccessibleStateType::FOCUSABLE ); + if( mpRepr->HasFocus() ) + pStateSetHelper->AddState( AccessibleStateType::FOCUSED ); + pStateSetHelper->AddState( AccessibleStateType::OPAQUE ); + + if( isShowing() ) + pStateSetHelper->AddState( AccessibleStateType::SHOWING ); + + if( isVisible() ) + pStateSetHelper->AddState( AccessibleStateType::VISIBLE ); + } + else + pStateSetHelper->AddState( AccessibleStateType::DEFUNC ); + + return pStateSetHelper; +} + +lang::Locale SAL_CALL SvxRectCtlAccessibleContext::getLocale( void ) throw( IllegalAccessibleComponentStateException, RuntimeException ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if( mxParent.is() ) + { + Reference< XAccessibleContext > xParentContext( mxParent->getAccessibleContext() ); + if( xParentContext.is() ) + return xParentContext->getLocale(); + } + + // No parent. Therefore throw exception to indicate this cluelessness. + throw IllegalAccessibleComponentStateException(); +} + +void SAL_CALL SvxRectCtlAccessibleContext::addEventListener( const Reference< XAccessibleEventListener >& xListener ) + throw( RuntimeException ) +{ + if (xListener.is()) + { + ::osl::MutexGuard aGuard( m_aMutex ); + if (!mnClientId) + mnClientId = comphelper::AccessibleEventNotifier::registerClient( ); + comphelper::AccessibleEventNotifier::addEventListener( mnClientId, xListener ); + } +} + +void SAL_CALL SvxRectCtlAccessibleContext::removeEventListener( const Reference< XAccessibleEventListener >& xListener ) + throw( RuntimeException ) +{ + if (xListener.is()) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + sal_Int32 nListenerCount = comphelper::AccessibleEventNotifier::removeEventListener( mnClientId, xListener ); + if ( !nListenerCount ) + { + // no listeners anymore + // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client), + // and at least to us not firing any events anymore, in case somebody calls + // NotifyAccessibleEvent, again + comphelper::AccessibleEventNotifier::revokeClient( mnClientId ); + mnClientId = 0; + } + } +} + +void SAL_CALL SvxRectCtlAccessibleContext::addFocusListener( const Reference< awt::XFocusListener >& xListener ) + throw( RuntimeException ) +{ + if( xListener.is() ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + ThrowExceptionIfNotAlive(); + + Reference< awt::XWindow > xWindow = VCLUnoHelper::GetInterface( mpRepr ); + if( xWindow.is() ) + xWindow->addFocusListener( xListener ); + } +} + +void SAL_CALL SvxRectCtlAccessibleContext::removeFocusListener( const Reference< awt::XFocusListener >& xListener ) + throw (RuntimeException) +{ + if( xListener.is() ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + ThrowExceptionIfNotAlive(); + + Reference< awt::XWindow > xWindow = VCLUnoHelper::GetInterface( mpRepr ); + if( xWindow.is() ) + xWindow->removeFocusListener( xListener ); + } +} + +void SAL_CALL SvxRectCtlAccessibleContext::grabFocus() throw( RuntimeException ) +{ + ::vos::OGuard aSolarGuard( Application::GetSolarMutex() ); + ::osl::MutexGuard aGuard( m_aMutex ); + + ThrowExceptionIfNotAlive(); + + mpRepr->GrabFocus(); +} + +Any SAL_CALL SvxRectCtlAccessibleContext::getAccessibleKeyBinding() throw( RuntimeException ) +{ + // here is no implementation, because here are no KeyBindings for every object + return Any(); +} + +sal_Int32 SvxRectCtlAccessibleContext::getForeground( ) + throw (::com::sun::star::uno::RuntimeException) +{ + ::vos::OGuard aSolarGuard( Application::GetSolarMutex() ); + ::osl::MutexGuard aGuard( m_aMutex ); + ThrowExceptionIfNotAlive(); + + return mpRepr->GetControlForeground().GetColor(); +} +sal_Int32 SvxRectCtlAccessibleContext::getBackground( ) + throw (::com::sun::star::uno::RuntimeException) +{ + ::vos::OGuard aSolarGuard( Application::GetSolarMutex() ); + ::osl::MutexGuard aGuard( m_aMutex ); + ThrowExceptionIfNotAlive(); + + return mpRepr->GetControlBackground().GetColor(); +} + +//===== XServiceInfo ======================================================== + +::rtl::OUString SAL_CALL SvxRectCtlAccessibleContext::getImplementationName( void ) throw( RuntimeException ) +{ + return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.comp.ui.SvxRectCtlAccessibleContext" ) ); +} + +sal_Bool SAL_CALL SvxRectCtlAccessibleContext::supportsService( const ::rtl::OUString& sServiceName ) throw( RuntimeException ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + // Iterate over all supported service names and return true if on of them + // matches the given name. + Sequence< ::rtl::OUString > aSupportedServices( getSupportedServiceNames() ); + int nLength = aSupportedServices.getLength(); + const ::rtl::OUString* pStr = aSupportedServices.getConstArray(); + + for( int i = nLength ; i ; --i, ++pStr ) + { + if( sServiceName == *pStr ) + return sal_True; + } + + return sal_False; +} + +Sequence< ::rtl::OUString > SAL_CALL SvxRectCtlAccessibleContext::getSupportedServiceNames( void ) throw( RuntimeException ) +{ + const ::rtl::OUString sServiceName( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.accessibility.AccessibleContext" ) ); + return Sequence< ::rtl::OUString >( &sServiceName, 1 ); +} + +//===== XTypeProvider ======================================================= + +Sequence< sal_Int8 > SAL_CALL SvxRectCtlAccessibleContext::getImplementationId( void ) throw( RuntimeException ) +{ + return getUniqueId(); +} + +//===== XAccessibleSelection ============================================= + +void SAL_CALL SvxRectCtlAccessibleContext::selectAccessibleChild( sal_Int32 nIndex ) throw( lang::IndexOutOfBoundsException, RuntimeException ) +{ + ::vos::OGuard aSolarGuard( Application::GetSolarMutex() ); + + ::osl::MutexGuard aGuard( m_aMutex ); + + checkChildIndex( nIndex ); + + ThrowExceptionIfNotAlive(); + + const ChildIndexToPointData* pData = IndexToPoint( nIndex, mbAngleMode ); + + DBG_ASSERT( pData, + "SvxRectCtlAccessibleContext::selectAccessibleChild(): this is an impossible state! Or at least should be..." ); + + // this does all wich is needed, including the change of the child's state! + mpRepr->SetActualRP( pData->ePoint ); +} + +sal_Bool SAL_CALL SvxRectCtlAccessibleContext::isAccessibleChildSelected( sal_Int32 nIndex ) throw( lang::IndexOutOfBoundsException, RuntimeException ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + checkChildIndex( nIndex ); + + return nIndex == mnSelectedChild; +} + +void SAL_CALL SvxRectCtlAccessibleContext::clearAccessibleSelection() throw( RuntimeException ) +{ + DBG_ASSERT( sal_False, "SvxRectCtlAccessibleContext::clearAccessibleSelection() is not possible!" ); +} + +void SAL_CALL SvxRectCtlAccessibleContext::selectAllAccessibleChildren() throw( RuntimeException ) +{ + // guard in selectAccessibleChild()! + + selectAccessibleChild( 0 ); // default per definition +} + +sal_Int32 SAL_CALL SvxRectCtlAccessibleContext::getSelectedAccessibleChildCount() throw( RuntimeException ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + return mnSelectedChild == NOCHILDSELECTED? 0 : 1; +} + +Reference< XAccessible > SAL_CALL SvxRectCtlAccessibleContext::getSelectedAccessibleChild( sal_Int32 nIndex ) + throw( lang::IndexOutOfBoundsException, RuntimeException ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + checkChildIndexOnSelection( nIndex ); + + return getAccessibleChild( mnSelectedChild ); +} + +void SAL_CALL SvxRectCtlAccessibleContext::deselectAccessibleChild( sal_Int32 /*nIndex*/ ) throw( lang::IndexOutOfBoundsException, RuntimeException ) +{ + ::rtl::OUString aMessage( RTL_CONSTASCII_USTRINGPARAM( "deselectAccessibleChild is not possible in this context" ) ); + + DBG_ASSERT( sal_False, "SvxRectCtlAccessibleContext::deselectAccessibleChild() is not possible!" ); + + throw lang::IndexOutOfBoundsException( aMessage, *this ); // never possible +} + +//===== internals ======================================================== + +void SvxRectCtlAccessibleContext::checkChildIndex( long nIndex ) throw( lang::IndexOutOfBoundsException ) +{ + if( nIndex < 0 || nIndex >= getAccessibleChildCount() ) + throw lang::IndexOutOfBoundsException(); +} + +void SvxRectCtlAccessibleContext::checkChildIndexOnSelection( long nIndex ) throw( lang::IndexOutOfBoundsException ) +{ + if( nIndex || mnSelectedChild == NOCHILDSELECTED ) + // in our case only for the first (0) _selected_ child this is a valid request + throw lang::IndexOutOfBoundsException(); +} + +void SvxRectCtlAccessibleContext::selectChild( long nNew ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if( nNew != mnSelectedChild ) + { + long nNumOfChilds = getAccessibleChildCount(); + if( nNew < nNumOfChilds ) + { // valid index + SvxRectCtlChildAccessibleContext* pChild; + if( mnSelectedChild != NOCHILDSELECTED ) + { // deselect old selected child if one is selected + pChild = mpChilds[ mnSelectedChild ]; + if( pChild ) + pChild->setStateChecked( sal_False ); + } + + // select new child + mnSelectedChild = nNew; + + if( nNew != NOCHILDSELECTED ) + { + pChild = mpChilds[ nNew ]; + if( pChild ) + pChild->setStateChecked( sal_True ); + } + } + else + mnSelectedChild = NOCHILDSELECTED; + } +} + +void SvxRectCtlAccessibleContext::selectChild( RECT_POINT eButton ) +{ + // no guard -> is done in next selectChild + selectChild( PointToIndex( eButton, mbAngleMode ) ); +} + +void SvxRectCtlAccessibleContext::setName( const ::rtl::OUString& rName ) +{ + Any aPreVal, aPostVal; + { + ::osl::MutexGuard aGuard( m_aMutex ); + + aPreVal <<= msName; + aPostVal <<= rName; + + msName = rName; + } + + const Reference< XInterface > xSource( *this ); + CommitChange( AccessibleEventObject( xSource, AccessibleEventId::NAME_CHANGED, aPreVal, aPostVal ) ); +} + +void SvxRectCtlAccessibleContext::setDescription( const ::rtl::OUString& rDescr ) +{ + Any aPreVal, aPostVal; + { + ::osl::MutexGuard aGuard( m_aMutex ); + + aPreVal <<= msDescription; + aPostVal <<= rDescr; + + msDescription = rDescr; + } + + const Reference< XInterface > xSource( *this ); + CommitChange( AccessibleEventObject( xSource, AccessibleEventId::DESCRIPTION_CHANGED, aPreVal, aPostVal ) ); +} + +void SvxRectCtlAccessibleContext::CommitChange( const AccessibleEventObject& rEvent ) +{ + if (mnClientId) + comphelper::AccessibleEventNotifier::addEvent( mnClientId, rEvent ); +} + +void SAL_CALL SvxRectCtlAccessibleContext::disposing() +{ + if( !rBHelper.bDisposed ) + { + { + ::osl::MutexGuard aGuard( m_aMutex ); + mpRepr = NULL; // object dies with representation + + SvxRectCtlChildAccessibleContext** p = mpChilds; + for( int i = MAX_NUM_OF_CHILDS ; i ; --i, ++p ) + { + SvxRectCtlChildAccessibleContext* pChild = *p; + if( pChild ) + { + pChild->dispose(); + pChild->release(); + *p = NULL; + } + } + + delete[] mpChilds; + mpChilds = NULL; + } + + { + ::osl::MutexGuard aGuard( m_aMutex ); + + // Send a disposing to all listeners. + if ( mnClientId ) + { + comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing( mnClientId, *this ); + mnClientId = 0; + } + + mxParent = Reference< XAccessible >(); + } + } +} + +Rectangle SvxRectCtlAccessibleContext::GetBoundingBoxOnScreen( void ) throw( RuntimeException ) +{ + ::vos::OGuard aSolarGuard( Application::GetSolarMutex() ); + ::osl::MutexGuard aGuard( m_aMutex ); + + ThrowExceptionIfNotAlive(); + + return Rectangle( mpRepr->GetParent()->OutputToScreenPixel( mpRepr->GetPosPixel() ), mpRepr->GetSizePixel() ); +} + +Rectangle SvxRectCtlAccessibleContext::GetBoundingBox( void ) throw( RuntimeException ) +{ + ::vos::OGuard aSolarGuard( Application::GetSolarMutex() ); + ::osl::MutexGuard aGuard( m_aMutex ); + + ThrowExceptionIfNotAlive(); + + return Rectangle( mpRepr->GetPosPixel(), mpRepr->GetSizePixel() ); +} + +Sequence< sal_Int8 > SvxRectCtlAccessibleContext::getUniqueId( void ) +{ + static OImplementationId* pId = 0; + if( !pId ) + { + MutexGuard aGuard( Mutex::getGlobalMutex() ); + if( !pId) + { + static OImplementationId aId; + pId = &aId; + } + } + return pId->getImplementationId(); +} + +void SvxRectCtlAccessibleContext::ThrowExceptionIfNotAlive( void ) throw( lang::DisposedException ) +{ + if( IsNotAlive() ) + throw lang::DisposedException(); +} + +// ------------------------------------------------------------------------------------------------- + + +DBG_NAME( SvxRectCtlChildAccessibleContext ) + + +SvxRectCtlChildAccessibleContext::SvxRectCtlChildAccessibleContext( + const Reference<XAccessible>& rxParent, + const Window& rParentWindow, + const ::rtl::OUString& rName, + const ::rtl::OUString& rDescription, + const Rectangle& rBoundingBox, + long nIndexInParent ) : + + SvxRectCtlChildAccessibleContext_Base( maMutex ), + msDescription( rDescription ), + msName( rName ), + mxParent(rxParent), + mpBoundingBox( new Rectangle( rBoundingBox ) ), + mrParentWindow( rParentWindow ), + mnClientId( 0 ), + mnIndexInParent( nIndexInParent ), + mbIsChecked( sal_False ) +{ + DBG_CTOR( SvxRectCtlChildAccessibleContext, NULL ); +} + + +SvxRectCtlChildAccessibleContext::~SvxRectCtlChildAccessibleContext() +{ + DBG_DTOR( SvxRectCtlChildAccessibleContext, NULL ); + + if( IsAlive() ) + { + osl_incrementInterlockedCount( &m_refCount ); + dispose(); // set mpRepr = NULL & release all childs + } +} + +//===== XAccessible ========================================================= + +Reference< XAccessibleContext> SAL_CALL SvxRectCtlChildAccessibleContext::getAccessibleContext( void ) throw( RuntimeException ) +{ + return this; +} + +//===== XAccessibleComponent ================================================ + +sal_Bool SAL_CALL SvxRectCtlChildAccessibleContext::containsPoint( const awt::Point& rPoint ) throw( RuntimeException ) +{ + // no guard -> done in getBounds() +// return GetBoundingBox().IsInside( VCLPoint( rPoint ) ); + return Rectangle( Point( 0, 0 ), GetBoundingBox().GetSize() ).IsInside( VCLPoint( rPoint ) ); +} + +Reference< XAccessible > SAL_CALL SvxRectCtlChildAccessibleContext::getAccessibleAtPoint( const awt::Point& /*rPoint*/ ) throw( RuntimeException ) +{ + return Reference< XAccessible >(); +} + +awt::Rectangle SAL_CALL SvxRectCtlChildAccessibleContext::getBounds() throw( RuntimeException ) +{ + // no guard -> done in getBoundingBox() + return AWTRectangle( GetBoundingBox() ); +} + +awt::Point SAL_CALL SvxRectCtlChildAccessibleContext::getLocation() throw( RuntimeException ) +{ + // no guard -> done in getBoundingBox() + return AWTPoint( GetBoundingBox().TopLeft() ); +} + +awt::Point SAL_CALL SvxRectCtlChildAccessibleContext::getLocationOnScreen() throw( RuntimeException ) +{ + // no guard -> done in getBoundingBoxOnScreen() + return AWTPoint( GetBoundingBoxOnScreen().TopLeft() ); +} + +awt::Size SAL_CALL SvxRectCtlChildAccessibleContext::getSize() throw( RuntimeException ) +{ + // no guard -> done in getBoundingBox() + return AWTSize( GetBoundingBox().GetSize() ); +} + +sal_Bool SAL_CALL SvxRectCtlChildAccessibleContext::isShowing() throw( RuntimeException ) +{ + return sal_True; +} + +sal_Bool SAL_CALL SvxRectCtlChildAccessibleContext::isVisible() throw( RuntimeException ) +{ + ::osl::MutexGuard aGuard( maMutex ); + + ThrowExceptionIfNotAlive(); + + return mxParent.is()? ( static_cast< SvxRectCtlAccessibleContext* >( mxParent.get() ) )->isVisible() : sal_False; +} + +sal_Bool SAL_CALL SvxRectCtlChildAccessibleContext::isFocusTraversable() throw( RuntimeException ) +{ + return sal_False; +} + +void SAL_CALL SvxRectCtlChildAccessibleContext::addFocusListener( const Reference< awt::XFocusListener >& /*xListener*/ ) + throw( RuntimeException ) +{ + OSL_ENSURE( false, "SvxRectCtlChildAccessibleContext::addFocusListener: not implemented" ); +} + +void SAL_CALL SvxRectCtlChildAccessibleContext::removeFocusListener( const Reference< awt::XFocusListener >& /*xListener*/ ) + throw (RuntimeException) +{ + OSL_ENSURE( false, "SvxRectCtlChildAccessibleContext::removeFocusListener: not implemented" ); +} + +void SAL_CALL SvxRectCtlChildAccessibleContext::grabFocus() throw( RuntimeException ) +{ +} + +Any SAL_CALL SvxRectCtlChildAccessibleContext::getAccessibleKeyBinding() throw( RuntimeException ) +{ + // here is no implementation, because here are no KeyBindings for every object + return Any(); +} +sal_Int32 SvxRectCtlChildAccessibleContext::getForeground( ) + throw (::com::sun::star::uno::RuntimeException) +{ + ::vos::OGuard aSolarGuard( Application::GetSolarMutex() ); + ::osl::MutexGuard aGuard( maMutex ); + ThrowExceptionIfNotAlive(); + return mrParentWindow.GetControlForeground().GetColor(); +} +sal_Int32 SvxRectCtlChildAccessibleContext::getBackground( ) + throw (::com::sun::star::uno::RuntimeException) +{ + ::vos::OGuard aSolarGuard( Application::GetSolarMutex() ); + ::osl::MutexGuard aGuard( maMutex ); + + ThrowExceptionIfNotAlive(); + return mrParentWindow.GetControlBackground().GetColor(); +} + +//===== XAccessibleContext ================================================== + +sal_Int32 SAL_CALL SvxRectCtlChildAccessibleContext::getAccessibleChildCount( void ) throw( RuntimeException ) +{ + return 0; +} + +Reference< XAccessible > SAL_CALL SvxRectCtlChildAccessibleContext::getAccessibleChild( sal_Int32 /*nIndex*/ ) throw ( RuntimeException, lang::IndexOutOfBoundsException ) +{ + throw lang::IndexOutOfBoundsException(); +} + +Reference< XAccessible > SAL_CALL SvxRectCtlChildAccessibleContext::getAccessibleParent( void ) throw( RuntimeException ) +{ + return mxParent; +} + +sal_Int32 SAL_CALL SvxRectCtlChildAccessibleContext::getAccessibleIndexInParent( void ) throw( RuntimeException ) +{ + return mnIndexInParent; +} + +sal_Int16 SAL_CALL SvxRectCtlChildAccessibleContext::getAccessibleRole( void ) throw( RuntimeException ) +{ + return AccessibleRole::RADIO_BUTTON; +} + +::rtl::OUString SAL_CALL SvxRectCtlChildAccessibleContext::getAccessibleDescription( void ) throw( RuntimeException ) +{ + ::osl::MutexGuard aGuard( maMutex ); + return msDescription; +} + +::rtl::OUString SAL_CALL SvxRectCtlChildAccessibleContext::getAccessibleName( void ) throw( RuntimeException ) +{ + ::osl::MutexGuard aGuard( maMutex ); + return msName; +} + +/** Return empty reference to indicate that the relation set is not + supported. +*/ +Reference<XAccessibleRelationSet> SAL_CALL SvxRectCtlChildAccessibleContext::getAccessibleRelationSet( void ) throw( RuntimeException ) +{ + return Reference< XAccessibleRelationSet >(); +} + +Reference< XAccessibleStateSet > SAL_CALL SvxRectCtlChildAccessibleContext::getAccessibleStateSet( void ) throw( RuntimeException ) +{ + ::osl::MutexGuard aGuard( maMutex ); + utl::AccessibleStateSetHelper* pStateSetHelper = new utl::AccessibleStateSetHelper; + + if( IsAlive() ) + { + if( mbIsChecked ) + { + pStateSetHelper->AddState( AccessibleStateType::CHECKED ); +// pStateSetHelper->AddState( AccessibleStateType::SELECTED ); + } + + pStateSetHelper->AddState( AccessibleStateType::ENABLED ); + pStateSetHelper->AddState( AccessibleStateType::SENSITIVE ); + pStateSetHelper->AddState( AccessibleStateType::OPAQUE ); + pStateSetHelper->AddState( AccessibleStateType::SELECTABLE ); + pStateSetHelper->AddState( AccessibleStateType::SHOWING ); + pStateSetHelper->AddState( AccessibleStateType::VISIBLE ); + } + else + pStateSetHelper->AddState( AccessibleStateType::DEFUNC ); + + return pStateSetHelper; +} + +lang::Locale SAL_CALL SvxRectCtlChildAccessibleContext::getLocale( void ) throw( IllegalAccessibleComponentStateException, RuntimeException ) +{ + ::osl::MutexGuard aGuard( maMutex ); + if( mxParent.is() ) + { + Reference< XAccessibleContext > xParentContext( mxParent->getAccessibleContext() ); + if( xParentContext.is() ) + return xParentContext->getLocale(); + } + + // No locale and no parent. Therefore throw exception to indicate this + // cluelessness. + throw IllegalAccessibleComponentStateException(); +} + +void SAL_CALL SvxRectCtlChildAccessibleContext::addEventListener( const Reference< XAccessibleEventListener >& xListener ) + throw( RuntimeException ) +{ + if (xListener.is()) + { + ::osl::MutexGuard aGuard( maMutex ); + if (!mnClientId) + mnClientId = comphelper::AccessibleEventNotifier::registerClient( ); + comphelper::AccessibleEventNotifier::addEventListener( mnClientId, xListener ); + } +} + + + + +void SAL_CALL SvxRectCtlChildAccessibleContext::removeEventListener( const Reference< XAccessibleEventListener >& xListener ) + throw( RuntimeException ) +{ + if (xListener.is()) + { + ::osl::MutexGuard aGuard( maMutex ); + + sal_Int32 nListenerCount = comphelper::AccessibleEventNotifier::removeEventListener( mnClientId, xListener ); + if ( !nListenerCount ) + { + // no listeners anymore + // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client), + // and at least to us not firing any events anymore, in case somebody calls + // NotifyAccessibleEvent, again + comphelper::AccessibleEventNotifier::revokeClient( mnClientId ); + mnClientId = 0; + } + } +} + +//===== XAccessibleValue ================================================ + +Any SAL_CALL SvxRectCtlChildAccessibleContext::getCurrentValue() throw( RuntimeException ) +{ + ThrowExceptionIfNotAlive(); + + Any aRet; + aRet <<= ( mbIsChecked? 1.0 : 0.0 ); + return aRet; +} + +sal_Bool SAL_CALL SvxRectCtlChildAccessibleContext::setCurrentValue( const Any& /*aNumber*/ ) throw( RuntimeException ) +{ + return sal_False; +} + +Any SAL_CALL SvxRectCtlChildAccessibleContext::getMaximumValue() throw( RuntimeException ) +{ + Any aRet; + aRet <<= 1.0; + return aRet; +} + +Any SAL_CALL SvxRectCtlChildAccessibleContext::getMinimumValue() throw( RuntimeException ) +{ + Any aRet; + aRet <<= 0.0; + return aRet; +} + +//===== XServiceInfo ======================================================== + +::rtl::OUString SAL_CALL SvxRectCtlChildAccessibleContext::getImplementationName( void ) throw( RuntimeException ) +{ + return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.comp.ui.SvxRectCtlChildAccessibleContext" ) ); +} + +sal_Bool SAL_CALL SvxRectCtlChildAccessibleContext::supportsService( const ::rtl::OUString& sServiceName ) throw( RuntimeException ) +{ + // Iterate over all supported service names and return true if on of them + // matches the given name. + ::osl::MutexGuard aGuard( maMutex ); + Sequence< ::rtl::OUString > aSupportedServices ( getSupportedServiceNames() ); + int nLength = aSupportedServices.getLength(); + for( int i = 0 ; i < nLength; ++i ) + { + if( sServiceName == aSupportedServices[ i ] ) + return sal_True; + } + + return sal_False; +} + +Sequence< ::rtl::OUString > SAL_CALL SvxRectCtlChildAccessibleContext::getSupportedServiceNames( void ) throw( RuntimeException ) +{ + const ::rtl::OUString sServiceName (RTL_CONSTASCII_USTRINGPARAM ("com.sun.star.accessibility.AccessibleContext")); + return Sequence< ::rtl::OUString >( &sServiceName, 1 ); +} + +//===== XTypeProvider ======================================================= + +Sequence< sal_Int8 > SAL_CALL SvxRectCtlChildAccessibleContext::getImplementationId( void ) throw( RuntimeException ) +{ + static OImplementationId* pId = 0; + if( !pId ) + { + MutexGuard aGuard( Mutex::getGlobalMutex() ); + if( !pId) + { + static OImplementationId aId; + pId = &aId; + } + } + return pId->getImplementationId(); +} + +//===== internal ============================================================ + +void SvxRectCtlChildAccessibleContext::CommitChange( const AccessibleEventObject& rEvent ) +{ + if (mnClientId) + comphelper::AccessibleEventNotifier::addEvent( mnClientId, rEvent ); +} + +void SAL_CALL SvxRectCtlChildAccessibleContext::disposing() +{ + if( !rBHelper.bDisposed ) + { + ::osl::MutexGuard aGuard( maMutex ); + + // Send a disposing to all listeners. + if ( mnClientId ) + { + comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing( mnClientId, *this ); + mnClientId = 0; + } + + mxParent = Reference< XAccessible >(); + + delete mpBoundingBox; + } +} + +void SvxRectCtlChildAccessibleContext::ThrowExceptionIfNotAlive( void ) throw( lang::DisposedException ) +{ + if( IsNotAlive() ) + throw lang::DisposedException(); +} + +Rectangle SvxRectCtlChildAccessibleContext::GetBoundingBoxOnScreen( void ) throw( RuntimeException ) +{ + ::osl::MutexGuard aGuard( maMutex ); + + // no ThrowExceptionIfNotAlive() because its done in GetBoundingBox() + Rectangle aRect( GetBoundingBox() ); + + return Rectangle( mrParentWindow.OutputToScreenPixel( aRect.TopLeft() ), aRect.GetSize() ); +} + +Rectangle SvxRectCtlChildAccessibleContext::GetBoundingBox( void ) throw( RuntimeException ) +{ + // no guard neccessary, because no one changes mpBoundingBox after creating it + ThrowExceptionIfNotAlive(); + + return *mpBoundingBox; +} + +void SvxRectCtlChildAccessibleContext::setStateChecked( sal_Bool bChecked ) +{ + if( mbIsChecked != bChecked ) + { + mbIsChecked = bChecked; + + const Reference< XInterface > xSource( *this ); + + Any aOld; + Any aNew; + Any& rMod = bChecked? aNew : aOld; + + rMod <<= AccessibleStateType::CHECKED; + + CommitChange( AccessibleEventObject( xSource, AccessibleEventId::STATE_CHANGED, aNew, aOld ) ); + } +} + |