/* -*- 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 <shapeuno.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 <table.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 <unotools/accessiblestatesethelper.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 <svx/unoshape.hxx>
#include <unotools/accessiblerelationsethelper.hxx>
#include <toolkit/helper/convert.hxx>
#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 <list>
#include <algorithm>
 
#include <AccessibleCell.hxx>
 
#include <svx/unoapi.hxx>
#include <scmod.hxx>
 
#ifdef indices
#undef indices
#endif
 
#ifdef extents
#undef extents
#endif
 
using namespace ::com::sun::star;
using namespace ::com::sun::star::accessibility;
using ::std::for_each;
 
    //=====  internal  ========================================================
 
struct ScAccessibleShapeData
{
    ScAccessibleShapeData() : pRelationCell(nullptr), bSelected(false), bSelectable(true) {}
    ~ScAccessibleShapeData();
    mutable rtl::Reference< ::accessibility::AccessibleShape > pAccShape;
    mutable ScAddress*          pRelationCell; // if it is NULL this shape is anchored on the table
    css::uno::Reference< css::drawing::XShape > xShape;
    mutable bool            bSelected;
    bool                    bSelectable;
};
 
ScAccessibleShapeData::~ScAccessibleShapeData()
{
    if (pAccShape.is())
    {
        pAccShape->dispose();
    }
}
 
struct ScShapeDataLess
{
    OUString msLayerId;
    OUString msZOrder;
    ScShapeDataLess()
        : msLayerId( "LayerID" ),
        msZOrder( "ZOrder" )
    {
    }
    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 (sal_uInt8(SC_LAYER_FRONT) == rLayerID)
        {
            rLayerID = 1;
        }
        else if (sal_uInt8(SC_LAYER_BACK) == rLayerID)
        {
            rLayerID = 0;
        }
        else if (sal_uInt8(SC_LAYER_INTERN) == rLayerID)
        {
            rLayerID = 2;
        }
        else if (sal_uInt8(SC_LAYER_CONTROLS) == rLayerID)
        {
            rLayerID = 3;
        }
    }
    bool LessThanSheet(const ScAccessibleShapeData* pData) const
    {
        bool bResult(false);
        uno::Reference< beans::XPropertySet> xProps(pData->xShape, uno::UNO_QUERY);
        if (xProps.is())
        {
            uno::Any aPropAny = xProps->getPropertyValue(msLayerId);
            sal_Int16 nLayerID = 0;
            if( aPropAny >>= nLayerID )
            {
                if (SdrLayerID(nLayerID) == SC_LAYER_BACK)
                    bResult = true;
            }
        }
        return bResult;
    }
    bool operator()(const ScAccessibleShapeData* pData1, const ScAccessibleShapeData* pData2) const
    {
        bool bResult(false);
        if (pData1 && pData2)
        {
            uno::Reference< beans::XPropertySet> xProps1(pData1->xShape, uno::UNO_QUERY);
            uno::Reference< beans::XPropertySet> xProps2(pData2->xShape, uno::UNO_QUERY);
            if (xProps1.is() && xProps2.is())
            {
                uno::Any aPropAny1 = xProps1->getPropertyValue(msLayerId);
                uno::Any aPropAny2 = xProps2->getPropertyValue(msLayerId);
                sal_Int16 nLayerID1(0);
                sal_Int16 nLayerID2(0);
                if( (aPropAny1 >>= nLayerID1) && (aPropAny2 >>= nLayerID2) )
                {
                    if (nLayerID1 == nLayerID2)
                    {
                        uno::Any aAny1 = xProps1->getPropertyValue(msZOrder);
                        sal_Int32 nZOrder1 = 0;
                        uno::Any aAny2 = xProps2->getPropertyValue(msZOrder);
                        sal_Int32 nZOrder2 = 0;
                        if ( (aAny1 >>= nZOrder1) && (aAny2 >>= nZOrder2) )
                            bResult = (nZOrder1 < nZOrder2);
                    }
                    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;
    }
};
 
struct DeselectShape
{
    void operator() (const ScAccessibleShapeData* pAccShapeData) const
    {
        if (pAccShapeData)
        {
            pAccShapeData->bSelected = false;
            if (pAccShapeData->pAccShape.is())
                pAccShapeData->pAccShape->ResetState(AccessibleStateType::SELECTED);
        }
    }
};
 
struct SelectShape
{
    uno::Reference < drawing::XShapes > xShapes;
    explicit SelectShape(const uno::Reference<drawing::XShapes>& xTemp) : xShapes(xTemp) {}
    void operator() (const ScAccessibleShapeData* pAccShapeData) const
    {
        if (pAccShapeData && pAccShapeData->bSelectable)
        {
            pAccShapeData->bSelected = true;
            if (pAccShapeData->pAccShape.is())
                pAccShapeData->pAccShape->SetState(AccessibleStateType::SELECTED);
            if (xShapes.is())
                xShapes->add(pAccShapeData->xShape);
        }
    }
};
 
struct Destroy
{
    void operator() (ScAccessibleShapeData* pData)
    {
        if (pData)
            DELETEZ(pData);
    }
};
 
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 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;
 
    utl::AccessibleRelationSetHelper* GetRelationSet(const ScAddress* pAddress) const;
 
    void VisAreaChanged() const;
private:
    typedef std::vector<ScAccessibleShapeData*> SortedShapes;
 
    mutable SortedShapes maZOrderedShapes; // a null pointer represents the sheet in the correct order
 
    mutable ::accessibility::AccessibleShapeTreeInfo maShapeTreeInfo;
    mutable css::uno::Reference<css::view::XSelectionSupplier> xSelectionSupplier;
    mutable size_t mnSdrObjCount;
    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;
 
    ScAddress* GetAnchor(const uno::Reference<drawing::XShape>& xShape) const;
    uno::Reference<XAccessibleRelationSet> GetRelationSet(const ScAccessibleShapeData* pData) const;
    void CheckWhetherAnchorChanged(const uno::Reference<drawing::XShape>& xShape) 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)
    :
    mnShapesSelected(0),
    mpViewShell(pViewShell),
    mpAccessibleDocument(pAccessibleDocument),
    meSplitPos(eSplitPos)
{
    if (mpViewShell)
    {
        SfxViewFrame* pViewFrame = mpViewShell->GetViewFrame();
        if (pViewFrame)
        {
            xSelectionSupplier = uno::Reference<view::XSelectionSupplier>(pViewFrame->GetFrame().GetController(), uno::UNO_QUERY);
            if (xSelectionSupplier.is())
            {
                if (mpAccessibleDocument)
                    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();
 
        uno::Reference<drawing::XShapes> xShapes(mpViewShell->getSelectedXShapes());
        if (xShapes.is())
            FindSelectedShapesChanges(xShapes);
    }
    if (pViewShell)
    {
        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()
{
    std::for_each(maZOrderedShapes.begin(), maZOrderedShapes.end(), Destroy());
    if (mpViewShell)
    {
        SfxBroadcaster* pDrawBC = mpViewShell->GetViewData().GetDocument()->GetDrawBroadcaster();
        if (pDrawBC)
            EndListening(*pDrawBC);
    }
    if (mpAccessibleDocument && xSelectionSupplier.is())
        xSelectionSupplier->removeSelectionChangeListener(mpAccessibleDocument);
}
 
void ScChildrenShapes::SetDrawBroadcaster()
{
    if (mpViewShell)
    {
        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)
{
    const SdrHint* pSdrHint = dynamic_cast<const SdrHint*>(&rHint);
    if (pSdrHint)
    {
        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
        {
            switch (pSdrHint->GetKind())
            {
                case SdrHintKind::ObjectChange :         // object changed
                {
                    uno::Reference<drawing::XShape> xShape (pObj->getUnoShape(), uno::UNO_QUERY);
                    if (xShape.is())
                    {
                        ScShapeDataLess aLess;
                        std::sort(maZOrderedShapes.begin(), maZOrderedShapes.end(), aLess); // sort, because the z index or layer could be changed
                        CheckWhetherAnchorChanged(xShape);
                    }
                }
                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 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
    ));
    if ( pReplacement.is() )
        pReplacement->Init();
 
    bool bResult(false);
    if (pReplacement.is())
    {
        OSL_ENSURE(pCurrentChild->GetXShape().get() == pReplacement->GetXShape().get(), "XShape changes and should be inserted sorted");
        SortedShapes::iterator aItr;
 
        if (FindShape(pCurrentChild->GetXShape(), aItr) || (aItr != maZOrderedShapes.end() && (*aItr)))
        {
            if ((*aItr)->pAccShape.is())
            {
                OSL_ENSURE((*aItr)->pAccShape == pCurrentChild, "wrong child found");
                AccessibleEventObject aEvent;
                aEvent.EventId = AccessibleEventId::CHILD;
                aEvent.Source = uno::Reference< XAccessibleContext >(mpAccessibleDocument);
                aEvent.OldValue <<= uno::Reference<XAccessible>(pCurrentChild);
 
                mpAccessibleDocument->CommitChange(aEvent); // child is gone - event
 
                pCurrentChild->dispose();
            }
            (*aItr)->pAccShape = pReplacement;
            AccessibleEventObject aEvent;
            aEvent.EventId = AccessibleEventId::CHILD;
            aEvent.Source = uno::Reference< XAccessibleContext >(mpAccessibleDocument);
            aEvent.NewValue <<= uno::Reference<XAccessible>(pReplacement.get());
 
            mpAccessibleDocument->CommitChange(aEvent); // child is new - event
            bResult = true;
        }
    }
    return bResult;
}
 
::accessibility::AccessibleControlShape * ScChildrenShapes::GetAccControlShapeFromModel(css::beans::XPropertySet* pSet)
{
    sal_Int32 count = GetCount();
    for (sal_Int32 index=0;index<count;index++)
    {
        ScAccessibleShapeData* pShape = maZOrderedShapes[index];
                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)
{
    sal_Int32 count = GetCount();
    for (sal_Int32 index=0;index<count;index++)
    {
        ScAccessibleShapeData* pShape = maZOrderedShapes[index];
            if (pShape && pShape->xShape == xShape )
            {
                css::uno::Reference< css::accessibility::XAccessible > xNewChild(  pShape->pAccShape.get() );
                if(xNewChild.get())
                return xNewChild;
            }
    }
    return nullptr;
}
 
sal_Int32 ScChildrenShapes::GetCount() const
{
    SdrPage* pDrawPage = GetDrawPage();
    if (pDrawPage && (maZOrderedShapes.size() == 1)) // the table is always in
    {
        mnSdrObjCount = pDrawPage->GetObjCount();
        maZOrderedShapes.reserve(mnSdrObjCount + 1); // the table is always in
        for (size_t i = 0; i < mnSdrObjCount; ++i)
        {
            SdrObject* pObj = pDrawPage->GetObj(i);
            if (pObj/* && (pObj->GetLayer() != SC_LAYER_INTERN)*/)
            {
                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.get();
 }
 
uno::Reference< XAccessible > ScChildrenShapes::Get(sal_Int32 nIndex) const
{
    if (maZOrderedShapes.size() <= 1)
        GetCount(); // fill list with filtered shapes (no internal shapes)
 
    if (static_cast<sal_uInt32>(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)
    {
        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(VCLPoint(rPoint));
                    aPoint -= VCLRectangle(pShape->pAccShape->getBounds()).TopLeft();
                    if (pShape->pAccShape->containsPoint(AWTPoint(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();
 
    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());
    uno::Reference<container::XIndexAccess> xIndexAccess(xShapes, uno::UNO_QUERY);
 
    if (xIndexAccess.is())
    {
        sal_Int32 nCount(xIndexAccess->getCount());
        if (nCount)
        {
            uno::Reference< drawing::XShape > xShape;
            uno::Reference< drawing::XShape > xIndexShape = maZOrderedShapes[nIndex]->xShape;
            sal_Int32 i(0);
            while (!bDebugResult && (i < nCount))
            {
                xIndexAccess->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();
 
    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();
 
    if (!maZOrderedShapes[nIndex])
        return;
 
    uno::Reference<drawing::XShape> xShape;
    if (!IsSelected(nIndex, xShape) && maZOrderedShapes[nIndex]->bSelectable)
    {
        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::makeAny(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();
 
    bool bSomethingSelected(true);
    try
    {
        xSelectionSupplier->select(uno::Any()); //deselects all
    }
    catch (lang::IllegalArgumentException&)
    {
        OSL_FAIL("nothing selected before");
        bSomethingSelected = false;
    }
 
    if (bSomethingSelected)
        std::for_each(maZOrderedShapes.begin(), maZOrderedShapes.end(), DeselectShape());
}
 
void ScChildrenShapes::SelectAll()
{
    if (!xSelectionSupplier.is())
        throw uno::RuntimeException();
 
    if (maZOrderedShapes.size() <= 1)
        GetCount(); // fill list with filtered shapes (no internal shapes)
 
    if (maZOrderedShapes.size() > 1)
    {
        uno::Reference<drawing::XShapes> xShapes = drawing::ShapeCollection::create(
                comphelper::getProcessComponentContext());
 
        try
        {
            std::for_each(maZOrderedShapes.begin(), maZOrderedShapes.end(), SelectShape(xShapes));
            xSelectionSupplier->select(uno::makeAny(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());
    uno::Reference<container::XIndexAccess> xIndexAccess(xShapes, uno::UNO_QUERY);
    if (xIndexAccess.is())
    {
        sal_uInt32 nCount(xIndexAccess->getCount());
        for (sal_uInt32 i = 0; i < nCount; ++i)
        {
            uno::Reference<drawing::XShape> xShape;
            xIndexAccess->getByIndex(i) >>= xShape;
            if (xShape.is())
                rShapes.push_back(xShape);
        }
    }
}
 
sal_Int32 ScChildrenShapes::GetSelectedCount() const
{
    if (!xSelectionSupplier.is())
        throw uno::RuntimeException();
 
    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 || static_cast<size_t>(nSelectedChildIndex) >= aShapes.size())
            return xAccessible;
 
        SortedShapes::iterator aItr;
        if (FindShape(aShapes[nSelectedChildIndex], aItr))
            xAccessible = Get(aItr - maZOrderedShapes.begin());
    }
    else
    {
        SortedShapes::iterator aItr = maZOrderedShapes.begin();
        SortedShapes::iterator aEndItr = maZOrderedShapes.end();
        bool bFound(false);
        while(!bFound && aItr != aEndItr)
        {
            if (*aItr)
            {
                if ((*aItr)->bSelected)
                {
                    if (nSelectedChildIndex == 0)
                        bFound = true;
                    else
                        --nSelectedChildIndex;
                }
            }
            else
            {
                if (nSelectedChildIndex == 0)
                    bFound = true;
                else
                    --nSelectedChildIndex;
            }
            if (!bFound)
                ++aItr;
        }
        if (bFound && *aItr)
            xAccessible = (*aItr)->pAccShape.get();
    }
 
    return xAccessible;
}
 
void ScChildrenShapes::Deselect(sal_Int32 nChildIndex)
{
    uno::Reference<drawing::XShape> xShape;
    if (IsSelected(nChildIndex, xShape)) // returns false if it is the sheet
    {
        if (xShape.is())
        {
            uno::Reference<drawing::XShapes> xShapes(mpViewShell->getSelectedXShapes());
            if (xShapes.is())
                xShapes->remove(xShape);
 
            try
            {
                xSelectionSupplier->select(uno::makeAny(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)
    {
        ScViewData& rViewData = mpViewShell->GetViewData();
        ScDocument* pDoc = rViewData.GetDocument();
        if (pDoc && pDoc->GetDrawLayer())
        {
            ScDrawLayer* pDrawLayer = pDoc->GetDrawLayer();
            if (pDrawLayer->HasObjects() && (pDrawLayer->GetPageCount() > nTab))
                pDrawPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(static_cast<sal_Int16>(nTab)));
        }
    }
    return pDrawPage;
}
 
struct SetRelation
{
    const ScChildrenShapes* mpChildrenShapes;
    mutable utl::AccessibleRelationSetHelper* mpRelationSet;
    const ScAddress* mpAddress;
    SetRelation(const ScChildrenShapes* pChildrenShapes, const ScAddress* pAddress)
        :
        mpChildrenShapes(pChildrenShapes),
        mpRelationSet(nullptr),
        mpAddress(pAddress)
    {
    }
    void operator() (const ScAccessibleShapeData* pAccShapeData) const
    {
        if (pAccShapeData &&
            ((!pAccShapeData->pRelationCell && !mpAddress) ||
            (pAccShapeData->pRelationCell && mpAddress && (*(pAccShapeData->pRelationCell) == *mpAddress))))
        {
            if (!mpRelationSet)
                mpRelationSet = new utl::AccessibleRelationSetHelper();
 
            AccessibleRelation aRelation;
            aRelation.TargetSet.realloc(1);
            aRelation.TargetSet[0] = mpChildrenShapes->Get(pAccShapeData);
            aRelation.RelationType = AccessibleRelationType::CONTROLLER_FOR;
 
            mpRelationSet->AddRelation(aRelation);
        }
    }
};
 
utl::AccessibleRelationSetHelper* ScChildrenShapes::GetRelationSet(const ScAddress* pAddress) const
{
    SetRelation aSetRelation(this, pAddress);
    ::std::for_each(maZOrderedShapes.begin(), maZOrderedShapes.end(), aSetRelation);
    return aSetRelation.mpRelationSet;
}
 
bool ScChildrenShapes::FindSelectedShapesChanges(const uno::Reference<drawing::XShapes>& xShapes) const
{
    bool bResult(false);
    SortedShapes aShapesList;
    uno::Reference<container::XIndexAccess> xIndexAcc(xShapes, uno::UNO_QUERY);
    if (xIndexAcc.is())
    {
        mnShapesSelected = xIndexAcc->getCount();
        for (sal_uInt32 i = 0; i < mnShapesSelected; ++i)
        {
            uno::Reference< drawing::XShape > xShape;
            xIndexAcc->getByIndex(i) >>= xShape;
            if (xShape.is())
            {
                ScAccessibleShapeData* pShapeData = new ScAccessibleShapeData();
                pShapeData->xShape = xShape;
                aShapesList.push_back(pShapeData);
            }
        }
    }
    else
        mnShapesSelected = 0;
    SdrObject *pFocusedObj = nullptr;
    if( mnShapesSelected == 1 && aShapesList.size() == 1)
    {
        pFocusedObj = GetSdrObjectFromXShape(aShapesList[0]->xShape);
    }
    ScShapeDataLess aLess;
    std::sort(aShapesList.begin(), aShapesList.end(), aLess);
    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;
    SdrObject* pUpObj = nullptr;
    bool bIsFocuseMarked = true;
    if( mpViewShell && mnShapesSelected == 1 && bWinFocus)
    {
        ScDrawView* pScDrawView = mpViewShell->GetViewData().GetScDrawView();
        if( pScDrawView )
        {
            if( pScDrawView->GetMarkedObjectList().GetMarkCount() == 1 )
            {
                pMarkList = &(pScDrawView->GetMarkedObjectList());
                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);
            pUpObj = pMarkedObj->getParentSdrObjectFromSdrObject();
 
            if( pMarkedObj == pFocusedObj )
            {
                if( 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_Int32 nCount =  pAccGroupShape->getAccessibleChildCount();
                            for( sal_Int32 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
    {
        SortedShapes::iterator vi = vecSelectedShapeAdd.begin();
        for (; vi != vecSelectedShapeAdd.end() ; ++vi )
        {
            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( (*vi)->pAccShape.get());
            aEvent.NewValue <<= xChild;
            mpAccessibleDocument->CommitChange(aEvent);
        }
    }
    SortedShapes::iterator vi = vecSelectedShapeRemove.begin();
    for (; vi != vecSelectedShapeRemove.end() ; ++vi )
    {
        AccessibleEventObject aEvent;
        aEvent.EventId =  AccessibleEventId::SELECTION_CHANGED_REMOVE;
        aEvent.Source = uno::Reference< XAccessible >(mpAccessibleDocument);
        uno::Reference< XAccessible > xChild( (*vi)->pAccShape.get());
        aEvent.NewValue <<= xChild;
        mpAccessibleDocument->CommitChange(aEvent);
    }
    std::for_each(aShapesList.begin(), aShapesList.end(), Destroy());
 
    return bResult;
}
 
ScAddress* ScChildrenShapes::GetAnchor(const uno::Reference<drawing::XShape>& xShape) const
{
    ScAddress* pAddress = nullptr;
    if (mpViewShell)
    {
        SvxShape* pShapeImp = SvxShape::getImplementation(xShape);
        uno::Reference<beans::XPropertySet> xShapeProp(xShape, uno::UNO_QUERY);
        if (pShapeImp && xShapeProp.is())
        {
            if (SdrObject *pSdrObj = pShapeImp->GetSdrObject())
            {
                if (ScDrawObjData *pAnchor = ScDrawLayer::GetObjData(pSdrObj))
                    return new ScAddress(pAnchor->maStart);
            }
        }
    }
 
    return pAddress;
}
 
uno::Reference<XAccessibleRelationSet> ScChildrenShapes::GetRelationSet(const ScAccessibleShapeData* pData) const
{
    utl::AccessibleRelationSetHelper* pRelationSet = new utl::AccessibleRelationSetHelper();
 
    if (pData && mpAccessibleDocument)
    {
        uno::Reference<XAccessible> xAccessible = mpAccessibleDocument->GetAccessibleSpreadsheet(); // should be the current table
        if (pData->pRelationCell && xAccessible.is())
        {
            uno::Reference<XAccessibleTable> xAccTable (xAccessible->getAccessibleContext(), uno::UNO_QUERY);
            if (xAccTable.is())
                xAccessible = xAccTable->getAccessibleCellAt(pData->pRelationCell->Row(), pData->pRelationCell->Col());
        }
        AccessibleRelation aRelation;
        aRelation.TargetSet.realloc(1);
        aRelation.TargetSet[0] = xAccessible;
        aRelation.RelationType = AccessibleRelationType::CONTROLLED_BY;
        pRelationSet->AddRelation(aRelation);
    }
 
    return pRelationSet;
}
 
void ScChildrenShapes::CheckWhetherAnchorChanged(const uno::Reference<drawing::XShape>& xShape) const
{
    SortedShapes::iterator aItr;
    if (FindShape(xShape, aItr))
        SetAnchor(xShape, *aItr);
}
 
void ScChildrenShapes::SetAnchor(const uno::Reference<drawing::XShape>& xShape, ScAccessibleShapeData* pData) const
{
    if (pData)
    {
        ScAddress* pAddress = GetAnchor(xShape);
        if ((pAddress && pData->pRelationCell && (*pAddress != *(pData->pRelationCell))) ||
            (!pAddress && pData->pRelationCell) || (pAddress && !pData->pRelationCell))
        {
            delete pData->pRelationCell;
            pData->pRelationCell = pAddress;
            if (pData->pAccShape.is())
                pData->pAccShape->SetRelationSet(GetRelationSet(pData));
        }
        else
            delete pAddress;
    }
}
 
void ScChildrenShapes::AddShape(const uno::Reference<drawing::XShape>& xShape, bool bCommitChange) const
{
    SortedShapes::iterator aFindItr;
    if (!FindShape(xShape, aFindItr))
    {
        ScAccessibleShapeData* pShape = new ScAccessibleShapeData();
        pShape->xShape = xShape;
        SortedShapes::iterator aNewItr = maZOrderedShapes.insert(aFindItr, pShape);
        SetAnchor(xShape, pShape);
 
        uno::Reference< beans::XPropertySet > xShapeProp(xShape, uno::UNO_QUERY);
        if (xShapeProp.is())
        {
            uno::Any aPropAny = xShapeProp->getPropertyValue("LayerID");
            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();
 
        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(aNewItr - maZOrderedShapes.begin());
 
            mpAccessibleDocument->CommitChange(aEvent); // new child - event
        }
    }
    else
    {
        OSL_FAIL("shape is always in the list");
    }
}
 
void ScChildrenShapes::RemoveShape(const uno::Reference<drawing::XShape>& xShape) const
{
    SortedShapes::iterator aItr;
    if (FindShape(xShape, aItr))
    {
        if (mpAccessibleDocument)
        {
            uno::Reference<XAccessible> xOldAccessible (Get(aItr - maZOrderedShapes.begin()));
 
            delete *aItr;
            maZOrderedShapes.erase(aItr);
 
            AccessibleEventObject aEvent;
            aEvent.EventId = AccessibleEventId::CHILD;
            aEvent.Source = uno::Reference< XAccessibleContext >(mpAccessibleDocument);
            aEvent.OldValue <<= xOldAccessible;
 
            mpAccessibleDocument->CommitChange(aEvent); // child is gone - event
        }
        else
        {
            delete *aItr;
            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
{
    bool bResult(false);
    ScAccessibleShapeData aShape;
    aShape.xShape = xShape;
    ScShapeDataLess aLess;
    rItr = std::lower_bound(maZOrderedShapes.begin(), maZOrderedShapes.end(), &aShape, aLess);
    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 = maZOrderedShapes.begin();
    SortedShapes::iterator aEndItr = maZOrderedShapes.end();
    bool bFound(false);
    while (!bFound && aDebugItr != aEndItr)
    {
        if (*aDebugItr && ((*aDebugItr)->xShape.get() == xShape.get()))
            bFound = true;
        else
            ++aDebugItr;
    }
    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;
}
 
namespace
{
    struct ScVisAreaChanged
    {
        explicit ScVisAreaChanged() {}
        void operator() (const ScAccessibleShapeData* pAccShapeData) const
        {
            if (pAccShapeData && pAccShapeData->pAccShape.is())
            {
                pAccShapeData->pAccShape->ViewForwarderChanged();
            }
        }
    };
}
 
void ScChildrenShapes::VisAreaChanged() const
{
    ScVisAreaChanged aVisAreaChanged;
    std::for_each(maZOrderedShapes.begin(), maZOrderedShapes.end(), aVisAreaChanged);
}
 
ScAccessibleDocument::ScAccessibleDocument(
        const uno::Reference<XAccessible>& rxParent,
        ScTabViewShell* pViewShell,
        ScSplitPos eSplitPos)
    : ScAccessibleDocumentBase(rxParent),
    mpViewShell(pViewShell),
    meSplitPos(eSplitPos),
    mpTempAccEdit(nullptr),
    mbCompleteSheetSelected(false)
{
    maVisArea = GetVisibleArea_Impl();
}
 
void ScAccessibleDocument::PreInit()
{
    if (mpViewShell)
    {
        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 (dynamic_cast<const ScAccGridWinFocusLostHint*>(&rHint) )
    {
        const ScAccGridWinFocusLostHint* 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 (dynamic_cast<const ScAccGridWinFocusGotHint*>(&rHint) )
    {
        const ScAccGridWinFocusGotHint* 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() );
            }
            else
            {
            if (mxTempAcc.is() && mpTempAccEdit)
                mpTempAccEdit->GotFocus();
            else if (mpAccessibleSpreadsheet.is())
                mpAccessibleSpreadsheet->GotFocus();
            else
                CommitFocusGained();
            }
        }
    }
    else
    {
        // only notify if child exist, otherwise it is not necessary
        if ((rHint.GetId() == SfxHintId::ScAccTableChanged) &&
            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->FireFirstCellFocus();
        }
        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();
                const EditEngine* pEditEng = rViewData.GetEditView(meSplitPos)->GetEditEngine();
                if (pEditEng && pEditEng->GetUpdateMode())
                {
                    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();
 
                mpTempAccEdit = nullptr;
                RemoveChild(mxTempAcc, true);
                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 aAnyTmp;
    if(rType == cppu::UnoType<XAccessibleGetAccFlowTo>::get())
    {
         css::uno::Reference<XAccessibleGetAccFlowTo> AccFromXShape = this;
         aAnyTmp <<= AccFromXShape;
         return aAnyTmp;
    }
    uno::Any aAny (ScAccessibleDocumentImpl::queryInterface(rType));
    return aAny.hasValue() ? aAny : ScAccessibleContextBase::queryInterface(rType);
}
 
void SAL_CALL ScAccessibleDocument::acquire()
    throw ()
{
    ScAccessibleContextBase::acquire();
}
 
void SAL_CALL ScAccessibleDocument::release()
    throw ()
{
    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(VCLRectangle(xComp->getBounds()));
                    if (aBound.IsInside(VCLPoint(rPoint)))
                        xAccessible = mxTempAcc;
                }
            }
            if (!xAccessible.is())
                xAccessible = GetAccessibleSpreadsheet();
        }
    }
    return xAccessible;
}
 
void SAL_CALL ScAccessibleDocument::grabFocus(  )
{
    SolarMutexGuard aGuard;
    IsObjectValid();
    if (getAccessibleParent().is())
    {
        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_Int32 SAL_CALL
    ScAccessibleDocument::getAccessibleChildCount()
{
    SolarMutexGuard aGuard;
    IsObjectValid();
    sal_Int32 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_Int32 nIndex)
{
    SolarMutexGuard aGuard;
    IsObjectValid();
    uno::Reference<XAccessible> xAccessible;
    if (nIndex >= 0)
    {
        sal_Int32 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.
uno::Reference<XAccessibleStateSet> SAL_CALL
    ScAccessibleDocument::getAccessibleStateSet()
{
    SolarMutexGuard aGuard;
    uno::Reference<XAccessibleStateSet> xParentStates;
    if (getAccessibleParent().is())
    {
        uno::Reference<XAccessibleContext> xParentContext = getAccessibleParent()->getAccessibleContext();
        xParentStates = xParentContext->getAccessibleStateSet();
    }
    utl::AccessibleStateSetHelper* pStateSet = new utl::AccessibleStateSetHelper();
    if (IsDefunc(xParentStates))
        pStateSet->AddState(AccessibleStateType::DEFUNC);
    else
    {
        pStateSet->AddState(AccessibleStateType::EDITABLE);
        pStateSet->AddState(AccessibleStateType::ENABLED);
        pStateSet->AddState(AccessibleStateType::OPAQUE);
        if (isShowing())
            pStateSet->AddState(AccessibleStateType::SHOWING);
        if (isVisible())
            pStateSet->AddState(AccessibleStateType::VISIBLE);
    }
    return pStateSet;
}
 
OUString SAL_CALL
    ScAccessibleDocument::getAccessibleName()
{
    SolarMutexGuard g;
 
    OUString aName = ScResId(STR_ACC_DOC_SPREADSHEET);
    ScDocument* pScDoc = GetDocument();
    if (!pScDoc)
        return aName;
 
    SfxObjectShell* 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_Int32 nChildIndex )
{
    SolarMutexGuard aGuard;
    IsObjectValid();
 
    if (mpChildrenShapes && mpViewShell)
    {
        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());
 
            if (mpChildrenShapes)
                mpChildrenShapes->Select(nChildIndex); // throws no lang::IndexOutOfBoundsException if Index is too high
 
            if (bWasTableSelected)
                mpViewShell->SelectAll();
        }
        else
        {
            if (mpViewShell)
                mpViewShell->SelectAll();
        }
    }
}
 
sal_Bool SAL_CALL
    ScAccessibleDocument::isAccessibleChildSelected( sal_Int32 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_Int32 SAL_CALL
    ScAccessibleDocument::getSelectedAccessibleChildCount(  )
{
    SolarMutexGuard aGuard;
    IsObjectValid();
    sal_Int32 nCount(0);
 
    if (mpChildrenShapes)
        nCount = mpChildrenShapes->GetSelectedCount();
 
    if (IsTableSelected())
        ++nCount;
 
    if (mxTempAcc.is())
        ++nCount;
 
    return nCount;
}
 
uno::Reference<XAccessible > SAL_CALL
    ScAccessibleDocument::getSelectedAccessibleChild( sal_Int32 nSelectedChildIndex )
{
    SolarMutexGuard aGuard;
    IsObjectValid();
    uno::Reference<XAccessible> xAccessible;
    if (mpChildrenShapes)
    {
        sal_Int32 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_Int32 nChildIndex )
{
    SolarMutexGuard aGuard;
    IsObjectValid();
 
    if (mpChildrenShapes && mpViewShell)
    {
        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())
        {
            if (mpChildrenShapes)
                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 OUString("ScAccessibleDocument");
}
 
uno::Sequence< OUString> SAL_CALL
    ScAccessibleDocument::getSupportedServiceNames()
{
    uno::Sequence< OUString > aSequence = ScAccessibleContextBase::getSupportedServiceNames();
    sal_Int32 nOldSize(aSequence.getLength());
    aSequence.realloc(nOldSize + 1);
 
    aSequence[nOldSize] = "com.sun.star.AccessibleSpreadsheetDocumentView";
 
    return aSequence;
}
 
//=====  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 += pWin->GetWindowExtentsRelative(nullptr).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  ========================================================
 
utl::AccessibleRelationSetHelper* ScAccessibleDocument::GetRelationSet(const ScAddress* pAddress) const
{
    utl::AccessibleRelationSetHelper* pRelationSet = nullptr;
    if (mpChildrenShapes)
        pRelationSet = mpChildrenShapes->GetRelationSet(pAddress);
    return pRelationSet;
}
 
OUString
    ScAccessibleDocument::createAccessibleDescription()
{
    OUString sDescription = STR_ACC_DOC_DESCR;
    return sDescription;
}
 
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;
}
 
tools::Rectangle ScAccessibleDocument::GetBoundingBoxOnScreen() const
{
    tools::Rectangle aRect;
    if (mpViewShell)
    {
        vcl::Window* pWindow = mpViewShell->GetWindowByPos(meSplitPos);
        if (pWindow)
            aRect = pWindow->GetWindowExtentsRelative(nullptr);
    }
    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.get();
}
 
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());
        aMarkData.MarkToMulti();
        if (aMarkData.IsAllMarked( ScRange( 0, 0, nTab, MAXCOL, MAXROW, nTab)))
            bResult = true;
    }
    return bResult;
}
 
bool ScAccessibleDocument::IsDefunc(
    const uno::Reference<XAccessibleStateSet>& rxParentStates)
{
    return ScAccessibleContextBase::IsDefunc() || (mpViewShell == nullptr) || !getAccessibleParent().is() ||
        (rxParentStates.is() && rxParentStates->contains(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;
            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())
    {
        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;
            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;
}
 
OUString ScAccessibleDocument::GetCurrentCellDescription()
{
    return 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 anyAtrribute;
 
    OUString sName;
    OUString sValue;
    sal_uInt16 sheetIndex;
    OUString sSheetName;
    sheetIndex = getVisibleTable();
    if(GetDocument()==nullptr)
        return anyAtrribute;
    GetDocument()->GetName(sheetIndex,sSheetName);
    sName = "page-name:";
    sValue = sName + sSheetName ;
    sName = ";page-number:";
    sValue += sName;
    sValue += OUString::number(sheetIndex+1) ;
    sName = ";total-pages:";
    sValue += sName;
    sValue += OUString::number(GetDocument()->GetTableCount());
    sValue += ";";
    anyAtrribute <<= sValue;
    return anyAtrribute;
}
 
css::uno::Sequence< css::uno::Any > ScAccessibleDocument::GetScAccFlowToSequence()
{
    if ( getAccessibleChildCount() )
    {
        uno::Reference < XAccessible > xSCTableAcc = getAccessibleChild( 0 ); // table
        if ( xSCTableAcc.is() )
        {
            uno::Reference < XAccessibleSelection > xAccSelection( xSCTableAcc, uno::UNO_QUERY );
            sal_Int32 nSelCount = xAccSelection->getSelectedAccessibleChildCount();
            if( nSelCount )
            {
                uno::Reference < XAccessible > xSel = xAccSelection->getSelectedAccessibleChild( 0 ); // selected cell
                if ( xSel.is() )
                {
                    uno::Reference < XAccessibleContext > xSelContext( xSel->getAccessibleContext() );
                    if ( xSelContext.is() )
                    {
                        if ( xSelContext->getAccessibleRole() == AccessibleRole::TABLE_CELL )
                        {
                            sal_Int32 nParaCount = 0;
                            uno::Sequence <uno::Any> aSequence(nSelCount);
                            for ( sal_Int32 i = 0; i < nSelCount; i++ )
                            {
                                xSel = xAccSelection->getSelectedAccessibleChild( i )   ;
                                if ( xSel.is() )
                                {
                                    xSelContext = xSel->getAccessibleContext();
                                    if ( xSelContext.is() )
                                    {
                                        if ( xSelContext->getAccessibleRole() == AccessibleRole::TABLE_CELL )
                                        {
                                            aSequence[nParaCount] <<= xSel;
                                            nParaCount++;
                                        }
                                    }
                                }
                            }
                            return aSequence;
                        }
                    }
                }
            }
        }
    }
    uno::Sequence <uno::Any> aEmpty;
    return aEmpty;
}
 
css::uno::Sequence< css::uno::Any >
        SAL_CALL ScAccessibleDocument::getAccFlowTo(const css::uno::Any& rAny, sal_Int32 nType)
{
    SolarMutexGuard g;
 
    const sal_Int32 SPELLCHECKFLOWTO = 1;
    const sal_Int32 FINDREPLACEFLOWTO = 2;
    if ( nType == SPELLCHECKFLOWTO )
    {
        uno::Reference< css::drawing::XShape > xShape;
        rAny >>= xShape;
        if ( xShape.is() )
        {
            uno::Reference < XAccessible > xAcc = mpChildrenShapes->GetAccessibleCaption(xShape);
            uno::Reference < XAccessibleSelection > xAccSelection( xAcc, uno::UNO_QUERY );
            if ( xAccSelection.is() )
            {
                if ( xAccSelection->getSelectedAccessibleChildCount() )
                {
                    uno::Reference < XAccessible > xSel = xAccSelection->getSelectedAccessibleChild( 0 );
                    if ( xSel.is() )
                    {
                        uno::Reference < XAccessibleContext > xSelContext( xSel->getAccessibleContext() );
                        if ( xSelContext.is() )
                        {
                            //if in sw we find the selected paragraph here
                            if ( xSelContext->getAccessibleRole() == AccessibleRole::PARAGRAPH )
                            {
                                uno::Sequence<uno::Any> aRet( 1 );
                                aRet[0] <<= xSel;
                                return aRet;
                            }
                        }
                    }
                }
            }
        }
        else
        {
            if ( getSelectedAccessibleChildCount() )
            {
                uno::Reference < XAccessible > xSel = getSelectedAccessibleChild( 0 );
                if ( xSel.is() )
                {
                    uno::Reference < XAccessibleContext > xSelContext( xSel->getAccessibleContext() );
                    if ( xSelContext.is() )
                    {
                        uno::Reference < XAccessibleSelection > xAccChildSelection( xSel, uno::UNO_QUERY );
                        if ( xAccChildSelection.is() )
                        {
                            if ( xAccChildSelection->getSelectedAccessibleChildCount() )
                            {
                                uno::Reference < XAccessible > xChildSel = xAccChildSelection->getSelectedAccessibleChild( 0 );
                                if ( xChildSel.is() )
                                {
                                    uno::Reference < css::accessibility::XAccessibleContext > xChildSelContext( xChildSel->getAccessibleContext() );
                                    if ( xChildSelContext.is() &&
                                        xChildSelContext->getAccessibleRole() == css::accessibility::AccessibleRole::PARAGRAPH )
                                    {
                                        uno::Sequence<uno::Any> aRet( 1 );
                                        aRet[0] <<= xChildSel;
                                        return aRet;
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    else if ( nType == FINDREPLACEFLOWTO )
    {
        bool bSuccess(false);
        rAny >>= bSuccess;
        if ( bSuccess )
        {
            uno::Sequence< uno::Any> aSeq = GetScAccFlowToSequence();
            if ( aSeq.getLength() )
            {
                return aSeq;
            }
            else if( mpAccessibleSpreadsheet.is() )
            {
                uno::Reference < XAccessible > xFindCellAcc = mpAccessibleSpreadsheet->GetActiveCell();
                // add xFindCellAcc to the return the Sequence
                uno::Sequence< uno::Any> aSeq2(1);
                aSeq2[0] <<= xFindCellAcc;
                return aSeq2;
            }
        }
    }
    uno::Sequence< uno::Any> aEmpty;
    return aEmpty;
}
 
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: */

V547 Expression 'mpChildrenShapes' is always true.

V1019 Compound assignment expression 'aPropAny2 >>= nLayerID2' is used inside condition.

V1019 Compound assignment expression 'aAny2 >>= nZOrder2' is used inside condition.

V547 Expression 'mpChildrenShapes' is always true.

V547 Expression 'mpViewShell' is always true.