/* -*- 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 <AccessibleDocument.hxx>
#include <AccessibleSpreadsheet.hxx>
#include <tabvwsh.hxx>
#include <AccessibilityHints.hxx>
#include <document.hxx>
#include <drwlayer.hxx>
#include <DrawModelBroadcaster.hxx>
#include <drawview.hxx>
#include <gridwin.hxx>
#include <AccessibleEditObject.hxx>
#include <userdat.hxx>
#include <scresid.hxx>
#include <strings.hrc>
#include <strings.hxx>
#include <markdata.hxx>
 
#include <com/sun/star/accessibility/AccessibleEventId.hpp>
#include <com/sun/star/accessibility/AccessibleStateType.hpp>
#include <com/sun/star/accessibility/AccessibleRelationType.hpp>
#include <com/sun/star/accessibility/AccessibleRole.hpp>
#include <com/sun/star/view/XSelectionSupplier.hpp>
#include <com/sun/star/drawing/ShapeCollection.hpp>
#include <com/sun/star/drawing/XShape.hpp>
#include <com/sun/star/drawing/XShapes.hpp>
#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
#include <o3tl/safeint.hxx>
#include <tools/gen.hxx>
#include <svx/svdpage.hxx>
#include <svx/svdobj.hxx>
#include <svx/ShapeTypeHandler.hxx>
#include <svx/AccessibleShape.hxx>
#include <svx/AccessibleShapeTreeInfo.hxx>
#include <svx/AccessibleShapeInfo.hxx>
#include <svx/IAccessibleParent.hxx>
#include <comphelper/sequence.hxx>
#include <sfx2/viewfrm.hxx>
#include <sfx2/docfile.hxx>
#include <toolkit/helper/vclunohelper.hxx>
#include <unotools/accessiblerelationsethelper.hxx>
#include <utility>
#include <vcl/svapp.hxx>
 
#include <svx/AccessibleControlShape.hxx>
#include <svx/SvxShapeTypes.hxx>
#include <sfx2/objsh.hxx>
#include <editeng/editview.hxx>
#include <editeng/editeng.hxx>
#include <comphelper/processfactory.hxx>
 
#include <algorithm>
 
#include <scmod.hxx>
 
#ifdef indices
#undef indices
#endif
 
#ifdef extents
#undef extents
#endif
 
using namespace ::com::sun::star;
using namespace ::com::sun::star::accessibility;
 
    //=====  internal  ========================================================
 
namespace {
 
struct ScAccessibleShapeData
{
    ScAccessibleShapeData(css::uno::Reference< css::drawing::XShape > xShape_);
    ~ScAccessibleShapeData();
    mutable rtl::Reference< ::accessibility::AccessibleShape > pAccShape;
    mutable std::optional<ScAddress> xRelationCell; // if it is NULL this shape is anchored on the table
    css::uno::Reference< css::drawing::XShape > xShape;
    mutable bool            bSelected;
    bool                    bSelectable;
    // cache these to make the sorting cheaper
    std::optional<sal_Int16> mxLayerID;
    std::optional<sal_Int32> mxZOrder;
};
 
}
 
ScAccessibleShapeData::ScAccessibleShapeData(css::uno::Reference< css::drawing::XShape > xShape_)
    : xShape(std::move(xShape_)),
    bSelected(false), bSelectable(true)
{
    static constexpr OUStringLiteral gsLayerId = u"LayerID";
    static constexpr OUStringLiteral gsZOrder = u"ZOrder";
    uno::Reference< beans::XPropertySet> xProps(xShape, uno::UNO_QUERY);
    if (xProps.is())
    {
        uno::Any aAny = xProps->getPropertyValue(gsLayerId);
        sal_Int16 nLayerID;
        if (aAny >>= nLayerID)
            mxLayerID = nLayerID;
        sal_Int32 nZOrder;
        aAny = xProps->getPropertyValue(gsZOrder);
        if (aAny >>= nZOrder)
            mxZOrder = nZOrder;
    }
}
 
ScAccessibleShapeData::~ScAccessibleShapeData()
{
    if (pAccShape.is())
    {
        pAccShape->dispose();
    }
}
 
namespace {
 
struct ScShapeDataLess
{
    static void ConvertLayerId(sal_Int16& rLayerID) // changes the number of the LayerId so it the accessibility order
    {
        // note: MSVC 2017 ICE's if this is written as "switch" so use "if"
        if (SC_LAYER_FRONT.get() == rLayerID)
        {
            rLayerID = 1;
        }
        else if (SC_LAYER_BACK.get() == rLayerID)
        {
            rLayerID = 0;
        }
        else if (SC_LAYER_INTERN.get() == rLayerID)
        {
            rLayerID = 2;
        }
        else if (SC_LAYER_CONTROLS.get() == rLayerID)
        {
            rLayerID = 3;
        }
    }
    static bool LessThanSheet(const ScAccessibleShapeData* pData)
    {
        bool bResult(false);
        if (pData->mxLayerID)
        {
            if (SdrLayerID(*pData->mxLayerID) == SC_LAYER_BACK)
                bResult = true;
        }
        return bResult;
    }
    bool operator()(const ScAccessibleShapeData* pData1, const ScAccessibleShapeData* pData2) const
    {
        bool bResult(false);
        if (pData1 && pData2)
        {
            if( pData1->mxLayerID && pData2->mxLayerID )
            {
                sal_Int16 nLayerID1 = *pData1->mxLayerID;
                sal_Int16 nLayerID2 = *pData2->mxLayerID;
                if (nLayerID1 == nLayerID2)
                {
                    if ( pData1->mxZOrder && pData2->mxZOrder )
                        bResult = (*pData1->mxZOrder < *pData2->mxZOrder);
                }
                else
                {
                    ConvertLayerId(nLayerID1);
                    ConvertLayerId(nLayerID2);
                    bResult = (nLayerID1 < nLayerID2);
                }
            }
        }
        else if (pData1 && !pData2)
            bResult = LessThanSheet(pData1);
        else if (!pData1 && pData2)
            bResult = !LessThanSheet(pData2);
        else
            bResult = false;
        return bResult;
    }
};
 
}
 
class ScChildrenShapes : public SfxListener,
    public ::accessibility::IAccessibleParent
{
public:
    ScChildrenShapes(ScAccessibleDocument* pAccessibleDocument, ScTabViewShell* pViewShell, ScSplitPos eSplitPos);
    virtual ~ScChildrenShapes() override;
 
    ///=====  SfxListener  =====================================================
 
    virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
 
    ///=====  IAccessibleParent  ===============================================
 
    virtual bool ReplaceChild (
        ::accessibility::AccessibleShape* pCurrentChild,
        const css::uno::Reference< css::drawing::XShape >& _rxShape,
        const tools::Long _nIndex,
        const ::accessibility::AccessibleShapeTreeInfo& _rShapeTreeInfo
    ) override;
 
    virtual ::accessibility::AccessibleControlShape* GetAccControlShapeFromModel
        (css::beans::XPropertySet* pSet) override;
    virtual css::uno::Reference< css::accessibility::XAccessible>
        GetAccessibleCaption (const css::uno::Reference<css::drawing::XShape>& xShape) override;
    ///=====  Internal  ========================================================
    void SetDrawBroadcaster();
 
    sal_Int32 GetCount() const;
    uno::Reference< XAccessible > Get(const ScAccessibleShapeData* pData) const;
    uno::Reference< XAccessible > Get(sal_Int32 nIndex) const;
    uno::Reference< XAccessible > GetAt(const awt::Point& rPoint) const;
 
    // gets the index of the shape starting on 0 (without the index of the table)
    // returns the selected shape
    bool IsSelected(sal_Int32 nIndex,
        css::uno::Reference<css::drawing::XShape>& rShape) const;
 
    bool SelectionChanged();
 
    void Select(sal_Int32 nIndex);
    void DeselectAll(); // deselect also the table
    void SelectAll();
    sal_Int32 GetSelectedCount() const;
    uno::Reference< XAccessible > GetSelected(sal_Int32 nSelectedChildIndex, bool bTabSelected) const;
    void Deselect(sal_Int32 nChildIndex);
 
    SdrPage* GetDrawPage() const;
 
    rtl::Reference<utl::AccessibleRelationSetHelper> GetRelationSet(const ScAddress* pAddress) const;
 
    void VisAreaChanged() const;
private:
    typedef std::vector<ScAccessibleShapeData*> SortedShapes;
    typedef std::unordered_map<css::uno::Reference< css::drawing::XShape >, ScAccessibleShapeData*> ShapesMap;
 
    mutable SortedShapes maZOrderedShapes; // a null pointer represents the sheet in the correct order
    mutable ShapesMap maShapesMap;
    mutable bool mbShapesNeedSorting; // set if maZOrderedShapes needs sorting
 
    mutable ::accessibility::AccessibleShapeTreeInfo maShapeTreeInfo;
    mutable css::uno::Reference<css::view::XSelectionSupplier> xSelectionSupplier;
    mutable sal_uInt32 mnShapesSelected;
    ScTabViewShell* mpViewShell;
    ScAccessibleDocument* mpAccessibleDocument;
    ScSplitPos meSplitPos;
 
    void FillShapes(std::vector < uno::Reference < drawing::XShape > >& rShapes) const;
    bool FindSelectedShapesChanges(const css::uno::Reference<css::drawing::XShapes>& xShapes) const;
 
    std::optional<ScAddress> GetAnchor(const uno::Reference<drawing::XShape>& xShape) const;
    rtl::Reference<utl::AccessibleRelationSetHelper> GetRelationSet(const ScAccessibleShapeData* pData) const;
    void SetAnchor(const uno::Reference<drawing::XShape>& xShape, ScAccessibleShapeData* pData) const;
    void AddShape(const uno::Reference<drawing::XShape>& xShape, bool bCommitChange) const;
    void RemoveShape(const uno::Reference<drawing::XShape>& xShape) const;
 
    bool FindShape(const uno::Reference<drawing::XShape>& xShape, SortedShapes::iterator& rItr) const;
 
    static sal_Int8 Compare(const ScAccessibleShapeData* pData1,
        const ScAccessibleShapeData* pData2);
};
 
ScChildrenShapes::ScChildrenShapes(ScAccessibleDocument* pAccessibleDocument, ScTabViewShell* pViewShell, ScSplitPos eSplitPos)
    :
    mbShapesNeedSorting(false),
    mnShapesSelected(0),
    mpViewShell(pViewShell),
    mpAccessibleDocument(pAccessibleDocument),
    meSplitPos(eSplitPos)
{
    if (mpViewShell)
    {
        SfxViewFrame& rViewFrame = mpViewShell->GetViewFrame();
        xSelectionSupplier = uno::Reference<view::XSelectionSupplier>(rViewFrame.GetFrame().GetController(), uno::UNO_QUERY);
        if (xSelectionSupplier.is())
        {
            xSelectionSupplier->addSelectionChangeListener(mpAccessibleDocument);
            uno::Reference<drawing::XShapes> xShapes(mpViewShell->getSelectedXShapes());
            if (xShapes.is())
                mnShapesSelected = xShapes->getCount();
        }
    }
 
    maZOrderedShapes.push_back(nullptr); // add an element which represents the table
 
    GetCount(); // fill list with filtered shapes (no internal shapes)
 
    if (mnShapesSelected)
    {
        //set flag on every selected shape
        if (!xSelectionSupplier.is())
            throw uno::RuntimeException("Could not get selected shapes. Null reference to xSelectionSupplier in ScChildrenShapes::ScChildrenShapes.");
 
        uno::Reference<drawing::XShapes> xShapes(mpViewShell->getSelectedXShapes());
        if (xShapes.is())
            FindSelectedShapesChanges(xShapes);
    }
    if (!pViewShell)
        return;
 
    ScViewData& rViewData = pViewShell->GetViewData();
    SfxBroadcaster* pDrawBC = rViewData.GetDocument().GetDrawBroadcaster();
    if (pDrawBC)
    {
        StartListening(*pDrawBC);
 
        maShapeTreeInfo.SetModelBroadcaster( new ScDrawModelBroadcaster(rViewData.GetDocument().GetDrawLayer()) );
        maShapeTreeInfo.SetSdrView(rViewData.GetScDrawView());
        maShapeTreeInfo.SetController(nullptr);
        maShapeTreeInfo.SetWindow(pViewShell->GetWindowByPos(meSplitPos));
        maShapeTreeInfo.SetViewForwarder(mpAccessibleDocument);
    }
}
 
ScChildrenShapes::~ScChildrenShapes()
{
    for (ScAccessibleShapeData* pShapeData : maZOrderedShapes)
        delete pShapeData;
    if (mpViewShell)
    {
        SfxBroadcaster* pDrawBC = mpViewShell->GetViewData().GetDocument().GetDrawBroadcaster();
        if (pDrawBC)
            EndListening(*pDrawBC);
    }
    if (mpAccessibleDocument && xSelectionSupplier.is())
        xSelectionSupplier->removeSelectionChangeListener(mpAccessibleDocument);
}
 
void ScChildrenShapes::SetDrawBroadcaster()
{
    if (!mpViewShell)
        return;
 
    ScViewData& rViewData = mpViewShell->GetViewData();
    SfxBroadcaster* pDrawBC = rViewData.GetDocument().GetDrawBroadcaster();
    if (pDrawBC)
    {
        StartListening(*pDrawBC, DuplicateHandling::Prevent);
 
        maShapeTreeInfo.SetModelBroadcaster( new ScDrawModelBroadcaster(rViewData.GetDocument().GetDrawLayer()) );
        maShapeTreeInfo.SetSdrView(rViewData.GetScDrawView());
        maShapeTreeInfo.SetController(nullptr);
        maShapeTreeInfo.SetWindow(mpViewShell->GetWindowByPos(meSplitPos));
        maShapeTreeInfo.SetViewForwarder(mpAccessibleDocument);
    }
}
 
void ScChildrenShapes::Notify(SfxBroadcaster&, const SfxHint& rHint)
{
    if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint)
        return;
    const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint);
 
    SdrObject* pObj = const_cast<SdrObject*>(pSdrHint->GetObject());
    if (!(pObj && /*(pObj->GetLayer() != SC_LAYER_INTERN) && */(pObj->getSdrPageFromSdrObject() == GetDrawPage()) &&
        (pObj->getSdrPageFromSdrObject() == pObj->getParentSdrObjListFromSdrObject())) ) //only do something if the object lies direct on the page
        return;
 
    switch (pSdrHint->GetKind())
    {
        case SdrHintKind::ObjectChange :         // object changed
        {
            uno::Reference<drawing::XShape> xShape (pObj->getUnoShape(), uno::UNO_QUERY);
            if (xShape.is())
            {
                mbShapesNeedSorting = true; // sort, because the z index or layer could be changed
                auto it = maShapesMap.find(xShape);
                if (it != maShapesMap.end())
                    SetAnchor(xShape, it->second);
            }
        }
        break;
        case SdrHintKind::ObjectInserted :    // new drawing object inserted
        {
            uno::Reference<drawing::XShape> xShape (pObj->getUnoShape(), uno::UNO_QUERY);
            if (xShape.is())
                AddShape(xShape, true);
        }
        break;
        case SdrHintKind::ObjectRemoved :     // Removed drawing object from list
        {
            uno::Reference<drawing::XShape> xShape (pObj->getUnoShape(), uno::UNO_QUERY);
            if (xShape.is())
                RemoveShape(xShape);
        }
        break;
        default :
        {
            // other events are not interesting
        }
        break;
    }
}
 
bool ScChildrenShapes::ReplaceChild (::accessibility::AccessibleShape* pCurrentChild,
        const css::uno::Reference< css::drawing::XShape >& _rxShape,
        const tools::Long /*_nIndex*/, const ::accessibility::AccessibleShapeTreeInfo& _rShapeTreeInfo)
{
    // create the new child
    rtl::Reference< ::accessibility::AccessibleShape > pReplacement(::accessibility::ShapeTypeHandler::Instance().CreateAccessibleObject (
        ::accessibility::AccessibleShapeInfo ( _rxShape, pCurrentChild->getAccessibleParent(), this ),
        _rShapeTreeInfo
    ));
 
    bool bResult(false);
    if (pReplacement.is())
    {
        OSL_ENSURE(pCurrentChild->GetXShape().get() == pReplacement->GetXShape().get(), "XShape changes and should be inserted sorted");
        auto it = maShapesMap.find(pCurrentChild->GetXShape());
        if (it != maShapesMap.end() && it->second->pAccShape.is())
        {
            OSL_ENSURE(it->second->pAccShape == pCurrentChild, "wrong child found");
            AccessibleEventObject aEvent;
            aEvent.EventId = AccessibleEventId::CHILD;
            aEvent.Source = uno::Reference< XAccessibleContext >(mpAccessibleDocument);
            aEvent.OldValue <<= uno::Reference<XAccessible>(pCurrentChild);
            aEvent.IndexHint = -1;
 
            mpAccessibleDocument->CommitChange(aEvent); // child is gone - event
 
            pCurrentChild->dispose();
        }
 
        // Init after above possible pCurrentChild->dispose so we don't trigger the assert
        // ScDrawModelBroadcaster::addShapeEventListener of duplicate listeners
        pReplacement->Init();
 
        if (it != maShapesMap.end())
        {
            it->second->pAccShape = pReplacement;
            AccessibleEventObject aEvent;
            aEvent.EventId = AccessibleEventId::CHILD;
            aEvent.Source = uno::Reference< XAccessibleContext >(mpAccessibleDocument);
            aEvent.NewValue <<= uno::Reference<XAccessible>(pReplacement);
            aEvent.IndexHint = -1;
 
            mpAccessibleDocument->CommitChange(aEvent); // child is new - event
            bResult = true;
        }
    }
    return bResult;
}
 
::accessibility::AccessibleControlShape * ScChildrenShapes::GetAccControlShapeFromModel(css::beans::XPropertySet* pSet)
{
    GetCount(); // populate
    for (ScAccessibleShapeData* pShape : maZOrderedShapes)
    {
        if (pShape)
        {
            rtl::Reference< ::accessibility::AccessibleShape > pAccShape(pShape->pAccShape);
            if (pAccShape.is() && ::accessibility::ShapeTypeHandler::Instance().GetTypeId (pAccShape->GetXShape()) == ::accessibility::DRAWING_CONTROL)
            {
                ::accessibility::AccessibleControlShape *pCtlAccShape = static_cast < ::accessibility::AccessibleControlShape* >(pAccShape.get());
                if (pCtlAccShape && pCtlAccShape->GetControlModel() == pSet)
                    return pCtlAccShape;
            }
        }
    }
    return nullptr;
}
 
css::uno::Reference < css::accessibility::XAccessible >
ScChildrenShapes::GetAccessibleCaption (const css::uno::Reference < css::drawing::XShape>& xShape)
{
    GetCount(); // populate
    auto it = maShapesMap.find(xShape);
    if (it == maShapesMap.end())
        return nullptr;
    ScAccessibleShapeData* pShape = it->second;
    css::uno::Reference< css::accessibility::XAccessible > xNewChild( pShape->pAccShape );
    if(xNewChild)
        return xNewChild;
    return nullptr;
}
 
sal_Int32 ScChildrenShapes::GetCount() const
{
    SdrPage* pDrawPage = GetDrawPage();
    if (pDrawPage && (maZOrderedShapes.size() == 1)) // the table is always in
    {
        size_t nSdrObjCount = pDrawPage->GetObjCount();
        maZOrderedShapes.reserve(nSdrObjCount + 1); // the table is always in
        for (const rtl::Reference<SdrObject>& pObj : *pDrawPage)
        {
            uno::Reference< drawing::XShape > xShape (pObj->getUnoShape(), uno::UNO_QUERY);
            AddShape(xShape, false); //inserts in the correct order
        }
    }
    return maZOrderedShapes.size();
}
 
uno::Reference< XAccessible > ScChildrenShapes::Get(const ScAccessibleShapeData* pData) const
{
    if (!pData)
        return nullptr;
 
    if (!pData->pAccShape.is())
    {
        ::accessibility::ShapeTypeHandler& rShapeHandler = ::accessibility::ShapeTypeHandler::Instance();
        ::accessibility::AccessibleShapeInfo aShapeInfo(pData->xShape, mpAccessibleDocument, const_cast<ScChildrenShapes*>(this));
        pData->pAccShape = rShapeHandler.CreateAccessibleObject(
            aShapeInfo, maShapeTreeInfo);
        if (pData->pAccShape.is())
        {
            pData->pAccShape->Init();
            if (pData->bSelected)
                pData->pAccShape->SetState(AccessibleStateType::SELECTED);
            if (!pData->bSelectable)
                pData->pAccShape->ResetState(AccessibleStateType::SELECTABLE);
            pData->pAccShape->SetRelationSet(GetRelationSet(pData));
        }
    }
    return pData->pAccShape;
 }
 
uno::Reference< XAccessible > ScChildrenShapes::Get(sal_Int32 nIndex) const
{
    if (maZOrderedShapes.size() <= 1)
        GetCount(); // fill list with filtered shapes (no internal shapes)
 
    if (mbShapesNeedSorting)
    {
        std::sort(maZOrderedShapes.begin(), maZOrderedShapes.end(), ScShapeDataLess());
        mbShapesNeedSorting = false;
    }
 
    if (o3tl::make_unsigned(nIndex) >= maZOrderedShapes.size())
        return nullptr;
 
    return Get(maZOrderedShapes[nIndex]);
}
 
uno::Reference< XAccessible > ScChildrenShapes::GetAt(const awt::Point& rPoint) const
{
    uno::Reference<XAccessible> xAccessible;
    if(mpViewShell)
    {
        if (mbShapesNeedSorting)
        {
            std::sort(maZOrderedShapes.begin(), maZOrderedShapes.end(), ScShapeDataLess());
            mbShapesNeedSorting = false;
        }
 
        sal_Int32 i(maZOrderedShapes.size() - 1);
        bool bFound(false);
        while (!bFound && i >= 0)
        {
            ScAccessibleShapeData* pShape = maZOrderedShapes[i];
            if (pShape)
            {
                if (!pShape->pAccShape.is())
                    Get(pShape);
 
                if (pShape->pAccShape.is())
                {
                    Point aPoint(VCLUnoHelper::ConvertToVCLPoint(rPoint));
                    aPoint
                        -= VCLUnoHelper::ConvertToVCLRect(pShape->pAccShape->getBounds()).TopLeft();
                    if (pShape->pAccShape->containsPoint(VCLUnoHelper::ConvertToAWTPoint(aPoint)))
                    {
                        xAccessible = pShape->pAccShape.get();
                        bFound = true;
                    }
                }
                else
                {
                    OSL_FAIL("I should have an accessible shape now!");
                }
            }
            else
                bFound = true; // this is the sheet and it lies before the rest of the shapes which are background shapes
 
            --i;
        }
    }
    return xAccessible;
}
 
bool ScChildrenShapes::IsSelected(sal_Int32 nIndex,
                        uno::Reference<drawing::XShape>& rShape) const
{
    bool bResult (false);
    if (maZOrderedShapes.size() <= 1)
        GetCount(); // fill list with filtered shapes (no internal shapes)
 
    if (!xSelectionSupplier.is())
        throw uno::RuntimeException("Could not get selected shapes. Null reference to xSelectionSupplier in ScChildrenShapes::IsSelected.");
 
    if (mbShapesNeedSorting)
    {
        std::sort(maZOrderedShapes.begin(), maZOrderedShapes.end(), ScShapeDataLess());
        mbShapesNeedSorting = false;
    }
 
    if (!maZOrderedShapes[nIndex])
        return false;
 
    bResult = maZOrderedShapes[nIndex]->bSelected;
    rShape = maZOrderedShapes[nIndex]->xShape;
 
#if OSL_DEBUG_LEVEL > 0 // test whether it is truly selected by a slower method
    uno::Reference< drawing::XShape > xReturnShape;
    bool bDebugResult(false);
    uno::Reference<drawing::XShapes> xShapes(mpViewShell->getSelectedXShapes());
 
    if (xShapes.is())
    {
        sal_Int32 nCount(xShapes->getCount());
        if (nCount)
        {
            uno::Reference< drawing::XShape > xShape;
            uno::Reference< drawing::XShape > xIndexShape = maZOrderedShapes[nIndex]->xShape;
            sal_Int32 i(0);
            while (!bDebugResult && (i < nCount))
            {
                xShapes->getByIndex(i) >>= xShape;
                if (xShape.is() && (xIndexShape.get() == xShape.get()))
                {
                    bDebugResult = true;
                    xReturnShape = xShape;
                }
                else
                    ++i;
            }
        }
    }
    OSL_ENSURE((bResult == bDebugResult) && ((bResult && (rShape.get() == xReturnShape.get())) || !bResult), "found the wrong shape or result");
#endif
 
    return bResult;
}
 
bool ScChildrenShapes::SelectionChanged()
{
    bool bResult(false);
    if (!xSelectionSupplier.is())
        throw uno::RuntimeException("Could not get selected shapes. Null reference to xSelectionSupplier in ScChildrenShapes::SelectionChanged.");
 
    uno::Reference<drawing::XShapes> xShapes(mpViewShell->getSelectedXShapes());
 
    bResult = FindSelectedShapesChanges(xShapes);
 
    return bResult;
}
 
void ScChildrenShapes::Select(sal_Int32 nIndex)
{
    if (maZOrderedShapes.size() <= 1)
        GetCount(); // fill list with filtered shapes (no internal shapes)
 
    if (!xSelectionSupplier.is())
        throw uno::RuntimeException("Could not get selected shapes. Null reference to xSelectionSupplier in ScChildrenShapes::Select.");
 
    if (mbShapesNeedSorting)
    {
        std::sort(maZOrderedShapes.begin(), maZOrderedShapes.end(), ScShapeDataLess());
        mbShapesNeedSorting = false;
    }
 
    if (!maZOrderedShapes[nIndex])
        return;
 
    uno::Reference<drawing::XShape> xShape;
    if (IsSelected(nIndex, xShape) || !maZOrderedShapes[nIndex]->bSelectable)
        return;
 
    uno::Reference<drawing::XShapes> xShapes(mpViewShell->getSelectedXShapes());
 
    if (!xShapes.is())
        xShapes = drawing::ShapeCollection::create(
                comphelper::getProcessComponentContext());
 
    xShapes->add(maZOrderedShapes[nIndex]->xShape);
 
    try
    {
        xSelectionSupplier->select(uno::Any(xShapes));
        maZOrderedShapes[nIndex]->bSelected = true;
        if (maZOrderedShapes[nIndex]->pAccShape.is())
            maZOrderedShapes[nIndex]->pAccShape->SetState(AccessibleStateType::SELECTED);
    }
    catch (lang::IllegalArgumentException&)
    {
    }
}
 
void ScChildrenShapes::DeselectAll()
{
    if (!xSelectionSupplier.is())
        throw uno::RuntimeException("Could not get selected shapes. Null reference to xSelectionSupplier in ScChildrenShapes::DeselectAll.");
 
    bool bSomethingSelected(true);
    try
    {
        xSelectionSupplier->select(uno::Any()); //deselects all
    }
    catch (lang::IllegalArgumentException&)
    {
        OSL_FAIL("nothing selected before");
        bSomethingSelected = false;
    }
 
    if (bSomethingSelected)
        for (const ScAccessibleShapeData* pAccShapeData : maZOrderedShapes)
            if (pAccShapeData)
            {
                pAccShapeData->bSelected = false;
                if (pAccShapeData->pAccShape.is())
                    pAccShapeData->pAccShape->ResetState(AccessibleStateType::SELECTED);
            }
};
 
 
void ScChildrenShapes::SelectAll()
{
    if (!xSelectionSupplier.is())
        throw uno::RuntimeException("Could not get selected shapes. Null reference to xSelectionSupplier in ScChildrenShapes::SelectAll.");
 
    if (maZOrderedShapes.size() <= 1)
        GetCount(); // fill list with filtered shapes (no internal shapes)
 
    if (maZOrderedShapes.size() <= 1)
        return;
 
    uno::Reference<drawing::XShapes> xShapes = drawing::ShapeCollection::create(
            comphelper::getProcessComponentContext());
 
    try
    {
        for (const ScAccessibleShapeData* pAccShapeData : maZOrderedShapes)
        {
            if (pAccShapeData && pAccShapeData->bSelectable)
            {
                pAccShapeData->bSelected = true;
                if (pAccShapeData->pAccShape.is())
                    pAccShapeData->pAccShape->SetState(AccessibleStateType::SELECTED);
                if (xShapes.is())
                    xShapes->add(pAccShapeData->xShape);
            }
        }
        xSelectionSupplier->select(uno::Any(xShapes));
    }
    catch (lang::IllegalArgumentException&)
    {
        SelectionChanged(); // find all selected shapes and set the flags
    }
}
 
void ScChildrenShapes::FillShapes(std::vector < uno::Reference < drawing::XShape > >& rShapes) const
{
    uno::Reference<drawing::XShapes> xShapes(mpViewShell->getSelectedXShapes());
    if (xShapes.is())
    {
        sal_uInt32 nCount(xShapes->getCount());
        for (sal_uInt32 i = 0; i < nCount; ++i)
        {
            uno::Reference<drawing::XShape> xShape;
            xShapes->getByIndex(i) >>= xShape;
            if (xShape.is())
                rShapes.push_back(xShape);
        }
    }
}
 
sal_Int32 ScChildrenShapes::GetSelectedCount() const
{
    if (!xSelectionSupplier.is())
        throw uno::RuntimeException("Could not get selected shapes. Null reference to xSelectionSupplier in ScChildrenShapes::GetSelectedCount.");
 
    std::vector < uno::Reference < drawing::XShape > > aShapes;
    FillShapes(aShapes);
 
    return aShapes.size();
}
 
uno::Reference< XAccessible > ScChildrenShapes::GetSelected(sal_Int32 nSelectedChildIndex, bool bTabSelected) const
{
    uno::Reference< XAccessible > xAccessible;
 
    if (maZOrderedShapes.size() <= 1)
        GetCount(); // fill list with shapes
 
    if (!bTabSelected)
    {
        std::vector < uno::Reference < drawing::XShape > > aShapes;
        FillShapes(aShapes);
 
        if (nSelectedChildIndex < 0 || o3tl::make_unsigned(nSelectedChildIndex) >= aShapes.size())
            return xAccessible;
 
        SortedShapes::iterator aItr;
        if (FindShape(aShapes[nSelectedChildIndex], aItr))
            xAccessible = Get(*aItr);
    }
    else
    {
        if (mbShapesNeedSorting)
        {
            std::sort(maZOrderedShapes.begin(), maZOrderedShapes.end(), ScShapeDataLess());
            mbShapesNeedSorting = false;
        }
        for(const auto& rpShape : maZOrderedShapes)
        {
            if (!rpShape || rpShape->bSelected)
            {
                if (nSelectedChildIndex == 0)
                {
                    if (rpShape)
                        xAccessible = rpShape->pAccShape.get();
                    break;
                }
                else
                    --nSelectedChildIndex;
            }
        }
    }
 
    return xAccessible;
}
 
void ScChildrenShapes::Deselect(sal_Int32 nChildIndex)
{
    uno::Reference<drawing::XShape> xShape;
    if (!IsSelected(nChildIndex, xShape)) // returns false if it is the sheet
        return;
 
    if (!xShape.is())
        return;
 
    uno::Reference<drawing::XShapes> xShapes(mpViewShell->getSelectedXShapes());
    if (xShapes.is())
        xShapes->remove(xShape);
 
    try
    {
        xSelectionSupplier->select(uno::Any(xShapes));
    }
    catch (lang::IllegalArgumentException&)
    {
        OSL_FAIL("something not selectable");
    }
 
    maZOrderedShapes[nChildIndex]->bSelected = false;
    if (maZOrderedShapes[nChildIndex]->pAccShape.is())
        maZOrderedShapes[nChildIndex]->pAccShape->ResetState(AccessibleStateType::SELECTED);
}
 
SdrPage* ScChildrenShapes::GetDrawPage() const
{
    SCTAB nTab(mpAccessibleDocument->getVisibleTable());
    SdrPage* pDrawPage = nullptr;
    if (mpViewShell)
    {
        ScDocument& rDoc = mpViewShell->GetViewData().GetDocument();
        if (ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer())
        {
            if (pDrawLayer->HasObjects() && (pDrawLayer->GetPageCount() > nTab))
                pDrawPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(static_cast<sal_Int16>(nTab)));
        }
    }
    return pDrawPage;
}
 
rtl::Reference<utl::AccessibleRelationSetHelper> ScChildrenShapes::GetRelationSet(const ScAddress* pAddress) const
{
    rtl::Reference<utl::AccessibleRelationSetHelper> pRelationSet;
    for (const ScAccessibleShapeData* pAccShapeData : maZOrderedShapes)
    {
        if (pAccShapeData &&
            ((!pAccShapeData->xRelationCell && !pAddress) ||
            (pAccShapeData->xRelationCell && pAddress && (*(pAccShapeData->xRelationCell) == *pAddress))))
        {
            if (!pRelationSet)
                pRelationSet = new utl::AccessibleRelationSetHelper();
 
            AccessibleRelation aRelation;
            aRelation.TargetSet = { Get(pAccShapeData) };
            aRelation.RelationType = AccessibleRelationType_CONTROLLER_FOR;
 
            pRelationSet->AddRelation(aRelation);
        }
    }
    return pRelationSet;
}
 
bool ScChildrenShapes::FindSelectedShapesChanges(const uno::Reference<drawing::XShapes>& xShapes) const
{
    bool bResult(false);
    SortedShapes aShapesList;
    if (xShapes.is())
    {
        mnShapesSelected = xShapes->getCount();
        for (sal_uInt32 i = 0; i < mnShapesSelected; ++i)
        {
            uno::Reference< drawing::XShape > xShape;
            xShapes->getByIndex(i) >>= xShape;
            if (xShape.is())
            {
                ScAccessibleShapeData* pShapeData = new ScAccessibleShapeData(xShape);
                aShapesList.push_back(pShapeData);
            }
        }
    }
    else
        mnShapesSelected = 0;
    SdrObject *pFocusedObj = nullptr;
    if( mnShapesSelected == 1 && aShapesList.size() == 1)
    {
        pFocusedObj = SdrObject::getSdrObjectFromXShape(aShapesList[0]->xShape);
    }
    std::sort(aShapesList.begin(), aShapesList.end(), ScShapeDataLess());
    SortedShapes vecSelectedShapeAdd;
    SortedShapes vecSelectedShapeRemove;
    bool bHasSelect=false;
    SortedShapes::iterator aXShapesItr(aShapesList.begin());
    SortedShapes::const_iterator aXShapesEndItr(aShapesList.end());
    SortedShapes::iterator aDataItr(maZOrderedShapes.begin());
    SortedShapes::const_iterator aDataEndItr(maZOrderedShapes.end());
    SortedShapes::const_iterator aFocusedItr = aDataEndItr;
    while(aDataItr != aDataEndItr)
    {
        if (*aDataItr) // is it really a shape or only the sheet
        {
            sal_Int8 nComp(0);
            if (aXShapesItr == aXShapesEndItr)
                nComp = -1; // simulate that the Shape is lower, so the selection state will be removed
            else
                nComp = Compare(*aDataItr, *aXShapesItr);
            if (nComp == 0)
            {
                if (!(*aDataItr)->bSelected)
                {
                    (*aDataItr)->bSelected = true;
                    if ((*aDataItr)->pAccShape.is())
                    {
                        (*aDataItr)->pAccShape->SetState(AccessibleStateType::SELECTED);
                        (*aDataItr)->pAccShape->SetState(AccessibleStateType::FOCUSED);
                        bResult = true;
                        vecSelectedShapeAdd.push_back(*aDataItr);
                    }
                    aFocusedItr = aDataItr;
                }
                else
                {
                     bHasSelect = true;
                }
                ++aDataItr;
                ++aXShapesItr;
            }
            else if (nComp < 0)
            {
                if ((*aDataItr)->bSelected)
                {
                    (*aDataItr)->bSelected = false;
                    if ((*aDataItr)->pAccShape.is())
                    {
                        (*aDataItr)->pAccShape->ResetState(AccessibleStateType::SELECTED);
                        (*aDataItr)->pAccShape->ResetState(AccessibleStateType::FOCUSED);
                        bResult = true;
                        vecSelectedShapeRemove.push_back(*aDataItr);
                    }
                }
                ++aDataItr;
            }
            else
            {
                OSL_FAIL("here is a selected shape which is not in the childlist");
                ++aXShapesItr;
                --mnShapesSelected;
            }
        }
        else
            ++aDataItr;
    }
    bool bWinFocus=false;
    if (mpViewShell)
    {
        ScGridWindow* pWin = static_cast<ScGridWindow*>(mpViewShell->GetWindowByPos(meSplitPos));
        if (pWin)
        {
            bWinFocus = pWin->HasFocus();
        }
    }
    const SdrMarkList* pMarkList = nullptr;
    SdrObject* pMarkedObj = nullptr;
    bool bIsFocuseMarked = true;
    if( mpViewShell && mnShapesSelected == 1 && bWinFocus)
    {
        ScDrawView* pScDrawView = mpViewShell->GetViewData().GetScDrawView();
        if( pScDrawView )
        {
            pMarkList = &(pScDrawView->GetMarkedObjectList());
            if( pMarkList->GetMarkCount() == 1 )
            {
                pMarkedObj = pMarkList->GetMark(0)->GetMarkedSdrObj();
                uno::Reference< drawing::XShape > xMarkedXShape (pMarkedObj->getUnoShape(), uno::UNO_QUERY);
                if( aFocusedItr != aDataEndItr &&
                    (*aFocusedItr)->xShape.is() &&
                    xMarkedXShape.is() &&
                    (*aFocusedItr)->xShape != xMarkedXShape )
                    bIsFocuseMarked = false;
            }
        }
    }
    //if ((aFocusedItr != aDataEndItr) && (*aFocusedItr)->pAccShape.is() && (mnShapesSelected == 1))
    if ( bIsFocuseMarked && (aFocusedItr != aDataEndItr) && (*aFocusedItr)->pAccShape.is() && (mnShapesSelected == 1) && bWinFocus)
    {
        (*aFocusedItr)->pAccShape->SetState(AccessibleStateType::FOCUSED);
    }
    else if( pFocusedObj && bWinFocus && pMarkList && pMarkList->GetMarkCount() == 1 && mnShapesSelected == 1 )
    {
        if( pMarkedObj )
        {
            uno::Reference< drawing::XShape > xMarkedXShape (pMarkedObj->getUnoShape(), uno::UNO_QUERY);
            SdrObject* pUpObj = pMarkedObj->getParentSdrObjectFromSdrObject();
 
            if( pMarkedObj == pFocusedObj && pUpObj )
            {
                uno::Reference< drawing::XShape > xUpGroupXShape (pUpObj->getUnoShape(), uno::UNO_QUERY);
                uno::Reference < XAccessible > xAccGroupShape =
                    const_cast<ScChildrenShapes*>(this)->GetAccessibleCaption( xUpGroupXShape );
                if( xAccGroupShape.is() )
                {
                    ::accessibility::AccessibleShape* pAccGroupShape =
                        static_cast< ::accessibility::AccessibleShape* >(xAccGroupShape.get());
                    if( pAccGroupShape )
                    {
                        sal_Int64 nCount =  pAccGroupShape->getAccessibleChildCount();
                        for( sal_Int64 i = 0; i < nCount; i++ )
                        {
                            uno::Reference<XAccessible> xAccShape = pAccGroupShape->getAccessibleChild(i);
                            if (xAccShape.is())
                            {
                                ::accessibility::AccessibleShape* pChildAccShape =  static_cast< ::accessibility::AccessibleShape* >(xAccShape.get());
                                uno::Reference< drawing::XShape > xChildShape = pChildAccShape->GetXShape();
                                if (xChildShape == xMarkedXShape)
                                {
                                    pChildAccShape->SetState(AccessibleStateType::FOCUSED);
                                }
                                else
                                {
                                    pChildAccShape->ResetState(AccessibleStateType::FOCUSED);
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    if (vecSelectedShapeAdd.size() >= 10 )
    {
        AccessibleEventObject aEvent;
        aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_WITHIN;
        aEvent.Source = uno::Reference< XAccessible >(mpAccessibleDocument);
        mpAccessibleDocument->CommitChange(aEvent);
    }
    else
    {
        for (const auto& rpShape : vecSelectedShapeAdd)
        {
            AccessibleEventObject aEvent;
            if (bHasSelect)
            {
                aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_ADD;
            }
            else
            {
                aEvent.EventId = AccessibleEventId::SELECTION_CHANGED;
            }
            aEvent.Source = uno::Reference< XAccessible >(mpAccessibleDocument);
            uno::Reference< XAccessible > xChild( rpShape->pAccShape );
            aEvent.NewValue <<= xChild;
            mpAccessibleDocument->CommitChange(aEvent);
        }
    }
    for (const auto& rpShape : vecSelectedShapeRemove)
    {
        AccessibleEventObject aEvent;
        aEvent.EventId =  AccessibleEventId::SELECTION_CHANGED_REMOVE;
        aEvent.Source = uno::Reference< XAccessible >(mpAccessibleDocument);
        uno::Reference< XAccessible > xChild( rpShape->pAccShape );
        aEvent.NewValue <<= xChild;
        mpAccessibleDocument->CommitChange(aEvent);
    }
    for(ScAccessibleShapeData*& pShapeData : aShapesList)
    {
        delete pShapeData;
        pShapeData = nullptr;
    }
    return bResult;
}
 
std::optional<ScAddress> ScChildrenShapes::GetAnchor(const uno::Reference<drawing::XShape>& xShape) const
{
    if (mpViewShell)
    {
        SdrObject* pSdrObj = SdrObject::getSdrObjectFromXShape(xShape);
        uno::Reference<beans::XPropertySet> xShapeProp(xShape, uno::UNO_QUERY);
        if (pSdrObj && xShapeProp.is())
        {
            if (ScDrawObjData *pAnchor = ScDrawLayer::GetObjData(pSdrObj))
                return std::optional<ScAddress>(pAnchor->maStart);
        }
    }
 
    return std::optional<ScAddress>();
}
 
rtl::Reference<utl::AccessibleRelationSetHelper> ScChildrenShapes::GetRelationSet(const ScAccessibleShapeData* pData) const
{
    rtl::Reference<utl::AccessibleRelationSetHelper> pRelationSet = new utl::AccessibleRelationSetHelper();
 
    if (pData && mpAccessibleDocument)
    {
        uno::Reference<XAccessible> xAccessible = mpAccessibleDocument->GetAccessibleSpreadsheet(); // should be the current table
        if (pData->xRelationCell && xAccessible.is())
        {
            sal_Int32 nRow = pData->xRelationCell->Row();
            sal_Int32 nColumn = pData->xRelationCell->Col();
            bool bPositionUnset = nRow == -1 && nColumn == -1;
            if (!bPositionUnset)
            {
                uno::Reference<XAccessibleTable> xAccTable(xAccessible->getAccessibleContext(), uno::UNO_QUERY);
                if (xAccTable.is())
                    xAccessible = xAccTable->getAccessibleCellAt(nRow, nColumn);
            }
        }
        AccessibleRelation aRelation;
        aRelation.TargetSet = { xAccessible };
        aRelation.RelationType = AccessibleRelationType_CONTROLLED_BY;
        pRelationSet->AddRelation(aRelation);
    }
 
    return pRelationSet;
}
 
void ScChildrenShapes::SetAnchor(const uno::Reference<drawing::XShape>& xShape, ScAccessibleShapeData* pData) const
{
    if (pData)
    {
        std::optional<ScAddress> xAddress = GetAnchor(xShape);
        if ((xAddress && pData->xRelationCell && (*xAddress != *(pData->xRelationCell))) ||
            (!xAddress && pData->xRelationCell) || (xAddress && !pData->xRelationCell))
        {
            pData->xRelationCell = std::move(xAddress);
            if (pData->pAccShape.is())
                pData->pAccShape->SetRelationSet(GetRelationSet(pData));
        }
    }
}
 
void ScChildrenShapes::AddShape(const uno::Reference<drawing::XShape>& xShape, bool bCommitChange) const
{
    assert( maShapesMap.find(xShape) == maShapesMap.end());
 
    ScAccessibleShapeData* pShape = new ScAccessibleShapeData(xShape);
    maZOrderedShapes.push_back(pShape);
    mbShapesNeedSorting = true;
    maShapesMap[xShape] = pShape;
    SetAnchor(xShape, pShape);
 
    uno::Reference< beans::XPropertySet > xShapeProp(xShape, uno::UNO_QUERY);
    if (xShapeProp.is())
    {
        uno::Any aPropAny = xShapeProp->getPropertyValue(u"LayerID"_ustr);
        sal_Int16 nLayerID = 0;
        if( aPropAny >>= nLayerID )
        {
            if( (SdrLayerID(nLayerID) == SC_LAYER_INTERN) || (SdrLayerID(nLayerID) == SC_LAYER_HIDDEN) )
                pShape->bSelectable = false;
            else
                pShape->bSelectable = true;
        }
    }
 
    if (!xSelectionSupplier.is())
        throw uno::RuntimeException("Could not get selected shapes. Null reference to xSelectionSupplier in ScChildrenShapes::AddShape.");
 
    uno::Reference<drawing::XShapes> xShapes(mpViewShell->getSelectedXShapes());
    uno::Reference<container::XEnumerationAccess> xEnumAcc(xShapes, uno::UNO_QUERY);
    if (xEnumAcc.is())
    {
        uno::Reference<container::XEnumeration> xEnum = xEnumAcc->createEnumeration();
        if (xEnum.is())
        {
            uno::Reference<drawing::XShape> xSelectedShape;
            bool bFound(false);
            while (!bFound && xEnum->hasMoreElements())
            {
                xEnum->nextElement() >>= xSelectedShape;
                if (xShape.is() && (xShape.get() == xSelectedShape.get()))
                {
                    pShape->bSelected = true;
                    bFound = true;
                }
            }
        }
    }
    if (mpAccessibleDocument && bCommitChange)
    {
        AccessibleEventObject aEvent;
        aEvent.EventId = AccessibleEventId::CHILD;
        aEvent.Source = uno::Reference< XAccessibleContext >(mpAccessibleDocument);
        aEvent.NewValue <<= Get(pShape);
        aEvent.IndexHint = -1;
 
        mpAccessibleDocument->CommitChange(aEvent); // new child - event
    }
}
 
void ScChildrenShapes::RemoveShape(const uno::Reference<drawing::XShape>& xShape) const
{
    if (mbShapesNeedSorting)
    {
        std::sort(maZOrderedShapes.begin(), maZOrderedShapes.end(), ScShapeDataLess());
        mbShapesNeedSorting = false;
    }
    SortedShapes::iterator aItr;
    if (FindShape(xShape, aItr))
    {
        if (mpAccessibleDocument)
        {
            uno::Reference<XAccessible> xOldAccessible (Get(*aItr));
 
            delete *aItr;
            maShapesMap.erase((*aItr)->xShape);
            maZOrderedShapes.erase(aItr);
 
            AccessibleEventObject aEvent;
            aEvent.EventId = AccessibleEventId::CHILD;
            aEvent.Source = uno::Reference< XAccessibleContext >(mpAccessibleDocument);
            aEvent.OldValue <<= xOldAccessible;
            aEvent.IndexHint = -1;
 
            mpAccessibleDocument->CommitChange(aEvent); // child is gone - event
        }
        else
        {
            delete *aItr;
            maShapesMap.erase((*aItr)->xShape);
            maZOrderedShapes.erase(aItr);
        }
    }
    else
    {
        OSL_FAIL("shape was not in internal list");
    }
}
 
bool ScChildrenShapes::FindShape(const uno::Reference<drawing::XShape>& xShape, ScChildrenShapes::SortedShapes::iterator& rItr) const
{
    if (mbShapesNeedSorting)
    {
        std::sort(maZOrderedShapes.begin(), maZOrderedShapes.end(), ScShapeDataLess());
        mbShapesNeedSorting = false;
    }
    bool bResult(false);
    ScAccessibleShapeData aShape(xShape);
    rItr = std::lower_bound(maZOrderedShapes.begin(), maZOrderedShapes.end(), &aShape, ScShapeDataLess());
    if ((rItr != maZOrderedShapes.end()) && (*rItr != nullptr) && ((*rItr)->xShape.get() == xShape.get()))
        bResult = true; // if the shape is found
 
#if OSL_DEBUG_LEVEL > 0 // test whether it finds truly the correct shape (perhaps it is not really sorted)
    SortedShapes::iterator aDebugItr = std::find_if(maZOrderedShapes.begin(), maZOrderedShapes.end(),
        [&xShape](const ScAccessibleShapeData* pShape) { return pShape && (pShape->xShape.get() == xShape.get()); });
    bool bResult2 = (aDebugItr != maZOrderedShapes.end());
    OSL_ENSURE((bResult == bResult2) && ((bResult && (rItr == aDebugItr)) || !bResult), "wrong Shape found");
#endif
    return bResult;
}
 
sal_Int8 ScChildrenShapes::Compare(const ScAccessibleShapeData* pData1,
        const ScAccessibleShapeData* pData2)
{
    ScShapeDataLess aLess;
 
    bool bResult1(aLess(pData1, pData2));
    bool bResult2(aLess(pData2, pData1));
 
    sal_Int8 nResult(0);
    if (!bResult1 && bResult2)
        nResult = 1;
    else if (bResult1 && !bResult2)
        nResult = -1;
 
    return nResult;
}
 
void ScChildrenShapes::VisAreaChanged() const
{
    for (const ScAccessibleShapeData* pAccShapeData: maZOrderedShapes)
        if (pAccShapeData && pAccShapeData->pAccShape.is())
            pAccShapeData->pAccShape->ViewForwarderChanged();
}
 
ScAccessibleDocument::ScAccessibleDocument(
        const uno::Reference<XAccessible>& rxParent,
        ScTabViewShell* pViewShell,
        ScSplitPos eSplitPos)
    : ScAccessibleDocumentBase(rxParent),
    mpViewShell(pViewShell),
    meSplitPos(eSplitPos),
    mbCompleteSheetSelected(false)
{
    maVisArea = GetVisibleArea_Impl();
}
 
void ScAccessibleDocument::PreInit()
{
    if (!mpViewShell)
        return;
 
    mpViewShell->AddAccessibilityObject(*this);
    vcl::Window *pWin = mpViewShell->GetWindowByPos(meSplitPos);
    if( pWin )
    {
        pWin->AddChildEventListener( LINK( this, ScAccessibleDocument, WindowChildEventListener ));
        sal_uInt16 nCount =   pWin->GetChildCount();
        for( sal_uInt16 i=0; i < nCount; ++i )
        {
            vcl::Window *pChildWin = pWin->GetChild( i );
            if( pChildWin &&
                AccessibleRole::EMBEDDED_OBJECT == pChildWin->GetAccessibleRole() )
                AddChild( pChildWin->GetAccessible(), false );
        }
    }
    ScViewData& rViewData = mpViewShell->GetViewData();
    if (rViewData.HasEditView(meSplitPos))
    {
        uno::Reference<XAccessible> xAcc = new ScAccessibleEditObject(this, rViewData.GetEditView(meSplitPos),
            mpViewShell->GetWindowByPos(meSplitPos), GetCurrentCellName(), GetCurrentCellDescription(),
            ScAccessibleEditObject::CellInEditMode);
        AddChild(xAcc, false);
    }
}
 
void ScAccessibleDocument::Init()
{
    if(!mpChildrenShapes)
        mpChildrenShapes.reset( new ScChildrenShapes(this, mpViewShell, meSplitPos) );
}
 
ScAccessibleDocument::~ScAccessibleDocument()
{
    if (!ScAccessibleContextBase::IsDefunc() && !rBHelper.bInDispose)
    {
        // increment refcount to prevent double call off dtor
        osl_atomic_increment( &m_refCount );
        dispose();
    }
}
 
void SAL_CALL ScAccessibleDocument::disposing()
{
    SolarMutexGuard aGuard;
    FreeAccessibleSpreadsheet();
    if (mpViewShell)
    {
        vcl::Window *pWin = mpViewShell->GetWindowByPos(meSplitPos);
        if( pWin )
            pWin->RemoveChildEventListener( LINK( this, ScAccessibleDocument, WindowChildEventListener ));
 
        mpViewShell->RemoveAccessibilityObject(*this);
        mpViewShell = nullptr;
    }
    mpChildrenShapes.reset();
 
    ScAccessibleDocumentBase::disposing();
}
 
void SAL_CALL ScAccessibleDocument::disposing( const lang::EventObject& /* Source */ )
{
    disposing();
}
 
    //=====  SfxListener  =====================================================
 
IMPL_LINK( ScAccessibleDocument, WindowChildEventListener, VclWindowEvent&, rEvent, void )
{
    OSL_ENSURE( rEvent.GetWindow(), "Window???" );
    switch ( rEvent.GetId() )
    {
    case VclEventId::WindowShow:  // send create on show for direct accessible children
        {
            vcl::Window* pChildWin = static_cast < vcl::Window * >( rEvent.GetData() );
            if( pChildWin && AccessibleRole::EMBEDDED_OBJECT == pChildWin->GetAccessibleRole() )
            {
                AddChild( pChildWin->GetAccessible(), true );
            }
        }
        break;
    case VclEventId::WindowHide:  // send destroy on hide for direct accessible children
        {
            vcl::Window* pChildWin = static_cast < vcl::Window * >( rEvent.GetData() );
            if( pChildWin && AccessibleRole::EMBEDDED_OBJECT == pChildWin->GetAccessibleRole() )
            {
                RemoveChild( pChildWin->GetAccessible(), true );
            }
        }
        break;
    default: break;
    }
}
 
void ScAccessibleDocument::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
{
    if (rHint.GetId() == SfxHintId::ScAccGridWinFocusLost )
    {
        auto pFocusLostHint = static_cast<const ScAccGridWinFocusLostHint*>(&rHint);
        if (pFocusLostHint->GetOldGridWin() == meSplitPos)
        {
            if (mxTempAcc.is() && mpTempAccEdit)
                mpTempAccEdit->LostFocus();
            else if (mpAccessibleSpreadsheet.is())
                mpAccessibleSpreadsheet->LostFocus();
            else
                CommitFocusLost();
        }
    }
    else if (rHint.GetId() == SfxHintId::ScAccGridWinFocusGot)
    {
        auto pFocusGotHint = static_cast<const ScAccGridWinFocusGotHint*>(&rHint);
        if (pFocusGotHint->GetNewGridWin() == meSplitPos)
        {
            uno::Reference<XAccessible> xAccessible;
            if (mpChildrenShapes)
            {
                bool bTabMarked(IsTableSelected());
                xAccessible = mpChildrenShapes->GetSelected(0, bTabMarked);
            }
            if( xAccessible.is() )
            {
                uno::Any aNewValue;
                aNewValue<<=AccessibleStateType::FOCUSED;
                static_cast< ::accessibility::AccessibleShape* >(xAccessible.get())->
                    CommitChange(AccessibleEventId::STATE_CHANGED,
                                aNewValue,
                                uno::Any(), -1 );
            }
            else
            {
            if (mxTempAcc.is() && mpTempAccEdit)
                mpTempAccEdit->GotFocus();
            else if (mpAccessibleSpreadsheet.is())
                mpAccessibleSpreadsheet->GotFocus();
            else
                CommitFocusGained();
            }
        }
    }
    else if (rHint.GetId() == SfxHintId::ScAccTableChanged)
    {
        // only notify if child exist, otherwise it is not necessary
        if (mpAccessibleSpreadsheet.is())
        {
            FreeAccessibleSpreadsheet();
 
            // Shapes / form controls after reload not accessible, rebuild the
            // mpChildrenShapes variable.
            mpChildrenShapes.reset( new ScChildrenShapes( this, mpViewShell, meSplitPos ) );
 
            AccessibleEventObject aEvent;
            aEvent.EventId = AccessibleEventId::INVALIDATE_ALL_CHILDREN;
            aEvent.Source = uno::Reference< XAccessibleContext >(this);
            CommitChange(aEvent); // all children changed
 
            if (mpAccessibleSpreadsheet.is())
                mpAccessibleSpreadsheet->GotFocus();
        }
    }
    else if (rHint.GetId() == SfxHintId::ScAccMakeDrawLayer)
    {
        if (mpChildrenShapes)
            mpChildrenShapes->SetDrawBroadcaster();
    }
    else if (rHint.GetId() == SfxHintId::ScAccEnterEditMode) // this event comes only on creating edit field of a cell
    {
        if (mpViewShell->GetViewData().GetEditActivePart() == meSplitPos)
        {
            ScViewData& rViewData = mpViewShell->GetViewData();
            EditEngine const& rEditEng = rViewData.GetEditView(meSplitPos)->getEditEngine();
            if (rEditEng.IsUpdateLayout())
            {
                mpTempAccEdit = new ScAccessibleEditObject(this, rViewData.GetEditView(meSplitPos),
                    mpViewShell->GetWindowByPos(meSplitPos), GetCurrentCellName(),
                    ScResId(STR_ACC_EDITLINE_DESCR), ScAccessibleEditObject::CellInEditMode);
                uno::Reference<XAccessible> xAcc = mpTempAccEdit;
 
                AddChild(xAcc, true);
 
                if (mpAccessibleSpreadsheet.is())
                    mpAccessibleSpreadsheet->LostFocus();
                else
                    CommitFocusLost();
 
                mpTempAccEdit->GotFocus();
            }
        }
    }
    else if (rHint.GetId() == SfxHintId::ScAccLeaveEditMode)
    {
        if (mxTempAcc.is())
        {
            if (mpTempAccEdit)
            {
                mpTempAccEdit->LostFocus();
            }
            RemoveChild(mxTempAcc, true);
            if (mpTempAccEdit)
            {
                // tdf#125982 a11y use-after-free of editengine by
                // ScAccessibleEditObjectTextData living past the
                // the editengine of the editview passed in above
                // in ScAccEnterEditMode
                mpTempAccEdit->dispose();
                mpTempAccEdit = nullptr;
            }
            if (mpAccessibleSpreadsheet.is() && mpViewShell && mpViewShell->IsActive())
                mpAccessibleSpreadsheet->GotFocus();
            else if( mpViewShell && mpViewShell->IsActive())
                CommitFocusGained();
        }
    }
    else if ((rHint.GetId() == SfxHintId::ScAccVisAreaChanged) || (rHint.GetId() == SfxHintId::ScAccWindowResized))
    {
        tools::Rectangle aOldVisArea(maVisArea);
        maVisArea = GetVisibleArea_Impl();
 
        if (maVisArea != aOldVisArea)
        {
            if (maVisArea.GetSize() != aOldVisArea.GetSize())
            {
                AccessibleEventObject aEvent;
                aEvent.EventId = AccessibleEventId::BOUNDRECT_CHANGED;
                aEvent.Source = uno::Reference< XAccessibleContext >(this);
 
                CommitChange(aEvent);
 
                if (mpAccessibleSpreadsheet.is())
                    mpAccessibleSpreadsheet->BoundingBoxChanged();
                if (mpAccessibleSpreadsheet.is() && mpViewShell && mpViewShell->IsActive())
                    mpAccessibleSpreadsheet->FireFirstCellFocus();
            }
            else if (mpAccessibleSpreadsheet.is())
            {
                mpAccessibleSpreadsheet->VisAreaChanged();
            }
            if (mpChildrenShapes)
                mpChildrenShapes->VisAreaChanged();
        }
    }
 
    ScAccessibleDocumentBase::Notify(rBC, rHint);
}
 
void SAL_CALL ScAccessibleDocument::selectionChanged( const lang::EventObject& /* aEvent */ )
{
    bool bSelectionChanged(false);
    if (mpAccessibleSpreadsheet.is())
    {
        bool bOldSelected(mbCompleteSheetSelected);
        mbCompleteSheetSelected = IsTableSelected();
        if (bOldSelected != mbCompleteSheetSelected)
        {
            mpAccessibleSpreadsheet->CompleteSelectionChanged(mbCompleteSheetSelected);
            bSelectionChanged = true;
        }
    }
 
    if (mpChildrenShapes && mpChildrenShapes->SelectionChanged())
        bSelectionChanged = true;
 
    if (bSelectionChanged)
    {
        AccessibleEventObject aEvent;
        aEvent.EventId = AccessibleEventId::SELECTION_CHANGED;
        aEvent.Source = uno::Reference< XAccessibleContext >(this);
 
        CommitChange(aEvent);
    }
}
 
    //=====  XInterface  =====================================================
 
uno::Any SAL_CALL ScAccessibleDocument::queryInterface( uno::Type const & rType )
{
    uno::Any aAny (ScAccessibleDocumentImpl::queryInterface(rType));
    return aAny.hasValue() ? aAny : ScAccessibleContextBase::queryInterface(rType);
}
 
void SAL_CALL ScAccessibleDocument::acquire()
    noexcept
{
    ScAccessibleContextBase::acquire();
}
 
void SAL_CALL ScAccessibleDocument::release()
    noexcept
{
    ScAccessibleContextBase::release();
}
 
    //=====  XAccessibleComponent  ============================================
 
uno::Reference< XAccessible > SAL_CALL ScAccessibleDocument::getAccessibleAtPoint(
        const awt::Point& rPoint )
{
    uno::Reference<XAccessible> xAccessible;
    if (containsPoint(rPoint))
    {
        SolarMutexGuard aGuard;
        IsObjectValid();
        if (mpChildrenShapes)
            xAccessible = mpChildrenShapes->GetAt(rPoint);
        if(!xAccessible.is())
        {
            if (mxTempAcc.is())
            {
                uno::Reference< XAccessibleContext > xCont(mxTempAcc->getAccessibleContext());
                uno::Reference< XAccessibleComponent > xComp(xCont, uno::UNO_QUERY);
                if (xComp.is())
                {
                    tools::Rectangle aBound(VCLUnoHelper::ConvertToVCLRect(xComp->getBounds()));
                    if (aBound.Contains(VCLUnoHelper::ConvertToVCLPoint(rPoint)))
                        xAccessible = mxTempAcc;
                }
            }
            if (!xAccessible.is())
                xAccessible = GetAccessibleSpreadsheet();
        }
    }
    return xAccessible;
}
 
void SAL_CALL ScAccessibleDocument::grabFocus(  )
{
    SolarMutexGuard aGuard;
    IsObjectValid();
    if (!getAccessibleParent().is())
        return;
 
    uno::Reference<XAccessibleComponent> xAccessibleComponent(getAccessibleParent()->getAccessibleContext(), uno::UNO_QUERY);
    if (xAccessibleComponent.is())
    {
        xAccessibleComponent->grabFocus();
        // grab only focus if it does not have the focus and it is not hidden
        if (mpViewShell &&
            (mpViewShell->GetViewData().GetActivePart() != meSplitPos) &&
            mpViewShell->GetWindowByPos(meSplitPos)->IsVisible())
        {
            mpViewShell->ActivatePart(meSplitPos);
        }
    }
}
 
    //=====  XAccessibleContext  ==============================================
 
    /// Return the number of currently visible children.
sal_Int64 SAL_CALL
    ScAccessibleDocument::getAccessibleChildCount()
{
    SolarMutexGuard aGuard;
    IsObjectValid();
    sal_Int64 nCount(1);
    if (mpChildrenShapes)
        nCount = mpChildrenShapes->GetCount(); // returns the count of the shapes inclusive the table
 
    if (mxTempAcc.is())
        ++nCount;
 
    return nCount;
}
 
    /// Return the specified child or NULL if index is invalid.
uno::Reference<XAccessible> SAL_CALL
    ScAccessibleDocument::getAccessibleChild(sal_Int64 nIndex)
{
    SolarMutexGuard aGuard;
    IsObjectValid();
    uno::Reference<XAccessible> xAccessible;
    if (nIndex >= 0)
    {
        sal_Int64 nCount(1);
        if (mpChildrenShapes)
        {
            xAccessible = mpChildrenShapes->Get(nIndex); // returns NULL if it is the table or out of range
            nCount = mpChildrenShapes->GetCount(); //there is always a table
        }
        if (!xAccessible.is())
        {
            if (nIndex < nCount)
                xAccessible = GetAccessibleSpreadsheet();
            else if (nIndex == nCount && mxTempAcc.is())
                xAccessible = mxTempAcc;
        }
    }
 
    if (!xAccessible.is())
        throw lang::IndexOutOfBoundsException();
 
    return xAccessible;
}
 
    /// Return the set of current states.
sal_Int64 SAL_CALL
    ScAccessibleDocument::getAccessibleStateSet()
{
    SolarMutexGuard aGuard;
    sal_Int64 nParentStates = 0;
    if (getAccessibleParent().is())
    {
        uno::Reference<XAccessibleContext> xParentContext = getAccessibleParent()->getAccessibleContext();
        nParentStates = xParentContext->getAccessibleStateSet();
    }
    sal_Int64 nStateSet = 0;
    if (IsDefunc(nParentStates))
        nStateSet |= AccessibleStateType::DEFUNC;
    else
    {
        nStateSet |= AccessibleStateType::EDITABLE;
        nStateSet |= AccessibleStateType::ENABLED;
        nStateSet |= AccessibleStateType::OPAQUE;
        if (isShowing())
            nStateSet |= AccessibleStateType::SHOWING;
        if (isVisible())
            nStateSet |= AccessibleStateType::VISIBLE;
    }
    return nStateSet;
}
 
OUString SAL_CALL
    ScAccessibleDocument::getAccessibleName()
{
    SolarMutexGuard g;
 
    OUString aName = ScResId(STR_ACC_DOC_SPREADSHEET);
    ScDocument* pScDoc = GetDocument();
    if (!pScDoc)
        return aName;
 
    ScDocShell* pObjSh = pScDoc->GetDocumentShell();
    if (!pObjSh)
        return aName;
 
    OUString aFileName;
    SfxMedium* pMed = pObjSh->GetMedium();
    if (pMed)
        aFileName = pMed->GetName();
 
    if (aFileName.isEmpty())
        aFileName = pObjSh->GetTitle(SFX_TITLE_APINAME);
 
    if (!aFileName.isEmpty())
    {
        OUString aReadOnly;
        if (pObjSh->IsReadOnly())
            aReadOnly = ScResId(STR_ACC_DOC_SPREADSHEET_READONLY);
 
        aName = aFileName + aReadOnly + " - " + aName;
    }
    return aName;
}
 
///=====  XAccessibleSelection  ===========================================
 
void SAL_CALL
    ScAccessibleDocument::selectAccessibleChild( sal_Int64 nChildIndex )
{
    SolarMutexGuard aGuard;
    IsObjectValid();
 
    if (!(mpChildrenShapes && mpViewShell))
        return;
 
    sal_Int32 nCount(mpChildrenShapes->GetCount()); // all shapes and the table
    if (mxTempAcc.is())
        ++nCount;
    if (nChildIndex < 0 || nChildIndex >= nCount)
        throw lang::IndexOutOfBoundsException();
 
    uno::Reference < XAccessible > xAccessible = mpChildrenShapes->Get(nChildIndex);
    if (xAccessible.is())
    {
        bool bWasTableSelected(IsTableSelected());
        mpChildrenShapes->Select(nChildIndex); // throws no lang::IndexOutOfBoundsException if Index is too high
        if (bWasTableSelected)
            mpViewShell->SelectAll();
    }
    else
    {
        mpViewShell->SelectAll();
    }
}
 
sal_Bool SAL_CALL
    ScAccessibleDocument::isAccessibleChildSelected( sal_Int64 nChildIndex )
{
    SolarMutexGuard aGuard;
    IsObjectValid();
    bool bResult(false);
 
    if (mpChildrenShapes)
    {
        sal_Int32 nCount(mpChildrenShapes->GetCount()); // all shapes and the table
        if (mxTempAcc.is())
            ++nCount;
        if (nChildIndex < 0 || nChildIndex >= nCount)
            throw lang::IndexOutOfBoundsException();
 
        uno::Reference < XAccessible > xAccessible = mpChildrenShapes->Get(nChildIndex);
        if (xAccessible.is())
        {
            uno::Reference<drawing::XShape> xShape;
            bResult = mpChildrenShapes->IsSelected(nChildIndex, xShape); // throws no lang::IndexOutOfBoundsException if Index is too high
        }
        else
        {
            if (mxTempAcc.is() && nChildIndex == nCount)
                bResult = true;
            else
                bResult = IsTableSelected();
        }
    }
    return bResult;
}
 
void SAL_CALL
    ScAccessibleDocument::clearAccessibleSelection(  )
{
    SolarMutexGuard aGuard;
    IsObjectValid();
 
    if (mpChildrenShapes)
        mpChildrenShapes->DeselectAll(); //deselects all (also the table)
}
 
void SAL_CALL
    ScAccessibleDocument::selectAllAccessibleChildren(  )
{
    SolarMutexGuard aGuard;
    IsObjectValid();
 
    if (mpChildrenShapes)
        mpChildrenShapes->SelectAll();
 
    // select table after shapes, because while selecting shapes the table will be deselected
    if (mpViewShell)
    {
        mpViewShell->SelectAll();
    }
}
 
sal_Int64 SAL_CALL
    ScAccessibleDocument::getSelectedAccessibleChildCount(  )
{
    SolarMutexGuard aGuard;
    IsObjectValid();
    sal_Int64 nCount(0);
 
    if (mpChildrenShapes)
        nCount = mpChildrenShapes->GetSelectedCount();
 
    if (IsTableSelected())
        ++nCount;
 
    if (mxTempAcc.is())
        ++nCount;
 
    return nCount;
}
 
uno::Reference<XAccessible > SAL_CALL
    ScAccessibleDocument::getSelectedAccessibleChild( sal_Int64 nSelectedChildIndex )
{
    SolarMutexGuard aGuard;
    IsObjectValid();
    uno::Reference<XAccessible> xAccessible;
    if (mpChildrenShapes)
    {
        sal_Int64 nCount(getSelectedAccessibleChildCount()); //all shapes and the table
        if (nSelectedChildIndex < 0 || nSelectedChildIndex >= nCount)
            throw lang::IndexOutOfBoundsException();
 
        bool bTabMarked(IsTableSelected());
 
        if (mpChildrenShapes)
            xAccessible = mpChildrenShapes->GetSelected(nSelectedChildIndex, bTabMarked); // throws no lang::IndexOutOfBoundsException if Index is too high
        if (mxTempAcc.is() && nSelectedChildIndex == nCount - 1)
            xAccessible = mxTempAcc;
        else if (bTabMarked)
            xAccessible = GetAccessibleSpreadsheet();
    }
 
    OSL_ENSURE(xAccessible.is(), "here should always be an accessible object or an exception thrown");
 
    return xAccessible;
}
 
void SAL_CALL
    ScAccessibleDocument::deselectAccessibleChild( sal_Int64 nChildIndex )
{
    SolarMutexGuard aGuard;
    IsObjectValid();
 
    if (!(mpChildrenShapes && mpViewShell))
        return;
 
    sal_Int32 nCount(mpChildrenShapes->GetCount()); // all shapes and the table
    if (mxTempAcc.is())
        ++nCount;
    if (nChildIndex < 0 || nChildIndex >= nCount)
        throw lang::IndexOutOfBoundsException();
 
    bool bTabMarked(IsTableSelected());
 
    uno::Reference < XAccessible > xAccessible = mpChildrenShapes->Get(nChildIndex);
    if (xAccessible.is())
    {
        mpChildrenShapes->Deselect(nChildIndex); // throws no lang::IndexOutOfBoundsException if Index is too high
        if (bTabMarked)
            mpViewShell->SelectAll(); // select the table again
    }
    else if (bTabMarked)
        mpViewShell->Unmark();
}
 
    //=====  XServiceInfo  ====================================================
 
OUString SAL_CALL
    ScAccessibleDocument::getImplementationName()
{
    return u"ScAccessibleDocument"_ustr;
}
 
uno::Sequence< OUString> SAL_CALL
    ScAccessibleDocument::getSupportedServiceNames()
{
    const css::uno::Sequence<OUString> vals { u"com.sun.star.AccessibleSpreadsheetDocumentView"_ustr };
    return comphelper::concatSequences(ScAccessibleContextBase::getSupportedServiceNames(), vals);
}
 
//=====  XTypeProvider  =======================================================
 
uno::Sequence< uno::Type > SAL_CALL ScAccessibleDocument::getTypes()
{
    return comphelper::concatSequences(ScAccessibleDocumentImpl::getTypes(), ScAccessibleContextBase::getTypes());
}
 
uno::Sequence<sal_Int8> SAL_CALL
    ScAccessibleDocument::getImplementationId()
{
    return css::uno::Sequence<sal_Int8>();
}
 
///=====  IAccessibleViewForwarder  ========================================
 
tools::Rectangle ScAccessibleDocument::GetVisibleArea_Impl() const
{
    tools::Rectangle aVisRect(GetBoundingBox());
 
    if (mpViewShell)
    {
        Point aPoint(mpViewShell->GetViewData().GetPixPos(meSplitPos)); // returns a negative Point
        aPoint.setX(-aPoint.getX());
        aPoint.setY(-aPoint.getY());
        aVisRect.SetPos(aPoint);
 
        ScGridWindow* pWin = static_cast<ScGridWindow*>(mpViewShell->GetWindowByPos(meSplitPos));
        if (pWin)
            aVisRect = pWin->PixelToLogic(aVisRect, pWin->GetDrawMapMode());
    }
 
    return aVisRect;
}
 
tools::Rectangle ScAccessibleDocument::GetVisibleArea() const
{
    SolarMutexGuard aGuard;
    IsObjectValid();
    return maVisArea;
}
 
Point ScAccessibleDocument::LogicToPixel (const Point& rPoint) const
{
    SolarMutexGuard aGuard;
    IsObjectValid();
    Point aPoint;
    ScGridWindow* pWin = static_cast<ScGridWindow*>(mpViewShell->GetWindowByPos(meSplitPos));
    if (pWin)
    {
        aPoint = pWin->LogicToPixel(rPoint, pWin->GetDrawMapMode());
        aPoint += Point(pWin->GetWindowExtentsAbsolute().TopLeft());
    }
    return aPoint;
}
 
Size ScAccessibleDocument::LogicToPixel (const Size& rSize) const
{
    SolarMutexGuard aGuard;
    IsObjectValid();
    Size aSize;
    ScGridWindow* pWin = static_cast<ScGridWindow*>(mpViewShell->GetWindowByPos(meSplitPos));
    if (pWin)
        aSize = pWin->LogicToPixel(rSize, pWin->GetDrawMapMode());
    return aSize;
}
 
    //=====  internal  ========================================================
 
rtl::Reference<utl::AccessibleRelationSetHelper> ScAccessibleDocument::GetRelationSet(const ScAddress* pAddress) const
{
    rtl::Reference<utl::AccessibleRelationSetHelper> pRelationSet;
    if (mpChildrenShapes)
        pRelationSet = mpChildrenShapes->GetRelationSet(pAddress);
    return pRelationSet;
}
 
OUString
    ScAccessibleDocument::createAccessibleDescription()
{
    return STR_ACC_DOC_DESCR;
}
 
OUString
    ScAccessibleDocument::createAccessibleName()
{
    SolarMutexGuard aGuard;
    IsObjectValid();
    OUString sName = ScResId(STR_ACC_DOC_NAME);
    sal_Int32 nNumber(sal_Int32(meSplitPos) + 1);
    sName += OUString::number(nNumber);
    return sName;
}
 
AbsoluteScreenPixelRectangle ScAccessibleDocument::GetBoundingBoxOnScreen() const
{
    AbsoluteScreenPixelRectangle aRect;
    if (mpViewShell)
    {
        vcl::Window* pWindow = mpViewShell->GetWindowByPos(meSplitPos);
        if (pWindow)
            aRect = pWindow->GetWindowExtentsAbsolute();
    }
    return aRect;
}
 
tools::Rectangle ScAccessibleDocument::GetBoundingBox() const
{
    tools::Rectangle aRect;
    if (mpViewShell)
    {
        vcl::Window* pWindow = mpViewShell->GetWindowByPos(meSplitPos);
        if (pWindow)
            aRect = pWindow->GetWindowExtentsRelative(*pWindow->GetAccessibleParentWindow());
    }
    return aRect;
}
 
SCTAB ScAccessibleDocument::getVisibleTable() const
{
    SCTAB nVisibleTable(0);
    if (mpViewShell)
        nVisibleTable = mpViewShell->GetViewData().GetTabNo();
    return nVisibleTable;
}
 
uno::Reference < XAccessible >
    ScAccessibleDocument::GetAccessibleSpreadsheet()
{
    if (!mpAccessibleSpreadsheet.is() && mpViewShell)
    {
        mpAccessibleSpreadsheet = new ScAccessibleSpreadsheet(this, mpViewShell, getVisibleTable(), meSplitPos);
        mpAccessibleSpreadsheet->Init();
        mbCompleteSheetSelected = IsTableSelected();
    }
    return mpAccessibleSpreadsheet;
}
 
void ScAccessibleDocument::FreeAccessibleSpreadsheet()
{
    if (mpAccessibleSpreadsheet.is())
    {
        mpAccessibleSpreadsheet->dispose();
        mpAccessibleSpreadsheet.clear();
    }
}
 
bool ScAccessibleDocument::IsTableSelected() const
{
    bool bResult (false);
    if(mpViewShell)
    {
        SCTAB nTab(getVisibleTable());
        //#103800#; use a copy of MarkData
        ScMarkData aMarkData(mpViewShell->GetViewData().GetMarkData());
        ScDocument* pDoc = GetDocument();
        if (aMarkData.IsAllMarked( ScRange( 0, 0, nTab, pDoc->MaxCol(), pDoc->MaxRow(), nTab)))
            bResult = true;
    }
    return bResult;
}
 
bool ScAccessibleDocument::IsDefunc(sal_Int64 nParentStates)
{
    return ScAccessibleContextBase::IsDefunc() || (mpViewShell == nullptr) || !getAccessibleParent().is() ||
        (nParentStates & AccessibleStateType::DEFUNC);
}
 
void ScAccessibleDocument::AddChild(const uno::Reference<XAccessible>& xAcc, bool bFireEvent)
{
    OSL_ENSURE(!mxTempAcc.is(), "this object should be removed before");
    if (xAcc.is())
    {
        mxTempAcc = xAcc;
        if( bFireEvent )
        {
            AccessibleEventObject aEvent;
            aEvent.Source = uno::Reference<XAccessibleContext>(this);
            aEvent.EventId = AccessibleEventId::CHILD;
            aEvent.NewValue <<= mxTempAcc;
            aEvent.IndexHint = getAccessibleChildCount() - 1;
            CommitChange( aEvent );
        }
    }
}
 
void ScAccessibleDocument::RemoveChild(const uno::Reference<XAccessible>& xAcc, bool bFireEvent)
{
    OSL_ENSURE(mxTempAcc.is(), "this object should be added before");
    if (!xAcc.is())
        return;
 
    OSL_ENSURE(xAcc.get() == mxTempAcc.get(), "only the same object should be removed");
    if( bFireEvent )
    {
        AccessibleEventObject aEvent;
        aEvent.Source = uno::Reference<XAccessibleContext>(this);
        aEvent.EventId = AccessibleEventId::CHILD;
        aEvent.OldValue <<= mxTempAcc;
        aEvent.IndexHint = -1;
        CommitChange( aEvent );
    }
    mxTempAcc = nullptr;
}
 
OUString ScAccessibleDocument::GetCurrentCellName() const
{
    OUString sName(ScResId(STR_ACC_CELL_NAME));
    if (mpViewShell)
    {
        // Document not needed, because only the cell address, but not the tablename is needed
        OUString sAddress(mpViewShell->GetViewData().GetCurPos().Format(ScRefFlags::VALID));
        sName = sName.replaceFirst("%1", sAddress);
    }
    return sName;
}
 
const OUString & ScAccessibleDocument::GetCurrentCellDescription()
{
    return EMPTY_OUSTRING;
}
 
ScDocument *ScAccessibleDocument::GetDocument() const
{
    return mpViewShell ? &mpViewShell->GetViewData().GetDocument() : nullptr;
}
 
ScAddress   ScAccessibleDocument::GetCurCellAddress() const
{
    return mpViewShell ? mpViewShell->GetViewData().GetCurPos() : ScAddress();
}
 
uno::Any SAL_CALL ScAccessibleDocument::getExtendedAttributes()
{
    SolarMutexGuard g;
 
    uno::Any anyAttribute;
 
    sal_uInt16 sheetIndex;
    OUString sSheetName;
    sheetIndex = getVisibleTable();
    if(GetDocument()==nullptr)
        return anyAttribute;
    GetDocument()->GetName(sheetIndex,sSheetName);
    OUString sValue = "page-name:" + sSheetName +
        ";page-number:" + OUString::number(sheetIndex+1) +
        ";total-pages:" + OUString::number(GetDocument()->GetTableCount()) + ";";
    anyAttribute <<= sValue;
    return anyAttribute;
}
 
sal_Int32 SAL_CALL ScAccessibleDocument::getForeground(  )
{
    return sal_Int32(COL_BLACK);
}
 
sal_Int32 SAL_CALL ScAccessibleDocument::getBackground(  )
{
    SolarMutexGuard aGuard;
    IsObjectValid();
    return sal_Int32(SC_MOD()->GetColorConfig().GetColorValue( ::svtools::DOCCOLOR ).nColor);
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V1053 Calling the 'GetBoundingBox' virtual function indirectly in the constructor may lead to unexpected result at runtime. Check lines: 'AccessibleDocument.cxx:1325', 'AccessibleDocument.cxx:1979', 'AccessibleDocument.hxx:223'.

V581 The conditional expressions of the 'if' statements situated alongside each other are identical. Check lines: 738, 741.

V728 An excessive check can be simplified. The '||' operator is surrounded by opposite expressions '!bResult' and 'bResult'.

V728 An excessive check can be simplified. The '||' operator is surrounded by opposite expressions '!bResult' and 'bResult'.