summaryrefslogtreecommitdiffstats
path: root/svx/source/accessibility
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
commit267c6f2ac71f92999e969232431ba04678e7437e (patch)
tree358c9467650e1d0a1d7227a21dac2e3d08b622b2 /svx/source/accessibility
parentInitial commit. (diff)
downloadlibreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz
libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'svx/source/accessibility')
-rw-r--r--svx/source/accessibility/AccessibleControlShape.cxx851
-rw-r--r--svx/source/accessibility/AccessibleEmptyEditSource.cxx332
-rw-r--r--svx/source/accessibility/AccessibleEmptyEditSource.hxx93
-rw-r--r--svx/source/accessibility/AccessibleFrameSelector.cxx381
-rw-r--r--svx/source/accessibility/AccessibleGraphicShape.cxx144
-rw-r--r--svx/source/accessibility/AccessibleOLEShape.cxx172
-rw-r--r--svx/source/accessibility/AccessibleShape.cxx1294
-rw-r--r--svx/source/accessibility/AccessibleShapeInfo.cxx64
-rw-r--r--svx/source/accessibility/AccessibleShapeTreeInfo.cxx120
-rw-r--r--svx/source/accessibility/AccessibleTextEventQueue.cxx92
-rw-r--r--svx/source/accessibility/AccessibleTextEventQueue.hxx90
-rw-r--r--svx/source/accessibility/AccessibleTextHelper.cxx1795
-rw-r--r--svx/source/accessibility/ChildrenManager.cxx115
-rw-r--r--svx/source/accessibility/ChildrenManagerImpl.cxx1123
-rw-r--r--svx/source/accessibility/ChildrenManagerImpl.hxx497
-rw-r--r--svx/source/accessibility/DescriptionGenerator.cxx188
-rw-r--r--svx/source/accessibility/GraphCtlAccessibleContext.cxx777
-rw-r--r--svx/source/accessibility/ShapeTypeHandler.cxx306
-rw-r--r--svx/source/accessibility/SvxShapeTypes.cxx165
-rw-r--r--svx/source/accessibility/charmapacc.cxx586
-rw-r--r--svx/source/accessibility/lookupcolorname.cxx121
-rw-r--r--svx/source/accessibility/lookupcolorname.hxx57
-rw-r--r--svx/source/accessibility/svxpixelctlaccessiblecontext.cxx444
-rw-r--r--svx/source/accessibility/svxrectctaccessiblecontext.cxx635
24 files changed, 10442 insertions, 0 deletions
diff --git a/svx/source/accessibility/AccessibleControlShape.cxx b/svx/source/accessibility/AccessibleControlShape.cxx
new file mode 100644
index 0000000000..3a6605fda6
--- /dev/null
+++ b/svx/source/accessibility/AccessibleControlShape.cxx
@@ -0,0 +1,851 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svx/AccessibleControlShape.hxx>
+#include <svx/AccessibleShapeInfo.hxx>
+#include <DescriptionGenerator.hxx>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/drawing/XControlShape.hpp>
+#include <com/sun/star/accessibility/AccessibleRelationType.hpp>
+#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/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/reflection/ProxyFactory.hpp>
+#include <com/sun/star/util/XModeChangeBroadcaster.hpp>
+#include <com/sun/star/container/XContainer.hpp>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/property.hxx>
+#include <unotools/accessiblerelationsethelper.hxx>
+#include <svx/IAccessibleParent.hxx>
+#include <svx/svdouno.hxx>
+#include <svx/ShapeTypeHandler.hxx>
+#include <svx/SvxShapeTypes.hxx>
+#include <comphelper/accessiblewrapper.hxx>
+#include <svx/svdview.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/strings.hrc>
+#include <vcl/svapp.hxx>
+#include <vcl/window.hxx>
+#include <sal/log.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+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
+{
+ constexpr OUString NAME_PROPERTY_NAME = u"Name"_ustr;
+ constexpr OUString DESC_PROPERTY_NAME = u"HelpText"_ustr;
+ constexpr OUString LABEL_PROPERTY_NAME = u"Label"_ustr;
+ constexpr OUString LABEL_CONTROL_PROPERTY_NAME = u"LabelControl"_ustr;
+
+ // return the property which should be used as AccessibleName
+ OUString lcl_getPreferredAccNameProperty( const Reference< XPropertySetInfo >& _rxPSI )
+ {
+ if ( _rxPSI.is() && _rxPSI->hasPropertyByName( LABEL_PROPERTY_NAME ) )
+ return LABEL_PROPERTY_NAME;
+ else
+ return NAME_PROPERTY_NAME;
+ }
+
+ // determines whether or not a state which belongs to the inner context needs to be forwarded to the "composed"
+ // context
+ bool isComposedState( const sal_Int64 _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
+ bool isAliveMode( const Reference< XControl >& _rxControl )
+ {
+ OSL_PRECOND( _rxControl.is(), "AccessibleControlShape::isAliveMode: invalid control" );
+ return _rxControl.is() && !_rxControl->isDesignMode();
+ }
+}
+
+AccessibleControlShape::AccessibleControlShape (
+ const AccessibleShapeInfo& rShapeInfo,
+ const AccessibleShapeTreeInfo& rShapeTreeInfo)
+ : AccessibleShape (rShapeInfo, rShapeTreeInfo)
+ , m_bListeningForName( false )
+ , m_bListeningForDesc( false )
+ , m_bMultiplexingStates( false )
+ , m_bDisposeNativeContext( false )
+ , m_bWaitingForControl( false )
+{
+ m_pChildManager = new comphelper::OWrappedAccessibleChildrenManager( comphelper::getProcessComponentContext() );
+
+ osl_atomic_increment( &m_refCount );
+ {
+ m_pChildManager->setOwningAccessible( this );
+ }
+ osl_atomic_decrement( &m_refCount );
+}
+
+AccessibleControlShape::~AccessibleControlShape()
+{
+ m_pChildManager.clear();
+
+ if ( m_xControlContextProxy.is() )
+ m_xControlContextProxy->setDelegator( nullptr );
+ 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
+}
+
+namespace {
+ Reference< XContainer > lcl_getControlContainer( const OutputDevice* _pWin, const SdrView* _pView )
+ {
+ Reference< XContainer > xReturn;
+ DBG_ASSERT( _pView, "lcl_getControlContainer: invalid view!" );
+ if ( _pView && _pView->GetSdrPageView())
+ {
+ xReturn.set(_pView->GetSdrPageView()->GetControlContainer( *_pWin ), css::uno::UNO_QUERY);
+ }
+ 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 mechanism
+ // 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...
+
+ // get the control which belongs to our model (relative to our view)
+ const vcl::Window* pViewWindow = maShapeTreeInfo.GetWindow();
+ SdrUnoObj* pUnoObjectImpl = dynamic_cast<SdrUnoObj*>(SdrObject::getSdrObjectFromXShape(mxShape));
+ 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->GetOutDev() );
+
+ 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->GetOutDev(), maShapeTreeInfo.GetSdrView() );
+ OSL_ENSURE( xControlContainer.is(), "AccessibleControlShape::Init: unable to find my ControlContainer!" );
+ if ( xControlContainer.is() )
+ {
+ xControlContainer->addContainerListener( this );
+ m_bWaitingForControl = 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 ) )
+ {
+ sal_Int64 nStates( getAccessibleStateSet( ) );
+ m_pChildManager->setTransientChildren( nStates & AccessibleStateType::MANAGES_DESCENDANTS );
+ }
+
+ // finally, aggregate a proxy for the control context
+ // first a factory for the proxy
+ Reference< XProxyFactory > xFactory = ProxyFactory::create( comphelper::getProcessComponentContext() );
+ // then the proxy itself
+ if ( xNativeControlContext.is() )
+ {
+ m_xControlContextProxy = xFactory->createProxy( xNativeControlContext );
+ m_xControlContextTypeAccess.set( xNativeControlContext, UNO_QUERY_THROW );
+ m_xControlContextComponent.set( xNativeControlContext, UNO_QUERY_THROW );
+
+ // aggregate the proxy
+ osl_atomic_increment( &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 unless the delegator of the proxy has been reset, too!
+ m_xControlContextProxy->setDelegator( *this );
+ }
+ osl_atomic_decrement( &m_refCount );
+
+ m_bDisposeNativeContext = 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_FAIL( "AccessibleControlShape::Init: could not \"aggregate\" the controls XAccessibleContext!" );
+ }
+}
+
+void SAL_CALL AccessibleControlShape::grabFocus()
+{
+ 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();
+ }
+}
+
+OUString SAL_CALL AccessibleControlShape::getImplementationName()
+{
+ return "com.sun.star.comp.accessibility.AccessibleControlShape";
+}
+
+OUString AccessibleControlShape::CreateAccessibleBaseName()
+{
+ OUString sName;
+
+ ShapeTypeId nShapeType = ShapeTypeHandler::Instance().GetTypeId (mxShape);
+ switch (nShapeType)
+ {
+ case DRAWING_CONTROL:
+ sName = "ControlShape";
+ break;
+ default:
+ sName = "UnknownAccessibleControlShape";
+ if (mxShape.is())
+ sName += ": " + mxShape->getShapeType();
+ }
+
+ return sName;
+}
+
+OUString
+ AccessibleControlShape::CreateAccessibleDescription()
+{
+ 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
+ OUString sDesc( getControlModelStringProperty( DESC_PROPERTY_NAME ) );
+ if ( sDesc.isEmpty() )
+ { // no -> use the default
+ aDG.Initialize (STR_ObjNameSingulUno);
+ aDG.AddProperty ("ControlBackground", DescriptionGenerator::PropertyType::Color);
+ aDG.AddProperty ( "ControlBorder", DescriptionGenerator::PropertyType::Integer);
+ }
+ // ensure that we are listening to the Name property
+ m_bListeningForDesc = ensureListeningState( m_bListeningForDesc, true, DESC_PROPERTY_NAME );
+ }
+ break;
+
+ default:
+ aDG.Initialize (u"Unknown accessible control shape");
+ if (mxShape.is())
+ {
+ aDG.AppendString (u"service name=");
+ aDG.AppendString (mxShape->getShapeType());
+ }
+ }
+
+ return aDG();
+}
+
+IMPLEMENT_FORWARD_REFCOUNT( AccessibleControlShape, AccessibleShape )
+IMPLEMENT_GET_IMPLEMENTATION_ID( AccessibleControlShape )
+
+void SAL_CALL AccessibleControlShape::propertyChange( const PropertyChangeEvent& _rEvent )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ // check if it is the name or the description
+ if ( _rEvent.PropertyName == NAME_PROPERTY_NAME
+ || _rEvent.PropertyName == LABEL_PROPERTY_NAME )
+ {
+ SetAccessibleName(
+ CreateAccessibleName(),
+ AccessibleContextBase::AutomaticallyCreated);
+ }
+ else if ( _rEvent.PropertyName == DESC_PROPERTY_NAME )
+ {
+ SetAccessibleDescription(
+ CreateAccessibleDescription(),
+ AccessibleContextBase::AutomaticallyCreated);
+ }
+#if OSL_DEBUG_LEVEL > 0
+ else
+ {
+ OSL_FAIL( "AccessibleControlShape::propertyChange: where did this come from?" );
+ }
+#endif
+}
+
+Any SAL_CALL AccessibleControlShape::queryInterface( const Type& _rType )
+{
+ 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()
+{
+ Sequence< Type > aShapeTypes = AccessibleShape::getTypes();
+ Sequence< Type > aOwnTypes = AccessibleControlShape_Base::getTypes();
+
+ Sequence< Type > aAggregateTypes;
+ if ( m_xControlContextTypeAccess.is() )
+ aAggregateTypes = m_xControlContextTypeAccess->getTypes();
+
+ // remove duplicates
+ return comphelper::combineSequences(comphelper::concatSequences( aShapeTypes, aOwnTypes), aAggregateTypes );
+}
+
+void SAL_CALL AccessibleControlShape::notifyEvent( const AccessibleEventObject& _rEvent )
+{
+ if ( AccessibleEventId::STATE_CHANGED == _rEvent.EventId )
+ {
+ // multiplex this change
+ sal_Int64 nLostState( 0 ), nGainedState( 0 );
+ _rEvent.OldValue >>= nLostState;
+ _rEvent.NewValue >>= nGainedState;
+
+ // don't multiplex states which the inner context is not responsible for
+ if ( isComposedState( nLostState ) )
+ AccessibleShape::ResetState( nLostState );
+
+ if ( isComposedState( nGainedState ) )
+ AccessibleShape::SetState( nGainedState );
+ }
+ else
+ {
+ AccessibleEventObject aTranslatedEvent( _rEvent );
+
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ // 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)
+{
+ // did it come from our inner context (the real one, not it's proxy!)?
+ SAL_INFO("sw.uno", "AccessibleControlShape::modeChanged");
+ Reference<XControl> xSource(rSource.Source, UNO_QUERY); // for faster compare
+ if(xSource.get() != m_xUnoControl.get())
+ {
+ SAL_WARN("sw.uno", "AccessibleControlShape::modeChanged: where did this come from?");
+ return;
+ }
+ SolarMutexGuard g;
+ // 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.
+ const bool bReplaced = mpParent->ReplaceChild(this, mxShape, 0, maShapeTreeInfo);
+ SAL_WARN_IF(!bReplaced, "sw.uno", "AccessibleControlShape::modeChanged: replacing ourselves away did fail");
+}
+
+void SAL_CALL AccessibleControlShape::disposing (const EventObject& _rSource)
+{
+ AccessibleShape::disposing( _rSource );
+}
+
+bool AccessibleControlShape::ensureListeningState(
+ const bool _bCurrentlyListening, const bool _bNeedNewListening,
+ const 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_FAIL( "AccessibleControlShape::ensureListeningState: this property does not exist at this model!" );
+ }
+ catch( const Exception& )
+ {
+ OSL_FAIL( "AccessibleControlShape::ensureListeningState: could not change the listening state!" );
+ }
+
+ return _bNeedNewListening;
+}
+
+sal_Int64 SAL_CALL AccessibleControlShape::getAccessibleChildCount( )
+{
+ 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_Int64 i )
+{
+ 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::getAccessibleChild: 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_Int64 nChildIndex = -1;
+ Reference< XAccessibleContext > xContext;
+ if ( xChild.is() )
+ xContext = xChild->getAccessibleContext( );
+ if ( xContext.is() )
+ nChildIndex = xContext->getAccessibleIndexInParent( );
+ SAL_WARN_IF( nChildIndex != i, "svx", "AccessibleControlShape::getAccessibleChild: index mismatch,"
+ " nChildIndex=" << nChildIndex << " vs i=" << i );
+#endif
+ return xChild;
+}
+
+Reference< XAccessibleRelationSet > SAL_CALL AccessibleControlShape::getAccessibleRelationSet( )
+{
+ rtl::Reference<utl::AccessibleRelationSetHelper> pRelationSetHelper = new utl::AccessibleRelationSetHelper;
+ ensureControlModelAccess();
+ AccessibleControlShape* pCtlAccShape = GetLabeledByControlShape();
+ if(pCtlAccShape)
+ {
+ Reference < XAccessible > xAcc (pCtlAccShape->getAccessibleContext(), UNO_QUERY);
+
+ css::uno::Sequence< css::uno::Reference< css::uno::XInterface > > aSequence { xAcc };
+ if( getAccessibleRole() == AccessibleRole::RADIO_BUTTON )
+ {
+ pRelationSetHelper->AddRelation( AccessibleRelation( AccessibleRelationType::MEMBER_OF, aSequence ) );
+ }
+ else
+ {
+ pRelationSetHelper->AddRelation( AccessibleRelation( AccessibleRelationType::LABELED_BY, aSequence ) );
+ }
+ }
+ return pRelationSetHelper;
+}
+
+OUString AccessibleControlShape::CreateAccessibleName()
+{
+ ensureControlModelAccess();
+
+ OUString sName;
+ sal_Int16 aAccessibleRole = getAccessibleRole();
+ if ( aAccessibleRole != AccessibleRole::SHAPE
+ && aAccessibleRole != AccessibleRole::RADIO_BUTTON )
+ {
+ AccessibleControlShape* pCtlAccShape = GetLabeledByControlShape();
+ if(pCtlAccShape)
+ {
+ sName = pCtlAccShape->CreateAccessibleName();
+ }
+ }
+
+ if (sName.isEmpty())
+ {
+ // check if we can obtain the "Name" resp. "Label" property from the model
+ const OUString& rAccNameProperty = lcl_getPreferredAccNameProperty( m_xModelPropsMeta );
+ 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, true, lcl_getPreferredAccNameProperty( m_xModelPropsMeta ) );
+
+ return sName;
+}
+
+void SAL_CALL AccessibleControlShape::disposing()
+{
+ // ensure we're not listening
+ m_bListeningForName = ensureListeningState( m_bListeningForName, false, lcl_getPreferredAccNameProperty( m_xModelPropsMeta ) );
+ m_bListeningForDesc = ensureListeningState( m_bListeningForDesc, false, DESC_PROPERTY_NAME );
+
+ 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_FAIL( "AccessibleControlShape::disposing: this should never happen!" );
+ if (auto pWindow = maShapeTreeInfo.GetWindow())
+ {
+ Reference< XContainer > xContainer = lcl_getControlContainer( pWindow->GetOutDev(), maShapeTreeInfo.GetSdrView() );
+ if ( xContainer.is() )
+ {
+ m_bWaitingForControl = false;
+ xContainer->removeContainerListener( this );
+ }
+ }
+ }
+
+ // forward the disposal 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 a 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 = false;
+ }
+
+ m_xUnoControl.clear();
+
+ // let the base do its stuff
+ AccessibleShape::disposing();
+}
+
+bool AccessibleControlShape::ensureControlModelAccess()
+{
+ if ( m_xControlModel.is() )
+ return true;
+
+ try
+ {
+ Reference< XControlShape > xShape( mxShape, UNO_QUERY );
+ if ( xShape.is() )
+ m_xControlModel.set(xShape->getControl(), css::uno::UNO_QUERY);
+
+ if ( m_xControlModel.is() )
+ m_xModelPropsMeta = m_xControlModel->getPropertySetInfo();
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "AccessibleControlShape::ensureControlModelAccess" );
+ }
+
+ 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->addAccessibleEventListener( this );
+ m_bMultiplexingStates = 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->removeAccessibleEventListener( this );
+ m_bMultiplexingStates = false;
+ }
+}
+
+OUString AccessibleControlShape::getControlModelStringProperty( const OUString& _rPropertyName ) const
+{
+ 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& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "OAccessibleControlContext::getModelStringProperty" );
+ }
+ 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
+
+bool AccessibleControlShape::SetState( sal_Int64 _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;
+
+ // 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
+ mnStateSet &= ~AccessibleStateType::ENABLED; // this is controlled by the UNO-control
+ mnStateSet &= ~AccessibleStateType::SENSITIVE; // this is controlled by the UNO-control
+ mnStateSet &= ~AccessibleStateType::FOCUSABLE; // this is controlled by the UNO-control
+ mnStateSet &= ~AccessibleStateType::SELECTABLE; // this does not hold for an alive UNO-control
+
+ // get my inner context
+ Reference< XAccessibleContext > xInnerContext( m_aControlContext );
+ OSL_PRECOND( xInnerContext.is(), "AccessibleControlShape::initializeComposedState: no inner context!" );
+ if ( !xInnerContext.is() )
+ return;
+
+ // get all states of the inner context
+ sal_Int64 nInnerStates( xInnerContext->getAccessibleStateSet() );
+
+ // look which one are to be propagated to the composed context
+ for ( int i = 0; i < 63; ++i )
+ {
+ sal_Int64 nState = sal_Int64(1) << i;
+ if ( (nInnerStates & nState) && isComposedState( nState ) )
+ {
+ mnStateSet |= nState;
+ }
+ }
+}
+
+void SAL_CALL AccessibleControlShape::elementInserted( const css::container::ContainerEvent& _rEvent )
+{
+ 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 && xMyModelNormalized) )
+ return;
+
+ // 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 = 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, 0, maShapeTreeInfo ) );
+}
+
+void SAL_CALL AccessibleControlShape::elementRemoved( const css::container::ContainerEvent& )
+{
+ // not interested in
+}
+
+void SAL_CALL AccessibleControlShape::elementReplaced( const css::container::ContainerEvent& )
+{
+ // not interested in
+}
+
+AccessibleControlShape* AccessibleControlShape::GetLabeledByControlShape( )
+{
+ if(m_xControlModel.is())
+ {
+ Any sCtlLabelBy;
+ // get the "label by" property value of the control
+ if (::comphelper::hasProperty(LABEL_CONTROL_PROPERTY_NAME, m_xControlModel))
+ {
+ sCtlLabelBy = m_xControlModel->getPropertyValue(LABEL_CONTROL_PROPERTY_NAME);
+ if( sCtlLabelBy.hasValue() )
+ {
+ Reference< XPropertySet > xAsSet (sCtlLabelBy, UNO_QUERY);
+ AccessibleControlShape* pCtlAccShape = mpParent->GetAccControlShapeFromModel(xAsSet.get());
+ return pCtlAccShape;
+ }
+ }
+ }
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/AccessibleEmptyEditSource.cxx b/svx/source/accessibility/AccessibleEmptyEditSource.cxx
new file mode 100644
index 0000000000..7ff9ec6610
--- /dev/null
+++ b/svx/source/accessibility/AccessibleEmptyEditSource.cxx
@@ -0,0 +1,332 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+// Global header
+
+
+#include <memory>
+#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
+{
+ namespace {
+
+ /** 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 OutputDevice& rViewWindow );
+
+ // from the SvxEditSource interface
+ SvxTextForwarder* GetTextForwarder() override;
+ SvxViewForwarder* GetViewForwarder() override;
+ SvxEditViewForwarder* GetEditViewForwarder( bool bCreate = false ) override;
+
+ std::unique_ptr<SvxEditSource> Clone() const override;
+
+ void UpdateData() override;
+
+ SfxBroadcaster& GetBroadcaster() const override;
+
+ 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() {}
+
+ // SvxEditSource
+ SvxTextForwarder* GetTextForwarder() override { return this; }
+ SvxViewForwarder* GetViewForwarder() override { return this; }
+ std::unique_ptr<SvxEditSource> Clone() const override { return nullptr; }
+ void UpdateData() override {}
+ SfxBroadcaster& GetBroadcaster() const override { return *const_cast<AccessibleEmptyEditSource_Impl*>(this); }
+
+ // SvxTextForwarder
+ sal_Int32 GetParagraphCount() const override { return 1; }
+ sal_Int32 GetTextLen( sal_Int32 /*nParagraph*/ ) const override { return 0; }
+ OUString GetText( const ESelection& /*rSel*/ ) const override { return OUString(); }
+ SfxItemSet GetAttribs( const ESelection& /*rSel*/, EditEngineAttribs /*nOnlyHardAttrib*/ = EditEngineAttribs::All ) const override
+ {
+ // 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( sal_Int32 /*nPara*/ ) const override { return GetAttribs(ESelection()); }
+ void SetParaAttribs( sal_Int32 /*nPara*/, const SfxItemSet& /*rSet*/ ) override {}
+ void RemoveAttribs( const ESelection& /*rSelection*/ ) override {}
+ void GetPortions( sal_Int32 /*nPara*/, std::vector<sal_Int32>& /*rList*/ ) const override {}
+
+ OUString GetStyleSheet(sal_Int32 /*nPara*/) const override { return OUString(); }
+ void SetStyleSheet(sal_Int32 /*nPara*/, const OUString& /*rStyleName*/) override {}
+
+ SfxItemState GetItemState( const ESelection& /*rSel*/, sal_uInt16 /*nWhich*/ ) const override { return SfxItemState::UNKNOWN; }
+ SfxItemState GetItemState( sal_Int32 /*nPara*/, sal_uInt16 /*nWhich*/ ) const override { return SfxItemState::UNKNOWN; }
+
+ SfxItemPool* GetPool() const override { return nullptr; }
+
+ void QuickInsertText( const OUString& /*rText*/, const ESelection& /*rSel*/ ) override {}
+ void QuickInsertField( const SvxFieldItem& /*rFld*/, const ESelection& /*rSel*/ ) override {}
+ void QuickSetAttribs( const SfxItemSet& /*rSet*/, const ESelection& /*rSel*/ ) override {}
+ void QuickInsertLineBreak( const ESelection& /*rSel*/ ) override {}
+
+ const SfxItemSet * GetEmptyItemSetPtr() override { return nullptr; }
+
+ void AppendParagraph() override {}
+ sal_Int32 AppendTextPortion( sal_Int32 /*nPara*/, const OUString & /*rText*/, const SfxItemSet & /*rSet*/ ) override { return 0; }
+
+ //XTextCopy
+ void CopyText(const SvxTextForwarder& ) override {}
+
+ OUString CalcFieldValue( const SvxFieldItem& /*rField*/, sal_Int32 /*nPara*/, sal_Int32 /*nPos*/, std::optional<Color>& /*rpTxtColor*/, std::optional<Color>& /*rpFldColor*/, std::optional<FontLineStyle>& /*rpFldLineStyle*/ ) override
+ {
+ return OUString();
+ }
+ void FieldClicked( const SvxFieldItem& ) override {}
+
+ bool IsValid() const override { return true; }
+
+ LanguageType GetLanguage( sal_Int32, sal_Int32 ) const override { return LANGUAGE_DONTKNOW; }
+ sal_Int32 GetFieldCount( sal_Int32 ) const override { return 0; }
+ EFieldInfo GetFieldInfo( sal_Int32, sal_uInt16 ) const override { return EFieldInfo(); }
+ EBulletInfo GetBulletInfo( sal_Int32 ) const override { return EBulletInfo(); }
+ tools::Rectangle GetCharBounds( sal_Int32, sal_Int32 ) const override { return tools::Rectangle(); }
+ tools::Rectangle GetParaBounds( sal_Int32 ) const override { return tools::Rectangle(); }
+ MapMode GetMapMode() const override { return MapMode(); }
+ OutputDevice* GetRefDevice() const override { return nullptr; }
+ bool GetIndexAtPoint( const Point&, sal_Int32&, sal_Int32& ) const override { return false; }
+ bool GetWordIndices( sal_Int32, sal_Int32, sal_Int32&, sal_Int32& ) const override { return false; }
+ bool GetAttributeRun( sal_Int32&, sal_Int32&, sal_Int32, sal_Int32, bool ) const override { return false; }
+ sal_Int32 GetLineCount( sal_Int32 nPara ) const override { return nPara == 0 ? 1 : 0; }
+ sal_Int32 GetLineLen( sal_Int32, sal_Int32 ) const override { return 0; }
+ void GetLineBoundaries( /*out*/sal_Int32 & rStart, /*out*/sal_Int32 & rEnd, sal_Int32 /*nParagraph*/, sal_Int32 /*nLine*/ ) const override { rStart = rEnd = 0; }
+ sal_Int32 GetLineNumberAtIndex( sal_Int32 /*nPara*/, sal_Int32 /*nIndex*/ ) const override { 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 behaviour here (problem
+ // when that changes: via accessibility API, it would no
+ // longer be possible to enter text in previously empty
+ // shapes).
+ bool Delete( const ESelection& ) override { return false; }
+ bool InsertText( const OUString&, const ESelection& ) override { return false; }
+ bool QuickFormatDoc( bool ) override { return true; }
+ sal_Int16 GetDepth( sal_Int32 ) const override { return -1; }
+ bool SetDepth( sal_Int32, sal_Int16 ) override { return true; }
+
+ Point LogicToPixel( const Point& rPoint, const MapMode& /*rMapMode*/ ) const override { return rPoint; }
+ Point PixelToLogic( const Point& rPoint, const MapMode& /*rMapMode*/ ) const override { return rPoint; }
+
+ };
+
+ }
+
+ // Implementing AccessibleProxyEditSource_Impl
+
+
+ AccessibleProxyEditSource_Impl::AccessibleProxyEditSource_Impl( SdrObject& rObj,
+ SdrView& rView,
+ const OutputDevice& rViewWindow ) :
+ maEditSource( rObj, nullptr, rView, rViewWindow )
+ {
+ }
+
+ SvxTextForwarder* AccessibleProxyEditSource_Impl::GetTextForwarder()
+ {
+ return maEditSource.GetTextForwarder();
+ }
+
+ SvxViewForwarder* AccessibleProxyEditSource_Impl::GetViewForwarder()
+ {
+ return maEditSource.GetViewForwarder();
+ }
+
+ SvxEditViewForwarder* AccessibleProxyEditSource_Impl::GetEditViewForwarder( bool bCreate )
+ {
+ return maEditSource.GetEditViewForwarder( bCreate );
+ }
+
+ std::unique_ptr<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 OutputDevice& rViewWindow ) :
+ mpEditSource( new AccessibleEmptyEditSource_Impl() ),
+ mrObj(rObj),
+ mrView(rView),
+ mrViewWindow(rViewWindow),
+ mbEditSourceEmpty( true )
+ {
+ StartListening( mrObj.getSdrModelFromSdrObject() );
+ }
+
+ AccessibleEmptyEditSource::~AccessibleEmptyEditSource()
+ {
+ if( !mbEditSourceEmpty )
+ {
+ // deregister as listener
+ if (mpEditSource)
+ EndListening( mpEditSource->GetBroadcaster() );
+ }
+ else
+ {
+ EndListening( mrObj.getSdrModelFromSdrObject() );
+ }
+ }
+
+ SvxTextForwarder* AccessibleEmptyEditSource::GetTextForwarder()
+ {
+ if (!mpEditSource)
+ return nullptr;
+
+ return mpEditSource->GetTextForwarder();
+ }
+
+ SvxViewForwarder* AccessibleEmptyEditSource::GetViewForwarder()
+ {
+ if (!mpEditSource)
+ return nullptr;
+
+ return mpEditSource->GetViewForwarder();
+ }
+
+ void AccessibleEmptyEditSource::Switch2ProxyEditSource()
+ {
+ // deregister EmptyEditSource model listener
+ EndListening( mrObj.getSdrModelFromSdrObject() );
+
+ ::std::unique_ptr< SvxEditSource > pProxySource( new AccessibleProxyEditSource_Impl(mrObj, mrView, mrViewWindow) );
+ mpEditSource.swap(pProxySource);
+
+ // register as listener
+ StartListening( mpEditSource->GetBroadcaster() );
+
+ // we've irrevocably a full EditSource now.
+ mbEditSourceEmpty = false;
+ }
+
+ SvxEditViewForwarder* AccessibleEmptyEditSource::GetEditViewForwarder( bool bCreate )
+ {
+ if (!mpEditSource)
+ return nullptr;
+
+ // switch edit source, if not yet done
+ if( mbEditSourceEmpty && bCreate )
+ Switch2ProxyEditSource();
+
+ return mpEditSource->GetEditViewForwarder( bCreate );
+ }
+
+ std::unique_ptr<SvxEditSource> AccessibleEmptyEditSource::Clone() const
+ {
+ if (!mpEditSource)
+ return nullptr;
+
+ return mpEditSource->Clone();
+ }
+
+ void AccessibleEmptyEditSource::UpdateData()
+ {
+ if (mpEditSource)
+ mpEditSource->UpdateData();
+ }
+
+ SfxBroadcaster& AccessibleEmptyEditSource::GetBroadcaster() const
+ {
+ return *const_cast<AccessibleEmptyEditSource*>(this);
+ }
+
+ void AccessibleEmptyEditSource::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
+ {
+ const SdrHint* pSdrHint = ( rHint.GetId() == SfxHintId::ThisIsAnSdrHint ? static_cast<const SdrHint*>(&rHint) : nullptr );
+
+ if( pSdrHint && pSdrHint->GetKind() == SdrHintKind::BeginEdit &&
+ &mrObj == pSdrHint->GetObject() && mpEditSource )
+ {
+ // 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()!=nullptr)
+ {
+ // When the SdrObject just got a para outliner object then
+ // switch the edit source.
+ if (pSdrHint->GetObject()->GetOutlinerParaObject() != nullptr)
+ Switch2ProxyEditSource();
+ }
+
+ // forward messages
+ Broadcast( rHint );
+ }
+
+} // end of namespace accessibility
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/AccessibleEmptyEditSource.hxx b/svx/source/accessibility/AccessibleEmptyEditSource.hxx
new file mode 100644
index 0000000000..6cfceeda46
--- /dev/null
+++ b/svx/source/accessibility/AccessibleEmptyEditSource.hxx
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SVX_SOURCE_ACCESSIBILITY_ACCESSIBLEEMPTYEDITSOURCE_HXX
+#define INCLUDED_SVX_SOURCE_ACCESSIBILITY_ACCESSIBLEEMPTYEDITSOURCE_HXX
+
+#include <svl/SfxBroadcaster.hxx>
+#include <svl/lstner.hxx>
+
+#include <memory>
+#include <editeng/unoedsrc.hxx>
+
+class SdrObject;
+class SdrView;
+class OutputDevice;
+
+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 OutputDevice& rViewWindow );
+ virtual ~AccessibleEmptyEditSource() override;
+
+ // from the SvxEditSource interface
+ SvxTextForwarder* GetTextForwarder() override;
+ SvxViewForwarder* GetViewForwarder() override;
+
+ std::unique_ptr<SvxEditSource> Clone() const override;
+
+ // this method internally switches from empty to proxy mode,
+ // creating an SvxTextEditSource for the functionality.
+ SvxEditViewForwarder* GetEditViewForwarder( bool bCreate = false ) override;
+
+ void UpdateData() override;
+ SfxBroadcaster& GetBroadcaster() const override;
+
+ // from the SfxListener interface
+ void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+
+ private:
+ void Switch2ProxyEditSource();
+
+ /** Pointer to edit source implementation. This is switched on
+ a GetEditViewForwarder( true ) call, to actually create a
+ SvxTextEditSource.
+
+ @dyn
+ */
+ std::unique_ptr< SvxEditSource > mpEditSource;
+
+ SdrObject& mrObj;
+ SdrView& mrView;
+ const OutputDevice& mrViewWindow;
+
+ bool mbEditSourceEmpty;
+ };
+
+} // namespace accessibility
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/AccessibleFrameSelector.cxx b/svx/source/accessibility/AccessibleFrameSelector.cxx
new file mode 100644
index 0000000000..39bdaa88fd
--- /dev/null
+++ b/svx/source/accessibility/AccessibleFrameSelector.cxx
@@ -0,0 +1,381 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <AccessibleFrameSelector.hxx>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <unotools/accessiblerelationsethelper.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <svx/frmsel.hxx>
+#include <svx/dialmgr.hxx>
+
+#include <frmsel.hrc>
+
+namespace svx::a11y {
+
+using ::com::sun::star::lang::IndexOutOfBoundsException;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::RuntimeException;
+
+using namespace ::com::sun::star::accessibility;
+
+
+AccFrameSelector::AccFrameSelector(FrameSelector& rFrameSel)
+ : mpFrameSel(&rFrameSel)
+{
+}
+
+AccFrameSelector::~AccFrameSelector()
+{
+}
+
+Reference< XAccessibleContext > AccFrameSelector::getAccessibleContext( )
+{
+ return this;
+}
+
+sal_Int64 AccFrameSelector::getAccessibleChildCount( )
+{
+ SolarMutexGuard aGuard;
+ IsValid();
+ return mpFrameSel->GetEnabledBorderCount();
+}
+
+Reference< XAccessible > AccFrameSelector::getAccessibleChild( sal_Int64 i )
+{
+ SolarMutexGuard aGuard;
+ IsValid();
+
+ if (i < 0 || i >= getAccessibleChildCount())
+ throw IndexOutOfBoundsException();
+
+ Reference< XAccessible > xRet = mpFrameSel->GetChildAccessible( i );
+ if( !xRet.is() )
+ throw RuntimeException();
+ return xRet;
+}
+
+Reference< XAccessible > AccFrameSelector::getAccessibleParent( )
+{
+ SolarMutexGuard aGuard;
+ IsValid();
+ Reference< XAccessible > xRet = mpFrameSel->getAccessibleParent();
+ return xRet;
+}
+
+sal_Int16 AccFrameSelector::getAccessibleRole( )
+{
+ return AccessibleRole::OPTION_PANE;
+}
+
+OUString AccFrameSelector::getAccessibleDescription( )
+{
+ SolarMutexGuard aGuard;
+ IsValid();
+ return SvxResId(RID_SVXSTR_FRMSEL_DESCRIPTIONS[0].first);
+}
+
+OUString AccFrameSelector::getAccessibleName( )
+{
+ SolarMutexGuard aGuard;
+ IsValid();
+ return SvxResId(RID_SVXSTR_FRMSEL_TEXTS[0].first);
+}
+
+Reference< XAccessibleRelationSet > AccFrameSelector::getAccessibleRelationSet( )
+{
+ SolarMutexGuard aGuard;
+ IsValid();
+ return mpFrameSel->get_accessible_relation_set();
+}
+
+sal_Int64 AccFrameSelector::getAccessibleStateSet( )
+{
+ SolarMutexGuard aGuard;
+ sal_Int64 nStateSet = 0;
+
+ if(!mpFrameSel)
+ nStateSet |= AccessibleStateType::DEFUNC;
+ else
+ {
+ // add standard states
+ nStateSet |=
+ AccessibleStateType::EDITABLE |
+ AccessibleStateType::FOCUSABLE |
+ AccessibleStateType::MULTI_SELECTABLE |
+ AccessibleStateType::SELECTABLE |
+ AccessibleStateType::SHOWING |
+ AccessibleStateType::VISIBLE |
+ AccessibleStateType::OPAQUE;
+ if(mpFrameSel->IsEnabled())
+ {
+ nStateSet |= AccessibleStateType::ENABLED;
+ nStateSet |= AccessibleStateType::SENSITIVE;
+ }
+
+ if (mpFrameSel->HasFocus())
+ {
+ nStateSet |= AccessibleStateType::ACTIVE;
+ nStateSet |= AccessibleStateType::FOCUSED;
+ nStateSet |= AccessibleStateType::SELECTED;
+ }
+ }
+ return nStateSet;
+}
+
+Reference< XAccessible > AccFrameSelector::getAccessibleAtPoint(
+ const css::awt::Point& aPt )
+{
+ SolarMutexGuard aGuard;
+ IsValid();
+ //aPt is relative to the frame selector
+ return mpFrameSel->GetChildAccessible( Point( aPt.X, aPt.Y ) );
+}
+
+void AccFrameSelector::grabFocus( )
+{
+ SolarMutexGuard aGuard;
+ IsValid();
+ mpFrameSel->GrabFocus();
+}
+
+sal_Int32 AccFrameSelector::getForeground( )
+{
+ SolarMutexGuard aGuard;
+
+ //see FrameSelector::Paint
+ const StyleSettings& rStyles = Application::GetSettings().GetStyleSettings();
+ return sal_Int32(rStyles.GetLabelTextColor());
+}
+
+sal_Int32 AccFrameSelector::getBackground( )
+{
+ SolarMutexGuard aGuard;
+
+ //see FrameSelector::Paint
+ const StyleSettings& rStyles = Application::GetSettings().GetStyleSettings();
+ return sal_Int32(rStyles.GetDialogColor());
+}
+
+css::awt::Rectangle AccFrameSelector::implGetBounds()
+{
+ SolarMutexGuard aGuard;
+ IsValid();
+
+ css::awt::Rectangle aRet;
+
+ const Point aOutPos;
+ Size aOutSize(mpFrameSel->GetOutputSizePixel());
+
+ aRet.X = aOutPos.X();
+ aRet.Y = aOutPos.Y();
+ aRet.Width = aOutSize.Width();
+ aRet.Height = aOutSize.Height();
+
+ return aRet;
+}
+
+css::awt::Point AccFrameSelector::getLocationOnScreen()
+{
+ SolarMutexGuard aGuard;
+ IsValid();
+
+ css::awt::Point aScreenLoc(0, 0);
+
+ if (weld::DrawingArea* pDrawingArea = mpFrameSel->GetDrawingArea())
+ {
+ AbsoluteScreenPixelPoint aPos = pDrawingArea->get_accessible_location_on_screen();
+ aScreenLoc.X = aPos.X();
+ aScreenLoc.Y = aPos.Y();
+ }
+
+ return aScreenLoc;
+}
+
+void AccFrameSelector::IsValid()
+{
+ if(!mpFrameSel)
+ throw RuntimeException();
+}
+
+void AccFrameSelector::Invalidate()
+{
+ mpFrameSel = nullptr;
+}
+
+AccFrameSelectorChild::AccFrameSelectorChild(FrameSelector& rFrameSel, FrameBorderType eBorder)
+ : mpFrameSel(&rFrameSel)
+ , meBorder(eBorder)
+{
+}
+
+AccFrameSelectorChild::~AccFrameSelectorChild()
+{
+}
+
+Reference< XAccessibleContext > AccFrameSelectorChild::getAccessibleContext( )
+{
+ return this;
+}
+
+sal_Int64 AccFrameSelectorChild::getAccessibleChildCount( )
+{
+ SolarMutexGuard aGuard;
+ IsValid();
+ return 0;
+}
+
+Reference< XAccessible > AccFrameSelectorChild::getAccessibleChild( sal_Int64 )
+{
+ throw RuntimeException();
+}
+
+Reference< XAccessible > AccFrameSelectorChild::getAccessibleParent( )
+{
+ SolarMutexGuard aGuard;
+ IsValid();
+ Reference< XAccessible > xRet = mpFrameSel->CreateAccessible();
+ return xRet;
+}
+
+sal_Int16 AccFrameSelectorChild::getAccessibleRole( )
+{
+ return AccessibleRole::CHECK_BOX;
+}
+
+OUString AccFrameSelectorChild::getAccessibleDescription( )
+{
+ SolarMutexGuard aGuard;
+ IsValid();
+ return SvxResId(RID_SVXSTR_FRMSEL_DESCRIPTIONS[static_cast<sal_uInt32>(meBorder)].first);
+}
+
+OUString AccFrameSelectorChild::getAccessibleName( )
+{
+ SolarMutexGuard aGuard;
+ IsValid();
+ return SvxResId(RID_SVXSTR_FRMSEL_TEXTS[static_cast<sal_uInt32>(meBorder)].first);
+}
+
+Reference< XAccessibleRelationSet > AccFrameSelectorChild::getAccessibleRelationSet( )
+{
+ SolarMutexGuard aGuard;
+ IsValid();
+ Reference< XAccessibleRelationSet > xRet = new utl::AccessibleRelationSetHelper;
+ return xRet;
+}
+
+sal_Int64 AccFrameSelectorChild::getAccessibleStateSet( )
+{
+ SolarMutexGuard aGuard;
+ sal_Int64 nStateSet = 0;
+
+ if(!mpFrameSel)
+ nStateSet |= AccessibleStateType::DEFUNC;
+ else
+ {
+ nStateSet |=
+ AccessibleStateType::EDITABLE |
+ AccessibleStateType::FOCUSABLE |
+ AccessibleStateType::MULTI_SELECTABLE |
+ AccessibleStateType::SELECTABLE |
+ AccessibleStateType::SHOWING |
+ AccessibleStateType::VISIBLE |
+ AccessibleStateType::OPAQUE;
+ if(mpFrameSel->IsEnabled())
+ {
+ nStateSet |= AccessibleStateType::ENABLED;
+ nStateSet |= AccessibleStateType::SENSITIVE;
+ }
+
+ if (mpFrameSel->HasFocus() && mpFrameSel->IsBorderSelected(meBorder))
+ {
+ nStateSet |= AccessibleStateType::ACTIVE;
+ nStateSet |= AccessibleStateType::FOCUSED;
+ nStateSet |= AccessibleStateType::SELECTED;
+ }
+ }
+ return nStateSet;
+}
+
+Reference< XAccessible > AccFrameSelectorChild::getAccessibleAtPoint(
+ const css::awt::Point& aPt )
+{
+ SolarMutexGuard aGuard;
+ IsValid();
+ //aPt is relative to the frame selector
+ return mpFrameSel->GetChildAccessible( Point( aPt.X, aPt.Y ) );
+}
+
+css::awt::Rectangle AccFrameSelectorChild::implGetBounds( )
+{
+ SolarMutexGuard aGuard;
+ IsValid();
+ const tools::Rectangle aSpot = mpFrameSel->GetClickBoundRect( meBorder );
+ Point aPos = aSpot.TopLeft();
+ Size aSz = aSpot.GetSize();
+ css::awt::Rectangle aRet;
+ aRet.X = aPos.X();
+ aRet.Y = aPos.Y();
+ aRet.Width = aSz.Width();
+ aRet.Height = aSz.Height();
+ return aRet;
+}
+
+void AccFrameSelectorChild::grabFocus( )
+{
+ SolarMutexGuard aGuard;
+ IsValid();
+ mpFrameSel->GrabFocus();
+}
+
+sal_Int32 AccFrameSelectorChild::getForeground( )
+{
+ SolarMutexGuard aGuard;
+
+ //see FrameSelector::Paint
+ const StyleSettings& rStyles = Application::GetSettings().GetStyleSettings();
+ return sal_Int32(rStyles.GetLabelTextColor());
+}
+
+sal_Int32 AccFrameSelectorChild::getBackground( )
+{
+ SolarMutexGuard aGuard;
+
+ //see FrameSelector::Paint
+ const StyleSettings& rStyles = Application::GetSettings().GetStyleSettings();
+ return sal_Int32(rStyles.GetDialogColor());
+}
+
+void AccFrameSelectorChild::IsValid()
+{
+ if(!mpFrameSel)
+ throw RuntimeException();
+}
+
+void AccFrameSelectorChild::Invalidate()
+{
+ mpFrameSel = nullptr;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/AccessibleGraphicShape.cxx b/svx/source/accessibility/AccessibleGraphicShape.cxx
new file mode 100644
index 0000000000..d94ce4886a
--- /dev/null
+++ b/svx/source/accessibility/AccessibleGraphicShape.cxx
@@ -0,0 +1,144 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svx/AccessibleGraphicShape.hxx>
+
+#include <svx/ShapeTypeHandler.hxx>
+#include <svx/SvxShapeTypes.hxx>
+#include <svx/svdobj.hxx>
+
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/drawing/XShapeDescriptor.hpp>
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/queryinterface.hxx>
+
+using namespace ::accessibility;
+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()
+{
+}
+
+// XAccessibleImage
+OUString SAL_CALL AccessibleGraphicShape::getAccessibleImageDescription()
+{
+ if (m_pShape)
+ return m_pShape->GetTitle();
+ return AccessibleShape::getAccessibleDescription ();
+}
+
+
+sal_Int32 SAL_CALL AccessibleGraphicShape::getAccessibleImageHeight()
+{
+ return AccessibleShape::getSize().Height;
+}
+
+
+sal_Int32 SAL_CALL AccessibleGraphicShape::getAccessibleImageWidth()
+{
+ return AccessibleShape::getSize().Width;
+}
+
+// XInterface
+css::uno::Any SAL_CALL
+ AccessibleGraphicShape::queryInterface (const css::uno::Type & rType)
+{
+ css::uno::Any aReturn = AccessibleShape::queryInterface (rType);
+ if ( ! aReturn.hasValue())
+ aReturn = ::cppu::queryInterface (rType,
+ static_cast<XAccessibleImage*>(this));
+ return aReturn;
+}
+
+
+void SAL_CALL
+ AccessibleGraphicShape::acquire()
+ noexcept
+{
+ AccessibleShape::acquire ();
+}
+
+
+void SAL_CALL
+ AccessibleGraphicShape::release()
+ noexcept
+{
+ AccessibleShape::release ();
+}
+
+// XServiceInfo
+OUString SAL_CALL
+ AccessibleGraphicShape::getImplementationName()
+{
+ return "AccessibleGraphicShape";
+}
+
+
+css::uno::Sequence< OUString> SAL_CALL
+ AccessibleGraphicShape::getSupportedServiceNames()
+{
+ ThrowIfDisposed ();
+ const css::uno::Sequence<OUString> vals { "com.sun.star.drawing.AccessibleGraphicShape" };
+ return comphelper::concatSequences(AccessibleShape::getSupportedServiceNames(), vals);
+}
+
+// XTypeProvider
+uno::Sequence<uno::Type> SAL_CALL
+ AccessibleGraphicShape::getTypes()
+{
+ // Get list of types from the context base implementation...
+ return comphelper::concatSequences(AccessibleShape::getTypes(),
+ uno::Sequence { cppu::UnoType<XAccessibleImage>::get() });
+}
+
+
+/// Create the base name of this object, i.e. the name without appended number.
+OUString
+ AccessibleGraphicShape::CreateAccessibleBaseName()
+{
+ OUString sName;
+
+ ShapeTypeId nShapeType = ShapeTypeHandler::Instance().GetTypeId (mxShape);
+ switch (nShapeType)
+ {
+ case DRAWING_GRAPHIC_OBJECT:
+ sName = "GraphicObjectShape";
+ break;
+
+ default:
+ sName = "UnknownAccessibleGraphicShape";
+ uno::Reference<drawing::XShapeDescriptor> xDescriptor (mxShape);
+ if (xDescriptor.is())
+ sName += ": " + xDescriptor->getShapeType();
+ }
+
+ return sName;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/AccessibleOLEShape.cxx b/svx/source/accessibility/AccessibleOLEShape.cxx
new file mode 100644
index 0000000000..9275ad1803
--- /dev/null
+++ b/svx/source/accessibility/AccessibleOLEShape.cxx
@@ -0,0 +1,172 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svx/AccessibleOLEShape.hxx>
+
+#include <svx/ShapeTypeHandler.hxx>
+#include <svx/SvxShapeTypes.hxx>
+#include <svx/svdoole2.hxx>
+
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/queryinterface.hxx>
+
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/drawing/XShapeDescriptor.hpp>
+
+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()
+{
+}
+
+// XAccessibleAction
+sal_Int32 SAL_CALL AccessibleOLEShape::getAccessibleActionCount()
+{
+ return 0;
+}
+
+
+sal_Bool SAL_CALL AccessibleOLEShape::doAccessibleAction (sal_Int32 /*nIndex*/)
+{
+ throw lang::IndexOutOfBoundsException();
+}
+
+
+OUString SAL_CALL AccessibleOLEShape::getAccessibleActionDescription (sal_Int32 /*nIndex*/)
+{
+ throw lang::IndexOutOfBoundsException();
+}
+
+
+Reference<XAccessibleKeyBinding> SAL_CALL AccessibleOLEShape::getAccessibleActionKeyBinding (sal_Int32 /*nIndex*/)
+{
+ throw lang::IndexOutOfBoundsException();
+}
+
+// XInterface
+css::uno::Any SAL_CALL
+ AccessibleOLEShape::queryInterface (const css::uno::Type & rType)
+{
+ css::uno::Any aReturn = AccessibleShape::queryInterface (rType);
+ if ( ! aReturn.hasValue())
+ aReturn = ::cppu::queryInterface (rType,
+ static_cast<XAccessibleAction*>(this));
+ return aReturn;
+}
+
+
+void SAL_CALL
+ AccessibleOLEShape::acquire()
+ noexcept
+{
+ AccessibleShape::acquire ();
+}
+
+
+void SAL_CALL
+ AccessibleOLEShape::release()
+ noexcept
+{
+ AccessibleShape::release ();
+}
+
+// XServiceInfo
+OUString SAL_CALL
+ AccessibleOLEShape::getImplementationName()
+{
+ return "AccessibleOLEShape";
+}
+
+
+css::uno::Sequence< OUString> SAL_CALL
+ AccessibleOLEShape::getSupportedServiceNames()
+{
+ ThrowIfDisposed();
+ const css::uno::Sequence<OUString> vals { "com.sun.star.drawing.AccessibleOLEShape" };
+ return comphelper::concatSequences(AccessibleShape::getSupportedServiceNames(), vals);
+}
+
+// XTypeProvider
+uno::Sequence<uno::Type> SAL_CALL AccessibleOLEShape::getTypes()
+{
+ // Get list of types from the context base implementation...
+ return comphelper::concatSequences(AccessibleShape::getTypes(),
+ uno::Sequence { cppu::UnoType<XAccessibleAction>::get() } );
+}
+
+// XAccessibleExtendedAttributes
+uno::Any SAL_CALL AccessibleOLEShape::getExtendedAttributes()
+{
+ uno::Any strRet;
+ OUString style;
+ if( m_pShape )
+ {
+ style = "style:" + static_cast<SdrOle2Obj*>(m_pShape)->GetStyleString();
+ }
+ style += ";";
+ strRet <<= style;
+ return strRet;
+}
+
+/// Set this object's name if is different to the current name.
+OUString
+ AccessibleOLEShape::CreateAccessibleBaseName()
+{
+ OUString sName;
+
+ ShapeTypeId nShapeType = ShapeTypeHandler::Instance().GetTypeId (mxShape);
+ switch (nShapeType)
+ {
+ case DRAWING_APPLET:
+ sName = "AppletOLEShape";
+ break;
+ case DRAWING_FRAME:
+ sName = "FrameOLEShape";
+ break;
+ case DRAWING_OLE:
+ sName = "OLEShape";
+ break;
+ case DRAWING_PLUGIN:
+ sName = "PluginOLEShape";
+ break;
+
+ default:
+ sName = "UnknownAccessibleOLEShape";
+ uno::Reference<drawing::XShapeDescriptor> xDescriptor (mxShape);
+ if (xDescriptor.is())
+ sName += ": " + xDescriptor->getShapeType();
+ }
+
+ return sName;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/AccessibleShape.cxx b/svx/source/accessibility/AccessibleShape.cxx
new file mode 100644
index 0000000000..1edc2c8a26
--- /dev/null
+++ b/svx/source/accessibility/AccessibleShape.cxx
@@ -0,0 +1,1294 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svx/AccessibleShape.hxx>
+#include <svx/AccessibleShapeInfo.hxx>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleRelationType.hpp>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/drawing/XShapes.hpp>
+#include <com/sun/star/document/XShapeEventBroadcaster.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/drawing/FillStyle.hpp>
+#include <com/sun/star/text/XText.hpp>
+#include <sal/log.hxx>
+#include <editeng/unoedsrc.hxx>
+#include <svx/AccessibleTextHelper.hxx>
+#include <svx/ChildrenManager.hxx>
+#include <svx/IAccessibleParent.hxx>
+#include <svx/IAccessibleViewForwarder.hxx>
+#include <svx/unoshtxt.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/unoapi.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/ShapeTypeHandler.hxx>
+#include <svx/SvxShapeTypes.hxx>
+
+#include <vcl/svapp.hxx>
+#include <vcl/window.hxx>
+#include <unotools/accessiblerelationsethelper.hxx>
+#include <svx/svdview.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <comphelper/sequence.hxx>
+#include "AccessibleEmptyEditSource.hxx"
+
+#include <algorithm>
+#include <memory>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::lang::IndexOutOfBoundsException;
+using ::com::sun::star::uno::RuntimeException;
+
+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::SHAPE),
+ mxShape (rShapeInfo.mxShape),
+ maShapeTreeInfo (rShapeTreeInfo),
+ m_nIndexInParent(-1),
+ mpParent (rShapeInfo.mpChildrenManager)
+{
+ m_pShape = SdrObject::getSdrObjectFromXShape(mxShape);
+ UpdateNameAndDescription();
+}
+
+AccessibleShape::~AccessibleShape()
+{
+ mpChildrenManager.reset();
+ mpText.reset();
+ SAL_INFO("svx", "~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()
+{
+ // 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.reset( new ChildrenManager (
+ this, xShapes, maShapeTreeInfo, *this) );
+ if (mpChildrenManager != nullptr)
+ mpChildrenManager->Update();
+
+ // Register at model as document::XEventListener.
+ if (mxShape.is() && maShapeTreeInfo.GetModelBroadcaster().is())
+ maShapeTreeInfo.GetModelBroadcaster()->addShapeEventListener(mxShape,
+ static_cast<document::XShapeEventListener*>(this));
+
+ // Beware! Here we leave the paths of the UNO API and descend into the
+ // depths of the core. Necessary for making the edit engine
+ // accessible.
+ Reference<text::XText> xText (mxShape, uno::UNO_QUERY);
+ if (!xText.is())
+ return;
+
+ SdrView* pView = maShapeTreeInfo.GetSdrView ();
+ const vcl::Window* pWindow = maShapeTreeInfo.GetWindow ();
+ if (!(pView != nullptr && pWindow != nullptr && mxShape.is()))
+ return;
+
+ // #107948# Determine whether shape text is empty
+ SdrObject* pSdrObject = SdrObject::getSdrObjectFromXShape(mxShape);
+ if( !pSdrObject )
+ return;
+
+ SdrTextObj* pTextObj = DynCastSdrTextObj( pSdrObject );
+ const bool hasOutlinerParaObject = (pTextObj && pTextObj->CanCreateEditOutlinerParaObject()) || (pSdrObject->GetOutlinerParaObject() != nullptr);
+
+ // create AccessibleTextHelper to handle this shape's text
+ if( !hasOutlinerParaObject )
+ {
+ // empty text -> use proxy edit source to delay creation of EditEngine
+ mpText.reset( new AccessibleTextHelper( std::make_unique<AccessibleEmptyEditSource >(*pSdrObject, *pView, *pWindow->GetOutDev()) ) );
+ }
+ else
+ {
+ // non-empty text -> use full-fledged edit source right away
+ mpText.reset( new AccessibleTextHelper( std::make_unique<SvxTextEditSource >(*pSdrObject, nullptr, *pView, *pWindow->GetOutDev()) ) );
+ }
+ if( pWindow->HasFocus() )
+ mpText->SetFocus();
+
+ mpText->SetEventSource(this);
+}
+
+
+void AccessibleShape::UpdateStates()
+{
+ // 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 ("FillStyle") >>= aFillStyle)
+ && aFillStyle == drawing::FillStyle_SOLID;
+ }
+ catch (css::beans::UnknownPropertyException&)
+ {
+ // Ignore.
+ }
+ }
+ }
+ }
+ if (bShapeIsOpaque)
+ mnStateSet |= AccessibleStateType::OPAQUE;
+ else
+ mnStateSet &= ~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);
+ }
+
+ if (bShapeIsSelected)
+ mnStateSet |= AccessibleStateType::SELECTED;
+ else
+ mnStateSet &= ~AccessibleStateType::SELECTED;
+}
+
+OUString AccessibleShape::GetStyle() const
+{
+ return ShapeTypeHandler::CreateAccessibleBaseName( mxShape );
+}
+
+bool AccessibleShape::SetState (sal_Int64 aState)
+{
+ bool bStateHasChanged = false;
+
+ if (aState == AccessibleStateType::FOCUSED && mpText != nullptr)
+ {
+ // Offer FOCUSED state to edit engine and detect whether the state
+ // changes.
+ bool bIsFocused = mpText->HaveFocus ();
+ mpText->SetFocus();
+ bStateHasChanged = (bIsFocused != mpText->HaveFocus ());
+ }
+ else
+ bStateHasChanged = AccessibleContextBase::SetState (aState);
+
+ return bStateHasChanged;
+}
+
+
+bool AccessibleShape::ResetState (sal_Int64 aState)
+{
+ bool bStateHasChanged = false;
+
+ if (aState == AccessibleStateType::FOCUSED && mpText != nullptr)
+ {
+ // Try to remove FOCUSED state from the edit engine and detect
+ // whether the state changes.
+ bool bIsFocused = mpText->HaveFocus ();
+ mpText->SetFocus (false);
+ bStateHasChanged = (bIsFocused != mpText->HaveFocus ());
+ }
+ else
+ bStateHasChanged = AccessibleContextBase::ResetState (aState);
+
+ return bStateHasChanged;
+}
+
+
+bool AccessibleShape::GetState (sal_Int64 aState)
+{
+ if (aState == AccessibleStateType::FOCUSED && mpText != nullptr)
+ {
+ // 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);
+}
+
+// OverWrite the parent's getAccessibleName method
+OUString SAL_CALL AccessibleShape::getAccessibleName()
+{
+ ThrowIfDisposed ();
+ if (m_pShape && !m_pShape->GetTitle().isEmpty())
+ return CreateAccessibleName() + " " + m_pShape->GetTitle();
+ else
+ return CreateAccessibleName();
+}
+
+OUString SAL_CALL AccessibleShape::getAccessibleDescription()
+{
+ ThrowIfDisposed ();
+ if( m_pShape && !m_pShape->GetDescription().isEmpty())
+ return m_pShape->GetDescription() ;
+ else
+ return " ";
+}
+
+// XAccessibleContext
+/** The children of this shape come from two sources: The children from
+ group or scene shapes and the paragraphs of text.
+*/
+sal_Int64 SAL_CALL
+ AccessibleShape::getAccessibleChildCount ()
+{
+ if (IsDisposed())
+ {
+ return 0;
+ }
+
+ sal_Int64 nChildCount = 0;
+
+ // Add the number of shapes that are children of this shape.
+ if (mpChildrenManager != nullptr)
+ nChildCount += mpChildrenManager->GetChildCount ();
+ // Add the number text paragraphs.
+ if (mpText != nullptr)
+ 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_Int64 nIndex)
+{
+ 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 != nullptr)
+ && (nIndex < mpChildrenManager->GetChildCount()))
+ {
+ xChild = mpChildrenManager->GetChild (nIndex);
+ }
+ else if (mpText != nullptr)
+ {
+ sal_Int64 nI = nIndex;
+ if (mpChildrenManager != nullptr)
+ nI -= mpChildrenManager->GetChildCount();
+ xChild = mpText->GetChild (nI);
+ }
+ else
+ throw lang::IndexOutOfBoundsException (
+ "shape has no child with index " + OUString::number(nIndex),
+ getXWeak());
+
+ return xChild;
+}
+
+uno::Reference<XAccessibleRelationSet> SAL_CALL
+ AccessibleShape::getAccessibleRelationSet()
+{
+ ::osl::MutexGuard aGuard (m_aMutex);
+ if (mpParent == nullptr)
+ return uno::Reference<XAccessibleRelationSet>();
+
+ rtl::Reference<::utl::AccessibleRelationSetHelper> pRelationSet = new utl::AccessibleRelationSetHelper;
+
+ //this mxshape is the captioned shape
+ uno::Sequence< uno::Reference< uno::XInterface > > aSequence { mpParent->GetAccessibleCaption(mxShape) };
+ if(aSequence[0])
+ {
+ pRelationSet->AddRelation(
+ AccessibleRelation( AccessibleRelationType::DESCRIBED_BY, aSequence ) );
+ }
+ return pRelationSet;
+}
+
+/** Return a copy of the state set.
+ Possible states are:
+ ENABLED
+ SHOWING
+ VISIBLE
+*/
+sal_Int64 SAL_CALL
+ AccessibleShape::getAccessibleStateSet()
+{
+ ::osl::MutexGuard aGuard (m_aMutex);
+
+ if (IsDisposed())
+ {
+ // Return a minimal state set that only contains the DEFUNC state.
+ return AccessibleContextBase::getAccessibleStateSet ();
+ }
+
+ // Merge current FOCUSED state from edit engine.
+ if (mpText)
+ {
+ if (mpText->HaveFocus())
+ mnStateSet |= AccessibleStateType::FOCUSED;
+ else
+ mnStateSet &= ~AccessibleStateType::FOCUSED;
+ }
+ //Just when the document is not read-only,set states EDITABLE,RESIZABLE,MOVEABLE
+ css::uno::Reference<XAccessible> xTempAcc = getAccessibleParent();
+ if( xTempAcc.is() )
+ {
+ css::uno::Reference<XAccessibleContext>
+ xTempAccContext = xTempAcc->getAccessibleContext();
+ if( xTempAccContext.is() )
+ {
+ sal_Int64 nState = xTempAccContext->getAccessibleStateSet();
+ if (nState & AccessibleStateType::EDITABLE)
+ {
+ mnStateSet |= AccessibleStateType::EDITABLE;
+ mnStateSet |= AccessibleStateType::RESIZABLE;
+ mnStateSet |= AccessibleStateType::MOVEABLE;
+ }
+ }
+ }
+
+ sal_Int64 nRetStateSet = mnStateSet;
+
+ if (mpParent && mpParent->IsDocumentSelAll())
+ {
+ nRetStateSet |= AccessibleStateType::SELECTED;
+ }
+
+ return nRetStateSet;
+}
+
+// XAccessibleComponent
+/** The implementation below is at the moment straightforward. It iterates
+ over all children (and thereby instances all children which have not
+ been already instantiated) until a child covering the specified 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)
+{
+ ::osl::MutexGuard aGuard (m_aMutex);
+
+ sal_Int64 nChildCount = getAccessibleChildCount ();
+ for (sal_Int64 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()
+{
+ SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard (m_aMutex);
+
+ ThrowIfDisposed ();
+ awt::Rectangle aBoundingBox;
+ if ( mxShape.is() )
+ {
+
+ static constexpr OUString sBoundRectName = u"BoundRect"_ustr;
+ static constexpr OUString sAnchorPositionName = u"AnchorPosition"_ustr;
+
+ // 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 const&)
+ {
+ // Handled below (bFoundBoundRect stays false).
+ }
+ }
+ else
+ SAL_WARN("svx", "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() == nullptr)
+ throw uno::RuntimeException (
+ "AccessibleShape has no valid view forwarder",
+ getXWeak());
+ ::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;
+
+ // Clip with parent (with coordinates relative to itself).
+ ::tools::Rectangle aBBox (
+ x, y, x + aPixelSize.getWidth(), y + aPixelSize.getHeight());
+ awt::Size aParentSize (xParentComponent->getSize());
+ ::tools::Rectangle aParentBBox (0,0, aParentSize.Width, aParentSize.Height);
+ aBBox = aBBox.GetIntersection (aParentBBox);
+ aBoundingBox = awt::Rectangle (
+ aBBox.Left(),
+ aBBox.Top(),
+ aBBox.getOpenWidth(),
+ aBBox.getOpenHeight());
+ }
+ else
+ {
+ SAL_INFO("svx", "parent does not support component");
+ aBoundingBox = awt::Rectangle (
+ aPixelPosition.getX(), aPixelPosition.getY(),
+ aPixelSize.getWidth(), aPixelSize.getHeight());
+ }
+ }
+
+ return aBoundingBox;
+}
+
+
+awt::Point SAL_CALL AccessibleShape::getLocation()
+{
+ ThrowIfDisposed ();
+ awt::Rectangle aBoundingBox (getBounds());
+ return awt::Point (aBoundingBox.X, aBoundingBox.Y);
+}
+
+
+awt::Point SAL_CALL AccessibleShape::getLocationOnScreen()
+{
+ 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
+ SAL_WARN("svx", "parent does not support XAccessibleComponent");
+ return aLocation;
+}
+
+
+awt::Size SAL_CALL AccessibleShape::getSize()
+{
+ ThrowIfDisposed ();
+ awt::Rectangle aBoundingBox (getBounds());
+ return awt::Size (aBoundingBox.Width, aBoundingBox.Height);
+}
+
+
+sal_Int32 SAL_CALL AccessibleShape::getForeground()
+{
+ ThrowIfDisposed ();
+ sal_Int32 nColor (0x0ffffffL);
+
+ try
+ {
+ uno::Reference<beans::XPropertySet> aSet (mxShape, uno::UNO_QUERY);
+ if (aSet.is())
+ {
+ uno::Any aColor;
+ aColor = aSet->getPropertyValue ("LineColor");
+ aColor >>= nColor;
+ }
+ }
+ catch (const css::beans::UnknownPropertyException &)
+ {
+ // Ignore exception and return default color.
+ }
+ return nColor;
+}
+
+
+sal_Int32 SAL_CALL AccessibleShape::getBackground()
+{
+ ThrowIfDisposed ();
+ Color nColor;
+
+ try
+ {
+ uno::Reference<beans::XPropertySet> aSet (mxShape, uno::UNO_QUERY);
+ if (aSet.is())
+ {
+ uno::Any aColor;
+ aColor = aSet->getPropertyValue ("FillColor");
+ aColor >>= nColor;
+ aColor = aSet->getPropertyValue ("FillTransparence");
+ short nTrans=0;
+ aColor >>= nTrans;
+ Color crBk(nColor);
+ if (nTrans == 0 )
+ {
+ crBk.SetAlpha(0);
+ }
+ else
+ {
+ nTrans = short(256 - nTrans / 100. * 256);
+ crBk.SetAlpha(255 - sal_uInt8(nTrans));
+ }
+ nColor = crBk;
+ }
+ }
+ catch (const css::beans::UnknownPropertyException &)
+ {
+ // Ignore exception and return default color.
+ }
+ return sal_Int32(nColor);
+}
+
+// XAccessibleEventBroadcaster
+void SAL_CALL AccessibleShape::addAccessibleEventListener (
+ const Reference<XAccessibleEventListener >& rxListener)
+{
+ if (rBHelper.bDisposed || rBHelper.bInDispose)
+ {
+ uno::Reference<uno::XInterface> xThis (
+ static_cast<lang::XComponent *>(this), uno::UNO_QUERY);
+ rxListener->disposing (lang::EventObject (xThis));
+ }
+ else
+ {
+ AccessibleContextBase::addAccessibleEventListener (rxListener);
+ if (mpText != nullptr)
+ mpText->AddEventListener (rxListener);
+ }
+}
+
+
+void SAL_CALL AccessibleShape::removeAccessibleEventListener (
+ const Reference<XAccessibleEventListener >& rxListener)
+{
+ AccessibleContextBase::removeAccessibleEventListener (rxListener);
+ if (mpText != nullptr)
+ mpText->RemoveEventListener (rxListener);
+}
+
+// XInterface
+css::uno::Any SAL_CALL
+ AccessibleShape::queryInterface (const css::uno::Type & rType)
+{
+ css::uno::Any aReturn = AccessibleContextBase::queryInterface (rType);
+ if ( ! aReturn.hasValue())
+ aReturn = ::cppu::queryInterface (rType,
+ static_cast<XAccessibleComponent*>(this),
+ static_cast<XAccessibleExtendedComponent*>(this),
+ static_cast< css::accessibility::XAccessibleSelection* >(this),
+ static_cast< css::accessibility::XAccessibleExtendedAttributes* >(this),
+ static_cast<document::XShapeEventListener*>(this),
+ static_cast<lang::XUnoTunnel*>(this),
+ static_cast<XAccessibleGroupPosition*>(this),
+ static_cast<XAccessibleHypertext*>(this)
+ );
+ return aReturn;
+}
+
+
+void SAL_CALL
+ AccessibleShape::acquire()
+ noexcept
+{
+ AccessibleContextBase::acquire ();
+}
+
+
+void SAL_CALL
+ AccessibleShape::release()
+ noexcept
+{
+ AccessibleContextBase::release ();
+}
+
+// XAccessibleSelection
+void SAL_CALL AccessibleShape::selectAccessibleChild( sal_Int64 )
+{
+}
+
+
+sal_Bool SAL_CALL AccessibleShape::isAccessibleChildSelected( sal_Int64 nChildIndex )
+{
+ uno::Reference<XAccessible> xAcc = getAccessibleChild( nChildIndex );
+ uno::Reference<XAccessibleContext> xContext;
+ if( xAcc.is() )
+ {
+ xContext = xAcc->getAccessibleContext();
+ }
+
+ if( xContext.is() )
+ {
+ if( xContext->getAccessibleRole() == AccessibleRole::PARAGRAPH )
+ {
+ uno::Reference< css::accessibility::XAccessibleText >
+ xText(xAcc, uno::UNO_QUERY);
+ if( xText.is() )
+ {
+ if( xText->getSelectionStart() >= 0 ) return true;
+ }
+ }
+ else if( xContext->getAccessibleRole() == AccessibleRole::SHAPE )
+ {
+ sal_Int64 pRState = xContext->getAccessibleStateSet();
+
+ return bool(pRState & AccessibleStateType::SELECTED);
+ }
+ }
+
+ return false;
+}
+
+
+void SAL_CALL AccessibleShape::clearAccessibleSelection( )
+{
+}
+
+
+void SAL_CALL AccessibleShape::selectAllAccessibleChildren( )
+{
+}
+
+
+sal_Int64 SAL_CALL AccessibleShape::getSelectedAccessibleChildCount()
+{
+ sal_Int64 nCount = 0;
+ sal_Int64 TotalCount = getAccessibleChildCount();
+ for( sal_Int64 i = 0; i < TotalCount; i++ )
+ if( isAccessibleChildSelected(i) ) nCount++;
+
+ return nCount;
+}
+
+
+Reference<XAccessible> SAL_CALL AccessibleShape::getSelectedAccessibleChild( sal_Int64 nSelectedChildIndex )
+{
+ if ( nSelectedChildIndex > getSelectedAccessibleChildCount() )
+ throw IndexOutOfBoundsException();
+ for (sal_Int64 i1 = 0, i2 = 0; i1 < getAccessibleChildCount(); i1++)
+ if( isAccessibleChildSelected(i1) )
+ {
+ if( i2 == nSelectedChildIndex )
+ return getAccessibleChild( i1 );
+ i2++;
+ }
+ return Reference<XAccessible>();
+}
+
+
+void SAL_CALL AccessibleShape::deselectAccessibleChild( sal_Int64 )
+{
+
+}
+
+// XAccessibleExtendedAttributes
+uno::Any SAL_CALL AccessibleShape::getExtendedAttributes()
+{
+ uno::Any strRet;
+ OUString style;
+ if( getAccessibleRole() != AccessibleRole::SHAPE ) return strRet;
+ if( m_pShape )
+ {
+ style = "style:" + GetStyle();
+ }
+ style += ";";
+ strRet <<= style;
+ return strRet;
+}
+
+// XServiceInfo
+OUString SAL_CALL
+ AccessibleShape::getImplementationName()
+{
+ return "AccessibleShape";
+}
+
+
+uno::Sequence<OUString> SAL_CALL
+ AccessibleShape::getSupportedServiceNames()
+{
+ ThrowIfDisposed ();
+ const css::uno::Sequence<OUString> vals { "com.sun.star.drawing.AccessibleShape" };
+ return comphelper::concatSequences(AccessibleContextBase::getSupportedServiceNames(), vals);
+}
+
+// XTypeProvider
+uno::Sequence<uno::Type> SAL_CALL
+ AccessibleShape::getTypes()
+{
+ 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
+ uno::Sequence<uno::Type> localTypesList = {
+ cppu::UnoType<lang::XEventListener>::get(),
+ cppu::UnoType<document::XEventListener>::get(),
+ cppu::UnoType<lang::XUnoTunnel>::get()
+ };
+
+ return comphelper::concatSequences(aTypeList, aComponentTypeList, localTypesList);
+}
+
+// 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 AccessibleShape::disposing (const lang::EventObject& aEvent)
+{
+ SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard (m_aMutex);
+
+ try
+ {
+ if (aEvent.Source == maShapeTreeInfo.GetModelBroadcaster())
+ {
+ // Remove reference to model broadcaster to allow it to pass
+ // away.
+ maShapeTreeInfo.SetModelBroadcaster(nullptr);
+ }
+
+ }
+ catch (uno::RuntimeException const&)
+ {
+ TOOLS_WARN_EXCEPTION("svx", "caught exception while disposing");
+ }
+ mpChildrenManager.reset();
+ mxShape.clear();
+ maShapeTreeInfo.dispose();
+ mpText.reset();
+}
+
+// document::XShapeEventListener
+void SAL_CALL
+ AccessibleShape::notifyShapeEvent (const document::EventObject& rEventObject)
+{
+ if (rEventObject.EventName != "ShapeModified")
+ return;
+
+ //Need to update text children when receiving ShapeModified hint when exiting edit mode for text box
+ if (mpText)
+ mpText->UpdateChildren();
+
+
+ // 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(), -1);
+
+ // Name and Description may have changed. Update the local
+ // values accordingly.
+ UpdateNameAndDescription();
+}
+
+// lang::XUnoTunnel
+UNO3_GETIMPLEMENTATION_IMPL(AccessibleShape)
+
+// IAccessibleViewForwarderListener
+void AccessibleShape::ViewForwarderChanged()
+{
+ // 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(), -1);
+
+ // Tell children manager of the modified view forwarder.
+ if (mpChildrenManager != nullptr)
+ mpChildrenManager->ViewForwarderChanged();
+
+ // 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.
+OUString AccessibleShape::CreateAccessibleBaseName()
+{
+ return ShapeTypeHandler::CreateAccessibleBaseName( mxShape );
+}
+
+
+OUString AccessibleShape::CreateAccessibleName()
+{
+ return GetFullAccessibleName(this);
+}
+
+OUString AccessibleShape::GetFullAccessibleName (AccessibleShape *shape)
+{
+ OUString sName (shape->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.
+ OUString nameStr;
+ if (shape->m_pShape)
+ nameStr = shape->m_pShape->GetName();
+ if (nameStr.isEmpty())
+ {
+ sName += " ";
+ }
+ else
+ {
+ sName = nameStr;
+ }
+
+ //If the new produced name if not the same with last,notify name changed
+ //Event
+ if (aAccName != sName && !aAccName.isEmpty())
+ {
+ uno::Any aOldValue, aNewValue;
+ aOldValue <<= aAccName;
+ aNewValue <<= sName;
+ CommitChange(
+ AccessibleEventId::NAME_CHANGED,
+ aNewValue,
+ aOldValue, -1);
+ }
+ aAccName = sName;
+ return sName;
+}
+
+// protected
+void AccessibleShape::disposing()
+{
+ SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard (m_aMutex);
+
+ // Make sure to send an event that this object loses the focus in the
+ // case that it has the focus.
+ mnStateSet &= ~AccessibleStateType::FOCUSED;
+
+ // Unregister from model.
+ if (mxShape.is() && maShapeTreeInfo.GetModelBroadcaster().is())
+ maShapeTreeInfo.GetModelBroadcaster()->removeShapeEventListener(mxShape,
+ static_cast<document::XShapeEventListener*>(this));
+
+ // Release the child containers.
+ if (mpChildrenManager != nullptr)
+ {
+ mpChildrenManager.reset();
+ }
+ if (mpText != nullptr)
+ {
+ mpText->Dispose();
+ mpText.reset();
+ }
+
+ // Cleanup. Remove references to objects to allow them to be
+ // destroyed.
+ mxShape = nullptr;
+ maShapeTreeInfo.dispose();
+
+ // Call base classes.
+ AccessibleContextBase::dispose ();
+}
+
+sal_Int64 SAL_CALL
+ AccessibleShape::getAccessibleIndexInParent()
+{
+ ThrowIfDisposed ();
+ // Use a simple but slow solution for now. Optimize later.
+
+ sal_Int64 nIndex = m_nIndexInParent;
+ if ( -1 == nIndex )
+ nIndex = AccessibleContextBase::getAccessibleIndexInParent();
+ return nIndex;
+}
+
+
+void AccessibleShape::UpdateNameAndDescription()
+{
+ // Ignore missing title, name, or description. There are fallbacks for
+ // them.
+ try
+ {
+ Reference<beans::XPropertySet> xSet (mxShape, uno::UNO_QUERY_THROW);
+
+ // Get the accessible name.
+ OUString sString = GetOptionalProperty(xSet, "Title");
+ if (!sString.isEmpty())
+ {
+ SetAccessibleName(sString, AccessibleContextBase::FromShape);
+ }
+ else
+ {
+ sString = GetOptionalProperty(xSet, "Name");
+ if (!sString.isEmpty())
+ SetAccessibleName(sString, AccessibleContextBase::FromShape);
+ }
+
+ // Get the accessible description.
+ sString = GetOptionalProperty(xSet, "Description");
+ if (!sString.isEmpty())
+ SetAccessibleDescription(sString, AccessibleContextBase::FromShape);
+ }
+ catch (uno::RuntimeException&)
+ {
+ }
+}
+
+// Return this object's role.
+sal_Int16 SAL_CALL AccessibleShape::getAccessibleRole()
+{
+ sal_Int16 nAccessibleRole = AccessibleRole::SHAPE ;
+ switch (ShapeTypeHandler::Instance().GetTypeId (mxShape))
+ {
+ case DRAWING_GRAPHIC_OBJECT:
+ nAccessibleRole = AccessibleRole::GRAPHIC ; break;
+ case DRAWING_OLE:
+ nAccessibleRole = AccessibleRole::EMBEDDED_OBJECT ; break;
+
+ default:
+ nAccessibleRole = AccessibleContextBase::getAccessibleRole();
+ break;
+ }
+
+ return nAccessibleRole;
+}
+
+namespace {
+
+//sort the drawing objects from up to down, from left to right
+struct XShapePosCompareHelper
+{
+ bool operator() ( const uno::Reference<drawing::XShape>& xshape1,
+ const uno::Reference<drawing::XShape>& xshape2 ) const
+ {
+ SdrObject* pObj1 = SdrObject::getSdrObjectFromXShape(xshape1);
+ SdrObject* pObj2 = SdrObject::getSdrObjectFromXShape(xshape2);
+ if(pObj1 && pObj2)
+ return pObj1->GetOrdNum() < pObj2->GetOrdNum();
+ else
+ return false;
+ }
+};
+
+}
+//end of group position
+
+// XAccessibleGroupPosition
+uno::Sequence< sal_Int32 > SAL_CALL
+AccessibleShape::getGroupPosition( const uno::Any& )
+{
+ // we will return the:
+ // [0] group level
+ // [1] similar items counts in the group
+ // [2] the position of the object in the group
+ uno::Sequence< sal_Int32 > aRet{ 0, 0, 0 };
+
+ css::uno::Reference<XAccessible> xParent = getAccessibleParent();
+ if (!xParent.is())
+ {
+ return aRet;
+ }
+ SdrObject *pObj = SdrObject::getSdrObjectFromXShape(mxShape);
+
+
+ if(pObj == nullptr )
+ {
+ return aRet;
+ }
+
+ // Compute object's group level.
+ sal_Int32 nGroupLevel = 0;
+ SdrObject * pUper = pObj->getParentSdrObjectFromSdrObject();
+ while( pUper )
+ {
+ ++nGroupLevel;
+ pUper = pUper->getParentSdrObjectFromSdrObject();
+ }
+
+ css::uno::Reference<XAccessibleContext> xParentContext = xParent->getAccessibleContext();
+ if( xParentContext->getAccessibleRole() == AccessibleRole::DOCUMENT ||
+ xParentContext->getAccessibleRole() == AccessibleRole::DOCUMENT_PRESENTATION ||
+ xParentContext->getAccessibleRole() == AccessibleRole::DOCUMENT_SPREADSHEET ||
+ xParentContext->getAccessibleRole() == AccessibleRole::DOCUMENT_TEXT )//Document
+ {
+ Reference< XAccessibleGroupPosition > xGroupPosition( xParent,uno::UNO_QUERY );
+ if ( xGroupPosition.is() )
+ {
+ aRet = xGroupPosition->getGroupPosition( uno::Any( getAccessibleContext() ) );
+ }
+ return aRet;
+ }
+ if (xParentContext->getAccessibleRole() != AccessibleRole::SHAPE)
+ {
+ return aRet;
+ }
+
+ SdrObjList *pGrpList = nullptr;
+ if( pObj->getParentSdrObjectFromSdrObject() )
+ pGrpList = pObj->getParentSdrObjectFromSdrObject()->GetSubList();
+ else
+ return aRet;
+
+ std::vector< uno::Reference<drawing::XShape> > vXShapes;
+ if (pGrpList)
+ {
+ const size_t nObj = pGrpList->GetObjCount();
+ for(size_t i = 0 ; i < nObj ; ++i)
+ {
+ SdrObject *pSubObj = pGrpList->GetObj(i);
+ if (pSubObj &&
+ xParentContext->getAccessibleChild(i)->getAccessibleContext()->getAccessibleRole() != AccessibleRole::GROUP_BOX)
+ {
+ vXShapes.push_back( GetXShapeForSdrObject(pSubObj) );
+ }
+ }
+ }
+
+ std::sort( vXShapes.begin(), vXShapes.end(), XShapePosCompareHelper() );
+
+ //get the index of the selected object in the group
+ //we start counting position from 1
+ sal_Int32 nPos = 1;
+ for ( const auto& rpShape : vXShapes )
+ {
+ if ( rpShape.get() == mxShape.get() )
+ {
+ sal_Int32* pArray = aRet.getArray();
+ pArray[0] = nGroupLevel;
+ pArray[1] = vXShapes.size();
+ pArray[2] = nPos;
+ break;
+ }
+ nPos++;
+ }
+
+ return aRet;
+}
+
+OUString AccessibleShape::getObjectLink( const uno::Any& )
+{
+ OUString aRet;
+
+ SdrObject *pObj = SdrObject::getSdrObjectFromXShape(mxShape);
+ if(pObj == nullptr )
+ {
+ return aRet;
+ }
+ if (maShapeTreeInfo.GetDocumentWindow().is())
+ {
+ Reference< XAccessibleGroupPosition > xGroupPosition( maShapeTreeInfo.GetDocumentWindow(), uno::UNO_QUERY );
+ if (xGroupPosition.is())
+ {
+ aRet = xGroupPosition->getObjectLink( uno::Any( getAccessibleContext() ) );
+ }
+ }
+ return aRet;
+}
+
+// XAccessibleHypertext
+sal_Int32 SAL_CALL AccessibleShape::getHyperLinkCount()
+{
+ // MT: Introduced with IA2 CWS, but SvxAccessibleHyperlink was redundant to svx::AccessibleHyperlink which we introduced meanwhile.
+ // Code need to be adapted...
+ return 0;
+
+ /*
+ SvxAccessibleHyperlink* pLink = new SvxAccessibleHyperlink(m_pShape,this);
+ if (pLink->IsValidHyperlink())
+ return 1;
+ else
+ return 0;
+ */
+}
+uno::Reference< XAccessibleHyperlink > SAL_CALL
+ AccessibleShape::getHyperLink( sal_Int32 )
+{
+ uno::Reference< XAccessibleHyperlink > xRet;
+ // MT: Introduced with IA2 CWS, but SvxAccessibleHyperlink was redundant to svx::AccessibleHyperlink which we introduced meanwhile.
+ // Code need to be adapted...
+ /*
+ SvxAccessibleHyperlink* pLink = new SvxAccessibleHyperlink(m_pShape,this);
+ if (pLink->IsValidHyperlink())
+ xRet = pLink;
+ if( !xRet.is() )
+ throw css::lang::IndexOutOfBoundsException();
+ */
+ return xRet;
+}
+sal_Int32 SAL_CALL AccessibleShape::getHyperLinkIndex( sal_Int32 )
+{
+ return 0;
+}
+// XAccessibleText
+sal_Int32 SAL_CALL AccessibleShape::getCaretPosition( ){return 0;}
+sal_Bool SAL_CALL AccessibleShape::setCaretPosition( sal_Int32 ){return false;}
+sal_Unicode SAL_CALL AccessibleShape::getCharacter( sal_Int32 ){return 0;}
+css::uno::Sequence< css::beans::PropertyValue > SAL_CALL AccessibleShape::getCharacterAttributes( sal_Int32, const css::uno::Sequence< OUString >& )
+{
+ uno::Sequence< css::beans::PropertyValue > aValues(0);
+ return aValues;
+}
+css::awt::Rectangle SAL_CALL AccessibleShape::getCharacterBounds( sal_Int32 )
+{
+ return css::awt::Rectangle(0, 0, 0, 0 );
+}
+sal_Int32 SAL_CALL AccessibleShape::getCharacterCount( ){return 0;}
+sal_Int32 SAL_CALL AccessibleShape::getIndexAtPoint( const css::awt::Point& ){return 0;}
+OUString SAL_CALL AccessibleShape::getSelectedText( ){return OUString();}
+sal_Int32 SAL_CALL AccessibleShape::getSelectionStart( ){return 0;}
+sal_Int32 SAL_CALL AccessibleShape::getSelectionEnd( ){return 0;}
+sal_Bool SAL_CALL AccessibleShape::setSelection( sal_Int32, sal_Int32 ){return true;}
+OUString SAL_CALL AccessibleShape::getText( ){return OUString();}
+OUString SAL_CALL AccessibleShape::getTextRange( sal_Int32, sal_Int32 ){return OUString();}
+css::accessibility::TextSegment SAL_CALL AccessibleShape::getTextAtIndex( sal_Int32, sal_Int16 )
+{
+ css::accessibility::TextSegment aResult;
+ return aResult;
+}
+css::accessibility::TextSegment SAL_CALL AccessibleShape::getTextBeforeIndex( sal_Int32, sal_Int16 )
+{
+ css::accessibility::TextSegment aResult;
+ return aResult;
+}
+css::accessibility::TextSegment SAL_CALL AccessibleShape::getTextBehindIndex( sal_Int32, sal_Int16 )
+{
+ css::accessibility::TextSegment aResult;
+ return aResult;
+}
+sal_Bool SAL_CALL AccessibleShape::copyText( sal_Int32, sal_Int32 ){return true;}
+sal_Bool SAL_CALL AccessibleShape::scrollSubstringTo( sal_Int32, sal_Int32, AccessibleScrollType ){return false;}
+
+} // end of namespace accessibility
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/AccessibleShapeInfo.cxx b/svx/source/accessibility/AccessibleShapeInfo.cxx
new file mode 100644
index 0000000000..8904480adc
--- /dev/null
+++ b/svx/source/accessibility/AccessibleShapeInfo.cxx
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svx/AccessibleShapeInfo.hxx>
+#include <utility>
+
+
+namespace accessibility {
+
+AccessibleShapeInfo::AccessibleShapeInfo (
+ css::uno::Reference<css::drawing::XShape> xShape,
+ css::uno::Reference<css::accessibility::XAccessible> xParent,
+ IAccessibleParent* pChildrenManager)
+ : mxShape (std::move(xShape)),
+ mxParent (std::move(xParent)),
+ mpChildrenManager (pChildrenManager)
+{
+ // empty.
+}
+
+
+AccessibleShapeInfo::AccessibleShapeInfo (
+ css::uno::Reference<css::drawing::XShape> xShape,
+ css::uno::Reference<css::accessibility::XAccessible> xParent)
+ : mxShape (std::move(xShape)),
+ mxParent (std::move(xParent)),
+ mpChildrenManager (nullptr)
+{
+ // empty.
+}
+
+AccessibleShapeInfo::AccessibleShapeInfo (const AccessibleShapeInfo &rOther)
+ : mxShape (rOther.mxShape),
+ mxParent (rOther.mxParent),
+ mpChildrenManager (rOther.mpChildrenManager)
+{
+ // empty.
+}
+
+
+AccessibleShapeInfo::~AccessibleShapeInfo()
+{
+ // empty.
+}
+
+} // end of namespace accessibility.
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/AccessibleShapeTreeInfo.cxx b/svx/source/accessibility/AccessibleShapeTreeInfo.cxx
new file mode 100644
index 0000000000..70fd6e44d8
--- /dev/null
+++ b/svx/source/accessibility/AccessibleShapeTreeInfo.cxx
@@ -0,0 +1,120 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <svx/AccessibleShapeTreeInfo.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/window.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+using ::com::sun::star::uno::Reference;
+
+namespace accessibility {
+
+AccessibleShapeTreeInfo::AccessibleShapeTreeInfo()
+ : mpView (nullptr),
+ mpWindow (nullptr),
+ mpViewForwarder (nullptr)
+{
+ // 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.
+}
+
+void AccessibleShapeTreeInfo::dispose()
+{
+ mxDocumentWindow.clear();
+ mxModelBroadcaster.clear();
+ mpView = nullptr;
+ mxController.clear();
+ mpWindow.reset();
+ mpViewForwarder = nullptr;
+}
+
+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()
+{
+ if (mpWindow)
+ {
+ SolarMutexGuard g;
+ mpWindow.reset();
+ }
+}
+
+void AccessibleShapeTreeInfo::SetDocumentWindow (
+ const Reference<XAccessibleComponent>& rxDocumentWindow)
+{
+ if (mxDocumentWindow != rxDocumentWindow)
+ mxDocumentWindow = rxDocumentWindow;
+}
+
+void AccessibleShapeTreeInfo::SetModelBroadcaster (
+ const Reference<document::XShapeEventBroadcaster>& rxModelBroadcaster)
+{
+ mxModelBroadcaster = rxModelBroadcaster;
+}
+
+void AccessibleShapeTreeInfo::SetSdrView (SdrView* pView)
+{
+ mpView = pView;
+}
+
+void AccessibleShapeTreeInfo::SetController (
+ const Reference<frame::XController>& rxController)
+{
+ mxController = rxController;
+}
+
+void AccessibleShapeTreeInfo::SetWindow(vcl::Window* pDevice)
+{
+ mpWindow = pDevice;
+}
+
+void AccessibleShapeTreeInfo::SetViewForwarder (const IAccessibleViewForwarder* pViewForwarder)
+{
+ mpViewForwarder = pViewForwarder;
+}
+
+} // end of namespace accessibility
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/AccessibleTextEventQueue.cxx b/svx/source/accessibility/AccessibleTextEventQueue.cxx
new file mode 100644
index 0000000000..a39123c45e
--- /dev/null
+++ b/svx/source/accessibility/AccessibleTextEventQueue.cxx
@@ -0,0 +1,92 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include "AccessibleTextEventQueue.hxx"
+
+#include <editeng/unoedhlp.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdpntv.hxx>
+
+namespace accessibility
+{
+
+
+ // EventQueue implementation
+
+
+ AccessibleTextEventQueue::AccessibleTextEventQueue()
+ {
+ }
+
+ AccessibleTextEventQueue::~AccessibleTextEventQueue()
+ {
+ Clear();
+ }
+
+ void AccessibleTextEventQueue::Append( const SdrHint& rHint )
+ {
+ // only enqueue the events we actually care about in
+ // AccessibleTextHelper_Impl::ProcessQueue(), because
+ // the cost of some events adds up.
+ auto eKind = rHint.GetKind();
+ if (eKind == SdrHintKind::BeginEdit
+ || eKind == SdrHintKind::EndEdit)
+ maEventQueue.push_back( new SdrHint( rHint ) );
+ }
+
+ void AccessibleTextEventQueue::Append( const TextHint& rHint )
+ {
+ maEventQueue.push_back( new TextHint( rHint ) );
+ }
+
+ void AccessibleTextEventQueue::Append( const SvxViewChangedHint& rHint )
+ {
+ maEventQueue.push_back( new SvxViewChangedHint( rHint ) );
+ }
+
+ void AccessibleTextEventQueue::Append( const SvxEditSourceHint& rHint )
+ {
+ maEventQueue.push_back( new SvxEditSourceHint( rHint ) );
+ }
+
+ ::std::unique_ptr< SfxHint > AccessibleTextEventQueue::PopFront()
+ {
+ ::std::unique_ptr< SfxHint > aRes( *(maEventQueue.begin()) );
+ maEventQueue.pop_front();
+ return aRes;
+ }
+
+ bool AccessibleTextEventQueue::IsEmpty() const
+ {
+ return maEventQueue.empty();
+ }
+
+ void AccessibleTextEventQueue::Clear()
+ {
+ // clear queue
+ for( auto p : maEventQueue)
+ delete p;
+ maEventQueue.clear();
+ }
+
+} // end of namespace accessibility
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/AccessibleTextEventQueue.hxx b/svx/source/accessibility/AccessibleTextEventQueue.hxx
new file mode 100644
index 0000000000..23dbf9faa6
--- /dev/null
+++ b/svx/source/accessibility/AccessibleTextEventQueue.hxx
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SVX_SOURCE_ACCESSIBILITY_ACCESSIBLETEXTEVENTQUEUE_HXX
+#define INCLUDED_SVX_SOURCE_ACCESSIBILITY_ACCESSIBLETEXTEVENTQUEUE_HXX
+
+#include <memory>
+#include <deque>
+#include <algorithm>
+
+class SfxHint;
+class SdrHint;
+class TextHint;
+class SvxViewChangedHint;
+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::deque< SfxHint* > EventQueue;
+
+ AccessibleTextEventQueue();
+ ~AccessibleTextEventQueue();
+
+ /// Append event to end of queue
+ void Append( const SdrHint& rHint );
+ /// Append event to end of queue
+ void Append( const TextHint& rHint );
+ /// Append event to end of queue
+ void Append( const SvxViewChangedHint& 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::unique_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 // INCLUDED_SVX_SOURCE_ACCESSIBILITY_ACCESSIBLETEXTEVENTQUEUE_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/AccessibleTextHelper.cxx b/svx/source/accessibility/AccessibleTextHelper.cxx
new file mode 100644
index 0000000000..f12281793b
--- /dev/null
+++ b/svx/source/accessibility/AccessibleTextHelper.cxx
@@ -0,0 +1,1795 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <cstdlib>
+#include <memory>
+#include <mutex>
+#include <utility>
+#include <algorithm>
+#include <sal/log.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/awt/Point.hpp>
+#include <com/sun/star/awt/Rectangle.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.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 <vcl/svapp.hxx>
+#include <vcl/textdata.hxx>
+#include <vcl/unohelp.hxx>
+
+
+// Project-local header
+
+
+#include "AccessibleTextEventQueue.hxx"
+#include <svx/AccessibleTextHelper.hxx>
+
+#include <editeng/unoedhlp.hxx>
+#include <editeng/unoedprx.hxx>
+#include <editeng/AccessibleParaManager.hxx>
+#include <editeng/AccessibleEditableTextPara.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdpntv.hxx>
+#include <cell.hxx>
+#include "../table/accessiblecell.hxx"
+#include <editeng/editdata.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+namespace accessibility
+{
+
+// AccessibleTextHelper_Impl declaration
+
+ template < typename first_type, typename second_type >
+ static ::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();
+ virtual ~AccessibleTextHelper_Impl() override;
+
+ // XAccessibleContext child handling methods
+ sal_Int64 getAccessibleChildCount() const;
+ uno::Reference< XAccessible > getAccessibleChild( sal_Int64 i );
+
+ // XAccessibleEventBroadcaster child related methods
+ void addAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener );
+ void removeAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener );
+
+ // XAccessibleComponent child related methods
+ uno::Reference< XAccessible > getAccessibleAtPoint( const awt::Point& aPoint );
+
+ SvxEditSourceAdapter& GetEditSource() const;
+
+ void SetEditSource( ::std::unique_ptr< SvxEditSource > && pEditSource );
+
+ void SetEventSource( const uno::Reference< XAccessible >& rInterface )
+ {
+ mxFrontEnd = rInterface;
+ }
+
+ void SetOffset( const Point& );
+ Point GetOffset() const
+ {
+ std::scoped_lock aGuard( maMutex ); Point aPoint( maOffset );
+ return aPoint;
+ }
+
+ void SetStartIndex( sal_Int32 nOffset );
+ sal_Int32 GetStartIndex() const
+ {
+ // Strictly correct only with locked solar mutex, // but
+ // here we rely on the fact that sal_Int32 access is
+ // atomic
+ return mnStartIndex;
+ }
+
+ void SetAdditionalChildStates( sal_Int64 nChildStates );
+
+ 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( bool bHaveFocus );
+ bool HaveFocus() const
+ {
+ // No locking of solar mutex here, since we rely on the fact
+ // that sal_Bool access is atomic
+ return mbThisHasFocus;
+ }
+ void SetChildFocus( sal_Int32 nChild, bool bHaveFocus );
+ void SetShapeFocus( bool bHaveFocus );
+ void ChangeChildFocus( sal_Int32 nNewChild );
+
+#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 position 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 ); }
+
+ // shutdown usage of current edit source on myself and the children.
+ void ShutdownEditSource();
+
+ void ParagraphsMoved( sal_Int32 nFirst, sal_Int32 nMiddle, sal_Int32 nLast );
+
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+
+ comphelper::AccessibleEventNotifier::TClientId getNotifierClientId() const { return mnNotifierClientId; }
+
+ // lock solar mutex before
+ SvxTextForwarder& GetTextForwarder() const;
+ // lock solar mutex before
+ SvxViewForwarder& GetViewForwarder() const;
+ // lock solar mutex before
+ SvxEditViewForwarder& GetEditViewForwarder() const;
+
+ // are we in edit mode?
+ bool IsActive() const;
+
+ // 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;
+
+ // Queued events from Notify() (guarded by solar mutex)
+ AccessibleTextEventQueue maEventQueue;
+
+ // spin lock to prevent notify in notify (guarded by solar mutex)
+ bool mbInNotify;
+
+ // whether the object or its children has the focus set (guarded by solar mutex)
+ bool mbGroupHasFocus;
+
+ // whether we (this object) has the focus set (guarded by solar mutex)
+ bool mbThisHasFocus;
+
+ mutable std::mutex maMutex;
+
+ /// our current offset to the containing shape/cell (guarded by maMutex)
+ Point maOffset;
+
+ /// client Id from AccessibleEventNotifier
+ comphelper::AccessibleEventNotifier::TClientId mnNotifierClientId;
+ static constexpr comphelper::AccessibleEventNotifier::TClientId snNotifierClientRevoked
+ = std::numeric_limits<comphelper::AccessibleEventNotifier::TClientId>::max();
+ };
+
+ AccessibleTextHelper_Impl::AccessibleTextHelper_Impl() :
+ maLastSelection( EE_PARA_NOT_FOUND,EE_INDEX_NOT_FOUND,EE_PARA_NOT_FOUND,EE_INDEX_NOT_FOUND ),
+ mnFirstVisibleChild( -1 ),
+ mnLastVisibleChild( -2 ),
+ mnStartIndex( 0 ),
+ mbInNotify( false ),
+ mbGroupHasFocus( false ),
+ mbThisHasFocus( 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())
+ {
+ SAL_INFO("svx", "received ID: " << mnNotifierClientId );
+ }
+
+ AccessibleTextHelper_Impl::~AccessibleTextHelper_Impl()
+ {
+ SolarMutexGuard aGuard;
+
+ try
+ {
+ // call Dispose here, too, since we've some resources not
+ // automatically freed otherwise
+ Dispose();
+ }
+ catch( const uno::Exception& ) {}
+ }
+
+ SvxTextForwarder& AccessibleTextHelper_Impl::GetTextForwarder() const
+ {
+ if( !maEditSource.IsValid() )
+ throw uno::RuntimeException("Unknown edit source", mxFrontEnd);
+
+ SvxTextForwarder* pTextForwarder = maEditSource.GetTextForwarder();
+
+ if( !pTextForwarder )
+ throw uno::RuntimeException("Unable to fetch text forwarder, model might be dead", mxFrontEnd);
+
+ if( !pTextForwarder->IsValid() )
+ throw uno::RuntimeException("Text forwarder is invalid, model might be dead", mxFrontEnd);
+
+ return *pTextForwarder;
+ }
+
+ SvxViewForwarder& AccessibleTextHelper_Impl::GetViewForwarder() const
+ {
+ if( !maEditSource.IsValid() )
+ throw uno::RuntimeException("Unknown edit source", mxFrontEnd);
+
+ SvxViewForwarder* pViewForwarder = maEditSource.GetViewForwarder();
+
+ if( !pViewForwarder )
+ throw uno::RuntimeException("Unable to fetch view forwarder, model might be dead", mxFrontEnd);
+
+ if( !pViewForwarder->IsValid() )
+ throw uno::RuntimeException("View forwarder is invalid, model might be dead", mxFrontEnd);
+
+ return *pViewForwarder;
+ }
+
+ SvxEditViewForwarder& AccessibleTextHelper_Impl::GetEditViewForwarder() const
+ {
+ if( !maEditSource.IsValid() )
+ throw uno::RuntimeException("Unknown edit source", mxFrontEnd);
+
+ SvxEditViewForwarder* pViewForwarder = maEditSource.GetEditViewForwarder();
+
+ if( !pViewForwarder )
+ {
+ throw uno::RuntimeException("No edit view forwarder, object not in edit mode", mxFrontEnd);
+ }
+
+ if( !pViewForwarder->IsValid() )
+ {
+ throw uno::RuntimeException("View forwarder is invalid, object not in edit mode", mxFrontEnd);
+ }
+
+ return *pViewForwarder;
+ }
+
+ SvxEditSourceAdapter& AccessibleTextHelper_Impl::GetEditSource() const
+ {
+ if( !maEditSource.IsValid() )
+ throw uno::RuntimeException("AccessibleTextHelper_Impl::GetEditSource: no edit source", mxFrontEnd );
+ return maEditSource;
+ }
+
+ namespace {
+
+ // functor for sending child events (no stand-alone function, they are maybe not inlined)
+ class AccessibleTextHelper_OffsetChildIndex
+ {
+ public:
+ explicit 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 )
+ {
+ 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( sal_Int64 nChildStates )
+ {
+ maParaManager.SetAdditionalChildStates( nChildStates );
+ }
+
+ void AccessibleTextHelper_Impl::SetChildFocus( sal_Int32 nChild, bool bHaveFocus )
+ {
+ if( bHaveFocus )
+ {
+ if( mbThisHasFocus )
+ SetShapeFocus( false );
+
+ maParaManager.SetFocus( nChild );
+
+ // we just received the focus, also send caret event then
+ UpdateSelection();
+
+ SAL_INFO("svx", "Paragraph " << nChild << " received focus");
+ }
+ else
+ {
+ maParaManager.SetFocus( -1 );
+
+ SAL_INFO("svx", "Paragraph " << nChild << " lost focus");
+
+ if( mbGroupHasFocus )
+ SetShapeFocus( true );
+ }
+ }
+
+ void AccessibleTextHelper_Impl::ChangeChildFocus( sal_Int32 nNewChild )
+ {
+ if( mbThisHasFocus )
+ SetShapeFocus( false );
+
+ mbGroupHasFocus = true;
+ maParaManager.SetFocus( nNewChild );
+
+ SAL_INFO("svx", "Paragraph " << nNewChild << " received focus");
+ }
+
+ void AccessibleTextHelper_Impl::SetShapeFocus( bool bHaveFocus )
+ {
+ bool bOldFocus( mbThisHasFocus );
+
+ mbThisHasFocus = bHaveFocus;
+
+ if( bOldFocus == bHaveFocus )
+ return;
+
+ if( bHaveFocus )
+ {
+ if( mxFrontEnd.is() )
+ {
+ AccessibleCell* pAccessibleCell = dynamic_cast< AccessibleCell* > ( mxFrontEnd.get() );
+ if ( !pAccessibleCell )
+ GotPropertyEvent( uno::Any(AccessibleStateType::FOCUSED), AccessibleEventId::STATE_CHANGED );
+ else // the focus event on cell should be fired on table directly
+ {
+ AccessibleTableShape* pAccTable = pAccessibleCell->GetParentTable();
+ if (pAccTable)
+ pAccTable->SetStateDirectly(AccessibleStateType::FOCUSED);
+ }
+ }
+ SAL_INFO("svx", "Parent object received focus" );
+ }
+ else
+ {
+ // The focus state should be reset directly on table.
+ //LostPropertyEvent( uno::makeAny(AccessibleStateType::FOCUSED), AccessibleEventId::STATE_CHANGED );
+ if( mxFrontEnd.is() )
+ {
+ AccessibleCell* pAccessibleCell = dynamic_cast< AccessibleCell* > ( mxFrontEnd.get() );
+ if ( !pAccessibleCell )
+ FireEvent( AccessibleEventId::STATE_CHANGED, uno::Any(), uno::Any(AccessibleStateType::FOCUSED) );
+ else
+ {
+ AccessibleTableShape* pAccTable = pAccessibleCell->GetParentTable();
+ if (pAccTable)
+ pAccTable->ResetStateDirectly(AccessibleStateType::FOCUSED);
+ }
+ }
+ SAL_INFO("svx", "Parent object lost focus" );
+ }
+ }
+
+ void AccessibleTextHelper_Impl::SetFocus( bool bHaveFocus )
+ {
+ 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 );
+ }
+
+ SAL_INFO("svx", "focus changed, Object " << this << ", state: " << (bHaveFocus ? "focused" : "not focused") );
+ }
+
+ bool AccessibleTextHelper_Impl::IsActive() const
+ {
+ try
+ {
+ SvxEditSource& rEditSource = GetEditSource();
+ SvxEditViewForwarder* pViewForwarder = rEditSource.GetEditViewForwarder();
+
+ if( !pViewForwarder )
+ return false;
+
+ if( mxFrontEnd.is() )
+ {
+ AccessibleCell* pAccessibleCell = dynamic_cast< AccessibleCell* > ( mxFrontEnd.get() );
+ if ( pAccessibleCell )
+ {
+ sdr::table::CellRef xCell = pAccessibleCell->getCellRef();
+ if ( xCell.is() )
+ return xCell->IsActiveCell();
+ }
+ }
+ return pViewForwarder->IsValid();
+ }
+ catch( const uno::RuntimeException& )
+ {
+ return false;
+ }
+ }
+
+ void AccessibleTextHelper_Impl::UpdateSelection()
+ {
+ try
+ {
+ ESelection aSelection;
+ if( GetEditViewForwarder().GetSelection( aSelection ) )
+ {
+ if( maLastSelection != aSelection &&
+ aSelection.nEndPara < maParaManager.GetNum() )
+ {
+ // #103998# Not that important, changed from assertion to trace
+ if( mbThisHasFocus )
+ {
+ SAL_INFO("svx", "Parent has focus!");
+ }
+
+ sal_Int32 nMaxValidParaIndex( 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::Any(static_cast<sal_Int32>(-1)),
+ uno::Any(maLastSelection.nEndPos) );
+ }
+
+ ChangeChildFocus( aSelection.nEndPara );
+
+ SAL_INFO(
+ "svx",
+ "focus changed, Object: " << this
+ << ", Paragraph: " << aSelection.nEndPara
+ << ", Last paragraph: "
+ << 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 <<= maLastSelection.nEndPos;
+ }
+ else
+ {
+ aOldCursor <<= static_cast<sal_Int32>(-1);
+ }
+
+ maParaManager.FireEvent( aSelection.nEndPara,
+ aSelection.nEndPara+1,
+ AccessibleEventId::CARET_CHANGED,
+ uno::Any(aSelection.nEndPos),
+ aOldCursor );
+ }
+
+ SAL_INFO(
+ "svx",
+ "caret changed, Object: " << this << ", New pos: "
+ << aSelection.nEndPos << ", Old pos: "
+ << maLastSelection.nEndPos << ", New para: "
+ << aSelection.nEndPara << ", Old para: "
+ << maLastSelection.nEndPara);
+
+ // #108947# Sort new range before calling FireEvent
+ ::std::pair<sal_Int32, sal_Int32> sortedSelection(
+ makeSortedPair(::std::min( aSelection.nStartPara, nMaxValidParaIndex ),
+ ::std::min( aSelection.nEndPara, nMaxValidParaIndex ) ) );
+
+ // #108947# Sort last range before calling FireEvent
+ ::std::pair<sal_Int32, sal_Int32> sortedLastSelection(
+ makeSortedPair(::std::min( maLastSelection.nStartPara, nMaxValidParaIndex ),
+ ::std::min( maLastSelection.nEndPara, nMaxValidParaIndex ) ) );
+
+ // event TEXT_SELECTION_CHANGED has to be submitted. (#i27299#)
+ const sal_Int16 nTextSelChgEventId =
+ AccessibleEventId::TEXT_SELECTION_CHANGED;
+ // #107037# notify selection change
+ if( maLastSelection.nStartPara == EE_PARA_NOT_FOUND )
+ {
+ // last selection is undefined
+ // use method <ESelection::HasRange()> (#i27299#)
+ if ( aSelection.HasRange() )
+ {
+ // selection was undefined, now is on
+ maParaManager.FireEvent( sortedSelection.first,
+ sortedSelection.second+1,
+ nTextSelChgEventId );
+ }
+ }
+ else
+ {
+ // last selection is valid
+ // use method <ESelection::HasRange()> (#i27299#)
+ if ( maLastSelection.HasRange() &&
+ !aSelection.HasRange() )
+ {
+ // selection was on, now is empty
+ maParaManager.FireEvent( sortedLastSelection.first,
+ sortedLastSelection.second+1,
+ nTextSelChgEventId );
+ }
+ // use method <ESelection::HasRange()> (#i27299#)
+ else if( !maLastSelection.HasRange() &&
+ aSelection.HasRange() )
+ {
+ // selection was empty, now is on
+ maParaManager.FireEvent( sortedSelection.first,
+ sortedSelection.second+1,
+ nTextSelChgEventId );
+ }
+ // no event TEXT_SELECTION_CHANGED event, if new and
+ // last selection are empty. (#i27299#)
+ else if ( maLastSelection.HasRange() &&
+ aSelection.HasRange() )
+ {
+ // 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_Int32 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 sal_Int32 nParaStartPos =
+ nPara == aTmpSel.nStartPara
+ ? aTmpSel.nStartPos : 0;
+ const sal_Int32 nParaEndPos =
+ nPara == aTmpSel.nEndPara
+ ? aTmpSel.nEndPos : -1;
+ const sal_Int32 nLastParaStartPos =
+ nPara == aTmpLastSel.nStartPara
+ ? aTmpLastSel.nStartPos : 0;
+ const sal_Int32 nLastParaEndPos =
+ nPara == aTmpLastSel.nEndPara
+ ? aTmpLastSel.nEndPos : -1;
+ 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()
+ {
+ // 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::unique_ptr< SvxEditSource >() );
+ }
+
+ void AccessibleTextHelper_Impl::SetEditSource( ::std::unique_ptr< SvxEditSource > && pEditSource )
+ {
+ // 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( std::move(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 )
+ {
+ // guard against non-atomic access to maOffset data structure
+ {
+ std::scoped_lock aGuard( maMutex );
+ maOffset = rPoint;
+ }
+
+ maParaManager.SetEEOffset( rPoint );
+
+ // in all cases, check visibility afterwards.
+ UpdateVisibleChildren();
+ UpdateBoundRect();
+ }
+
+ void AccessibleTextHelper_Impl::UpdateVisibleChildren( bool bBroadcastEvents )
+ {
+ try
+ {
+ SvxTextForwarder& rCacheTF = GetTextForwarder();
+ sal_Int32 nParas=rCacheTF.GetParagraphCount();
+
+ // GetTextForwarder might have replaced everything, update
+ // paragraph count in case it's outdated
+ maParaManager.SetNum( nParas );
+
+ mnFirstVisibleChild = -1;
+ mnLastVisibleChild = -2;
+
+ for( sal_Int32 nCurrPara=0; nCurrPara<nParas; ++nCurrPara )
+ {
+ if (nCurrPara == 0)
+ mnFirstVisibleChild = nCurrPara;
+ mnLastVisibleChild = nCurrPara;
+ if (mxFrontEnd.is() && bBroadcastEvents)
+ {
+ // child not yet created?
+ if (!maParaManager.HasCreatedChild(nCurrPara))
+ {
+ GotPropertyEvent( uno::Any( maParaManager.CreateChild( nCurrPara - mnFirstVisibleChild,
+ mxFrontEnd, GetEditSource(), nCurrPara ).first ),
+ AccessibleEventId::CHILD );
+ }
+ }
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ OSL_FAIL("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);
+ }
+ }
+
+ void AccessibleTextHelper_Impl::UpdateBoundRect()
+ {
+ // send BOUNDRECT_CHANGED to affected children
+ for(auto it = maParaManager.begin(); it != maParaManager.end(); ++it)
+ {
+ ::accessibility::AccessibleParaManager::WeakChild& rChild = *it;
+ // retrieve hard reference from weak one
+ auto 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
+ rChild = ::accessibility::AccessibleParaManager::WeakChild( rChild.first, aNewRect );
+ }
+ }
+ }
+ }
+
+#ifdef DBG_UTIL
+ void AccessibleTextHelper_Impl::CheckInvariants() const
+ {
+ if( mnFirstVisibleChild >= 0 &&
+ mnFirstVisibleChild > mnLastVisibleChild )
+ {
+ OSL_FAIL( "AccessibleTextHelper: range invalid" );
+ }
+ }
+#endif
+
+ namespace {
+
+ // functor for sending child events (no stand-alone function, they are maybe not inlined)
+ class AccessibleTextHelper_LostChildEvent
+ {
+ public:
+ explicit AccessibleTextHelper_LostChildEvent( AccessibleTextHelper_Impl& rImpl ) : mrImpl(rImpl) {}
+ void operator()( const ::accessibility::AccessibleParaManager::WeakChild& rPara )
+ {
+ // retrieve hard reference from weak one
+ auto aHardRef( rPara.first.get() );
+
+ if( aHardRef.is() )
+ mrImpl.FireEvent(AccessibleEventId::CHILD, uno::Any(), uno::Any(css::uno::Reference<css::accessibility::XAccessible>(aHardRef)) );
+ }
+
+ private:
+ AccessibleTextHelper_Impl& mrImpl;
+ };
+
+ }
+
+ void AccessibleTextHelper_Impl::ParagraphsMoved( sal_Int32 nFirst, sal_Int32 nMiddle, sal_Int32 nLast )
+ {
+ 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) )
+ return;
+
+ // 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 InvalidateFlags::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.
+ }
+
+ namespace {
+
+ // functor for sending child events (no stand-alone function, they are maybe not inlined)
+ class AccessibleTextHelper_ChildrenTextChanged
+ {
+ public:
+ void operator()( ::accessibility::AccessibleEditableTextPara& rPara )
+ {
+ rPara.TextChanged();
+ }
+ };
+
+ /** functor processing queue events
+
+ Reacts on SfxHintId::TextParaInserted/REMOVED events and stores
+ their content
+ */
+ class AccessibleTextHelper_QueueFunctor
+ {
+ public:
+ AccessibleTextHelper_QueueFunctor() :
+ mnParasChanged( 0 ),
+ mnParaIndex(-1),
+ mnHintId(SfxHintId::NONE)
+ {}
+ void operator()( const SfxHint* pEvent )
+ {
+ if( !pEvent || mnParasChanged == -1 )
+ return;
+
+ // determine hint type
+ const TextHint* pTextHint = dynamic_cast<const TextHint*>( pEvent );
+ const SvxEditSourceHint* pEditSourceHint = dynamic_cast<const SvxEditSourceHint*>( pEvent );
+
+ if( !(!pEditSourceHint && pTextHint &&
+ (pTextHint->GetId() == SfxHintId::TextParaInserted ||
+ pTextHint->GetId() == SfxHintId::TextParaRemoved )) )
+ return;
+
+ 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"
+ */
+ sal_Int32 GetNumberOfParasChanged() const { return mnParasChanged; }
+ /** Query index of last added/removed paragraph
+
+ @return index of lastly added paragraphs, -1 for none
+ added so far.
+ */
+ sal_Int32 GetParaIndex() const { return mnParaIndex; }
+ /** Query hint id of last interesting event
+
+ @return hint id of last interesting event (REMOVED/INSERTED).
+ */
+ SfxHintId GetHintId() const { return mnHintId; }
+
+ private:
+ /** number of paragraphs changed during queue processing. -1 for
+ "every paragraph changed"
+ */
+ sal_Int32 mnParasChanged;
+ /// index of paragraph added/removed last
+ sal_Int32 mnParaIndex;
+ /// TextHint ID (removed/inserted) of last interesting event
+ SfxHintId mnHintId;
+ };
+
+ }
+
+ void AccessibleTextHelper_Impl::ProcessQueue()
+ {
+ // 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( std::abs( nNewParas - nCurrParas ) == 1 &&
+ aFunctor.GetNumberOfParasChanged() == 1 )
+ {
+ // #103483# Exactly one paragraph added/removed. This is
+ // the normal case, optimize event handling here.
+
+ if( aFunctor.GetHintId() == SfxHintId::TextParaInserted )
+ {
+ // 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::Any( getAccessibleChild( aFunctor.GetParaIndex() -
+ mnFirstVisibleChild + GetStartIndex() ) ),
+ AccessibleEventId::CHILD );
+ }
+ catch( const uno::Exception& )
+ {
+ OSL_FAIL("AccessibleTextHelper_Impl::ProcessQueue: could not create new paragraph");
+ }
+ }
+ else if( aFunctor.GetHintId() == SfxHintId::TextParaRemoved )
+ {
+ ::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(begin->first.get());
+
+ // 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::Any( xPara) );
+ }
+#ifdef DBG_UTIL
+ else
+ OSL_FAIL("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;
+ }
+
+ bool bUpdatedBoundRectAndVisibleChildren(false);
+
+ while( !maEventQueue.IsEmpty() )
+ {
+ ::std::unique_ptr< SfxHint > pHint( maEventQueue.PopFront() );
+ if (pHint)
+ {
+ const SfxHint& rHint = *pHint;
+
+ // Note, if you add events here, you need to update the AccessibleTextEventQueue::Append
+ // code, because only the events we process here, are actually queued there.
+
+ try
+ {
+
+ if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint)
+ {
+ const SdrHint* pSdrHint = static_cast< const SdrHint* >( &rHint );
+
+ switch( pSdrHint->GetKind() )
+ {
+ case SdrHintKind::BeginEdit:
+ {
+ if(!IsActive())
+ {
+ break;
+ }
+ // change children state
+ maParaManager.SetActive();
+
+ // per definition, edit mode text has the focus
+ SetFocus( true );
+ break;
+ }
+
+ case SdrHintKind::EndEdit:
+ {
+ // focused child now loses focus
+ ESelection aSelection;
+ if( GetEditViewForwarder().GetSelection( aSelection ) )
+ SetChildFocus( aSelection.nEndPara, false );
+
+ // change children state
+ maParaManager.SetActive( false );
+
+ maLastSelection = ESelection( EE_PARA_NOT_FOUND, EE_INDEX_NOT_FOUND,
+ EE_PARA_NOT_FOUND, EE_INDEX_NOT_FOUND);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ else if( const SvxEditSourceHint* pEditSourceHint = dynamic_cast<const SvxEditSourceHint*>( &rHint ) )
+ {
+ switch( pEditSourceHint->GetId() )
+ {
+ case SfxHintId::EditSourceParasMoved:
+ {
+ 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 SfxHintId::EditSourceSelectionChanged:
+ // notify listeners
+ try
+ {
+ UpdateSelection();
+ }
+ // maybe we're not in edit mode (this is not an error)
+ catch( const uno::Exception& ) {}
+ break;
+ default: break;
+ }
+ }
+ else if( const TextHint* pTextHint = dynamic_cast<const TextHint*>( &rHint ) )
+ {
+ const sal_Int32 nParas = GetTextForwarder().GetParagraphCount();
+
+ switch( pTextHint->GetId() )
+ {
+ case SfxHintId::TextModified:
+ {
+ // notify listeners
+ sal_Int32 nPara( pTextHint->GetValue() );
+
+ // #108900# Delegate change event to children
+ AccessibleTextHelper_ChildrenTextChanged aNotifyChildrenFunctor;
+
+ if( nPara == 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 SfxHintId::TextParaInserted:
+ // already happened above
+ break;
+
+ case SfxHintId::TextParaRemoved:
+ // already happened above
+ break;
+
+ case SfxHintId::TextHeightChanged:
+ // visibility changed, done below
+ break;
+
+ case SfxHintId::TextViewScrolled:
+ // visibility changed, done below
+ break;
+ default: break;
+ }
+
+ // in all cases, check visibility afterwards.
+ if (!bUpdatedBoundRectAndVisibleChildren)
+ {
+ UpdateVisibleChildren();
+ UpdateBoundRect();
+ bUpdatedBoundRectAndVisibleChildren = true;
+ }
+ }
+ else if (rHint.GetId() == SfxHintId::SvxViewChanged)
+ {
+ // just check visibility
+ if (!bUpdatedBoundRectAndVisibleChildren)
+ {
+ UpdateVisibleChildren();
+ UpdateBoundRect();
+ bUpdatedBoundRectAndVisibleChildren = true;
+ }
+ }
+ // it's VITAL to keep the SfxSimpleHint last! It's the base of some classes above!
+ else if( rHint.GetId() == SfxHintId::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& ) {}
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ }
+ }
+
+ void AccessibleTextHelper_Impl::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
+ {
+ // precondition: solar mutex locked
+ DBG_TESTSOLARMUTEX();
+
+ // precondition: not in a recursion
+ if( mbInNotify )
+ return;
+
+ mbInNotify = true;
+
+ try
+ {
+ // Process notification event, arranged in order of likelihood of
+ // occurrence to avoid unnecessary dynamic_cast. Note that
+ // SvxEditSourceHint is derived from TextHint, so has to be checked
+ // before that.
+ if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint)
+ {
+ const SdrHint* pSdrHint = static_cast< const SdrHint* >( &rHint );
+ // 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.
+ maEventQueue.Append( *pSdrHint );
+ }
+ else if (rHint.GetId() == SfxHintId::SvxViewChanged)
+ {
+ const SvxViewChangedHint* pViewHint = static_cast<const SvxViewChangedHint*>(&rHint);
+ // process visibility right away, if not within an
+ // open EE notification frame. Otherwise, event
+ // processing would be delayed until next EE
+ // notification sequence.
+ maEventQueue.Append( *pViewHint );
+ }
+ else if( const SvxEditSourceHint* pEditSourceHint = dynamic_cast<const SvxEditSourceHint*>( &rHint ) )
+ {
+ // EditEngine should emit TEXT_SELECTION_CHANGED events (#i27299#)
+ maEventQueue.Append( *pEditSourceHint );
+ }
+ else if( const TextHint* pTextHint = dynamic_cast<const TextHint*>( &rHint ) )
+ {
+ // EditEngine should emit TEXT_SELECTION_CHANGED events (#i27299#)
+ if(pTextHint->GetId() == SfxHintId::TextProcessNotifications)
+ ProcessQueue();
+ else
+ maEventQueue.Append( *pTextHint );
+ }
+ // it's VITAL to keep the SfxHint last! It's the base of the classes above!
+ else if( rHint.GetId() == SfxHintId::Dying )
+ {
+ // handle this event _at once_, because after that, objects are invalid
+ // 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& ) {}
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ mbInNotify = false;
+ }
+
+ mbInNotify = false;
+ }
+
+ void AccessibleTextHelper_Impl::Dispose()
+ {
+ if( getNotifierClientId() != snNotifierClientRevoked)
+ {
+ try
+ {
+ // #106234# Unregister from EventNotifier
+ ::comphelper::AccessibleEventNotifier::revokeClient( getNotifierClientId() );
+ SAL_INFO("svx", "disposed ID: " << mnNotifierClientId );
+ }
+ catch( const uno::Exception& ) {}
+
+ mnNotifierClientId = snNotifierClientRevoked;
+ }
+
+ 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::unique_ptr< SvxEditSource >() );
+ mxFrontEnd = nullptr;
+ }
+
+ void AccessibleTextHelper_Impl::FireEvent( const sal_Int16 nEventId, const uno::Any& rNewValue, const uno::Any& rOldValue ) const
+ {
+ // -- object locked --
+ AccessibleEventObject aEvent;
+ {
+ std::scoped_lock aGuard(maMutex);
+
+ DBG_ASSERT(mxFrontEnd.is(), "AccessibleTextHelper::FireEvent: no event source set");
+
+ if (mxFrontEnd.is())
+ aEvent = AccessibleEventObject(mxFrontEnd->getAccessibleContext(), nEventId,
+ rNewValue, rOldValue, -1);
+ else
+ aEvent = AccessibleEventObject(uno::Reference<uno::XInterface>(), nEventId,
+ rNewValue, rOldValue, -1);
+
+ // 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
+ }
+ // -- until here --
+
+ FireEvent(aEvent);
+ }
+
+ void AccessibleTextHelper_Impl::FireEvent( const AccessibleEventObject& rEvent ) const
+ {
+ // #106234# Delegate to EventNotifier
+ if (getNotifierClientId() != snNotifierClientRevoked)
+ ::comphelper::AccessibleEventNotifier::addEvent( getNotifierClientId(), rEvent );
+ }
+
+ // XAccessibleContext
+ sal_Int64 AccessibleTextHelper_Impl::getAccessibleChildCount() const
+ {
+ return mnLastVisibleChild - mnFirstVisibleChild + 1;
+ }
+
+ uno::Reference< XAccessible > AccessibleTextHelper_Impl::getAccessibleChild( sal_Int64 i )
+ {
+ i -= GetStartIndex();
+
+ if( 0 > i || i >= getAccessibleChildCount() ||
+ GetTextForwarder().GetParagraphCount() <= i )
+ {
+ throw lang::IndexOutOfBoundsException("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 nullptr;
+ }
+
+ void AccessibleTextHelper_Impl::addAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener )
+ {
+ if( getNotifierClientId() != snNotifierClientRevoked )
+ ::comphelper::AccessibleEventNotifier::addEventListener( getNotifierClientId(), xListener );
+ }
+
+ void AccessibleTextHelper_Impl::removeAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener )
+ {
+ if( getNotifierClientId() == snNotifierClientRevoked )
+ return;
+
+ const sal_Int32 nListenerCount = ::comphelper::AccessibleEventNotifier::removeEventListener( getNotifierClientId(), 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::TClientId nId( getNotifierClientId() );
+ mnNotifierClientId = snNotifierClientRevoked;
+ ::comphelper::AccessibleEventNotifier::revokeClient( nId );
+ }
+ }
+
+ uno::Reference< XAccessible > AccessibleTextHelper_Impl::getAccessibleAtPoint( const awt::Point& _aPoint )
+ {
+ // make given position relative
+ if( !mxFrontEnd.is() )
+ throw uno::RuntimeException("AccessibleTextHelper_Impl::getAccessibleAt: frontend invalid", mxFrontEnd );
+
+ uno::Reference< XAccessibleContext > xFrontEndContext = mxFrontEnd->getAccessibleContext();
+
+ if( !xFrontEndContext.is() )
+ throw uno::RuntimeException("AccessibleTextHelper_Impl::getAccessibleAt: frontend invalid", mxFrontEnd );
+
+ uno::Reference< XAccessibleComponent > xFrontEndComponent( xFrontEndContext, uno::UNO_QUERY_THROW );
+
+ // #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_Int64 nChild;
+ for( nChild=mnFirstVisibleChild; nChild <= mnLastVisibleChild; ++nChild )
+ {
+ DBG_ASSERT(nChild >= 0,
+ "AccessibleTextHelper_Impl::getAccessibleAt: index value overflow");
+
+ tools::Rectangle aParaBounds( rCacheTF.GetParaBounds( nChild ) );
+
+ if( aParaBounds.Contains( aLogPoint ) )
+ return getAccessibleChild( nChild - mnFirstVisibleChild + GetStartIndex() );
+ }
+
+ // found none
+ return nullptr;
+ }
+
+
+ // AccessibleTextHelper implementation (simply forwards to impl)
+
+ AccessibleTextHelper::AccessibleTextHelper( ::std::unique_ptr< SvxEditSource > && pEditSource ) :
+ mpImpl( new AccessibleTextHelper_Impl() )
+ {
+ SolarMutexGuard aGuard;
+
+ SetEditSource( std::move(pEditSource) );
+ }
+
+ AccessibleTextHelper::~AccessibleTextHelper()
+ {
+ }
+
+ const SvxEditSource& AccessibleTextHelper::GetEditSource() const
+ {
+#ifdef DBG_UTIL
+ mpImpl->CheckInvariants();
+
+ const SvxEditSource& aEditSource = mpImpl->GetEditSource();
+
+ mpImpl->CheckInvariants();
+
+ return aEditSource;
+#else
+ return mpImpl->GetEditSource();
+#endif
+ }
+
+ void AccessibleTextHelper::SetEditSource( ::std::unique_ptr< SvxEditSource > && pEditSource )
+ {
+#ifdef DBG_UTIL
+ // precondition: solar mutex locked
+ DBG_TESTSOLARMUTEX();
+
+ mpImpl->CheckInvariants();
+#endif
+
+ mpImpl->SetEditSource( std::move(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
+ }
+
+ void AccessibleTextHelper::SetFocus( bool bHaveFocus )
+ {
+#ifdef DBG_UTIL
+ // precondition: solar mutex locked
+ DBG_TESTSOLARMUTEX();
+
+ mpImpl->CheckInvariants();
+#endif
+
+ mpImpl->SetFocus( bHaveFocus );
+
+#ifdef DBG_UTIL
+ mpImpl->CheckInvariants();
+#endif
+ }
+
+ bool AccessibleTextHelper::HaveFocus()
+ {
+#ifdef DBG_UTIL
+ mpImpl->CheckInvariants();
+
+ bool bRet( mpImpl->HaveFocus() );
+
+ mpImpl->CheckInvariants();
+
+ return bRet;
+#else
+ return mpImpl->HaveFocus();
+#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
+ }
+
+ 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( sal_Int64 nChildStates )
+ {
+ mpImpl->SetAdditionalChildStates( nChildStates );
+ }
+
+ void AccessibleTextHelper::UpdateChildren()
+ {
+#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::UpdateSelection()
+ {
+#ifdef DBG_UTIL
+ // precondition: solar mutex locked
+ DBG_TESTSOLARMUTEX();
+ mpImpl->CheckInvariants();
+#endif
+
+ 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
+ SolarMutexGuard aGuard;
+
+#ifdef DBG_UTIL
+ mpImpl->CheckInvariants();
+#endif
+
+ mpImpl->Dispose();
+
+#ifdef DBG_UTIL
+ mpImpl->CheckInvariants();
+#endif
+ }
+
+ // XAccessibleContext
+ sal_Int64 AccessibleTextHelper::GetChildCount() const
+ {
+ SolarMutexGuard aGuard;
+
+#ifdef DBG_UTIL
+ mpImpl->CheckInvariants();
+
+ sal_Int64 nRet = mpImpl->getAccessibleChildCount();
+
+ mpImpl->CheckInvariants();
+
+ return nRet;
+#else
+ return mpImpl->getAccessibleChildCount();
+#endif
+ }
+
+ uno::Reference< XAccessible > AccessibleTextHelper::GetChild( sal_Int64 i )
+ {
+ SolarMutexGuard aGuard;
+
+#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 )
+ {
+#ifdef DBG_UTIL
+ mpImpl->CheckInvariants();
+
+ mpImpl->addAccessibleEventListener( xListener );
+
+ mpImpl->CheckInvariants();
+#else
+ mpImpl->addAccessibleEventListener( xListener );
+#endif
+ }
+
+ void AccessibleTextHelper::RemoveEventListener( const uno::Reference< XAccessibleEventListener >& xListener )
+ {
+#ifdef DBG_UTIL
+ mpImpl->CheckInvariants();
+
+ mpImpl->removeAccessibleEventListener( xListener );
+
+ mpImpl->CheckInvariants();
+#else
+ mpImpl->removeAccessibleEventListener( xListener );
+#endif
+ }
+
+ // XAccessibleComponent
+ uno::Reference< XAccessible > AccessibleTextHelper::GetAt( const awt::Point& aPoint )
+ {
+ SolarMutexGuard aGuard;
+
+#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
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/ChildrenManager.cxx b/svx/source/accessibility/ChildrenManager.cxx
new file mode 100644
index 0000000000..2cac5153a5
--- /dev/null
+++ b/svx/source/accessibility/ChildrenManager.cxx
@@ -0,0 +1,115 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svx/ChildrenManager.hxx>
+#include "ChildrenManagerImpl.hxx"
+#include <sal/log.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+using ::com::sun::star::uno::Reference;
+
+namespace accessibility {
+
+// AccessibleChildrenManager
+ChildrenManager::ChildrenManager (
+ const css::uno::Reference<XAccessible>& rxParent,
+ const css::uno::Reference<drawing::XShapes>& rxShapeList,
+ const AccessibleShapeTreeInfo& rShapeTreeInfo,
+ AccessibleContextBase& rContext)
+ : mpImpl(
+ new ChildrenManagerImpl(
+ rxParent, rxShapeList, rShapeTreeInfo, rContext))
+{
+ mpImpl->Init ();
+}
+
+
+ChildrenManager::~ChildrenManager()
+{
+ mpImpl->dispose();
+
+ // empty
+ SAL_INFO("svx", "~ChildrenManager");
+}
+
+sal_Int64 ChildrenManager::GetChildCount() const noexcept
+{
+ return mpImpl->GetChildCount();
+}
+
+css::uno::Reference<XAccessible> ChildrenManager::GetChild (sal_Int64 nIndex)
+{
+ return mpImpl->GetChild (nIndex);
+}
+
+const css::uno::Reference<css::drawing::XShape>& ChildrenManager::GetChildShape(sal_Int64 nIndex)
+{
+ return mpImpl->GetChildShape(nIndex);
+}
+
+void ChildrenManager::Update (bool bCreateNewObjectsOnDemand)
+{
+ mpImpl->Update (bCreateNewObjectsOnDemand);
+}
+
+void ChildrenManager::SetShapeList (const css::uno::Reference<css::drawing::XShapes>& xShapeList)
+{
+ mpImpl->SetShapeList (xShapeList);
+}
+
+void ChildrenManager::AddAccessibleShape (rtl::Reference<AccessibleShape> const & shape)
+{
+ mpImpl->AddAccessibleShape (shape);
+}
+
+void ChildrenManager::ClearAccessibleShapeList()
+{
+ mpImpl->ClearAccessibleShapeList ();
+}
+
+void ChildrenManager::SetInfo (AccessibleShapeTreeInfo const & rShapeTreeInfo)
+{
+ mpImpl->SetInfo (rShapeTreeInfo);
+}
+
+void ChildrenManager::UpdateSelection()
+{
+ mpImpl->UpdateSelection ();
+}
+
+bool ChildrenManager::HasFocus() const
+{
+ return mpImpl->HasFocus ();
+}
+
+void ChildrenManager::RemoveFocus()
+{
+ mpImpl->RemoveFocus ();
+}
+
+// IAccessibleViewForwarderListener
+void ChildrenManager::ViewForwarderChanged()
+{
+ mpImpl->ViewForwarderChanged();
+}
+
+} // end of namespace accessibility
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/ChildrenManagerImpl.cxx b/svx/source/accessibility/ChildrenManagerImpl.cxx
new file mode 100644
index 0000000000..5271f1887b
--- /dev/null
+++ b/svx/source/accessibility/ChildrenManagerImpl.cxx
@@ -0,0 +1,1123 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <cassert>
+
+#include "ChildrenManagerImpl.hxx"
+#include <svx/ShapeTypeHandler.hxx>
+#include <svx/AccessibleControlShape.hxx>
+#include <svx/AccessibleShape.hxx>
+#include <svx/AccessibleShapeInfo.hxx>
+#include <svx/IAccessibleViewForwarder.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/document/XShapeEventBroadcaster.hpp>
+#include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+#include <com/sun/star/container/XChild.hpp>
+#include <comphelper/lok.hxx>
+#include <comphelper/types.hxx>
+#include <o3tl/safeint.hxx>
+#include <rtl/ustring.hxx>
+#include <tools/debug.hxx>
+#include <svx/SvxShapeTypes.hxx>
+#include <vcl/window.hxx>
+#include <shapecollection.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)
+{
+ sal_Int32 i=0;
+ for (auto& rItem : _rList)
+ {
+ rItem.setIndexAtAccessibleShape(i);
+ ++i;
+ }
+}
+}
+
+// AccessibleChildrenManager
+ChildrenManagerImpl::ChildrenManagerImpl (
+ uno::Reference<XAccessible> xParent,
+ uno::Reference<drawing::XShapes> xShapeList,
+ const AccessibleShapeTreeInfo& rShapeTreeInfo,
+ AccessibleContextBase& rContext)
+ : mxShapeList (std::move(xShapeList)),
+ mxParent (std::move(xParent)),
+ maShapeTreeInfo (rShapeTreeInfo),
+ mrContext (rContext),
+ mpFocusedShape(nullptr)
+{
+}
+
+
+ChildrenManagerImpl::~ChildrenManagerImpl()
+{
+ DBG_ASSERT (m_bDisposed, "~AccessibleDrawDocumentView: object has not been disposed");
+}
+
+
+void ChildrenManagerImpl::Init()
+{
+ // 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));
+}
+
+
+sal_Int64 ChildrenManagerImpl::GetChildCount() const noexcept
+{
+ return maVisibleChildren.size();
+}
+
+
+const css::uno::Reference<css::drawing::XShape>& ChildrenManagerImpl::GetChildShape(sal_Int64 nIndex)
+{
+ // Check whether the given index is valid.
+ if (nIndex < 0 || o3tl::make_unsigned(nIndex) >= maVisibleChildren.size())
+ throw lang::IndexOutOfBoundsException (
+ "no accessible child with index " + OUString::number(nIndex),
+ mxParent);
+ return maVisibleChildren[nIndex].mxShape;
+}
+
+/** Return the requested accessible child object. Create it if it is not
+ yet in the cache.
+*/
+uno::Reference<XAccessible>
+ ChildrenManagerImpl::GetChild (sal_Int64 nIndex)
+{
+ // Check whether the given index is valid.
+ if (nIndex < 0 || o3tl::make_unsigned(nIndex) >= maVisibleChildren.size())
+ throw lang::IndexOutOfBoundsException (
+ "no accessible child with index " + OUString::number(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)
+{
+ if ( ! rChildDescriptor.mxAccessibleShape.is())
+ {
+ SolarMutexGuard g;
+ // 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);
+ // Create accessible object that corresponds to the descriptor's
+ // shape.
+ rtl::Reference<AccessibleShape> pShape(
+ ShapeTypeHandler::Instance().CreateAccessibleObject (
+ aShapeInfo,
+ maShapeTreeInfo));
+ rChildDescriptor.mxAccessibleShape = pShape;
+ if ( pShape.is() )
+ {
+ pShape->Init();
+ pShape->setIndexInParent(_nIndex);
+ }
+ }
+ }
+
+ return rChildDescriptor.mxAccessibleShape;
+}
+
+
+/** 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() == nullptr)
+ return;
+ tools::Rectangle aVisibleArea = maShapeTreeInfo.GetViewForwarder()->GetVisibleArea();
+
+ // 1. Create a local list of visible shapes.
+ ChildDescriptorListType aChildList;
+ CreateListOfVisibleShapes (aChildList);
+
+ // 2. Replace the current list of visible shapes with the new one. Do
+ // the same with the visible area.
+ {
+ SolarMutexGuard g;
+
+ // Use swap to copy the contents of the new list in constant time.
+ maVisibleChildren.swap (aChildList);
+
+ // 3. Merge the information that is already known about the visible
+ // shapes from the previous list into the new list and identify
+ // old children that are now unused
+ std::vector<ChildDescriptor*> aUnusedChildList = MergeAccessibilityInformation (aChildList);
+
+ adjustIndexInParentOfShapes(maVisibleChildren);
+
+ // 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 (aUnusedChildList);
+
+ aUnusedChildList.clear();
+ 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)
+ return;
+
+ //operate on a copy of the list and restore it afterwards to guard
+ //against the pathological case where maVisibleChildren gets modified
+ //by other calls to this object while CreateAccessibilityObjects
+ //executes which can happen when java is disabled and the "enable-java"
+ //dialog appears during the instantiation of the linguistic components
+ //triggered by the creation of shapes belonging to the a11y objects
+ //
+ //i.e. launch start-center, launch impress with java disabled and
+ //a java-using linguistic component installed
+ maVisibleChildren.swap(aChildList);
+ CreateAccessibilityObjects(aChildList);
+ maVisibleChildren.swap(aChildList);
+}
+
+void ChildrenManagerImpl::CreateListOfVisibleShapes (
+ ChildDescriptorListType& raDescriptorList)
+{
+ SolarMutexGuard g;
+
+ OSL_ASSERT (maShapeTreeInfo.GetViewForwarder() != nullptr);
+
+ tools::Rectangle aVisibleArea = maShapeTreeInfo.GetViewForwarder()->GetVisibleArea();
+
+ // Add the visible shapes for which the accessible objects already exist.
+ for (const auto& rpShape : maAccessibleShapes)
+ {
+ if (rpShape.is())
+ {
+ uno::Reference<XAccessibleComponent> xComponent (
+ rpShape->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.emplace_back(rpShape);
+ }
+ }
+ }
+
+ // Add the visible shapes for which only the XShapes exist.
+ if (!mxShapeList.is() || !mxShapeList->hasElements())
+ return;
+
+ sal_Int32 nShapeCount = mxShapeList->getCount();
+ raDescriptorList.reserve( nShapeCount );
+ awt::Point aPos;
+ awt::Size aSize;
+ tools::Rectangle aBoundingBox;
+ uno::Reference<drawing::XShape> xShape;
+ for (sal_Int32 i=0; i<nShapeCount; ++i)
+ {
+ mxShapeList->getByIndex(i) >>= xShape;
+ aPos = xShape->getPosition();
+ aSize = xShape->getSize();
+
+ aBoundingBox.SetLeft( aPos.X );
+ aBoundingBox.SetTop( aPos.Y );
+ aBoundingBox.SetRight( aPos.X + aSize.Width );
+ aBoundingBox.SetBottom( aPos.Y + aSize.Height );
+
+ // Insert shape if it is visible, i.e. its bounding box overlaps
+ // the visible area. In the LOK case we skip the overlap check
+ // since we could remove a shape that is visible on the client.
+ if ( aBoundingBox.Overlaps(aVisibleArea) || comphelper::LibreOfficeKit::isActive())
+ raDescriptorList.emplace_back(xShape);
+ }
+}
+
+namespace
+{
+
+bool childDescriptorLess(const ChildDescriptor& lhs, const ChildDescriptor& rhs)
+{
+
+ auto pLhsShape = lhs.mxShape.get();
+ auto pRhsShape = rhs.mxShape.get();
+ if (pLhsShape || pRhsShape)
+ return pLhsShape < pRhsShape;
+ return lhs.mxAccessibleShape.get() < rhs.mxAccessibleShape.get();
+}
+
+bool childDescriptorPtrLess(const ChildDescriptor* lhs, const ChildDescriptor* rhs)
+{
+ return childDescriptorLess(*lhs, *rhs);
+}
+
+}
+
+void ChildrenManagerImpl::RemoveNonVisibleChildren (
+ const std::vector<ChildDescriptor*>& rNonVisibleChildren)
+{
+ for (ChildDescriptor* pChild : rNonVisibleChildren)
+ {
+ // 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 (pChild->mxShape.is())
+ {
+ UnregisterAsDisposeListener (pChild->mxShape);
+ pChild->disposeAccessibleObject (mrContext);
+ }
+ else
+ {
+ AccessibleShape* pAccessibleShape = pChild->GetAccessibleShape();
+ pAccessibleShape->ResetState (AccessibleStateType::VISIBLE);
+ pChild->mxAccessibleShape = nullptr;
+ }
+ }
+}
+
+std::vector<ChildDescriptor*> ChildrenManagerImpl::MergeAccessibilityInformation (
+ ChildDescriptorListType& raOldChildList)
+
+{
+ // create a working copy of the vector of current children with pointers to elements,
+ // sort the old list and copy by mxShape, and then walk old/current lists in parallel,
+ // which avoids an O(n^2) loop
+ // (order of maVisibleChildren must remain unchanged to not randomly change a11y tree)
+ std::vector<ChildDescriptor*> aSortedVisibleChildren(maVisibleChildren.size());
+ std::transform(maVisibleChildren.begin(), maVisibleChildren.end(),
+ aSortedVisibleChildren.begin(), [](auto& e) {return &e;});
+ std::sort(aSortedVisibleChildren.begin(), aSortedVisibleChildren.end(), childDescriptorPtrLess);
+
+ // old list can be reordered without problems
+ std::sort(raOldChildList.begin(), raOldChildList.end(), childDescriptorLess);
+
+ ChildDescriptorListType::const_iterator aOldChildDescriptor = raOldChildList.begin();
+ ChildDescriptorListType::const_iterator aEndOldChildren = raOldChildList.end();
+ for (ChildDescriptor* pChild : aSortedVisibleChildren)
+ {
+ while (aOldChildDescriptor != aEndOldChildren && childDescriptorLess(*aOldChildDescriptor, *pChild))
+ {
+ aOldChildDescriptor++;
+ }
+
+ // Copy accessible shape if that exists in the old descriptor.
+ if (aOldChildDescriptor != aEndOldChildren && *aOldChildDescriptor == *pChild &&
+ aOldChildDescriptor->mxAccessibleShape.is())
+ {
+ pChild->mxAccessibleShape = aOldChildDescriptor->mxAccessibleShape;
+ pChild->mbCreateEventPending = false;
+ }
+ else
+ RegisterAsDisposeListener (pChild->mxShape);
+ }
+
+ // collect list of children that are in the old, but not the new vector
+ std::vector<ChildDescriptor*> aObsoleteChildren;
+
+ auto newIt = aSortedVisibleChildren.begin();
+ auto newEnd = aSortedVisibleChildren.end();
+ for (ChildDescriptor& rOldChild : raOldChildList)
+ {
+ while (newIt != newEnd && childDescriptorLess(**newIt, rOldChild))
+ newIt++;
+ if (newIt == newEnd || !(**newIt == rOldChild) )
+ aObsoleteChildren.push_back(&rOldChild);
+ }
+
+ return aObsoleteChildren;
+}
+
+void ChildrenManagerImpl::SendVisibleAreaEvents (
+ ChildDescriptorListType& raNewChildList)
+{
+ for (const auto& rChild : raNewChildList)
+ {
+ // 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 = rChild.GetAccessibleShape ();
+ if (pShape != nullptr)
+ pShape->ViewForwarderChanged();
+ }
+}
+
+
+void ChildrenManagerImpl::CreateAccessibilityObjects (
+ ChildDescriptorListType& raNewChildList)
+{
+ sal_Int32 nPos = 0;
+ for ( auto& rChild : raNewChildList)
+ {
+ // Create the associated accessible object when the flag says so and
+ // it does not yet exist.
+ if ( ! rChild.mxAccessibleShape.is() )
+ GetChild (rChild, nPos);
+ if (rChild.mxAccessibleShape.is() && rChild.mbCreateEventPending)
+ {
+ rChild.mbCreateEventPending = false;
+ mrContext.CommitChange (
+ AccessibleEventId::CHILD,
+ uno::Any(uno::Reference<XAccessible>(rChild.mxAccessibleShape)),
+ uno::Any(), -1);
+ }
+ ++nPos;
+ }
+}
+
+
+void ChildrenManagerImpl::AddShape (const Reference<drawing::XShape>& rxShape)
+{
+ if (!rxShape.is())
+ return;
+
+ SolarMutexClearableGuard aGuard;
+
+ // Test visibility of the shape.
+ tools::Rectangle aVisibleArea = maShapeTreeInfo.GetViewForwarder()->GetVisibleArea();
+ awt::Point aPos = rxShape->getPosition();
+ awt::Size aSize = rxShape->getSize();
+
+ tools::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())
+ return;
+
+ Reference<drawing::XShapes> xParent (xChild->getParent(), uno::UNO_QUERY);
+ if (xParent != mxShapeList)
+ return;
+
+ if (!aBoundingBox.Overlaps(aVisibleArea) && !comphelper::LibreOfficeKit::isActive())
+ return;
+
+ // Add shape to list of visible shapes.
+ maVisibleChildren.emplace_back(rxShape);
+
+ // Create accessibility object.
+ ChildDescriptor& rDescriptor = maVisibleChildren.back();
+ GetChild (rDescriptor, maVisibleChildren.size()-1);
+
+ // Inform listeners about new child.
+ uno::Any aNewShape;
+ aNewShape <<= uno::Reference<XAccessible>(rDescriptor.mxAccessibleShape);
+ aGuard.clear();
+ mrContext.CommitChange (
+ AccessibleEventId::CHILD,
+ aNewShape,
+ uno::Any(),
+ maVisibleChildren.size() - 1);
+ RegisterAsDisposeListener(rxShape);
+}
+
+
+void ChildrenManagerImpl::RemoveShape (const Reference<drawing::XShape>& rxShape)
+{
+ if (!rxShape.is())
+ return;
+
+ SolarMutexGuard g;
+
+ // Search shape in list of visible children.
+ ChildDescriptorListType::iterator I (
+ ::std::find (maVisibleChildren.begin(), maVisibleChildren.end(),
+ ChildDescriptor (rxShape)));
+ if (I == maVisibleChildren.end())
+ return;
+
+ // Remove descriptor from that list.
+ Reference<XAccessible> xHoldAlive(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 css::uno::Reference<css::drawing::XShapes>& xShapeList)
+{
+ mxShapeList = xShapeList;
+}
+
+
+void ChildrenManagerImpl::AddAccessibleShape (rtl::Reference<AccessibleShape> const & shape)
+{
+ assert(shape.is());
+ maAccessibleShapes.push_back (shape);
+}
+
+
+void ChildrenManagerImpl::ClearAccessibleShapeList()
+{
+ // 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(), -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.
+ for (auto& rChild : aLocalVisibleChildren)
+ if ( rChild.mxAccessibleShape.is() && rChild.mxShape.is() )
+ {
+ rChild.mxAccessibleShape->dispose();
+ rChild.mxAccessibleShape = nullptr;
+ }
+
+ // Dispose all objects in the accessible shape list.
+ for (auto& rpShape : aLocalAccessibleShapes)
+ if (rpShape.is())
+ {
+ // Dispose the object.
+ rpShape->dispose();
+ rpShape = nullptr;
+ }
+}
+
+
+/** 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;
+ {
+ SolarMutexGuard g;
+ xCurrentBroadcaster = maShapeTreeInfo.GetModelBroadcaster();
+ xCurrentController = maShapeTreeInfo.GetController();
+ xCurrentSelectionSupplier.set( 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)
+ return;
+
+ // 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)
+{
+ 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 = nullptr;
+ }
+ }
+}
+
+// document::XEventListener
+/** Listen for new and removed shapes.
+*/
+void SAL_CALL
+ ChildrenManagerImpl::notifyEvent (
+ const document::EventObject& rEventObject)
+{
+ // tdf#158169 if we are already disposed, execute no actions, but inform the
+ // caller that we are disposed
+ if ( m_bDisposed )
+ throw lang::DisposedException();
+
+ if (rEventObject.EventName == "ShapeInserted")
+ AddShape (Reference<drawing::XShape>(rEventObject.Source, uno::UNO_QUERY));
+ else if (rEventObject.EventName == "ShapeRemoved")
+ RemoveShape (Reference<drawing::XShape>(rEventObject.Source, uno::UNO_QUERY));
+ // else ignore unknown event.
+}
+
+// view::XSelectionChangeListener
+void SAL_CALL
+ ChildrenManagerImpl::selectionChanged (const lang::EventObject& /*rEvent*/)
+{
+ UpdateSelection ();
+}
+
+
+void ChildrenManagerImpl::impl_dispose()
+{
+ 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 (nullptr);
+
+ try
+ {
+ // Remove from broadcaster.
+ if (maShapeTreeInfo.GetModelBroadcaster().is())
+ maShapeTreeInfo.GetModelBroadcaster()->removeEventListener (
+ static_cast<document::XEventListener*>(this));
+ maShapeTreeInfo.SetModelBroadcaster (nullptr);
+ }
+ catch( uno::RuntimeException& )
+ {}
+
+ ClearAccessibleShapeList ();
+ SetShapeList (nullptr);
+}
+
+
+void ChildrenManagerImpl::disposing(std::unique_lock<std::mutex>&)
+{
+ impl_dispose();
+}
+
+// IAccessibleViewForwarderListener
+void ChildrenManagerImpl::ViewForwarderChanged()
+{
+ Update(false);
+}
+
+// IAccessibleParent
+bool ChildrenManagerImpl::ReplaceChild (
+ AccessibleShape* pCurrentChild,
+ const css::uno::Reference< css::drawing::XShape >& _rxShape,
+ const tools::Long /*_nIndex*/,
+ const AccessibleShapeTreeInfo& _rShapeTreeInfo)
+{
+ // 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.
+ auto I = std::find_if(maVisibleChildren.begin(), maVisibleChildren.end(),
+ [&pCurrentChild](const ChildDescriptor& rChild) { return rChild.GetAccessibleShape() == pCurrentChild; });
+
+ if (I != maVisibleChildren.end())
+ {
+ // Dispose the current child and send an event about its deletion.
+ pCurrentChild->dispose();
+ mrContext.CommitChange (
+ AccessibleEventId::CHILD,
+ uno::Any(),
+ uno::Any (uno::Reference<XAccessible>(I->mxAccessibleShape)), -1);
+
+ // Replace with replacement and send an event about existence
+ // of the new child.
+ AccessibleShapeInfo aShapeInfo( _rxShape, pCurrentChild->getAccessibleParent(), this );
+ // create the new child
+ rtl::Reference<AccessibleShape> pNewChild(ShapeTypeHandler::Instance().CreateAccessibleObject (
+ aShapeInfo,
+ _rShapeTreeInfo
+ ));
+ if ( pNewChild.is() )
+ pNewChild->Init();
+
+ I->mxAccessibleShape = pNewChild.get();
+ mrContext.CommitChange (
+ AccessibleEventId::CHILD,
+ uno::Any (uno::Reference<XAccessible>(I->mxAccessibleShape)),
+ uno::Any(), -1);
+
+ return true;
+ }
+
+ // When not found among the visible children we have to search the list
+ // of accessible shapes. This is not yet implemented.
+ return false;
+}
+
+// Add the impl method for IAccessibleParent interface
+AccessibleControlShape * ChildrenManagerImpl::GetAccControlShapeFromModel(css::beans::XPropertySet* pSet)
+{
+ sal_Int64 count = GetChildCount();
+ for (sal_Int64 index=0;index<count;index++)
+ {
+ AccessibleShape* pAccShape = maVisibleChildren[index].GetAccessibleShape();
+ if (pAccShape && ::accessibility::ShapeTypeHandler::Instance().GetTypeId(pAccShape->GetXShape()) == DRAWING_CONTROL)
+ {
+ auto* pCtlAccShape = static_cast<::accessibility::AccessibleControlShape*>(pAccShape);
+ if (pCtlAccShape->GetControlModel() == pSet)
+ return pCtlAccShape;
+ }
+ }
+ return nullptr;
+}
+uno::Reference<XAccessible>
+ ChildrenManagerImpl::GetAccessibleCaption (const uno::Reference<drawing::XShape>& xShape)
+{
+ auto I = std::find_if(maVisibleChildren.begin(), maVisibleChildren.end(),
+ [&xShape](const ChildDescriptor& rChild) { return rChild.mxShape.get() == xShape.get(); });
+ if (I != maVisibleChildren.end())
+ return I->mxAccessibleShape;
+ return uno::Reference<XAccessible> ();
+}
+
+/** 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()
+{
+ // Remember the current and new focused shape.
+ AccessibleShape* pCurrentlyFocusedShape = nullptr;
+ AccessibleShape* pNewFocusedShape = nullptr;
+ typedef std::pair< AccessibleShape* , sal_Bool > PAIR_SHAPE;//sal_Bool Selected,UnSelected.
+ typedef std::vector< PAIR_SHAPE > VEC_SHAPE;
+ VEC_SHAPE vecSelect;
+ int nAddSelect=0;
+ bool bHasSelectedShape=false;
+ if (!maVisibleChildren.empty())
+ {
+ 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.set( xSelectionSupplier->getSelection(), uno::UNO_QUERY);
+ xSelectedShape.set( xSelectionSupplier->getSelection(), uno::UNO_QUERY);
+ }
+
+ // tdf#139220 to quickly find if a given drawing::XShape is selected
+ std::vector<css::uno::Reference<css::drawing::XShape>> aSortedSelectedShapes;
+ if (!xSelectedShape.is() && xSelectedShapeAccess.is())
+ {
+ sal_Int32 nCount = xSelectedShapeAccess->getCount();
+ aSortedSelectedShapes.reserve(nCount);
+ if (auto pSvxShape = dynamic_cast<SvxShapeCollection*>(xSelectedShapeAccess.get()))
+ {
+ pSvxShape->getAllShapes(aSortedSelectedShapes);
+ }
+ else
+ for (sal_Int32 i = 0; i < nCount; ++i)
+ {
+ css::uno::Reference<css::drawing::XShape> xShape(xSelectedShapeAccess->getByIndex(i), uno::UNO_QUERY);
+ aSortedSelectedShapes.push_back(xShape);
+ }
+ std::sort(aSortedSelectedShapes.begin(), aSortedSelectedShapes.end());
+ }
+
+ for (const auto& rChild : maVisibleChildren)
+ {
+ AccessibleShape* pAccessibleShape = rChild.GetAccessibleShape();
+ if (rChild.mxAccessibleShape.is() && rChild.mxShape.is() && pAccessibleShape!=nullptr)
+ {
+ short nRole = pAccessibleShape->getAccessibleRole();
+ bool bDrawShape = (
+ nRole == AccessibleRole::GRAPHIC ||
+ nRole == AccessibleRole::EMBEDDED_OBJECT ||
+ nRole == AccessibleRole::SHAPE ||
+ nRole == AccessibleRole::IMAGE_MAP ||
+ nRole == AccessibleRole::TABLE_CELL ||
+ nRole == AccessibleRole::TABLE );
+ bool bShapeIsSelected = false;
+
+ // Look up the shape in the (single or multi-) selection.
+ if (xSelectedShape.is())
+ {
+ if (rChild.mxShape == xSelectedShape)
+ {
+ bShapeIsSelected = true;
+ pNewFocusedShape = pAccessibleShape;
+ }
+ }
+ else if (!aSortedSelectedShapes.empty())
+ {
+ if (std::binary_search(aSortedSelectedShapes.begin(), aSortedSelectedShapes.end(), rChild.mxShape))
+ {
+ bShapeIsSelected = true;
+ // In a multi-selection no shape has the focus.
+ if (aSortedSelectedShapes.size() == 1)
+ pNewFocusedShape = pAccessibleShape;
+ }
+ }
+
+ // Set or reset the SELECTED state.
+ if (bShapeIsSelected)
+ {
+ if (pAccessibleShape->SetState (AccessibleStateType::SELECTED))
+ {
+ if (bDrawShape)
+ {
+ vecSelect.emplace_back(pAccessibleShape,true);
+ ++nAddSelect;
+ }
+ }
+ else
+ {//Selected not change,has selected shape before
+ bHasSelectedShape=true;
+ }
+ }
+ else
+ //pAccessibleShape->ResetState (AccessibleStateType::SELECTED);
+ {
+ if(pAccessibleShape->ResetState (AccessibleStateType::SELECTED))
+ {
+ if(bDrawShape)
+ {
+ vecSelect.emplace_back(pAccessibleShape,false);
+ }
+ }
+ }
+ // Does the shape have the current selection?
+ if (pAccessibleShape->GetState (AccessibleStateType::FOCUSED))
+ pCurrentlyFocusedShape = pAccessibleShape;
+ }
+ }
+ }
+
+ vcl::Window *pParentWindow = maShapeTreeInfo.GetWindow();
+ bool bShapeActive= false;
+ // For table cell, the table's parent must be checked to make sure it has focus.
+ if (pParentWindow)
+ {
+ vcl::Window *pPWindow = pParentWindow->GetParent();
+ if (pParentWindow->HasFocus() || (pPWindow && pPWindow->HasFocus()))
+ bShapeActive =true;
+ }
+ // Move focus from current to newly focused shape.
+ if (pCurrentlyFocusedShape != pNewFocusedShape)
+ {
+ if (pCurrentlyFocusedShape != nullptr)
+ pCurrentlyFocusedShape->ResetState (AccessibleStateType::FOCUSED);
+ if (pNewFocusedShape != nullptr && bShapeActive)
+ pNewFocusedShape->SetState (AccessibleStateType::FOCUSED);
+ }
+
+ if (nAddSelect >= 10 )//fire selection within
+ {
+ mrContext.CommitChange(AccessibleEventId::SELECTION_CHANGED_WITHIN,uno::Any(),uno::Any(), -1);
+ nAddSelect =0 ;//not fire selection event
+ }
+ for (VEC_SHAPE::reverse_iterator vi = vecSelect.rbegin(), aEndVecSelect = vecSelect.rend(); vi != aEndVecSelect ;++vi)
+ {
+ PAIR_SHAPE &pairShape= *vi;
+ Reference< XAccessible > xShape(pairShape.first);
+ uno::Any anyShape;
+ anyShape <<= xShape;
+
+ if (pairShape.second)//Selection add
+ {
+ if (bHasSelectedShape)
+ {
+ if ( nAddSelect > 0 )
+ {
+ mrContext.CommitChange(AccessibleEventId::SELECTION_CHANGED_ADD,anyShape,uno::Any(), -1);
+ }
+ }
+ else
+ {
+ //if has not selected shape ,first selected shape is fire selection event;
+ if (nAddSelect > 0 )
+ {
+ mrContext.CommitChange(AccessibleEventId::SELECTION_CHANGED,anyShape,uno::Any(), -1);
+ }
+ if (nAddSelect > 1 )//check other selected shape fire selection add event
+ {
+ bHasSelectedShape=true;
+ }
+ }
+ }
+ else //selection remove
+ {
+ mrContext.CommitChange(AccessibleEventId::SELECTION_CHANGED_REMOVE,anyShape,uno::Any(), -1);
+ }
+ }
+
+ // We need to know when text content is no more edited but shape is still selected.
+ // For instance when ESC is pressed.
+ // The only difference is provided by nSelectedChildCount: on editing is equal to 1.
+ // In the following a shape get selected, but it was already selected, anyway editing is no more active.
+ if (comphelper::LibreOfficeKit::isActive() && pNewFocusedShape && nAddSelect == 0)
+ {
+ sal_Int64 nChildCount = pNewFocusedShape->getAccessibleChildCount();
+ sal_Int64 nSelectedChildCount = pNewFocusedShape->getSelectedAccessibleChildCount();
+ if (nChildCount > 0 && nSelectedChildCount == 0)
+ {
+ Reference< XAccessible > xShape(pNewFocusedShape);
+ uno::Any anyShape;
+ anyShape <<= xShape;
+ mrContext.CommitChange(AccessibleEventId::SELECTION_CHANGED,anyShape,uno::Any(), -1);
+ }
+ }
+
+ // Remember whether there is a shape that now has the focus.
+ mpFocusedShape = pNewFocusedShape;
+}
+
+
+bool ChildrenManagerImpl::HasFocus() const
+{
+ return mpFocusedShape != nullptr;
+}
+
+
+void ChildrenManagerImpl::RemoveFocus()
+{
+ if (mpFocusedShape != nullptr)
+ {
+ mpFocusedShape->ResetState (AccessibleStateType::FOCUSED);
+ mpFocusedShape = nullptr;
+ }
+}
+
+
+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),
+ mbCreateEventPending (true)
+{
+ // Empty.
+}
+
+
+ChildDescriptor::ChildDescriptor (const rtl::Reference<AccessibleShape>& rxAccessibleShape)
+ : mxAccessibleShape (rxAccessibleShape),
+ mbCreateEventPending (true)
+{
+ // Make sure that the accessible object has the <const>VISIBLE</const>
+ // state set.
+ AccessibleShape* pAccessibleShape = GetAccessibleShape();
+ pAccessibleShape->SetState (AccessibleStateType::VISIBLE);
+}
+
+void ChildDescriptor::setIndexAtAccessibleShape(sal_Int32 _nIndex)
+{
+ AccessibleShape* pShape = GetAccessibleShape();
+ if ( pShape )
+ pShape->setIndexInParent(_nIndex);
+}
+
+
+void ChildDescriptor::disposeAccessibleObject (AccessibleContextBase& rParent)
+{
+ if (!mxAccessibleShape.is())
+ return;
+
+ // Send event that the shape has been removed.
+ uno::Any aOldValue;
+ aOldValue <<= uno::Reference<XAccessible>(mxAccessibleShape);
+ rParent.CommitChange (
+ AccessibleEventId::CHILD,
+ uno::Any(),
+ aOldValue, -1);
+
+ // Dispose and remove the object.
+ if (mxAccessibleShape.is())
+ mxAccessibleShape->dispose();
+
+ mxAccessibleShape = nullptr;
+}
+
+
+} // end of namespace accessibility
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/ChildrenManagerImpl.hxx b/svx/source/accessibility/ChildrenManagerImpl.hxx
new file mode 100644
index 0000000000..2de34e10da
--- /dev/null
+++ b/svx/source/accessibility/ChildrenManagerImpl.hxx
@@ -0,0 +1,497 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SVX_SOURCE_ACCESSIBILITY_CHILDRENMANAGERIMPL_HXX
+#define INCLUDED_SVX_SOURCE_ACCESSIBILITY_CHILDRENMANAGERIMPL_HXX
+
+#include <svx/IAccessibleViewForwarderListener.hxx>
+#include <svx/IAccessibleParent.hxx>
+#include <svx/AccessibleShapeTreeInfo.hxx>
+#include <editeng/AccessibleContextBase.hxx>
+#include <comphelper/compbase.hxx>
+#include <tools/gen.hxx>
+#include <vector>
+#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>
+
+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 sent 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 final
+ : public comphelper::WeakComponentImplHelper<
+ css::document::XEventListener,
+ css::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 separately 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
+ Bundle of information passed down the shape tree.
+ @param rContext
+ An accessible context object that is called for firing events
+ for new and deleted children, i.e. that holds a list of
+ listeners to be informed.
+ */
+ ChildrenManagerImpl (css::uno::Reference<css::accessibility::XAccessible> xParent,
+ css::uno::Reference<css::drawing::XShapes> xShapeList,
+ const AccessibleShapeTreeInfo& rShapeTreeInfo,
+ AccessibleContextBase& rContext);
+
+ /** If there still are managed children these are disposed and
+ released.
+ */
+ virtual ~ChildrenManagerImpl() override;
+
+ /** Do that part of the initialization that you can not or should not do
+ in the constructor like registering at broadcasters.
+ */
+ void Init();
+
+ /** Return the number of currently visible accessible children.
+ @return
+ If there are no children a 0 is returned.
+ */
+ sal_Int64 GetChildCount() const noexcept;
+
+ /// @throws css::uno::RuntimeException
+ /// @throws css::lang::IndexOutOfBoundsException
+ const css::uno::Reference<css::drawing::XShape>& GetChildShape(sal_Int64 nIndex);
+ /** 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.
+ @throws
+ Throws an IndexOutOfBoundsException if the index is not valid.
+ */
+ css::uno::Reference<css::accessibility::XAccessible>
+ GetChild (sal_Int64 nIndex);
+
+ /** 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.
+ @throws css::uno::RuntimeException
+ */
+ css::uno::Reference<css::accessibility::XAccessible>
+ GetChild (ChildDescriptor& aChildDescriptor,sal_Int32 _nIndex);
+
+ /** 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 separate 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);
+
+ /** 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 css::uno::Reference<css::drawing::XShapes>& xShapeList);
+
+ /** Add an 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 shape
+ The new shape that is added to the list of accessible shapes; must
+ be non-null.
+ */
+ void AddAccessibleShape (rtl::Reference<AccessibleShape> const & shape);
+
+ /** Clear the lists of accessible shapes and that of visible accessible
+ shapes. The list of UNO shapes is not modified.
+ */
+ void ClearAccessibleShapeList();
+
+ /** 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();
+
+ /** 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() const;
+
+ /** 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();
+
+ // lang::XEventListener
+ virtual void SAL_CALL
+ disposing (const css::lang::EventObject& rEventObject) override;
+
+ // document::XEventListener
+ virtual void SAL_CALL
+ notifyEvent (const css::document::EventObject& rEventObject) override;
+
+ // view::XSelectionChangeListener
+ virtual void SAL_CALL
+ selectionChanged (const css::lang::EventObject& rEvent) override;
+
+ // 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</member> which creates accessible objects of
+ new shapes immediately. Other change types are passed to the
+ visible accessible children without calling
+ <member>Update</member>.
+ @param pViewForwarder
+ The modified view forwarder. Use this one from now on.
+ */
+ virtual void ViewForwarderChanged() override;
+
+ // 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 whether the replacement has been
+ finished successfully.
+ */
+ virtual bool ReplaceChild (
+ AccessibleShape* pCurrentChild,
+ const css::uno::Reference< css::drawing::XShape >& _rxShape,
+ const tools::Long _nIndex,
+ const AccessibleShapeTreeInfo& _rShapeTreeInfo
+ ) override;
+
+ // Add the impl method for IAccessibleParent interface
+ virtual AccessibleControlShape* GetAccControlShapeFromModel
+ (css::beans::XPropertySet* pSet) override;
+ virtual css::uno::Reference<css::accessibility::XAccessible>
+ GetAccessibleCaption (const css::uno::Reference<css::drawing::XShape>& xShape) override;
+
+private:
+ /** 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>.
+ */
+ css::uno::Reference<css::drawing::XShapes> mxShapeList;
+
+ /** This list of additional accessible shapes that can or shall not be
+ created by the shape factory.
+ */
+ typedef std::vector< rtl::Reference< AccessibleShape> > 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.
+ */
+ tools::Rectangle maVisibleArea;
+
+ /** The parent of the shapes. It is used for creating accessible
+ objects for given shapes.
+ */
+ css::uno::Reference<css::accessibility::XAccessible> mxParent;
+
+ /** Bundle 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 removed children.
+ */
+ AccessibleContextBase& mrContext;
+
+ /** This method is called from the component helper base class while
+ disposing.
+ */
+ virtual void disposing(std::unique_lock<std::mutex>&) override;
+
+ void impl_dispose();
+
+ ChildrenManagerImpl (const ChildrenManagerImpl&) = delete;
+ ChildrenManagerImpl& operator= (const ChildrenManagerImpl&) = delete;
+
+ /** 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 std::vector<ChildDescriptor*>& rNonVisibleChildren);
+
+ /** Merge the information that is already known about the visible shapes
+ from the old list into the current list, and return a list of
+ children that are in the old list, but not the current one.
+ @param raChildList
+ Information is merged to the current list of visible children
+ from this list. The old list can get reordered.
+ @return
+ Vector of children that are in the old list, but not the current
+ one.
+ */
+ std::vector<ChildDescriptor*> 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.
+ */
+ static 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 object 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 css::uno::Reference<css::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 css::uno::Reference<css::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 css::uno::Reference<css::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 css::uno::Reference<css::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.
+ */
+ css::uno::Reference<css::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.
+ */
+ rtl::Reference<AccessibleShape> 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() const { return mxAccessibleShape.get(); }
+
+ /** 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
+ listeners 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 css::uno::Reference<css::drawing::XShape>& xShape);
+
+ /** Create a new descriptor for the specified shape with empty reference
+ to the original shape.
+ */
+ explicit ChildDescriptor (const rtl::Reference<AccessibleShape>& rxAccessibleShape);
+
+ /** 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.
+ */
+ bool operator == (const ChildDescriptor& aDescriptor) const
+ {
+ return (
+ this == &aDescriptor ||
+ (
+ (mxShape.get() == aDescriptor.mxShape.get() ) &&
+ (mxShape.is() || mxAccessibleShape.get() == aDescriptor.mxAccessibleShape.get())
+ )
+ );
+ }
+
+};
+
+
+} // end of namespace accessibility
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/DescriptionGenerator.cxx b/svx/source/accessibility/DescriptionGenerator.cxx
new file mode 100644
index 0000000000..ad133fa8f4
--- /dev/null
+++ b/svx/source/accessibility/DescriptionGenerator.cxx
@@ -0,0 +1,188 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#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/XNamed.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <utility>
+#include <vcl/svapp.hxx>
+
+// Includes for string resources.
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+
+#include "lookupcolorname.hxx"
+
+using namespace ::com::sun::star;
+
+namespace accessibility
+{
+DescriptionGenerator::DescriptionGenerator(uno::Reference<drawing::XShape> xShape)
+ : mxShape(std::move(xShape))
+ , mxSet(mxShape, uno::UNO_QUERY)
+ , mbIsFirstProperty(true)
+{
+}
+
+DescriptionGenerator::~DescriptionGenerator() {}
+
+void DescriptionGenerator::Initialize(TranslateId pResourceId)
+{
+ // Get the string from the resource for the specified id.
+ OUString sPrefix;
+ {
+ SolarMutexGuard aGuard;
+ sPrefix = SvxResId(pResourceId);
+ }
+
+ // Forward the call with the resulting string.
+ Initialize(sPrefix);
+}
+
+void DescriptionGenerator::Initialize(std::u16string_view sPrefix)
+{
+ msDescription = sPrefix;
+ if (!mxSet.is())
+ return;
+
+ {
+ SolarMutexGuard aGuard;
+
+ msDescription.append(' ');
+ msDescription.append(SvxResId(RID_SVXSTR_A11Y_WITH));
+ msDescription.append(' ');
+
+ msDescription.append(SvxResId(RID_SVXSTR_A11Y_STYLE));
+ msDescription.append('=');
+ }
+
+ try
+ {
+ if (mxSet.is())
+ {
+ uno::Any aValue = mxSet->getPropertyValue("Style");
+ uno::Reference<container::XNamed> xStyle(aValue, uno::UNO_QUERY);
+ if (xStyle.is())
+ msDescription.append(xStyle->getName());
+ }
+ else
+ msDescription.append("<no style>");
+ }
+ catch (const css::beans::UnknownPropertyException&)
+ {
+ msDescription.append("<unknown>");
+ }
+}
+
+OUString DescriptionGenerator::operator()()
+{
+ msDescription.append('.');
+ return msDescription.makeStringAndClear();
+}
+
+void DescriptionGenerator::AddProperty(const OUString& sPropertyName, PropertyType aType)
+{
+ uno::Reference<beans::XPropertyState> xState(mxShape, uno::UNO_QUERY);
+ if (!xState.is()
+ || xState->getPropertyState(sPropertyName) == beans::PropertyState_DEFAULT_VALUE)
+ return;
+
+ if (!mxSet.is())
+ return;
+
+ // Append a separator from previous Properties.
+ if (!mbIsFirstProperty)
+ msDescription.append(',');
+ else
+ {
+ SolarMutexGuard aGuard;
+
+ msDescription.append(' ');
+ msDescription.append(SvxResId(RID_SVXSTR_A11Y_AND));
+ msDescription.append(' ');
+ mbIsFirstProperty = false;
+ }
+
+ // Delegate to type specific property handling.
+ switch (aType)
+ {
+ case PropertyType::Color:
+ AddColor(sPropertyName);
+ break;
+ case PropertyType::Integer:
+ AddInteger(sPropertyName);
+ break;
+ }
+}
+
+void DescriptionGenerator::AppendString(std::u16string_view sString)
+{
+ msDescription.append(sString);
+}
+
+/** 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)
+{
+ msDescription.append('=');
+
+ try
+ {
+ tools::Long nValue(0);
+ if (mxSet.is())
+ {
+ uno::Any aValue = mxSet->getPropertyValue(sPropertyName);
+ aValue >>= nValue;
+ }
+
+ msDescription.append(lookUpColorName(nValue));
+ }
+ catch (const css::beans::UnknownPropertyException&)
+ {
+ msDescription.append("<unknown>");
+ }
+}
+
+void DescriptionGenerator::AddInteger(const OUString& sPropertyName)
+{
+ msDescription.append('=');
+
+ try
+ {
+ if (mxSet.is())
+ {
+ uno::Any aValue = mxSet->getPropertyValue(sPropertyName);
+ tools::Long nValue = 0;
+ aValue >>= nValue;
+ msDescription.append(nValue);
+ }
+ }
+ catch (const css::beans::UnknownPropertyException&)
+ {
+ msDescription.append("<unknown>");
+ }
+}
+
+} // end of namespace accessibility
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/GraphCtlAccessibleContext.cxx b/svx/source/accessibility/GraphCtlAccessibleContext.cxx
new file mode 100644
index 0000000000..1287f7e9ea
--- /dev/null
+++ b/svx/source/accessibility/GraphCtlAccessibleContext.cxx
@@ -0,0 +1,777 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#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/accessibility/IllegalAccessibleComponentStateException.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/mutex.hxx>
+#include <tools/gen.hxx>
+#include <svtools/colorcfg.hxx>
+#include <comphelper/accessibleeventnotifier.hxx>
+#include <svx/sdrpaintwindow.hxx>
+
+#include <svx/ShapeTypeHandler.hxx>
+#include <svx/AccessibleShapeInfo.hxx>
+#include <GraphCtlAccessibleContext.hxx>
+#include <svx/graphctl.hxx>
+#include <svx/strings.hrc>
+#include <svx/svdpage.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/sdrhittesthelper.hxx>
+
+// namespaces
+using namespace ::cppu;
+using namespace ::osl;
+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(
+ GraphCtrl& rRepr ) :
+
+ SvxGraphCtrlAccessibleContext_Base( m_aMutex ),
+ mpControl( &rRepr ),
+ mpModel (nullptr),
+ mpPage (nullptr),
+ mpView (nullptr),
+ mnClientId( 0 ),
+ mbDisposed( false )
+{
+ if (mpControl != nullptr)
+ {
+ mpModel = mpControl->GetSdrModel();
+ if (mpModel != nullptr)
+ mpPage = mpModel->GetPage( 0 );
+ mpView = mpControl->GetSdrView();
+
+ if( mpModel == nullptr || mpPage == nullptr || mpView == nullptr )
+ {
+ mbDisposed = true;
+ // Set all the pointers to NULL just in case they are used as
+ // a disposed flag.
+ mpModel = nullptr;
+ mpPage = nullptr;
+ mpView = nullptr;
+ }
+ }
+
+ {
+ ::SolarMutexGuard aSolarGuard;
+ msName = SvxResId( RID_SVXSTR_GRAPHCTRL_ACC_NAME );
+ msDescription = SvxResId( RID_SVXSTR_GRAPHCTRL_ACC_DESCRIPTION );
+ }
+
+ maTreeInfo.SetSdrView( mpView );
+ maTreeInfo.SetWindow(mpControl->GetDrawingArea()->get_ref_device().GetOwnerWindow());
+ maTreeInfo.SetViewForwarder( 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 > SvxGraphCtrlAccessibleContext::getAccessible( const SdrObject* pObj )
+{
+ Reference<XAccessible> xAccessibleShape;
+
+ if( pObj )
+ {
+ // see if we already created an XAccessible for the given SdrObject
+ ShapesMapType::const_iterator iter = mxShapes.find( pObj );
+
+ if( iter != mxShapes.end() )
+ {
+ // if we already have one, return it
+ xAccessibleShape = (*iter).second.get();
+ }
+ else
+ {
+ // create a new one and remember in our internal map
+ Reference< XShape > xShape( Reference< XShape >::query( const_cast<SdrObject*>(pObj)->getUnoShape() ) );
+
+ css::uno::Reference<css::accessibility::XAccessible> xParent(getAccessibleParent());
+ AccessibleShapeInfo aShapeInfo (xShape,xParent);
+ // Create accessible object that corresponds to the descriptor's shape.
+ rtl::Reference<AccessibleShape> pAcc(ShapeTypeHandler::Instance().CreateAccessibleObject(
+ aShapeInfo, maTreeInfo));
+ xAccessibleShape = pAcc.get();
+ if (pAcc.is())
+ {
+ pAcc->Init ();
+ }
+ mxShapes[pObj] = pAcc;
+
+ // Create event and inform listeners of the object creation.
+ CommitChange( AccessibleEventId::CHILD, Any( xAccessibleShape ), Any( Reference<XAccessible>() ) );
+ }
+ }
+
+ return xAccessibleShape;
+}
+
+// XAccessible
+Reference< XAccessibleContext > SAL_CALL SvxGraphCtrlAccessibleContext::getAccessibleContext()
+{
+ return this;
+}
+
+// XAccessibleComponent
+sal_Bool SAL_CALL SvxGraphCtrlAccessibleContext::containsPoint( const awt::Point& rPoint )
+{
+ // 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 )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ Reference< XAccessible > xAccessible;
+
+ if( !mpControl )
+ {
+ throw DisposedException();
+ }
+
+ Point aPnt( rPoint.X, rPoint.Y );
+ aPnt = mpControl->GetDrawingArea()->get_ref_device().PixelToLogic(aPnt);
+
+ SdrObject* pObj = nullptr;
+
+ if(mpView && mpView->GetSdrPageView())
+ {
+ pObj = SdrObjListPrimitiveHit(*mpPage, aPnt, {1, 1}, *mpView->GetSdrPageView(), nullptr, false);
+ }
+
+ if( pObj )
+ xAccessible = getAccessible( pObj );
+
+ return xAccessible;
+}
+
+awt::Rectangle SAL_CALL SvxGraphCtrlAccessibleContext::getBounds()
+{
+ const SolarMutexGuard aSolarGuard;
+
+ if (nullptr == mpControl)
+ throw DisposedException();
+
+ const Point aOutPos;
+ const Size aOutSize( mpControl->GetOutputSizePixel() );
+ awt::Rectangle aRet;
+
+ aRet.X = aOutPos.X();
+ aRet.Y = aOutPos.Y();
+ aRet.Width = aOutSize.Width();
+ aRet.Height = aOutSize.Height();
+
+ return aRet;
+}
+
+awt::Point SAL_CALL SvxGraphCtrlAccessibleContext::getLocation()
+{
+ const SolarMutexGuard aSolarGuard;
+
+ if (nullptr == mpControl)
+ throw DisposedException();
+
+ const awt::Rectangle aRect( getBounds() );
+ awt::Point aRet;
+
+ aRet.X = aRect.X;
+ aRet.Y = aRect.Y;
+
+ return aRet;
+}
+
+awt::Point SAL_CALL SvxGraphCtrlAccessibleContext::getLocationOnScreen()
+{
+ const SolarMutexGuard aSolarGuard;
+
+ if (nullptr == mpControl)
+ throw DisposedException();
+
+ awt::Point aScreenLoc(0, 0);
+
+ auto xParent(getAccessibleParent());
+ if (xParent)
+ {
+ css::uno::Reference<css::accessibility::XAccessibleContext> xParentContext(xParent->getAccessibleContext());
+ css::uno::Reference<css::accessibility::XAccessibleComponent> xParentComponent(xParentContext, css::uno::UNO_QUERY);
+ OSL_ENSURE( xParentComponent.is(), "ValueSetAcc::getLocationOnScreen: no parent component!" );
+ if ( xParentComponent.is() )
+ {
+ awt::Point aParentScreenLoc( xParentComponent->getLocationOnScreen() );
+ awt::Point aOwnRelativeLoc( getLocation() );
+ aScreenLoc.X = aParentScreenLoc.X + aOwnRelativeLoc.X;
+ aScreenLoc.Y = aParentScreenLoc.Y + aOwnRelativeLoc.Y;
+ }
+ }
+
+ return aScreenLoc;
+}
+
+awt::Size SAL_CALL SvxGraphCtrlAccessibleContext::getSize()
+{
+ const SolarMutexGuard aSolarGuard;
+
+ if (nullptr == mpControl)
+ throw DisposedException();
+
+ const awt::Rectangle aRect( getBounds() );
+ awt::Size aRet;
+
+ aRet.Width = aRect.Width;
+ aRet.Height = aRect.Height;
+
+ return aRet;
+}
+
+// XAccessibleContext
+sal_Int64 SAL_CALL SvxGraphCtrlAccessibleContext::getAccessibleChildCount()
+{
+ ::SolarMutexGuard aGuard;
+
+ if( nullptr == mpPage )
+ throw DisposedException();
+
+ return mpPage->GetObjCount();
+}
+
+
+/** returns the SdrObject at index nIndex from the model of this graph */
+SdrObject* SvxGraphCtrlAccessibleContext::getSdrObject( sal_Int64 nIndex )
+{
+ ::SolarMutexGuard aGuard;
+
+ if( nullptr == mpPage )
+ throw DisposedException();
+
+ if( (nIndex < 0) || ( o3tl::make_unsigned(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 (
+ getXWeak(),
+ nEventId,
+ rNewValue,
+ rOldValue, -1);
+
+ if (mnClientId)
+ comphelper::AccessibleEventNotifier::addEvent( mnClientId, aEvent );
+}
+
+Reference< XAccessible > SAL_CALL SvxGraphCtrlAccessibleContext::getAccessibleChild( sal_Int64 nIndex )
+{
+ ::SolarMutexGuard aGuard;
+
+ return getAccessible( getSdrObject( nIndex ) );
+}
+
+Reference< XAccessible > SAL_CALL SvxGraphCtrlAccessibleContext::getAccessibleParent()
+{
+ ::SolarMutexGuard aGuard;
+
+ if( nullptr == mpControl )
+ throw DisposedException();
+
+ return mpControl->GetDrawingArea()->get_accessible_parent();
+}
+
+sal_Int64 SAL_CALL SvxGraphCtrlAccessibleContext::getAccessibleIndexInParent()
+{
+ ::SolarMutexGuard aGuard;
+ // Use a simple but slow solution for now. Optimize later.
+
+ // Iterate over all the parent's children and search for this object.
+ css::uno::Reference<css::accessibility::XAccessible> xParent(getAccessibleParent());
+ if (xParent.is())
+ {
+ Reference< XAccessibleContext > xParentContext( xParent->getAccessibleContext() );
+ if( xParentContext.is() )
+ {
+ sal_Int64 nChildCount = xParentContext->getAccessibleChildCount();
+ for( sal_Int64 i = 0 ; i < nChildCount ; ++i )
+ {
+ Reference< XAccessible > xChild( xParentContext->getAccessibleChild( i ) );
+ if( xChild.is() )
+ {
+ Reference< XAccessibleContext > xChildContext = xChild->getAccessibleContext();
+ if( xChildContext == static_cast<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()
+{
+ return AccessibleRole::PANEL;
+}
+
+
+OUString SAL_CALL SvxGraphCtrlAccessibleContext::getAccessibleDescription()
+{
+ ::SolarMutexGuard aGuard;
+ return msDescription;
+}
+
+
+OUString SAL_CALL SvxGraphCtrlAccessibleContext::getAccessibleName()
+{
+ ::SolarMutexGuard aGuard;
+ return msName;
+}
+
+
+/** Return empty reference to indicate that the relation set is not
+ supported.
+*/
+Reference< XAccessibleRelationSet > SAL_CALL SvxGraphCtrlAccessibleContext::getAccessibleRelationSet()
+{
+ return Reference< XAccessibleRelationSet >();
+}
+
+
+sal_Int64 SAL_CALL SvxGraphCtrlAccessibleContext::getAccessibleStateSet()
+{
+ ::SolarMutexGuard aGuard;
+
+ sal_Int64 nStateSet = 0;
+
+ if ( rBHelper.bDisposed || mbDisposed )
+ {
+ nStateSet |= AccessibleStateType::DEFUNC;
+ }
+ else
+ {
+ nStateSet |= AccessibleStateType::FOCUSABLE;
+ if( mpControl->HasFocus() )
+ nStateSet |= AccessibleStateType::FOCUSED;
+ nStateSet |= AccessibleStateType::OPAQUE;
+ nStateSet |= AccessibleStateType::SHOWING;
+ nStateSet |= AccessibleStateType::VISIBLE;
+ }
+
+ return nStateSet;
+}
+
+
+lang::Locale SAL_CALL SvxGraphCtrlAccessibleContext::getLocale()
+{
+ ::SolarMutexGuard aGuard;
+
+ css::uno::Reference<css::accessibility::XAccessible> xParent(getAccessibleParent());
+ if (xParent.is())
+ {
+ Reference< XAccessibleContext > xParentContext( xParent->getAccessibleContext() );
+ if( xParentContext.is() )
+ return xParentContext->getLocale();
+ }
+
+ // No parent. Therefore throw exception to indicate this cluelessness.
+ throw IllegalAccessibleComponentStateException();
+}
+
+// XAccessibleEventListener
+void SAL_CALL SvxGraphCtrlAccessibleContext::addAccessibleEventListener( const Reference< XAccessibleEventListener >& xListener )
+{
+ if (xListener.is())
+ {
+ ::SolarMutexGuard aGuard;
+ if (!mnClientId)
+ mnClientId = comphelper::AccessibleEventNotifier::registerClient( );
+ comphelper::AccessibleEventNotifier::addEventListener( mnClientId, xListener );
+ }
+}
+
+
+void SAL_CALL SvxGraphCtrlAccessibleContext::removeAccessibleEventListener( const Reference< XAccessibleEventListener >& xListener )
+{
+ if (!xListener.is())
+ return;
+
+ ::SolarMutexGuard aGuard;
+
+ 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::grabFocus()
+{
+ ::SolarMutexGuard aGuard;
+
+ if( nullptr == mpControl )
+ throw DisposedException();
+
+ mpControl->GrabFocus();
+}
+
+sal_Int32 SAL_CALL SvxGraphCtrlAccessibleContext::getForeground()
+{
+ svtools::ColorConfig aColorConfig;
+ Color nColor = aColorConfig.GetColorValue( svtools::FONTCOLOR ).nColor;
+ return static_cast<sal_Int32>(nColor);
+}
+
+sal_Int32 SAL_CALL SvxGraphCtrlAccessibleContext::getBackground()
+{
+ Color nColor = Application::GetSettings().GetStyleSettings().GetWindowColor();
+ return static_cast<sal_Int32>(nColor);
+}
+
+// XServiceInfo
+OUString SAL_CALL SvxGraphCtrlAccessibleContext::getImplementationName()
+{
+ return "com.sun.star.comp.ui.SvxGraphCtrlAccessibleContext";
+}
+
+sal_Bool SAL_CALL SvxGraphCtrlAccessibleContext::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+Sequence< OUString > SAL_CALL SvxGraphCtrlAccessibleContext::getSupportedServiceNames()
+{
+ return { "com.sun.star.accessibility.Accessible",
+ "com.sun.star.accessibility.AccessibleContext",
+ "com.sun.star.drawing.AccessibleGraphControl" };
+}
+
+// XTypeProvider
+Sequence<sal_Int8> SAL_CALL SvxGraphCtrlAccessibleContext::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// XServiceName
+OUString SvxGraphCtrlAccessibleContext::getServiceName()
+{
+ return "com.sun.star.accessibility.AccessibleContext";
+}
+
+// XAccessibleSelection
+void SAL_CALL SvxGraphCtrlAccessibleContext::selectAccessibleChild( sal_Int64 nIndex )
+{
+ ::SolarMutexGuard aGuard;
+
+ if( nullptr == mpView )
+ throw DisposedException();
+
+ if (nIndex < 0 || nIndex >= getAccessibleChildCount())
+ throw lang::IndexOutOfBoundsException();
+
+ SdrObject* pObj = getSdrObject( nIndex );
+
+ if( pObj )
+ mpView->MarkObj( pObj, mpView->GetSdrPageView());
+}
+
+
+sal_Bool SAL_CALL SvxGraphCtrlAccessibleContext::isAccessibleChildSelected( sal_Int64 nIndex )
+{
+ ::SolarMutexGuard aGuard;
+
+ if( nullptr == mpView )
+ throw DisposedException();
+
+ if (nIndex < 0 || nIndex >= getAccessibleChildCount())
+ throw lang::IndexOutOfBoundsException();
+
+ return mpView->IsObjMarked( getSdrObject( nIndex ) );
+}
+
+
+void SAL_CALL SvxGraphCtrlAccessibleContext::clearAccessibleSelection()
+{
+ ::SolarMutexGuard aGuard;
+
+ if( nullptr == mpView )
+ throw DisposedException();
+
+ mpView->UnmarkAllObj();
+}
+
+
+void SAL_CALL SvxGraphCtrlAccessibleContext::selectAllAccessibleChildren()
+{
+ ::SolarMutexGuard aGuard;
+
+ if( nullptr == mpView )
+ throw DisposedException();
+
+ mpView->MarkAllObj();
+}
+
+
+sal_Int64 SAL_CALL SvxGraphCtrlAccessibleContext::getSelectedAccessibleChildCount()
+{
+ ::SolarMutexGuard aGuard;
+
+ if( nullptr == mpView )
+ throw DisposedException();
+
+ const SdrMarkList& rList = mpView->GetMarkedObjectList();
+ return static_cast<sal_Int64>(rList.GetMarkCount());
+}
+
+
+Reference< XAccessible > SAL_CALL SvxGraphCtrlAccessibleContext::getSelectedAccessibleChild( sal_Int64 nIndex )
+{
+ ::SolarMutexGuard aGuard;
+
+ checkChildIndexOnSelection( nIndex );
+
+ Reference< XAccessible > xAccessible;
+
+ const SdrMarkList& rList = mpView->GetMarkedObjectList();
+ SdrObject* pObj = rList.GetMark(static_cast<size_t>(nIndex))->GetMarkedSdrObj();
+ if( pObj )
+ xAccessible = getAccessible( pObj );
+
+ return xAccessible;
+}
+
+
+void SAL_CALL SvxGraphCtrlAccessibleContext::deselectAccessibleChild( sal_Int64 nIndex )
+{
+ ::SolarMutexGuard aGuard;
+
+ checkChildIndexOnSelection( nIndex );
+
+ if( !mpView )
+ return;
+
+ const SdrMarkList& rList = mpView->GetMarkedObjectList();
+
+ SdrObject* pObj = getSdrObject( nIndex );
+ if( !pObj )
+ return;
+
+ SdrMarkList aRefList( rList );
+
+ SdrPageView* pPV = mpView->GetSdrPageView();
+ mpView->UnmarkAllObj( pPV );
+
+ const size_t nCount = aRefList.GetMarkCount();
+ for( size_t nMark = 0; nMark < nCount; ++nMark )
+ {
+ if( aRefList.GetMark(nMark)->GetMarkedSdrObj() != pObj )
+ mpView->MarkObj( aRefList.GetMark(nMark)->GetMarkedSdrObj(), pPV );
+ }
+}
+
+// internals
+void SvxGraphCtrlAccessibleContext::checkChildIndexOnSelection(sal_Int64 nIndex )
+{
+ if( nIndex < 0 || nIndex >= getSelectedAccessibleChildCount() )
+ throw lang::IndexOutOfBoundsException();
+}
+
+
+/** Replace the model, page, and view pointers by the ones provided
+ (explicitly and implicitly).
+*/
+void SvxGraphCtrlAccessibleContext::setModelAndView (
+ SdrModel* pModel,
+ SdrView* pView)
+{
+ ::SolarMutexGuard aGuard;
+
+ mpModel = pModel;
+ if (mpModel != nullptr)
+ mpPage = mpModel->GetPage( 0 );
+ mpView = pView;
+
+ if (mpModel == nullptr || mpPage == nullptr || mpView == nullptr)
+ {
+ mbDisposed = true;
+
+ // Set all the pointers to NULL just in case they are used as
+ // a disposed flag.
+ mpModel = nullptr;
+ mpPage = nullptr;
+ mpView = nullptr;
+ }
+
+ maTreeInfo.SetSdrView (mpView);
+}
+
+
+void SAL_CALL SvxGraphCtrlAccessibleContext::disposing()
+{
+ ::SolarMutexGuard aGuard;
+
+ if( mbDisposed )
+ return;
+
+ mbDisposed = true;
+
+ mpControl = nullptr; // object dies with representation
+ mpView = nullptr;
+ mpPage = nullptr;
+
+ {
+ for (const auto& rEntry : mxShapes)
+ {
+ rtl::Reference<XAccessible> pAcc(rEntry.second);
+ Reference< XComponent > xComp( pAcc.get(), UNO_QUERY );
+ if( xComp.is() )
+ xComp->dispose();
+ }
+
+ mxShapes.clear();
+ }
+
+ // Send a disposing to all listeners.
+ if ( mnClientId )
+ {
+ comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing( mnClientId, *this );
+ mnClientId = 0;
+ }
+}
+
+void SvxGraphCtrlAccessibleContext::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
+{
+ if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint)
+ {
+ const SdrHint* pSdrHint = static_cast<const SdrHint*>( &rHint );
+ switch( pSdrHint->GetKind() )
+ {
+ case SdrHintKind::ObjectChange:
+ {
+ ShapesMapType::iterator iter = mxShapes.find( pSdrHint->GetObject() );
+
+ if( iter != mxShapes.end() )
+ {
+ // if we already have one, return it
+ rtl::Reference<AccessibleShape> pShape((*iter).second);
+
+ if( pShape.is() )
+ pShape->CommitChange( AccessibleEventId::VISIBLE_DATA_CHANGED, uno::Any(), uno::Any(), -1 );
+ }
+ }
+ break;
+
+ case SdrHintKind::ObjectInserted:
+ CommitChange( AccessibleEventId::CHILD, Any( getAccessible( pSdrHint->GetObject() ) ) , uno::Any());
+ break;
+ case SdrHintKind::ObjectRemoved:
+ CommitChange( AccessibleEventId::CHILD, uno::Any(), Any( getAccessible( pSdrHint->GetObject() ) ) );
+ break;
+ case SdrHintKind::ModelCleared:
+ dispose();
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ // Has our SdDrawDocument just died?
+ if(rHint.GetId() == SfxHintId::Dying)
+ {
+ dispose();
+ }
+ }
+}
+
+// IAccessibleViewforwarder
+tools::Rectangle SvxGraphCtrlAccessibleContext::GetVisibleArea() const
+{
+ tools::Rectangle aVisArea;
+
+ if( mpView && mpView->PaintWindowCount())
+ {
+ SdrPaintWindow* pPaintWindow = mpView->GetPaintWindow(0);
+ aVisArea = pPaintWindow->GetVisibleArea();
+ }
+
+ return aVisArea;
+}
+
+Point SvxGraphCtrlAccessibleContext::LogicToPixel (const Point& rPoint) const
+{
+ if( mpControl )
+ {
+ return mpControl->GetDrawingArea()->get_ref_device().LogicToPixel (rPoint) + mpControl->GetPositionInDialog();
+ }
+ else
+ {
+ return rPoint;
+ }
+}
+
+Size SvxGraphCtrlAccessibleContext::LogicToPixel (const Size& rSize) const
+{
+ if( mpControl )
+ return mpControl->GetDrawingArea()->get_ref_device().LogicToPixel(rSize);
+ else
+ return rSize;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/ShapeTypeHandler.cxx b/svx/source/accessibility/ShapeTypeHandler.cxx
new file mode 100644
index 0000000000..1b169c761a
--- /dev/null
+++ b/svx/source/accessibility/ShapeTypeHandler.cxx
@@ -0,0 +1,306 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <svx/ShapeTypeHandler.hxx>
+#include <svx/SvxShapeTypes.hxx>
+#include <svx/AccessibleShapeInfo.hxx>
+#include <vcl/svapp.hxx>
+#include <svx/AccessibleShape.hxx>
+#include <svx/dialmgr.hxx>
+
+#include <svx/svdoashp.hxx>
+
+#include <svx/strings.hrc>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+namespace accessibility {
+
+// Pointer to the shape type handler singleton.
+ShapeTypeHandler* ShapeTypeHandler::instance = nullptr;
+
+
+// Create an empty reference to an accessible object.
+static rtl::Reference<AccessibleShape>
+ CreateEmptyShapeReference (
+ const AccessibleShapeInfo& /*rShapeInfo*/,
+ const AccessibleShapeTreeInfo& /*rShapeTreeInfo*/,
+ ShapeTypeId /*nId*/)
+{
+ return nullptr;
+}
+
+
+ShapeTypeHandler& ShapeTypeHandler::Instance()
+{
+ // Using double check pattern to make sure that exactly one instance of
+ // the shape type handler is instantiated.
+ if (instance == nullptr)
+ {
+ SolarMutexGuard aGuard;
+ if (instance == nullptr)
+ {
+ // 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::const_iterator I (maServiceNameToSlotId.find (aServiceName));
+ if (I != maServiceNameToSlotId.end())
+ {
+ 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
+{
+ if (rxShape.is())
+ return GetTypeId (rxShape->getShapeType());
+ else
+ return -1;
+}
+
+
+/** 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.
+*/
+rtl::Reference<AccessibleShape>
+ ShapeTypeHandler::CreateAccessibleObject (
+ const AccessibleShapeInfo& rShapeInfo,
+ const AccessibleShapeTreeInfo& rShapeTreeInfo) const
+{
+ ShapeTypeId nSlotId (GetSlotId (rShapeInfo.mxShape));
+ rtl::Reference<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()
+ : 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 = "UNKNOWN_SHAPE_TYPE";
+ maShapeTypeDescriptorList[0].maCreateFunction = CreateEmptyShapeReference;
+ maServiceNameToSlotId[maShapeTypeDescriptorList[0].msServiceName] = 0;
+}
+
+
+ShapeTypeHandler::~ShapeTypeHandler()
+{
+ // 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 = nullptr;
+}
+
+
+void ShapeTypeHandler::AddShapeTypeList (int nDescriptorCount,
+ ShapeTypeDescriptor const aDescriptorList[])
+{
+ SolarMutexGuard aGuard;
+
+ // 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++)
+ {
+ // 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;
+ }
+}
+
+
+tools::Long ShapeTypeHandler::GetSlotId (const OUString& aServiceName) const
+{
+ tServiceNameToSlotId::const_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.
+tools::Long ShapeTypeHandler::GetSlotId (const uno::Reference<drawing::XShape>& rxShape) const
+{
+ if (rxShape.is())
+ return GetSlotId (rxShape->getShapeType());
+ else
+ return 0;
+}
+
+/// get the accessible base name for an object
+OUString ShapeTypeHandler::CreateAccessibleBaseName (const uno::Reference<drawing::XShape>& rxShape)
+{
+ TranslateId pResourceId;
+ 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:
+ pResourceId = STR_ObjNameSingulCube3d;
+ break;
+ case DRAWING_3D_EXTRUDE:
+ pResourceId = STR_ObjNameSingulExtrude3d;
+ break;
+ case DRAWING_3D_LATHE:
+ pResourceId = STR_ObjNameSingulLathe3d;
+ break;
+ case DRAWING_3D_SCENE:
+ pResourceId = STR_ObjNameSingulScene3d;
+ break;
+ case DRAWING_3D_SPHERE:
+ pResourceId = STR_ObjNameSingulSphere3d;
+ break;
+ case DRAWING_CAPTION:
+ pResourceId = STR_ObjNameSingulCAPTION;
+ break;
+ case DRAWING_CLOSED_BEZIER:
+ pResourceId = STR_ObjNameSingulPATHFILL;
+ break;
+ case DRAWING_CLOSED_FREEHAND:
+ pResourceId = STR_ObjNameSingulFREEFILL;
+ break;
+ case DRAWING_CONNECTOR:
+ pResourceId = STR_ObjNameSingulEDGE;
+ break;
+ case DRAWING_CONTROL:
+ pResourceId = STR_ObjNameSingulUno;
+ break;
+ case DRAWING_ELLIPSE:
+ pResourceId = STR_ObjNameSingulCIRCE;
+ break;
+ case DRAWING_GROUP:
+ pResourceId = STR_ObjNameSingulGRUP;
+ break;
+ case DRAWING_LINE:
+ pResourceId = STR_ObjNameSingulLINE;
+ break;
+ case DRAWING_MEASURE:
+ pResourceId = STR_ObjNameSingulMEASURE;
+ break;
+ case DRAWING_OPEN_BEZIER:
+ pResourceId = STR_ObjNameSingulPATHLINE;
+ break;
+ case DRAWING_OPEN_FREEHAND:
+ pResourceId = STR_ObjNameSingulFREELINE;
+ break;
+ case DRAWING_PAGE:
+ pResourceId = STR_ObjNameSingulPAGE;
+ break;
+ case DRAWING_POLY_LINE:
+ pResourceId = STR_ObjNameSingulPLIN;
+ break;
+ case DRAWING_POLY_LINE_PATH:
+ pResourceId = STR_ObjNameSingulPLIN;
+ break;
+ case DRAWING_POLY_POLYGON:
+ pResourceId = STR_ObjNameSingulPOLY;
+ break;
+ case DRAWING_POLY_POLYGON_PATH:
+ pResourceId = STR_ObjNameSingulPOLY;
+ break;
+ case DRAWING_RECTANGLE:
+ pResourceId = STR_ObjNameSingulRECT;
+ break;
+ case DRAWING_CUSTOM:
+ pResourceId = STR_ObjNameSingulCUSTOMSHAPE;
+
+ if (SdrObject* pSdrObject = SdrObject::getSdrObjectFromXShape(rxShape))
+ {
+ if (auto pCustomShape = dynamic_cast<SdrObjCustomShape*>(pSdrObject))
+ {
+ if (pCustomShape->IsTextPath())
+ pResourceId = STR_ObjNameSingulFONTWORK;
+ else
+ {
+ pResourceId = {};
+ sName = pCustomShape->GetCustomShapeName();
+ }
+ }
+ }
+ break;
+ case DRAWING_TEXT:
+ pResourceId = STR_ObjNameSingulTEXT;
+ break;
+ default:
+ pResourceId = {};
+ sName = "UnknownAccessibleShape";
+ if (rxShape.is())
+ sName += ": " + rxShape->getShapeType();
+ break;
+ }
+
+ if (pResourceId)
+ {
+ SolarMutexGuard aGuard;
+ sName = SvxResId(pResourceId);
+ }
+
+ return sName;
+}
+
+} // end of namespace accessibility
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/SvxShapeTypes.cxx b/svx/source/accessibility/SvxShapeTypes.cxx
new file mode 100644
index 0000000000..b02a153a4c
--- /dev/null
+++ b/svx/source/accessibility/SvxShapeTypes.cxx
@@ -0,0 +1,165 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <svx/SvxShapeTypes.hxx>
+#include <svx/AccessibleShape.hxx>
+#include <svx/AccessibleGraphicShape.hxx>
+#include <svx/AccessibleOLEShape.hxx>
+#include <svx/AccessibleControlShape.hxx>
+#include <svx/ShapeTypeHandler.hxx>
+#include <AccessibleTableShape.hxx>
+
+namespace accessibility {
+
+static rtl::Reference<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:
+ // Default accessibility shape for
+ // css::drawing::CustomShape (#i37790#)
+ case DRAWING_CUSTOM:
+ // Default accessibility shape for
+ // css::drawing::MediaShape (#i85429#)
+ 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 nullptr;
+ }
+}
+
+void RegisterDrawShapeTypes()
+{
+ /** List of shape type descriptors corresponding to the
+ <type>SvxShapeTypes</type> enum.
+ */
+ static ShapeTypeDescriptor const aSvxShapeTypeList[] = {
+ ShapeTypeDescriptor ( DRAWING_TEXT, "com.sun.star.drawing.TextShape",
+ CreateSvxAccessibleShape),
+ ShapeTypeDescriptor (DRAWING_RECTANGLE, "com.sun.star.drawing.RectangleShape",
+ CreateSvxAccessibleShape),
+ ShapeTypeDescriptor ( DRAWING_ELLIPSE, "com.sun.star.drawing.EllipseShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_CONTROL, "com.sun.star.drawing.ControlShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_CONNECTOR, "com.sun.star.drawing.ConnectorShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_MEASURE, "com.sun.star.drawing.MeasureShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_LINE, "com.sun.star.drawing.LineShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_POLY_POLYGON, "com.sun.star.drawing.PolyPolygonShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_POLY_LINE, "com.sun.star.drawing.PolyLineShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_OPEN_BEZIER, "com.sun.star.drawing.OpenBezierShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_CLOSED_BEZIER, "com.sun.star.drawing.ClosedBezierShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_OPEN_FREEHAND, "com.sun.star.drawing.OpenFreeHandShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_CLOSED_FREEHAND, "com.sun.star.drawing.ClosedFreeHandShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_POLY_POLYGON_PATH, "com.sun.star.drawing.PolyPolygonPathShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_POLY_LINE_PATH, "com.sun.star.drawing.PolyLinePathShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_GRAPHIC_OBJECT, "com.sun.star.drawing.GraphicObjectShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_GROUP, "com.sun.star.drawing.GroupShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_OLE, "com.sun.star.drawing.OLE2Shape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_PAGE, "com.sun.star.drawing.PageShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_CAPTION, "com.sun.star.drawing.CaptionShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_FRAME, "com.sun.star.drawing.FrameShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_PLUGIN, "com.sun.star.drawing.PluginShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_APPLET, "com.sun.star.drawing.AppletShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_3D_SCENE, "com.sun.star.drawing.Shape3DSceneObject",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_3D_CUBE, "com.sun.star.drawing.Shape3DCubeObject",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_3D_SPHERE, "com.sun.star.drawing.Shape3DSphereObject",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_3D_LATHE, "com.sun.star.drawing.Shape3DLatheObject",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_3D_EXTRUDE, "com.sun.star.drawing.Shape3DExtrudeObject",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_CUSTOM, "com.sun.star.drawing.CustomShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_TABLE, "com.sun.star.drawing.TableShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_MEDIA, "com.sun.star.drawing.MediaShape",
+ CreateSvxAccessibleShape ),
+
+ };
+
+ // Crash while inserting callout with activated accessibility (#i37790#)
+ ShapeTypeHandler::Instance().AddShapeTypeList ( DRAWING_END, aSvxShapeTypeList);
+}
+
+} // end of namespace accessibility
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/charmapacc.cxx b/svx/source/accessibility/charmapacc.cxx
new file mode 100644
index 0000000000..b5a0544d75
--- /dev/null
+++ b/svx/source/accessibility/charmapacc.cxx
@@ -0,0 +1,586 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vcl/settings.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 <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <o3tl/temporary.hxx>
+#include <osl/interlck.h>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <comphelper/accessiblecontexthelper.hxx>
+#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;
+
+SvxShowCharSetItem::SvxShowCharSetItem( SvxShowCharSet& rParent,SvxShowCharSetAcc* _pParent,sal_uInt16 _nPos ) :
+ mrParent( rParent )
+ ,mnId( _nPos )
+ ,m_pParent(_pParent)
+{
+}
+
+SvxShowCharSetItem::~SvxShowCharSetItem()
+{
+ if ( m_xItem.is() )
+ {
+ m_xItem->ParentDestroyed();
+ m_xItem.clear();
+ }
+}
+
+rtl::Reference<SvxShowCharSetItemAcc> SvxShowCharSetItem::GetAccessible()
+{
+ if( !m_xItem.is() )
+ {
+ m_xItem = new SvxShowCharSetItemAcc( this );
+ }
+
+ return m_xItem;
+}
+
+SvxShowCharSetAcc::SvxShowCharSetAcc(SvxShowCharSet* pParent)
+ : m_pParent(pParent)
+{
+ osl_atomic_increment(&m_refCount);
+ {
+ lateInit(this);
+ }
+ osl_atomic_decrement(&m_refCount);
+}
+
+SvxShowCharSetAcc::~SvxShowCharSetAcc()
+{
+ ensureDisposed();
+}
+
+void SAL_CALL SvxShowCharSetAcc::disposing()
+{
+ OAccessibleSelectionHelper::disposing();
+ for (auto& rxChild : m_aChildren)
+ rxChild->dispose();
+
+ m_aChildren.clear();
+ m_pParent = nullptr;
+}
+
+bool SvxShowCharSetAcc::implIsSelected( sal_Int64 nAccessibleChildIndex )
+{
+ if (!m_pParent)
+ return false;
+
+ if (nAccessibleChildIndex < 0 || nAccessibleChildIndex >= getAccessibleChildCount())
+ throw IndexOutOfBoundsException();
+
+ return m_pParent->IsSelected(sal::static_int_cast<sal_uInt16>(nAccessibleChildIndex));
+}
+
+// select the specified child => watch for special ChildIndexes (ACCESSIBLE_SELECTION_CHILD_xxx)
+void SvxShowCharSetAcc::implSelect(sal_Int64 nAccessibleChildIndex, bool bSelect)
+{
+ if (!m_pParent)
+ return;
+
+ if (nAccessibleChildIndex < 0 || nAccessibleChildIndex >= getAccessibleChildCount())
+ throw IndexOutOfBoundsException();
+
+ if (bSelect)
+ m_pParent->SelectIndex(nAccessibleChildIndex, true);
+ else
+ m_pParent->DeSelect();
+}
+
+css::awt::Rectangle SvxShowCharSetAcc::implGetBounds()
+{
+ awt::Rectangle aRet;
+
+ if (m_pParent)
+ {
+ const Point aOutPos;//( m_pParent->GetPosPixel() );
+ Size aOutSize( m_pParent->GetOutputSizePixel());
+
+ aRet.X = aOutPos.X();
+ aRet.Y = aOutPos.Y();
+ aRet.Width = aOutSize.Width();
+ aRet.Height = aOutSize.Height();
+ }
+
+ return aRet;
+}
+
+sal_Int64 SAL_CALL SvxShowCharSetAcc::getAccessibleChildCount()
+{
+ OExternalLockGuard aGuard( this );
+
+ return m_pParent->getMaxCharCount();
+}
+
+uno::Reference< css::accessibility::XAccessible > SAL_CALL SvxShowCharSetAcc::getAccessibleChild( sal_Int64 i )
+{
+ OExternalLockGuard aGuard( this );
+
+ rtl::Reference< SvxShowCharSetItemAcc > xRet;
+ SvxShowCharSetItem* pItem = m_pParent->ImplGetItem( static_cast< sal_uInt16 >( i ) );
+
+ if( !pItem )
+ throw lang::IndexOutOfBoundsException();
+
+ pItem->m_pParent = this;
+ xRet = pItem->GetAccessible();
+ m_aChildren.push_back(xRet);
+
+ return xRet;
+}
+
+uno::Reference< css::accessibility::XAccessible > SAL_CALL SvxShowCharSetAcc::getAccessibleParent()
+{
+ OExternalLockGuard aGuard( this );
+
+ if (m_pParent)
+ return m_pParent->getAccessibleParent();
+ return uno::Reference<css::accessibility::XAccessible>();
+}
+
+sal_Int16 SAL_CALL SvxShowCharSetAcc::getAccessibleRole()
+{
+ return css::accessibility::AccessibleRole::TABLE;
+}
+
+OUString SAL_CALL SvxShowCharSetAcc::getAccessibleDescription()
+{
+ OExternalLockGuard aGuard( this );
+ return SvxResId( RID_SVXSTR_CHARACTER_SELECTION );
+}
+
+
+OUString SAL_CALL SvxShowCharSetAcc::getAccessibleName()
+{
+ OExternalLockGuard aGuard( this );
+
+ return SvxResId( RID_SVXSTR_CHAR_SEL_DESC );
+}
+
+
+uno::Reference< css::accessibility::XAccessibleRelationSet > SAL_CALL SvxShowCharSetAcc::getAccessibleRelationSet()
+{
+ return uno::Reference< css::accessibility::XAccessibleRelationSet >();
+}
+
+
+sal_Int64 SAL_CALL SvxShowCharSetAcc::getAccessibleStateSet()
+{
+ OExternalLockGuard aGuard( this );
+
+ sal_Int64 nStateSet = 0;
+
+ if (m_pParent)
+ {
+ // SELECTABLE
+ nStateSet |= AccessibleStateType::FOCUSABLE;
+ if (m_pParent->HasFocus())
+ {
+ nStateSet |= AccessibleStateType::FOCUSED;
+ nStateSet |= AccessibleStateType::ACTIVE;
+ }
+ if (m_pParent->IsEnabled())
+ {
+ nStateSet |= AccessibleStateType::ENABLED;
+ nStateSet |= AccessibleStateType::SENSITIVE;
+ }
+ if (m_pParent->IsVisible())
+ nStateSet |= AccessibleStateType::VISIBLE;
+
+ nStateSet |= AccessibleStateType::MANAGES_DESCENDANTS;
+ }
+
+ return nStateSet;
+}
+
+
+uno::Reference< css::accessibility::XAccessible > SAL_CALL SvxShowCharSetAcc::getAccessibleAtPoint( const awt::Point& aPoint )
+{
+ OExternalLockGuard aGuard( this );
+
+ uno::Reference< css::accessibility::XAccessible > xRet;
+ const sal_uInt16 nItemId = sal::static_int_cast<sal_uInt16>(
+ m_pParent->PixelToMapIndex( Point( aPoint.X, aPoint.Y ) ));
+
+ if( sal_uInt16(-1) != nItemId )
+ {
+ SvxShowCharSetItem* pItem = m_pParent->ImplGetItem( nItemId );
+ xRet = pItem->GetAccessible();
+ }
+ return xRet;
+}
+
+void SAL_CALL SvxShowCharSetAcc::grabFocus()
+{
+ OExternalLockGuard aGuard( this );
+
+ m_pParent->GrabFocus();
+}
+
+sal_Int32 SAL_CALL SvxShowCharSetAcc::getAccessibleRowCount( )
+{
+ return ((getAccessibleChildCount()-1) / COLUMN_COUNT) + 1;
+}
+
+sal_Int32 SAL_CALL SvxShowCharSetAcc::getAccessibleColumnCount( )
+{
+ return COLUMN_COUNT;
+}
+
+OUString SAL_CALL SvxShowCharSetAcc::getAccessibleRowDescription( sal_Int32 /*nRow*/ )
+{
+ return OUString();
+}
+
+OUString SAL_CALL SvxShowCharSetAcc::getAccessibleColumnDescription( sal_Int32 /*nColumn*/ )
+{
+ return OUString();
+}
+
+sal_Int32 SAL_CALL SvxShowCharSetAcc::getAccessibleRowExtentAt( sal_Int32 /*nRow*/, sal_Int32 /*nColumn*/ )
+{
+ return 1;
+}
+
+sal_Int32 SAL_CALL SvxShowCharSetAcc::getAccessibleColumnExtentAt( sal_Int32 /*nRow*/, sal_Int32 /*nColumn*/ )
+{
+ return 1;
+}
+
+Reference< XAccessibleTable > SAL_CALL SvxShowCharSetAcc::getAccessibleRowHeaders( )
+{
+ return Reference< XAccessibleTable >();
+}
+
+Reference< XAccessibleTable > SAL_CALL SvxShowCharSetAcc::getAccessibleColumnHeaders( )
+{
+ return Reference< XAccessibleTable >();
+}
+
+Sequence< sal_Int32 > SAL_CALL SvxShowCharSetAcc::getSelectedAccessibleRows( )
+{
+ OExternalLockGuard aGuard( this );
+
+ return { SvxShowCharSet::GetRowPos(m_pParent->GetSelectIndexId()) };
+}
+
+Sequence< sal_Int32 > SAL_CALL SvxShowCharSetAcc::getSelectedAccessibleColumns( )
+{
+ OExternalLockGuard aGuard( this );
+
+ return { SvxShowCharSet::GetColumnPos(m_pParent->GetSelectIndexId()) };
+}
+
+sal_Bool SAL_CALL SvxShowCharSetAcc::isAccessibleRowSelected( sal_Int32 nRow )
+{
+ OExternalLockGuard aGuard( this );
+
+ return SvxShowCharSet::GetRowPos(m_pParent->GetSelectIndexId()) == nRow;
+}
+
+sal_Bool SAL_CALL SvxShowCharSetAcc::isAccessibleColumnSelected( sal_Int32 nColumn )
+{
+ OExternalLockGuard aGuard( this );
+ ensureAlive();
+ return SvxShowCharSet::GetColumnPos(m_pParent->GetSelectIndexId()) == nColumn;
+}
+
+Reference< XAccessible > SAL_CALL SvxShowCharSetAcc::getAccessibleCellAt( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ OExternalLockGuard aGuard( this );
+
+ svx::SvxShowCharSetItem* pItem = m_pParent->ImplGetItem(
+ sal::static_int_cast<sal_uInt16>(getAccessibleIndex(nRow,nColumn) ));
+ if ( !pItem )
+ throw IndexOutOfBoundsException();
+ return pItem->GetAccessible();
+}
+
+Reference< XAccessible > SAL_CALL SvxShowCharSetAcc::getAccessibleCaption( )
+{
+ return Reference< XAccessible >();
+}
+
+Reference< XAccessible > SAL_CALL SvxShowCharSetAcc::getAccessibleSummary( )
+{
+ return Reference< XAccessible >();
+}
+
+sal_Bool SAL_CALL SvxShowCharSetAcc::isAccessibleSelected( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ OExternalLockGuard aGuard( this );
+
+ return m_pParent->GetSelectIndexId() == getAccessibleIndex(nRow,nColumn);
+}
+
+sal_Int64 SAL_CALL SvxShowCharSetAcc::getAccessibleIndex( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ return (static_cast<sal_Int64>(nRow) * COLUMN_COUNT) + nColumn;
+}
+
+sal_Int32 SAL_CALL SvxShowCharSetAcc::getAccessibleRow( sal_Int64 nChildIndex )
+{
+ OExternalLockGuard aGuard( this );
+
+ return SvxShowCharSet::GetRowPos(sal::static_int_cast<sal_uInt16>(nChildIndex));
+}
+
+sal_Int32 SAL_CALL SvxShowCharSetAcc::getAccessibleColumn( sal_Int64 nChildIndex )
+{
+ OExternalLockGuard aGuard( this );
+
+ return SvxShowCharSet::GetColumnPos(sal::static_int_cast<sal_uInt16>(nChildIndex));
+}
+
+
+SvxShowCharSetItemAcc::SvxShowCharSetItemAcc( SvxShowCharSetItem* pParent ) : mpParent( pParent )
+{
+ OSL_ENSURE(pParent,"NO parent supplied!");
+ osl_atomic_increment(&m_refCount);
+ { // #b6211265 #
+ lateInit(this);
+ }
+ osl_atomic_decrement(&m_refCount);
+}
+
+
+SvxShowCharSetItemAcc::~SvxShowCharSetItemAcc()
+{
+ ensureDisposed();
+}
+
+void SvxShowCharSetItemAcc::ParentDestroyed()
+{
+ const ::osl::MutexGuard aGuard( GetMutex() );
+ mpParent = nullptr;
+}
+
+sal_Int64 SAL_CALL SvxShowCharSetItemAcc::getAccessibleChildCount()
+{
+ return 0;
+}
+
+
+uno::Reference< css::accessibility::XAccessible > SAL_CALL SvxShowCharSetItemAcc::getAccessibleChild( sal_Int64 /*i*/ )
+{
+ throw lang::IndexOutOfBoundsException();
+}
+
+
+uno::Reference< css::accessibility::XAccessible > SAL_CALL SvxShowCharSetItemAcc::getAccessibleParent()
+{
+ OExternalLockGuard aGuard( this );
+
+ return mpParent->m_pParent;
+}
+
+
+sal_Int16 SAL_CALL SvxShowCharSetItemAcc::getAccessibleRole()
+{
+ return css::accessibility::AccessibleRole::TABLE_CELL;
+}
+
+
+OUString SAL_CALL SvxShowCharSetItemAcc::getAccessibleDescription()
+{
+ OExternalLockGuard aGuard( this );
+
+ OUString sDescription;
+
+ const OUString aCharStr( mpParent->maText);
+ const sal_UCS4 c = aCharStr.iterateCodePoints( &o3tl::temporary(sal_Int32(0)) );
+ const int tmp_len = (c < 0x10000) ? 4 : 6;
+ char buf[16] = "0x0000";
+ sal_UCS4 c_Shifted = c;
+ for( int i = 0; i < tmp_len; ++i )
+ {
+ char h = static_cast<char>(c_Shifted & 0x0F);
+ buf[tmp_len+1-i] = (h > 9) ? (h - 10 + 'A') : (h + '0');
+ c_Shifted >>= 4;
+ }
+ if( c < 256 )
+ snprintf( buf+6, 10, " (%" SAL_PRIuUINT32 ")", c );
+
+ sDescription = SvxResId( RID_SVXSTR_CHARACTER_CODE )
+ + " "
+ + OUString(buf, strlen(buf), RTL_TEXTENCODING_ASCII_US);
+
+ return sDescription;
+}
+
+
+OUString SAL_CALL SvxShowCharSetItemAcc::getAccessibleName()
+{
+ OExternalLockGuard aGuard( this );
+
+ OUString aRet;
+
+ if( mpParent )
+ {
+ aRet = mpParent->maText;
+
+ if (aRet.isEmpty())
+ aRet = getAccessibleDescription();
+ }
+
+ return aRet;
+}
+
+
+uno::Reference< css::accessibility::XAccessibleRelationSet > SAL_CALL SvxShowCharSetItemAcc::getAccessibleRelationSet()
+{
+ return uno::Reference< css::accessibility::XAccessibleRelationSet >();
+}
+
+
+sal_Int64 SAL_CALL SvxShowCharSetItemAcc::getAccessibleStateSet()
+{
+ OExternalLockGuard aGuard( this );
+
+ sal_Int64 nStateSet = 0;
+
+ if( mpParent )
+ {
+ if (mpParent->mrParent.IsEnabled())
+ {
+ nStateSet |= css::accessibility::AccessibleStateType::ENABLED;
+ // SELECTABLE
+ nStateSet |= css::accessibility::AccessibleStateType::SELECTABLE;
+ nStateSet |= css::accessibility::AccessibleStateType::FOCUSABLE;
+ }
+
+ // SELECTED
+ if( mpParent->mrParent.GetSelectIndexId() == mpParent->mnId )
+ {
+ nStateSet |= css::accessibility::AccessibleStateType::SELECTED;
+ if (mpParent->mrParent.HasChildFocus())
+ nStateSet |= css::accessibility::AccessibleStateType::FOCUSED;
+ }
+ if ( mpParent->mnId >= mpParent->mrParent.FirstInView() && mpParent->mnId <= mpParent->mrParent.LastInView() )
+ {
+ nStateSet |= AccessibleStateType::VISIBLE;
+ nStateSet |= AccessibleStateType::SHOWING;
+ }
+ nStateSet |= AccessibleStateType::TRANSIENT;
+ }
+
+ return nStateSet;
+}
+
+
+sal_Int32 SvxShowCharSetItemAcc::getAccessibleActionCount()
+{
+ return 1;
+}
+
+
+sal_Bool SvxShowCharSetItemAcc::doAccessibleAction ( sal_Int32 nIndex )
+{
+ OExternalLockGuard aGuard( this );
+
+ if( nIndex == 0 )
+ {
+ mpParent->mrParent.OutputIndex( mpParent->mnId );
+ return true;
+ }
+ throw IndexOutOfBoundsException();
+}
+
+
+OUString SvxShowCharSetItemAcc::getAccessibleActionDescription ( sal_Int32 nIndex )
+{
+ if( nIndex == 0 )
+ return "press";
+ throw IndexOutOfBoundsException();
+}
+
+
+Reference< css::accessibility::XAccessibleKeyBinding > SvxShowCharSetItemAcc::getAccessibleActionKeyBinding( sal_Int32 nIndex )
+{
+ if( nIndex == 0 )
+ return Reference< css::accessibility::XAccessibleKeyBinding >();
+ throw IndexOutOfBoundsException();
+}
+
+
+void SAL_CALL SvxShowCharSetItemAcc::grabFocus()
+{
+ // nothing to do
+}
+
+awt::Rectangle SvxShowCharSetItemAcc::implGetBounds( )
+{
+ awt::Rectangle aRet;
+
+ if( mpParent )
+ {
+ tools::Rectangle aRect( mpParent->maRect );
+ tools::Rectangle aParentRect(Point(), 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< css::accessibility::XAccessible > SAL_CALL SvxShowCharSetItemAcc::getAccessibleAtPoint( const awt::Point& /*aPoint*/ )
+{
+ return uno::Reference< css::accessibility::XAccessible >();
+}
+
+sal_Int32 SAL_CALL SvxShowCharSetAcc::getForeground( )
+{
+ OExternalLockGuard aGuard( this );
+
+ //see SvxShowCharSet::InitSettings
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ return static_cast<sal_Int32>(rStyleSettings.GetDialogTextColor());
+}
+
+sal_Int32 SAL_CALL SvxShowCharSetAcc::getBackground( )
+{
+ OExternalLockGuard aGuard( this );
+
+ //see SvxShowCharSet::InitSettings
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ return static_cast<sal_Int32>(rStyleSettings.GetWindowColor());
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/lookupcolorname.cxx b/svx/source/accessibility/lookupcolorname.cxx
new file mode 100644
index 0000000000..373dae4693
--- /dev/null
+++ b/svx/source/accessibility/lookupcolorname.cxx
@@ -0,0 +1,121 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/drawing/ColorTable.hpp>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <comphelper/processfactory.hxx>
+#include <rtl/ustring.hxx>
+#include <vcl/svapp.hxx>
+
+#include "lookupcolorname.hxx"
+#include <unordered_map>
+
+namespace
+{
+class ColorNameMap
+{
+public:
+ ColorNameMap();
+ ColorNameMap(const ColorNameMap&) = delete;
+ ColorNameMap& operator=(const ColorNameMap&) = delete;
+
+ OUString lookUp(tools::Long color) const;
+
+private:
+ typedef std::unordered_map<tools::Long, OUString> Map;
+
+ Map map_;
+};
+
+ColorNameMap::ColorNameMap()
+{
+ css::uno::Sequence<OUString> aNames;
+ css::uno::Reference<css::container::XNameAccess> xNA;
+
+ try
+ {
+ // Create color table in which to look up the given color.
+ css::uno::Reference<css::container::XNameContainer> xColorTable
+ = css::drawing::ColorTable::create(comphelper::getProcessComponentContext());
+
+ // Get list of color names in order to iterate over the color table.
+
+ // Lock the solar mutex here as workaround for missing lock in
+ // called function.
+ SolarMutexGuard aGuard;
+ xNA = xColorTable;
+ aNames = xColorTable->getElementNames();
+ }
+ catch (css::uno::RuntimeException const&)
+ {
+ // When an exception occurred then we 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())
+ return;
+
+ for (const auto& rName : std::as_const(aNames))
+ {
+ // Get the numerical value for the i-th color name.
+ try
+ {
+ css::uno::Any aColor = xNA->getByName(rName);
+ tools::Long nColor = 0;
+ aColor >>= nColor;
+ map_[nColor] = rName;
+ }
+ catch (css::uno::RuntimeException const&)
+ {
+ // Ignore the exception: the color who lead to the exception
+ // is not included into the map.
+ }
+ }
+}
+
+OUString ColorNameMap::lookUp(tools::Long color) const
+{
+ Map::const_iterator i(map_.find(color));
+ if (i != map_.end())
+ {
+ return i->second;
+ }
+ // Did not find the given color; return its RGB tuple representation:
+ return "#" + OUString::number(color, 16);
+}
+}
+
+namespace accessibility
+{
+OUString lookUpColorName(tools::Long color)
+{
+ static ColorNameMap theColorNameMap;
+ return theColorNameMap.lookUp(color);
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/lookupcolorname.hxx b/svx/source/accessibility/lookupcolorname.hxx
new file mode 100644
index 0000000000..0d752c0fab
--- /dev/null
+++ b/svx/source/accessibility/lookupcolorname.hxx
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SVX_SOURCE_ACCESSIBILITY_LOOKUPCOLORNAME_HXX
+#define INCLUDED_SVX_SOURCE_ACCESSIBILITY_LOOKUPCOLORNAME_HXX
+
+#include <sal/config.h>
+
+#include <rtl/ustring.hxx>
+#include <tools/long.hxx>
+
+namespace accessibility
+{
+/** This is a color name lookup targeted to be used by the accessibility
+ <type>DescriptionGenerator</type> class. It encapsulates a
+ <type>com.sun.star.drawing.ColorTable</type> and provides an inverse look
+ up of color names for given numerical color descriptions (the RGB values
+ encoded as an integer).
+
+ <p>The implementation uses as singleton so that the
+ <type>com.sun.star.drawing.ColorTable</type> object needs to be created
+ only once. That singleton instance for now lives until the application
+ terminates. However, the color table from which it takes its values may
+ change during this time. Reacting to these changes remains a task for the
+ future.</p>
+
+ @param nColor
+ This integer is the sum of the 8 Bit red value shifted left 16 Bits, the
+ green value shifted left 8 Bits, and the unshifted blue value.
+
+ @return
+ The returned string is either the color name of the specified color or,
+ when no name exists, a string of the form "#RRGGBB" with two hexadecimal
+ digits for each color component.
+*/
+OUString lookUpColorName(tools::Long color);
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/svxpixelctlaccessiblecontext.cxx b/svx/source/accessibility/svxpixelctlaccessiblecontext.cxx
new file mode 100644
index 0000000000..8c0e0ca1c0
--- /dev/null
+++ b/svx/source/accessibility/svxpixelctlaccessiblecontext.cxx
@@ -0,0 +1,444 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#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/IndexOutOfBoundsException.hpp>
+#include <toolkit/helper/convert.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <osl/mutex.hxx>
+#include <tools/debug.hxx>
+#include <tools/gen.hxx>
+
+#include <svx/dlgctrl.hxx>
+
+#include <svxpixelctlaccessiblecontext.hxx>
+
+using namespace ::cppu;
+using namespace ::osl;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::accessibility;
+
+SvxPixelCtlAccessible::SvxPixelCtlAccessible(SvxPixelCtl* pControl)
+ : mpPixelCtl(pControl)
+{
+}
+
+SvxPixelCtlAccessible::~SvxPixelCtlAccessible()
+{
+ ensureDisposed();
+}
+
+uno::Reference< XAccessibleContext > SvxPixelCtlAccessible::getAccessibleContext( )
+{
+ return this;
+}
+
+sal_Int64 SvxPixelCtlAccessible::getAccessibleChildCount( )
+{
+ return SvxPixelCtl::GetSquares();
+}
+uno::Reference< XAccessible > SvxPixelCtlAccessible::getAccessibleChild( sal_Int64 i )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( i < 0 || i >= getAccessibleChildCount())
+ throw lang::IndexOutOfBoundsException();
+ Reference< XAccessible > xChild;
+ if (mpPixelCtl)
+ xChild = CreateChild(i, mpPixelCtl->IndexToPoint(i));
+ return xChild;
+}
+
+uno::Reference< XAccessible > SvxPixelCtlAccessible::getAccessibleParent( )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if (mpPixelCtl)
+ return mpPixelCtl->getAccessibleParent();
+ return uno::Reference<css::accessibility::XAccessible>();
+}
+
+sal_Int16 SvxPixelCtlAccessible::getAccessibleRole( )
+{
+ return AccessibleRole::LIST;
+}
+
+OUString SvxPixelCtlAccessible::getAccessibleDescription( )
+{
+
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return mpPixelCtl ? mpPixelCtl->GetAccessibleDescription() : "";
+}
+
+OUString SvxPixelCtlAccessible::getAccessibleName( )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return mpPixelCtl ? mpPixelCtl->GetAccessibleName() : "";
+}
+
+Reference< XAccessibleRelationSet > SAL_CALL SvxPixelCtlAccessible::getAccessibleRelationSet()
+{
+ if (mpPixelCtl)
+ return mpPixelCtl->get_accessible_relation_set();
+ return uno::Reference<css::accessibility::XAccessibleRelationSet>();
+}
+
+sal_Int64 SvxPixelCtlAccessible::getAccessibleStateSet( )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ sal_Int64 nStateSet = 0;
+
+ if (mpPixelCtl)
+ {
+ nStateSet |=
+ AccessibleStateType::FOCUSABLE |
+ AccessibleStateType::SELECTABLE |
+ AccessibleStateType::SHOWING |
+ AccessibleStateType::VISIBLE |
+ AccessibleStateType::OPAQUE;
+ if (mpPixelCtl->IsEnabled())
+ nStateSet |= AccessibleStateType::ENABLED;
+ if (mpPixelCtl->HasFocus())
+ nStateSet |= AccessibleStateType::FOCUSED;
+ nStateSet |= AccessibleStateType::MANAGES_DESCENDANTS;
+ }
+
+ return nStateSet;
+}
+
+uno::Reference<XAccessible > SAL_CALL SvxPixelCtlAccessible::getAccessibleAtPoint (
+ const awt::Point& rPoint)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ Reference< XAccessible > xRet;
+
+ if (mpPixelCtl)
+ {
+ tools::Long nIndex = mpPixelCtl->PointToIndex(Point(rPoint.X, rPoint.Y));
+ xRet = CreateChild(nIndex, mpPixelCtl->IndexToPoint(nIndex));
+ }
+
+ return xRet;
+}
+
+awt::Rectangle SvxPixelCtlAccessible::implGetBounds()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ awt::Rectangle aRet;
+
+ if (mpPixelCtl)
+ {
+ const Point aOutPos;
+ Size aOutSize(mpPixelCtl->GetOutputSizePixel());
+
+ aRet.X = aOutPos.X();
+ aRet.Y = aOutPos.Y();
+ aRet.Width = aOutSize.Width();
+ aRet.Height = aOutSize.Height();
+ }
+
+ return aRet;
+}
+
+void SvxPixelCtlAccessible::grabFocus( )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if (mpPixelCtl)
+ mpPixelCtl->GrabFocus();
+}
+
+sal_Int32 SvxPixelCtlAccessible::getForeground( )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ //see SvxPixelCtl::Paint
+ const StyleSettings& rStyles = Application::GetSettings().GetStyleSettings();
+ return sal_Int32(rStyles.GetLabelTextColor());
+}
+
+sal_Int32 SvxPixelCtlAccessible::getBackground( )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ //see SvxPixelCtl::Paint
+ const StyleSettings& rStyles = Application::GetSettings().GetStyleSettings();
+ return sal_Int32(rStyles.GetDialogColor());
+}
+
+void SvxPixelCtlAccessible::implSelect(sal_Int64 nChildIndex, bool bSelect)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if ( nChildIndex < 0 || nChildIndex >= getAccessibleChildCount())
+ throw lang::IndexOutOfBoundsException();
+
+ if (!mpPixelCtl)
+ return;
+
+ tools::Long nIndex = mpPixelCtl->ShowPosition(mpPixelCtl->IndexToPoint(nChildIndex));
+ NotifyChild(nIndex, bSelect, false);
+}
+
+bool SvxPixelCtlAccessible::implIsSelected(sal_Int64 nChildIndex)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if (!mpPixelCtl)
+ return false;
+
+ return mpPixelCtl->GetFocusPosIndex() == nChildIndex;
+}
+
+void SAL_CALL SvxPixelCtlAccessible::disposing()
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+ OAccessibleSelectionHelper::disposing();
+ m_xCurChild.clear();
+ mpPixelCtl = nullptr;
+}
+
+void SvxPixelCtlAccessible::NotifyChild(tools::Long nIndex,bool bSelect ,bool bCheck)
+{
+ DBG_ASSERT( !(!bSelect && !bCheck),"" );//non is false
+
+ rtl::Reference<SvxPixelCtlAccessibleChild> pChild = m_xCurChild;
+ if (pChild && pChild->getAccessibleIndexInParent() == nIndex )
+ {
+ if (bSelect)
+ {
+ pChild->SelectChild(true);
+ }
+ if (bCheck)
+ {
+ pChild->ChangePixelColorOrBG(mpPixelCtl->GetBitmapPixel(sal_uInt16(nIndex)) != 0);
+ pChild->CheckChild();
+ }
+ return;
+ }
+ rtl::Reference<SvxPixelCtlAccessibleChild> xNewChild = CreateChild(nIndex, mpPixelCtl->IndexToPoint(nIndex));
+ DBG_ASSERT(xNewChild,"Child Must be Valid");
+
+ Any aNewValue,aOldValue;
+ aNewValue <<= uno::Reference<XAccessible>(xNewChild);
+ NotifyAccessibleEvent(AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, aOldValue, aNewValue);
+
+ if (bSelect)
+ {
+ if (pChild)
+ {
+ pChild->SelectChild(false);
+ }
+ xNewChild->SelectChild(true);
+ }
+ if (bCheck)
+ {
+ xNewChild->CheckChild();
+ }
+ m_xCurChild = xNewChild;
+}
+
+rtl::Reference<SvxPixelCtlAccessibleChild> SvxPixelCtlAccessible::CreateChild (tools::Long nIndex,Point mPoint)
+{
+ bool bPixelColorOrBG = mpPixelCtl->GetBitmapPixel(sal_uInt16(nIndex)) != 0;
+ Size size(mpPixelCtl->GetWidth() / SvxPixelCtl::GetLineCount(), mpPixelCtl->GetHeight() / SvxPixelCtl::GetLineCount());
+ rtl::Reference<SvxPixelCtlAccessibleChild> xChild = new SvxPixelCtlAccessibleChild(*mpPixelCtl,
+ bPixelColorOrBG,
+ tools::Rectangle(mPoint,size),
+ this,
+ nIndex);
+
+ return xChild;
+}
+
+void SvxPixelCtlAccessibleChild::CheckChild()
+{
+ Any aChecked;
+ aChecked <<= AccessibleStateType::CHECKED;
+
+ if (m_bPixelColorOrBG)//Current Child State
+ {
+ NotifyAccessibleEvent(AccessibleEventId::STATE_CHANGED, Any(), aChecked);
+ }
+ else
+ {
+ NotifyAccessibleEvent(AccessibleEventId::STATE_CHANGED, aChecked, Any());
+ }
+}
+
+void SvxPixelCtlAccessibleChild::SelectChild( bool bSelect)
+{
+ Any aSelected;
+ aSelected <<= AccessibleStateType::SELECTED;
+
+ if (bSelect)
+ {
+ NotifyAccessibleEvent(AccessibleEventId::STATE_CHANGED, Any(), aSelected);
+ }
+ else
+ {
+ NotifyAccessibleEvent(AccessibleEventId::STATE_CHANGED, aSelected, Any());
+ }
+}
+
+SvxPixelCtlAccessibleChild::SvxPixelCtlAccessibleChild( SvxPixelCtl& rWindow, bool bPixelColorOrBG,
+ const tools::Rectangle& rBoundingBox, rtl::Reference<SvxPixelCtlAccessible> xParent,
+ tools::Long nIndexInParent)
+ : mrParentWindow( rWindow )
+ , mxParent(std::move(xParent))
+ , m_bPixelColorOrBG(bPixelColorOrBG)
+ , maBoundingBox( rBoundingBox )
+ , mnIndexInParent( nIndexInParent )
+{
+}
+
+SvxPixelCtlAccessibleChild::~SvxPixelCtlAccessibleChild()
+{
+ ensureDisposed();
+}
+
+// XAccessible
+uno::Reference< XAccessibleContext> SAL_CALL SvxPixelCtlAccessibleChild::getAccessibleContext()
+{
+ return this;
+}
+
+uno::Reference< XAccessible > SAL_CALL SvxPixelCtlAccessibleChild::getAccessibleAtPoint( const awt::Point& )
+{
+ return uno::Reference< XAccessible >();
+}
+
+void SAL_CALL SvxPixelCtlAccessibleChild::grabFocus()
+{
+}
+
+sal_Int32 SvxPixelCtlAccessibleChild::getForeground()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return mxParent.is() ? mxParent->getForeground() : -1;
+}
+
+sal_Int32 SvxPixelCtlAccessibleChild::getBackground()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return mxParent.is() ? mxParent->getBackground() : -1;
+}
+
+// XAccessibleContext
+sal_Int64 SAL_CALL SvxPixelCtlAccessibleChild::getAccessibleChildCount()
+{
+ return 0;
+}
+
+uno::Reference< XAccessible > SAL_CALL SvxPixelCtlAccessibleChild::getAccessibleChild( sal_Int64 )
+{
+ throw lang::IndexOutOfBoundsException();
+}
+
+uno::Reference< XAccessible > SAL_CALL SvxPixelCtlAccessibleChild::getAccessibleParent()
+{
+ return mxParent;
+}
+
+sal_Int16 SAL_CALL SvxPixelCtlAccessibleChild::getAccessibleRole()
+{
+ return AccessibleRole::CHECK_BOX;
+}
+
+OUString SAL_CALL SvxPixelCtlAccessibleChild::getAccessibleDescription()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ return GetName();
+}
+
+OUString SAL_CALL SvxPixelCtlAccessibleChild::getAccessibleName()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return GetName();
+}
+
+/** Return empty uno::Reference to indicate that the relation set is not
+ supported.
+*/
+uno::Reference<XAccessibleRelationSet> SAL_CALL SvxPixelCtlAccessibleChild::getAccessibleRelationSet()
+{
+ return uno::Reference< XAccessibleRelationSet >();
+}
+
+sal_Int64 SAL_CALL SvxPixelCtlAccessibleChild::getAccessibleStateSet()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ sal_Int64 nStateSet = 0;
+
+ if (!rBHelper.bDisposed)
+ {
+ nStateSet |= AccessibleStateType::TRANSIENT;
+ nStateSet |= AccessibleStateType::ENABLED;
+ nStateSet |= AccessibleStateType::OPAQUE;
+ nStateSet |= AccessibleStateType::SELECTABLE;
+ nStateSet |= AccessibleStateType::SHOWING;
+ nStateSet |= AccessibleStateType::VISIBLE;
+
+ tools::Long nIndex = mrParentWindow.GetFocusPosIndex();
+ if ( nIndex == mnIndexInParent)
+ {
+ nStateSet |= AccessibleStateType::SELECTED;
+ }
+ if (mrParentWindow.GetBitmapPixel(sal_uInt16(mnIndexInParent)))
+ {
+ nStateSet |= AccessibleStateType::CHECKED;
+ }
+ }
+ else
+ nStateSet |= AccessibleStateType::DEFUNC;
+
+ return nStateSet;
+}
+
+void SAL_CALL SvxPixelCtlAccessibleChild::disposing()
+{
+ OAccessibleComponentHelper::disposing();
+ mxParent.clear();
+}
+
+awt::Rectangle SvxPixelCtlAccessibleChild::implGetBounds()
+{
+ // no guard necessary, because no one changes maBoundingBox after creating it
+ return AWTRectangle(maBoundingBox);
+}
+
+OUString SvxPixelCtlAccessibleChild::GetName() const
+{
+ sal_Int32 nXIndex = mnIndexInParent % SvxPixelCtl::GetLineCount();
+ sal_Int32 nYIndex = mnIndexInParent / SvxPixelCtl::GetLineCount();
+
+ OUString str = "("
+ + OUString::number(nXIndex)
+ + ","
+ + OUString::number(nYIndex)
+ + ")";
+ return str;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/svxrectctaccessiblecontext.cxx b/svx/source/accessibility/svxrectctaccessiblecontext.cxx
new file mode 100644
index 0000000000..45360e6c86
--- /dev/null
+++ b/svx/source/accessibility/svxrectctaccessiblecontext.cxx
@@ -0,0 +1,635 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svxrectctaccessiblecontext.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/IndexOutOfBoundsException.hpp>
+#include <toolkit/helper/convert.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <osl/mutex.hxx>
+#include <tools/debug.hxx>
+#include <tools/gen.hxx>
+#include <sal/log.hxx>
+#include <vcl/settings.hxx>
+#include <svx/strings.hrc>
+#include <svx/dlgctrl.hxx>
+#include <svx/dialmgr.hxx>
+#include <com/sun/star/accessibility/AccessibleRelationType.hpp>
+#include <unotools/accessiblerelationsethelper.hxx>
+
+using namespace ::cppu;
+using namespace ::osl;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::accessibility;
+
+using namespace ::com::sun::star::lang;
+
+#define MAX_NUM_OF_CHILDREN 9
+#define NOCHILDSELECTED -1
+
+// internal
+namespace
+{
+ struct ChildIndexToPointData
+ {
+ TranslateId pResIdName;
+ TranslateId pResIdDescr;
+ RectPoint ePoint;
+ };
+}
+
+
+static const ChildIndexToPointData* IndexToPoint( tools::Long nIndex )
+{
+ DBG_ASSERT( nIndex < 9 && nIndex >= 0, "-IndexToPoint(): invalid child index! You have been warned..." );
+
+ // 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, RectPoint::LT }, // 0
+ { RID_SVXSTR_RECTCTL_ACC_CHLD_MT, RID_SVXSTR_RECTCTL_ACC_CHLD_MT, RectPoint::MT }, // 1
+ { RID_SVXSTR_RECTCTL_ACC_CHLD_RT, RID_SVXSTR_RECTCTL_ACC_CHLD_RT, RectPoint::RT }, // 2
+ { RID_SVXSTR_RECTCTL_ACC_CHLD_LM, RID_SVXSTR_RECTCTL_ACC_CHLD_LM, RectPoint::LM }, // 3
+ { RID_SVXSTR_RECTCTL_ACC_CHLD_MM, RID_SVXSTR_RECTCTL_ACC_CHLD_MM, RectPoint::MM }, // 4
+ { RID_SVXSTR_RECTCTL_ACC_CHLD_RM, RID_SVXSTR_RECTCTL_ACC_CHLD_RM, RectPoint::RM }, // 5
+ { RID_SVXSTR_RECTCTL_ACC_CHLD_LB, RID_SVXSTR_RECTCTL_ACC_CHLD_LB, RectPoint::LB }, // 6
+ { RID_SVXSTR_RECTCTL_ACC_CHLD_MB, RID_SVXSTR_RECTCTL_ACC_CHLD_MB, RectPoint::MB }, // 7
+ { RID_SVXSTR_RECTCTL_ACC_CHLD_RB, RID_SVXSTR_RECTCTL_ACC_CHLD_RB, RectPoint::RB } // 8
+ };
+
+ return pCornerData + nIndex;
+}
+
+
+static tools::Long PointToIndex( RectPoint ePoint )
+{
+ tools::Long nRet( static_cast<tools::Long>(ePoint) );
+ // corner control
+ // corners are counted from left to right and top to bottom
+ DBG_ASSERT( int(RectPoint::LT) == 0 && int(RectPoint::MT) == 1 && int(RectPoint::RT) == 2 && int(RectPoint::LM) == 3 && int(RectPoint::MM) == 4 && int(RectPoint::RM) == 5 &&
+ int(RectPoint::LB) == 6 && int(RectPoint::MB) == 7 && int(RectPoint::RB) == 8, "*PointToIndex(): unexpected enum value!" );
+
+ nRet = static_cast<tools::Long>(ePoint);
+
+ return nRet;
+}
+
+SvxRectCtlAccessibleContext::SvxRectCtlAccessibleContext(SvxRectCtl* pRepr)
+ : mpRepr(pRepr)
+ , mnSelectedChild(NOCHILDSELECTED)
+{
+ {
+ ::SolarMutexGuard aSolarGuard;
+ msName = SvxResId( RID_SVXSTR_RECTCTL_ACC_CORN_NAME );
+ msDescription = SvxResId( RID_SVXSTR_RECTCTL_ACC_CORN_DESCR );
+ }
+
+ mvChildren.resize(MAX_NUM_OF_CHILDREN);
+}
+
+SvxRectCtlAccessibleContext::~SvxRectCtlAccessibleContext()
+{
+ ensureDisposed();
+}
+
+Reference< XAccessible > SAL_CALL SvxRectCtlAccessibleContext::getAccessibleAtPoint( const awt::Point& rPoint )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ Reference< XAccessible > xRet;
+
+ tools::Long nChild = mpRepr ? PointToIndex(mpRepr->GetApproxRPFromPixPt(rPoint)) : NOCHILDSELECTED;
+
+ if (nChild != NOCHILDSELECTED)
+ xRet = getAccessibleChild( nChild );
+
+ return xRet;
+}
+
+// XAccessibleContext
+sal_Int64 SAL_CALL SvxRectCtlAccessibleContext::getAccessibleChildCount()
+{
+ return SvxRectCtl::NO_CHILDREN;
+}
+
+Reference< XAccessible > SAL_CALL SvxRectCtlAccessibleContext::getAccessibleChild( sal_Int64 nIndex )
+{
+ checkChildIndex( nIndex );
+
+ Reference< XAccessible > xChild(mvChildren[ nIndex ]);
+ if( !xChild.is() )
+ {
+ ::SolarMutexGuard aSolarGuard;
+
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ xChild = mvChildren[ nIndex ].get();
+
+ if (!xChild.is() && mpRepr)
+ {
+ const ChildIndexToPointData* p = IndexToPoint( nIndex );
+
+ tools::Rectangle aFocusRect( mpRepr->CalculateFocusRectangle( p->ePoint ) );
+
+ rtl::Reference<SvxRectCtlChildAccessibleContext> pChild = new SvxRectCtlChildAccessibleContext(this,
+ SvxResId(p->pResIdName), SvxResId(p->pResIdDescr), aFocusRect, nIndex );
+ mvChildren[ nIndex ] = pChild;
+ xChild = pChild;
+
+ // set actual state
+ if( mnSelectedChild == nIndex )
+ pChild->setStateChecked( true );
+ }
+ }
+
+ return xChild;
+}
+
+Reference< XAccessible > SAL_CALL SvxRectCtlAccessibleContext::getAccessibleParent()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if (mpRepr)
+ return mpRepr->getAccessibleParent();
+ return uno::Reference<css::accessibility::XAccessible>();
+}
+
+sal_Int16 SAL_CALL SvxRectCtlAccessibleContext::getAccessibleRole()
+{
+ return AccessibleRole::PANEL;
+}
+
+OUString SAL_CALL SvxRectCtlAccessibleContext::getAccessibleDescription()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return msDescription + " Please use arrow key to selection.";
+}
+
+OUString SAL_CALL SvxRectCtlAccessibleContext::getAccessibleName()
+{
+ ::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()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if (mpRepr)
+ return mpRepr->get_accessible_relation_set();
+ return uno::Reference<css::accessibility::XAccessibleRelationSet>();
+}
+
+sal_Int64 SAL_CALL SvxRectCtlAccessibleContext::getAccessibleStateSet()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ sal_Int64 nStateSet = 0;
+
+ if (mpRepr)
+ {
+ nStateSet |= AccessibleStateType::ENABLED;
+ nStateSet |= AccessibleStateType::FOCUSABLE;
+ if( mpRepr->HasFocus() )
+ nStateSet |= AccessibleStateType::FOCUSED;
+ nStateSet |= AccessibleStateType::OPAQUE;
+
+ nStateSet |= AccessibleStateType::SHOWING;
+
+ if( mpRepr->IsVisible() )
+ nStateSet |= AccessibleStateType::VISIBLE;
+ }
+ else
+ nStateSet |= AccessibleStateType::DEFUNC;
+
+ return nStateSet;
+}
+
+void SAL_CALL SvxRectCtlAccessibleContext::grabFocus()
+{
+ ::SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if (mpRepr)
+ mpRepr->GrabFocus();
+}
+
+sal_Int32 SvxRectCtlAccessibleContext::getForeground()
+{
+ ::SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ //see SvxRectCtl::Paint
+ const StyleSettings& rStyles = Application::GetSettings().GetStyleSettings();
+ return sal_Int32(rStyles.GetLabelTextColor());
+}
+
+sal_Int32 SvxRectCtlAccessibleContext::getBackground( )
+{
+ ::SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ //see SvxRectCtl::Paint
+ const StyleSettings& rStyles = Application::GetSettings().GetStyleSettings();
+ return sal_Int32(rStyles.GetDialogColor());
+}
+
+// XAccessibleSelection
+void SvxRectCtlAccessibleContext::implSelect(sal_Int64 nIndex, bool bSelect)
+{
+ ::SolarMutexGuard aSolarGuard;
+
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ checkChildIndex( nIndex );
+
+ const ChildIndexToPointData* pData = IndexToPoint( nIndex );
+
+ DBG_ASSERT(pData, "SvxRectCtlAccessibleContext::selectAccessibleChild(): this is an impossible state! Or at least should be...");
+
+ if (mpRepr)
+ {
+ if (bSelect)
+ {
+ // this does all what is needed, including the change of the child's state!
+ mpRepr->SetActualRP( pData->ePoint );
+ }
+ else
+ {
+ SAL_WARN( "svx", "SvxRectCtlAccessibleContext::clearAccessibleSelection() is not possible!" );
+ }
+ }
+}
+
+bool SvxRectCtlAccessibleContext::implIsSelected( sal_Int64 nIndex )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ checkChildIndex( nIndex );
+
+ return nIndex == mnSelectedChild;
+}
+
+// internals
+void SvxRectCtlAccessibleContext::checkChildIndex( sal_Int64 nIndex )
+{
+ if( nIndex < 0 || nIndex >= getAccessibleChildCount() )
+ throw lang::IndexOutOfBoundsException();
+}
+
+void SvxRectCtlAccessibleContext::FireChildFocus( RectPoint eButton )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ tools::Long nNew = PointToIndex( eButton );
+ tools::Long nNumOfChildren = getAccessibleChildCount();
+ if( nNew < nNumOfChildren )
+ {
+ // select new child
+ mnSelectedChild = nNew;
+ if( nNew != NOCHILDSELECTED )
+ {
+ if( mvChildren[ nNew ].is() )
+ mvChildren[ nNew ]->FireFocusEvent();
+ }
+ else
+ {
+ Any aOld;
+ Any aNew;
+ aNew <<= AccessibleStateType::FOCUSED;
+ NotifyAccessibleEvent(AccessibleEventId::STATE_CHANGED, aOld, aNew);
+ }
+ }
+ else
+ mnSelectedChild = NOCHILDSELECTED;
+}
+
+void SvxRectCtlAccessibleContext::selectChild( tools::Long nNew )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if( nNew == mnSelectedChild )
+ return;
+
+ tools::Long nNumOfChildren = getAccessibleChildCount();
+ if( nNew < nNumOfChildren )
+ { // valid index
+ if( mnSelectedChild != NOCHILDSELECTED )
+ { // deselect old selected child if one is selected
+ SvxRectCtlChildAccessibleContext* pChild = mvChildren[ mnSelectedChild ].get();
+ if( pChild )
+ pChild->setStateChecked( false );
+ }
+
+ // select new child
+ mnSelectedChild = nNew;
+
+ if( nNew != NOCHILDSELECTED )
+ {
+ if( mvChildren[ nNew ].is() )
+ mvChildren[ nNew ]->setStateChecked( true );
+ }
+ }
+ else
+ mnSelectedChild = NOCHILDSELECTED;
+}
+
+void SvxRectCtlAccessibleContext::selectChild(RectPoint eButton )
+{
+ // no guard -> is done in next selectChild
+ selectChild(PointToIndex( eButton ));
+}
+
+void SAL_CALL SvxRectCtlAccessibleContext::disposing()
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+ OAccessibleSelectionHelper::disposing();
+ for (auto & rxChild : mvChildren)
+ {
+ if( rxChild.is() )
+ rxChild->dispose();
+ }
+ mvChildren.clear();
+ mpRepr = nullptr;
+}
+
+awt::Rectangle SvxRectCtlAccessibleContext::implGetBounds()
+{
+ ::SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ awt::Rectangle aRet;
+
+ if (mpRepr)
+ {
+ const Point aOutPos;
+ Size aOutSize(mpRepr->GetOutputSizePixel());
+
+ aRet.X = aOutPos.X();
+ aRet.Y = aOutPos.Y();
+ aRet.Width = aOutSize.Width();
+ aRet.Height = aOutSize.Height();
+ }
+
+ return aRet;
+}
+
+SvxRectCtlChildAccessibleContext::SvxRectCtlChildAccessibleContext(
+ const Reference<XAccessible>& rxParent,
+ OUString aName,
+ OUString aDescription,
+ const tools::Rectangle& rBoundingBox,
+ tools::Long nIndexInParent )
+ : msDescription(std::move( aDescription ))
+ , msName(std::move( aName ))
+ , mxParent(rxParent)
+ , maBoundingBox( rBoundingBox )
+ , mnIndexInParent( nIndexInParent )
+ , mbIsChecked( false )
+{
+}
+
+SvxRectCtlChildAccessibleContext::~SvxRectCtlChildAccessibleContext()
+{
+ ensureDisposed();
+}
+
+Reference< XAccessible > SAL_CALL SvxRectCtlChildAccessibleContext::getAccessibleAtPoint( const awt::Point& /*rPoint*/ )
+{
+ return Reference< XAccessible >();
+}
+
+void SAL_CALL SvxRectCtlChildAccessibleContext::grabFocus()
+{
+}
+
+sal_Int32 SvxRectCtlChildAccessibleContext::getForeground( )
+{
+ ::SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ //see SvxRectCtl::Paint
+ const StyleSettings& rStyles = Application::GetSettings().GetStyleSettings();
+ return sal_Int32(rStyles.GetLabelTextColor());
+}
+
+sal_Int32 SvxRectCtlChildAccessibleContext::getBackground( )
+{
+ ::SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ //see SvxRectCtl::Paint
+ const StyleSettings& rStyles = Application::GetSettings().GetStyleSettings();
+ return sal_Int32(rStyles.GetDialogColor());
+}
+
+// XAccessibleContext
+sal_Int64 SAL_CALL SvxRectCtlChildAccessibleContext::getAccessibleChildCount()
+{
+ return 0;
+}
+
+Reference< XAccessible > SAL_CALL SvxRectCtlChildAccessibleContext::getAccessibleChild( sal_Int64 /*nIndex*/ )
+{
+ throw lang::IndexOutOfBoundsException();
+}
+
+Reference< XAccessible > SAL_CALL SvxRectCtlChildAccessibleContext::getAccessibleParent()
+{
+ return mxParent;
+}
+
+sal_Int16 SAL_CALL SvxRectCtlChildAccessibleContext::getAccessibleRole()
+{
+ return AccessibleRole::RADIO_BUTTON;
+}
+
+OUString SAL_CALL SvxRectCtlChildAccessibleContext::getAccessibleDescription()
+{
+ return msDescription;
+}
+
+OUString SAL_CALL SvxRectCtlChildAccessibleContext::getAccessibleName()
+{
+ return msName;
+}
+
+/** Return empty reference to indicate that the relation set is not
+ supported.
+*/
+Reference<XAccessibleRelationSet> SAL_CALL SvxRectCtlChildAccessibleContext::getAccessibleRelationSet()
+{
+ rtl::Reference<utl::AccessibleRelationSetHelper> pRelationSetHelper = new utl::AccessibleRelationSetHelper;
+ if( mxParent.is() )
+ {
+ uno::Sequence< uno::Reference< uno::XInterface > > aSequence { mxParent };
+ pRelationSetHelper->AddRelation( css::accessibility::AccessibleRelation( css::accessibility::AccessibleRelationType::MEMBER_OF, aSequence ) );
+ }
+
+ return pRelationSetHelper;
+}
+
+sal_Int64 SAL_CALL SvxRectCtlChildAccessibleContext::getAccessibleStateSet()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ sal_Int64 nStateSet = 0;
+
+ if (!rBHelper.bDisposed)
+ {
+ if( mbIsChecked )
+ {
+ nStateSet |= AccessibleStateType::CHECKED;
+ }
+
+ nStateSet |= AccessibleStateType::ENABLED;
+ nStateSet |= AccessibleStateType::SENSITIVE;
+ nStateSet |= AccessibleStateType::OPAQUE;
+ nStateSet |= AccessibleStateType::SELECTABLE;
+ nStateSet |= AccessibleStateType::SHOWING;
+ nStateSet |= AccessibleStateType::VISIBLE;
+ }
+ else
+ nStateSet |= AccessibleStateType::DEFUNC;
+
+ return nStateSet;
+}
+
+// XAccessibleValue
+Any SAL_CALL SvxRectCtlChildAccessibleContext::getCurrentValue()
+{
+ Any aRet;
+ aRet <<= ( mbIsChecked? 1.0 : 0.0 );
+ return aRet;
+}
+
+sal_Bool SAL_CALL SvxRectCtlChildAccessibleContext::setCurrentValue( const Any& /*aNumber*/ )
+{
+ return false;
+}
+
+Any SAL_CALL SvxRectCtlChildAccessibleContext::getMaximumValue()
+{
+ Any aRet;
+ aRet <<= 1.0;
+ return aRet;
+}
+
+Any SAL_CALL SvxRectCtlChildAccessibleContext::getMinimumValue()
+{
+ Any aRet;
+ aRet <<= 0.0;
+ return aRet;
+}
+
+Any SAL_CALL SvxRectCtlChildAccessibleContext::getMinimumIncrement()
+{
+ Any aRet;
+ aRet <<= 1.0;
+ return aRet;
+}
+
+
+// XAccessibleAction
+
+
+sal_Int32 SvxRectCtlChildAccessibleContext::getAccessibleActionCount( )
+{
+ return 1;
+}
+
+
+sal_Bool SvxRectCtlChildAccessibleContext::doAccessibleAction ( sal_Int32 nIndex )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if ( nIndex < 0 || nIndex >= getAccessibleActionCount() )
+ throw IndexOutOfBoundsException();
+
+ Reference<XAccessibleSelection> xSelection( mxParent, UNO_QUERY);
+
+ xSelection->selectAccessibleChild(mnIndexInParent);
+
+ return true;
+}
+
+
+OUString SvxRectCtlChildAccessibleContext::getAccessibleActionDescription ( sal_Int32 nIndex )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if ( nIndex < 0 || nIndex >= getAccessibleActionCount() )
+ throw IndexOutOfBoundsException();
+
+ return "select";
+}
+
+
+Reference< XAccessibleKeyBinding > SvxRectCtlChildAccessibleContext::getAccessibleActionKeyBinding( sal_Int32 nIndex )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if ( nIndex < 0 || nIndex >= getAccessibleActionCount() )
+ throw IndexOutOfBoundsException();
+
+ return Reference< XAccessibleKeyBinding >();
+}
+
+void SAL_CALL SvxRectCtlChildAccessibleContext::disposing()
+{
+ OAccessibleComponentHelper::disposing();
+ mxParent.clear();
+}
+
+awt::Rectangle SvxRectCtlChildAccessibleContext::implGetBounds( )
+{
+ // no guard necessary, because no one changes maBoundingBox after creating it
+ return AWTRectangle(maBoundingBox);
+}
+
+void SvxRectCtlChildAccessibleContext::setStateChecked( bool bChecked )
+{
+ if( mbIsChecked == bChecked )
+ return;
+
+ mbIsChecked = bChecked;
+
+ Any aOld;
+ Any aNew;
+ Any& rMod = bChecked? aNew : aOld;
+
+ //Send the STATE_CHANGED(Focused) event to accessible
+ rMod <<= AccessibleStateType::FOCUSED;
+ NotifyAccessibleEvent(AccessibleEventId::STATE_CHANGED, aOld, aNew);
+
+ rMod <<= AccessibleStateType::CHECKED;
+
+ NotifyAccessibleEvent(AccessibleEventId::STATE_CHANGED, aOld, aNew);
+}
+
+void SvxRectCtlChildAccessibleContext::FireFocusEvent()
+{
+ Any aOld;
+ Any aNew;
+ aNew <<= AccessibleStateType::FOCUSED;
+ NotifyAccessibleEvent(AccessibleEventId::STATE_CHANGED, aOld, aNew);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */