diff options
Diffstat (limited to 'svx/source/accessibility/AccessibleShape.cxx')
-rw-r--r-- | svx/source/accessibility/AccessibleShape.cxx | 1314 |
1 files changed, 1314 insertions, 0 deletions
diff --git a/svx/source/accessibility/AccessibleShape.cxx b/svx/source/accessibility/AccessibleShape.cxx new file mode 100644 index 000000000..03996fe7f --- /dev/null +++ b/svx/source/accessibility/AccessibleShape.cxx @@ -0,0 +1,1314 @@ +/* -*- 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/accessiblestatesethelper.hxx> +#include <unotools/accessiblerelationsethelper.hxx> +#include <svx/svdview.hxx> +#include <tools/diagnose_ex.h> +#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 = dynamic_cast<SdrTextObj*>( 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() +{ + if (mxStateSet == nullptr) + return; + + // Set the opaque state for certain shape types when their fill style is + // solid. + bool bShapeIsOpaque = false; + switch (ShapeTypeHandler::Instance().GetTypeId (mxShape)) + { + case DRAWING_PAGE: + case DRAWING_RECTANGLE: + case DRAWING_TEXT: + { + uno::Reference<beans::XPropertySet> xSet (mxShape, uno::UNO_QUERY); + if (xSet.is()) + { + try + { + drawing::FillStyle aFillStyle; + bShapeIsOpaque = ( xSet->getPropertyValue ("FillStyle") >>= aFillStyle) + && aFillStyle == drawing::FillStyle_SOLID; + } + catch (css::beans::UnknownPropertyException&) + { + // Ignore. + } + } + } + } + if (bShapeIsOpaque) + mxStateSet->AddState (AccessibleStateType::OPAQUE); + else + mxStateSet->RemoveState (AccessibleStateType::OPAQUE); + + // Set the selected state. + bool bShapeIsSelected = false; + // XXX fix_me this has to be done with an extra interface later on + if ( m_pShape && maShapeTreeInfo.GetSdrView() ) + { + bShapeIsSelected = maShapeTreeInfo.GetSdrView()->IsObjMarked(m_pShape); + } + + if (bShapeIsSelected) + mxStateSet->AddState (AccessibleStateType::SELECTED); + else + mxStateSet->RemoveState (AccessibleStateType::SELECTED); +} + +OUString AccessibleShape::GetStyle() const +{ + return ShapeTypeHandler::CreateAccessibleBaseName( mxShape ); +} + +bool AccessibleShape::SetState (sal_Int16 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_Int16 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_Int16 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_Int32 SAL_CALL + AccessibleShape::getAccessibleChildCount () +{ + if (IsDisposed()) + { + return 0; + } + + sal_Int32 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_Int32 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_Int32 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), + static_cast<uno::XWeak*>(this)); + + 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 +*/ +uno::Reference<XAccessibleStateSet> 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 (); + } + + ::utl::AccessibleStateSetHelper* pStateSet = mxStateSet.get(); + + if (!pStateSet) + return Reference<XAccessibleStateSet>(); + + // Merge current FOCUSED state from edit engine. + if (mpText) + { + if (mpText->HaveFocus()) + pStateSet->AddState (AccessibleStateType::FOCUSED); + else + pStateSet->RemoveState (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() ) + { + css::uno::Reference<XAccessibleStateSet> rState = + xTempAccContext->getAccessibleStateSet(); + if (rState.is()) + { + const css::uno::Sequence<short> aStates = rState->getStates(); + if (std::find(aStates.begin(), aStates.end(), AccessibleStateType::EDITABLE) != aStates.end()) + { + pStateSet->AddState (AccessibleStateType::EDITABLE); + pStateSet->AddState (AccessibleStateType::RESIZABLE); + pStateSet->AddState (AccessibleStateType::MOVEABLE); + } + } + } + } + + // Create a copy of the state set that may be modified by the + // caller without affecting the current state set. + Reference<XAccessibleStateSet> xStateSet(new ::utl::AccessibleStateSetHelper(*pStateSet)); + + if (mpParent && mpParent->IsDocumentSelAll()) + { + ::utl::AccessibleStateSetHelper* pCopyStateSet = + static_cast<::utl::AccessibleStateSetHelper*>(xStateSet.get()); + pCopyStateSet->AddState (AccessibleStateType::SELECTED); + } + + return xStateSet; +} + +// XAccessibleComponent +/** The implementation below is at the moment straightforward. It iterates + over all children (and thereby instances all children which have not + been already 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_Int32 nChildCount = getAccessibleChildCount (); + for (sal_Int32 i=0; i<nChildCount; ++i) + { + Reference<XAccessible> xChild (getAccessibleChild (i)); + if (xChild.is()) + { + Reference<XAccessibleComponent> xChildComponent ( + xChild->getAccessibleContext(), uno::UNO_QUERY); + if (xChildComponent.is()) + { + awt::Rectangle aBBox (xChildComponent->getBounds()); + if ( (aPoint.X >= aBBox.X) + && (aPoint.Y >= aBBox.Y) + && (aPoint.X < aBBox.X+aBBox.Width) + && (aPoint.Y < aBBox.Y+aBBox.Height) ) + return xChild; + } + } + } + + // Have not found a child under the given point. Returning empty + // reference to indicate this. + return uno::Reference<XAccessible>(); +} + + +awt::Rectangle SAL_CALL AccessibleShape::getBounds() +{ + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard (m_aMutex); + + ThrowIfDisposed (); + awt::Rectangle aBoundingBox; + if ( mxShape.is() ) + { + + static constexpr OUStringLiteral sBoundRectName = u"BoundRect"; + static constexpr OUStringLiteral sAnchorPositionName = u"AnchorPosition"; + + // Get the shape's bounding box in internal coordinates (in 100th of + // mm). Use the property BoundRect. Only if that is not supported ask + // the shape for its position and size directly. + Reference<beans::XPropertySet> xSet (mxShape, uno::UNO_QUERY); + Reference<beans::XPropertySetInfo> xSetInfo; + bool bFoundBoundRect = false; + if (xSet.is()) + { + xSetInfo = xSet->getPropertySetInfo (); + if (xSetInfo.is()) + { + if (xSetInfo->hasPropertyByName (sBoundRectName)) + { + try + { + uno::Any aValue = xSet->getPropertyValue (sBoundRectName); + aValue >>= aBoundingBox; + bFoundBoundRect = true; + } + catch (beans::UnknownPropertyException 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", + static_cast<uno::XWeak*>(this)); + ::Size aPixelSize = maShapeTreeInfo.GetViewForwarder()->LogicToPixel ( + ::Size (aBoundingBox.Width, aBoundingBox.Height)); + ::Point aPixelPosition = maShapeTreeInfo.GetViewForwarder()->LogicToPixel ( + ::Point (aBoundingBox.X, aBoundingBox.Y)); + + // Clip the shape's bounding box with the bounding box of its parent. + Reference<XAccessibleComponent> xParentComponent ( + getAccessibleParent(), uno::UNO_QUERY); + if (xParentComponent.is()) + { + // Make the coordinates relative to the parent. + awt::Point aParentLocation (xParentComponent->getLocationOnScreen()); + int x = aPixelPosition.getX() - aParentLocation.X; + int y = aPixelPosition.getY() - aParentLocation.Y; + + // 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.getWidth(), + aBBox.getHeight()); + } + 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_Int32 ) +{ +} + + +sal_Bool SAL_CALL AccessibleShape::isAccessibleChildSelected( sal_Int32 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 ) + { + Reference< XAccessibleStateSet > pRState = xContext->getAccessibleStateSet(); + if( !pRState.is() ) + return false; + + const uno::Sequence<short> aStates = pRState->getStates(); + return std::find(aStates.begin(), aStates.end(), AccessibleStateType::SELECTED) != aStates.end(); + } + } + + return false; +} + + +void SAL_CALL AccessibleShape::clearAccessibleSelection( ) +{ +} + + +void SAL_CALL AccessibleShape::selectAllAccessibleChildren( ) +{ +} + + +sal_Int32 SAL_CALL AccessibleShape::getSelectedAccessibleChildCount() +{ + sal_Int32 nCount = 0; + sal_Int32 TotalCount = getAccessibleChildCount(); + for( sal_Int32 i = 0; i < TotalCount; i++ ) + if( isAccessibleChildSelected(i) ) nCount++; + + return nCount; +} + + +Reference<XAccessible> SAL_CALL AccessibleShape::getSelectedAccessibleChild( sal_Int32 nSelectedChildIndex ) +{ + if ( nSelectedChildIndex > getSelectedAccessibleChildCount() ) + throw IndexOutOfBoundsException(); + sal_Int32 i1, i2; + for( 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_Int32 ) +{ + +} + +// 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"); + } +} + +// 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()); + + // 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()); + + // 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); + } + 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. + ::utl::AccessibleStateSetHelper* pStateSet = mxStateSet.get(); + if (pStateSet != nullptr) + pStateSet->RemoveState (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_Int32 SAL_CALL + AccessibleShape::getAccessibleIndexInParent() +{ + ThrowIfDisposed (); + // Use a simple but slow solution for now. Optimize later. + + sal_Int32 nIndex = m_nIndexInParent; + if ( -1 == nIndex ) + nIndex = AccessibleContextBase::getAccessibleIndexInParent(); + return nIndex; +} + + +void AccessibleShape::UpdateNameAndDescription() +{ + // 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: */ |