/* -*- 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 <rtl/ref.hxx>
#include <cppuhelper/weakref.hxx>
#include <vcl/window.hxx>
#include <svx/svdmodel.hxx>
#include <svx/unomod.hxx>
#include <algorithm>
#include <map>
#include <unordered_map>
#include <list>
#include <vector>
#include <accmap.hxx>
#include "acccontext.hxx"
#include "accdoc.hxx"
#include <strings.hrc>
#include "accpreview.hxx"
#include "accpage.hxx"
#include "accpara.hxx"
#include "accheaderfooter.hxx"
#include "accfootnote.hxx"
#include "acctextframe.hxx"
#include "accgraphic.hxx"
#include "accembedded.hxx"
#include "acccell.hxx"
#include "acctable.hxx"
#include <fesh.hxx>
#include <rootfrm.hxx>
#include <txtfrm.hxx>
#include <hffrm.hxx>
#include <ftnfrm.hxx>
#include <cellfrm.hxx>
#include <tabfrm.hxx>
#include <pagefrm.hxx>
#include <flyfrm.hxx>
#include <ndtyp.hxx>
#include <IDocumentDrawModelAccess.hxx>
#include <svx/AccessibleShapeInfo.hxx>
#include <svx/ShapeTypeHandler.hxx>
#include <svx/SvxShapeTypes.hxx>
#include <svx/svdpage.hxx>
#include <com/sun/star/accessibility/AccessibleEventId.hpp>
#include <com/sun/star/accessibility/AccessibleStateType.hpp>
#include <com/sun/star/accessibility/AccessibleRole.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/document/XShapeEventBroadcaster.hpp>
#include <cppuhelper/implbase.hxx>
#include <comphelper/interfacecontainer2.hxx>
#include <pagepreviewlayout.hxx>
#include <dcontact.hxx>
#include <svx/unoapi.hxx>
#include <svx/svdmark.hxx>
#include <doc.hxx>
#include <drawdoc.hxx>
#include <pam.hxx>
#include <ndtxt.hxx>
#include <dflyobj.hxx>
#include <prevwpage.hxx>
#include <calbck.hxx>
#include <undobj.hxx>
#include <tools/diagnose_ex.h>
#include <tools/debug.hxx>

using namespace ::com::sun::star;
using namespace ::com::sun::star::accessibility;
using namespace ::sw::access;

namespace {

struct SwFrameFunc
{
    bool operator()( const SwFrame * p1, const SwFrame * p2) const
    {
        return p1 < p2;
    }
};

}

class SwAccessibleContextMap_Impl
{
public:
    typedef const SwFrame *                                               key_type;
    typedef uno::WeakReference < XAccessible >                          mapped_type;
    typedef std::pair<const key_type,mapped_type>                       value_type;
    typedef SwFrameFunc                                                   key_compare;
    typedef std::map<key_type,mapped_type,key_compare>::iterator        iterator;
    typedef std::map<key_type,mapped_type,key_compare>::const_iterator  const_iterator;
private:
    std::map <key_type,mapped_type,key_compare> maMap;
public:

#if OSL_DEBUG_LEVEL > 0
    bool mbLocked;
#endif

    SwAccessibleContextMap_Impl()
#if OSL_DEBUG_LEVEL > 0
        : mbLocked( false )
#endif
    {}

    iterator begin() { return maMap.begin(); }
    iterator end() { return maMap.end(); }
    bool empty() const { return maMap.empty(); }
    void clear() { maMap.clear(); }
    iterator find(const key_type& key) { return maMap.find(key); }
    template<class... Args>
    std::pair<iterator,bool> emplace(Args&&... args) { return maMap.emplace(std::forward<Args>(args)...); }
    iterator erase(const_iterator const & pos) { return maMap.erase(pos); }
};

namespace {

class SwDrawModellListener_Impl : public SfxListener,
    public ::cppu::WeakImplHelper< document::XShapeEventBroadcaster >
{
    mutable ::osl::Mutex maListenerMutex;
    ::comphelper::OInterfaceContainerHelper2 maEventListeners;
    std::unordered_multimap<css::uno::Reference< css::drawing::XShape >, css::uno::Reference< css::document::XShapeEventListener >> maShapeListeners;
    SdrModel *mpDrawModel;
protected:
    virtual ~SwDrawModellListener_Impl() override;

public:
    explicit SwDrawModellListener_Impl( SdrModel *pDrawModel );

    // css::document::XEventBroadcaster
    virtual void SAL_CALL addEventListener( const uno::Reference< document::XEventListener >& xListener ) override;
    virtual void SAL_CALL removeEventListener( const uno::Reference< document::XEventListener >& xListener ) override;
    // css::document::XShapeEventBroadcaster
    virtual void SAL_CALL addShapeEventListener( const css::uno::Reference< css::drawing::XShape >& xShape, const css::uno::Reference< css::document::XShapeEventListener >& xListener ) override;
    virtual void SAL_CALL removeShapeEventListener( const css::uno::Reference< css::drawing::XShape >& xShape, const css::uno::Reference< css::document::XShapeEventListener >& xListener ) override;

    virtual void        Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
    void Dispose();
};

}

SwDrawModellListener_Impl::SwDrawModellListener_Impl( SdrModel *pDrawModel ) :
    maEventListeners( maListenerMutex ),
    mpDrawModel( pDrawModel )
{
    StartListening( *mpDrawModel );
}

SwDrawModellListener_Impl::~SwDrawModellListener_Impl()
{
    Dispose();
}

void SAL_CALL SwDrawModellListener_Impl::addEventListener( const uno::Reference< document::XEventListener >& xListener )
{
    maEventListeners.addInterface( xListener );
}

void SAL_CALL SwDrawModellListener_Impl::removeEventListener( const uno::Reference< document::XEventListener >& xListener )
{
    maEventListeners.removeInterface( xListener );
}

void SAL_CALL SwDrawModellListener_Impl::addShapeEventListener(
                const css::uno::Reference< css::drawing::XShape >& xShape,
                const uno::Reference< document::XShapeEventListener >& xListener )
{
    assert(xShape.is() && "no shape?");
    osl::MutexGuard aGuard(maListenerMutex);
    maShapeListeners.emplace(xShape, xListener);
}

void SAL_CALL SwDrawModellListener_Impl::removeShapeEventListener(
                const css::uno::Reference< css::drawing::XShape >& xShape,
                const uno::Reference< document::XShapeEventListener >& xListener )
{
    osl::MutexGuard aGuard(maListenerMutex);
    auto [itBegin, itEnd] = maShapeListeners.equal_range(xShape);
    for (auto it = itBegin; it != itEnd; ++it)
        if (it->second == xListener)
        {
            maShapeListeners.erase(it);
            return;
        }
}

void SwDrawModellListener_Impl::Notify( SfxBroadcaster& /*rBC*/,
        const SfxHint& rHint )
{
    // do not broadcast notifications for writer fly frames, because there
    // are no shapes that need to know about them.
    if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint)
        return;
    const SdrHint *pSdrHint = static_cast<const SdrHint*>( &rHint );
    if (pSdrHint->GetObject() &&
           ( dynamic_cast< const SwFlyDrawObj* >(pSdrHint->GetObject()) !=  nullptr ||
              dynamic_cast< const SwVirtFlyDrawObj* >(pSdrHint->GetObject()) !=  nullptr ||
             typeid(SdrObject) == typeid(pSdrHint->GetObject()) ) )
    {
        return;
    }

    OSL_ENSURE( mpDrawModel, "draw model listener is disposed" );
    if( !mpDrawModel )
        return;

    document::EventObject aEvent;
    if( !SvxUnoDrawMSFactory::createEvent( mpDrawModel, pSdrHint, aEvent ) )
        return;

    ::comphelper::OInterfaceIteratorHelper2 aIter( maEventListeners );
    while( aIter.hasMoreElements() )
    {
        uno::Reference < document::XEventListener > xListener( aIter.next(),
                                                uno::UNO_QUERY );
        try
        {
            xListener->notifyEvent( aEvent );
        }
        catch( uno::RuntimeException const & )
        {
            TOOLS_WARN_EXCEPTION("sw.a11y", "Runtime exception caught while notifying shape");
        }
    }

    // right now, we're only handling the specific event necessary to fix this performance problem
    if (pSdrHint->GetKind() == SdrHintKind::ObjectChange)
    {
        auto pSdrObject = const_cast<SdrObject*>(pSdrHint->GetObject());
        uno::Reference<drawing::XShape> xShape(pSdrObject->getUnoShape(), uno::UNO_QUERY);
        osl::MutexGuard aGuard(maListenerMutex);
        auto [itBegin, itEnd] = maShapeListeners.equal_range(xShape);
        for (auto it = itBegin; it != itEnd; ++it)
            it->second->notifyShapeEvent(aEvent);
    }
}

void SwDrawModellListener_Impl::Dispose()
{
    if (mpDrawModel != nullptr) {
        EndListening( *mpDrawModel );
    }
    mpDrawModel = nullptr;
}

namespace {

struct SwShapeFunc
{
    bool operator()( const SdrObject * p1, const SdrObject * p2) const
    {
        return p1 < p2;
    }
};

}

typedef std::pair < const SdrObject *, ::rtl::Reference < ::accessibility::AccessibleShape > > SwAccessibleObjShape_Impl;

class SwAccessibleShapeMap_Impl
{
public:

    typedef const SdrObject *                                           key_type;
    typedef uno::WeakReference<XAccessible>                             mapped_type;
    typedef std::pair<const key_type,mapped_type>                       value_type;
    typedef SwShapeFunc                                                 key_compare;
    typedef std::map<key_type,mapped_type,key_compare>::iterator        iterator;
    typedef std::map<key_type,mapped_type,key_compare>::const_iterator  const_iterator;

private:

    ::accessibility::AccessibleShapeTreeInfo    maInfo;
    std::map<key_type,mapped_type,SwShapeFunc>  maMap;

public:

    explicit SwAccessibleShapeMap_Impl( SwAccessibleMap const *pMap )
        : maMap()
    {
        maInfo.SetSdrView( pMap->GetShell()->GetDrawView() );
        maInfo.SetDevice( pMap->GetShell()->GetWin() );
        maInfo.SetViewForwarder( pMap );
        uno::Reference < document::XShapeEventBroadcaster > xModelBroadcaster =
            new SwDrawModellListener_Impl(
                    pMap->GetShell()->getIDocumentDrawModelAccess().GetOrCreateDrawModel() );
        maInfo.SetModelBroadcaster( xModelBroadcaster );
    }

    ~SwAccessibleShapeMap_Impl();

    const ::accessibility::AccessibleShapeTreeInfo& GetInfo() const { return maInfo; }

    std::unique_ptr<SwAccessibleObjShape_Impl[]> Copy( size_t& rSize,
        const SwFEShell *pFESh,
        SwAccessibleObjShape_Impl  **pSelShape ) const;

    iterator end() { return maMap.end(); }
    const_iterator cbegin() const { return maMap.cbegin(); }
    const_iterator cend() const { return maMap.cend(); }
    bool empty() const { return maMap.empty(); }
    iterator find(const key_type& key) { return maMap.find(key); }
    template<class... Args>
    std::pair<iterator,bool> emplace(Args&&... args) { return maMap.emplace(std::forward<Args>(args)...); }
    iterator erase(const_iterator const & pos) { return maMap.erase(pos); }
};

SwAccessibleShapeMap_Impl::~SwAccessibleShapeMap_Impl()
{
    uno::Reference < document::XEventBroadcaster > xBrd( maInfo.GetModelBroadcaster() );
    if( xBrd.is() )
        static_cast < SwDrawModellListener_Impl * >( xBrd.get() )->Dispose();
}

std::unique_ptr<SwAccessibleObjShape_Impl[]>
    SwAccessibleShapeMap_Impl::Copy(
            size_t& rSize, const SwFEShell *pFESh,
            SwAccessibleObjShape_Impl **pSelStart ) const
{
    std::unique_ptr<SwAccessibleObjShape_Impl[]> pShapes;
    SwAccessibleObjShape_Impl *pSelShape = nullptr;

    size_t nSelShapes = pFESh ? pFESh->IsObjSelected() : 0;
    rSize = maMap.size();

    if( rSize > 0 )
    {
        pShapes.reset(new SwAccessibleObjShape_Impl[rSize]);

        SwAccessibleObjShape_Impl *pShape = pShapes.get();
        pSelShape = &(pShapes[rSize]);
        for( const auto& rEntry : maMap )
        {
            const SdrObject *pObj = rEntry.first;
            uno::Reference < XAccessible > xAcc( rEntry.second );
            if( nSelShapes && pFESh && pFESh->IsObjSelected( *pObj ) )
            {
                // selected objects are inserted from the back
                --pSelShape;
                pSelShape->first = pObj;
                pSelShape->second =
                    static_cast < ::accessibility::AccessibleShape* >(
                                                    xAcc.get() );
                --nSelShapes;
            }
            else
            {
                pShape->first = pObj;
                pShape->second =
                    static_cast < ::accessibility::AccessibleShape* >(
                                                    xAcc.get() );
                ++pShape;
            }
        }
        assert(pSelShape == pShape);
    }

    if( pSelStart )
        *pSelStart = pSelShape;

    return pShapes;
}

struct SwAccessibleEvent_Impl
{
public:
    enum EventType { CARET_OR_STATES,
                     INVALID_CONTENT,
                     POS_CHANGED,
                     CHILD_POS_CHANGED,
                     SHAPE_SELECTION,
                     DISPOSE,
                     INVALID_ATTR };

private:
    SwRect      maOldBox;                       // the old bounds for CHILD_POS_CHANGED
                                                // and POS_CHANGED
    uno::WeakReference < XAccessible > mxAcc;   // The object that fires the event
    SwAccessibleChild maFrameOrObj;             // the child for CHILD_POS_CHANGED and
                                                // the same as xAcc for any other
                                                // event type
    EventType   meType;                         // The event type
    AccessibleStates mnStates;                 // check states or update caret pos

public:
    const SwFrame* mpParentFrame;   // The object that fires the event
    bool IsNoXaccParentFrame() const
    {
        return CHILD_POS_CHANGED == meType && mpParentFrame != nullptr;
    }

public:
    SwAccessibleEvent_Impl( EventType eT,
                            SwAccessibleContext *pA,
                            const SwAccessibleChild& rFrameOrObj )
        : mxAcc( pA ),
          maFrameOrObj( rFrameOrObj ),
          meType( eT ),
          mnStates( AccessibleStates::NONE ),
          mpParentFrame( nullptr )
    {}

    SwAccessibleEvent_Impl( EventType eT,
                            const SwAccessibleChild& rFrameOrObj )
        : maFrameOrObj( rFrameOrObj ),
          meType( eT ),
          mnStates( AccessibleStates::NONE ),
          mpParentFrame( nullptr )
    {
        assert(SwAccessibleEvent_Impl::DISPOSE == meType &&
                "wrong event constructor, DISPOSE only");
    }

    explicit SwAccessibleEvent_Impl( EventType eT )
        : meType( eT ),
          mnStates( AccessibleStates::NONE ),
          mpParentFrame( nullptr )
    {
        assert(SwAccessibleEvent_Impl::SHAPE_SELECTION == meType &&
                "wrong event constructor, SHAPE_SELECTION only" );
    }

    SwAccessibleEvent_Impl( EventType eT,
                            SwAccessibleContext *pA,
                            const SwAccessibleChild& rFrameOrObj,
                            const SwRect& rR )
        : maOldBox( rR ),
          mxAcc( pA ),
          maFrameOrObj( rFrameOrObj ),
          meType( eT ),
          mnStates( AccessibleStates::NONE ),
          mpParentFrame( nullptr )
    {
        assert((SwAccessibleEvent_Impl::CHILD_POS_CHANGED == meType ||
                SwAccessibleEvent_Impl::POS_CHANGED == meType) &&
                "wrong event constructor, (CHILD_)POS_CHANGED only" );
    }

    SwAccessibleEvent_Impl( EventType eT,
                            SwAccessibleContext *pA,
                            const SwAccessibleChild& rFrameOrObj,
                            const AccessibleStates _nStates )
        : mxAcc( pA ),
          maFrameOrObj( rFrameOrObj ),
          meType( eT ),
          mnStates( _nStates ),
          mpParentFrame( nullptr )
    {
        assert( SwAccessibleEvent_Impl::CARET_OR_STATES == meType &&
                "wrong event constructor, CARET_OR_STATES only" );
    }

    SwAccessibleEvent_Impl( EventType eT, const SwFrame *pParentFrame,
                const SwAccessibleChild& rFrameOrObj, const SwRect& rR ) :
        maOldBox( rR ),
        maFrameOrObj( rFrameOrObj ),
        meType( eT ),
        mnStates( AccessibleStates::NONE ),
        mpParentFrame( pParentFrame )
    {
        assert( SwAccessibleEvent_Impl::CHILD_POS_CHANGED == meType &&
            "wrong event constructor, CHILD_POS_CHANGED only" );
    }

    // <SetType(..)> only used in method <SwAccessibleMap::AppendEvent(..)>
    void SetType( EventType eT )
    {
        meType = eT;
    }
    EventType GetType() const
    {
        return meType;
    }

    ::rtl::Reference < SwAccessibleContext > GetContext() const
    {
        uno::Reference < XAccessible > xTmp( mxAcc );
        ::rtl::Reference < SwAccessibleContext > xAccImpl(
                            static_cast<SwAccessibleContext*>( xTmp.get() ) );

        return xAccImpl;
    }

    const SwRect& GetOldBox() const
    {
        return maOldBox;
    }
    // <SetOldBox(..)> only used in method <SwAccessibleMap::AppendEvent(..)>
    void SetOldBox( const SwRect& rOldBox )
    {
        maOldBox = rOldBox;
    }

    const SwAccessibleChild& GetFrameOrObj() const
    {
        return maFrameOrObj;
    }

    // <SetStates(..)> only used in method <SwAccessibleMap::AppendEvent(..)>
    void SetStates( AccessibleStates _nStates )
    {
        mnStates |= _nStates;
    }

    bool IsUpdateCursorPos() const
    {
        return bool(mnStates & AccessibleStates::CARET);
    }
    bool IsInvalidateStates() const
    {
        return bool(mnStates & (AccessibleStates::EDITABLE | AccessibleStates::OPAQUE));
    }
    bool IsInvalidateRelation() const
    {
        return bool(mnStates & (AccessibleStates::RELATION_FROM | AccessibleStates::RELATION_TO));
    }
    bool IsInvalidateTextSelection() const
    {
        return bool( mnStates & AccessibleStates::TEXT_SELECTION_CHANGED );
    }

    bool IsInvalidateTextAttrs() const
    {
        return bool( mnStates & AccessibleStates::TEXT_ATTRIBUTE_CHANGED );
    }

    AccessibleStates GetStates() const
    {
        return mnStates;
    }

    AccessibleStates GetAllStates() const
    {
        return mnStates;
    }
};

class SwAccessibleEventList_Impl
{
    std::list<SwAccessibleEvent_Impl> maEvents;
    bool mbFiring;

public:
    SwAccessibleEventList_Impl()
        : mbFiring( false )
    {}

    void SetFiring()
    {
        mbFiring = true;
    }
    bool IsFiring() const
    {
        return mbFiring;
    }

    void MoveMissingXAccToEnd();

    size_t size() const { return maEvents.size(); }
    std::list<SwAccessibleEvent_Impl>::iterator begin() { return maEvents.begin(); }
    std::list<SwAccessibleEvent_Impl>::iterator end() { return maEvents.end(); }
    std::list<SwAccessibleEvent_Impl>::iterator insert( const std::list<SwAccessibleEvent_Impl>::iterator& aIter,
                                                        const SwAccessibleEvent_Impl& rEvent )
    {
        return maEvents.insert( aIter, rEvent );
    }
    std::list<SwAccessibleEvent_Impl>::iterator erase( const std::list<SwAccessibleEvent_Impl>::iterator& aPos )
    {
        return maEvents.erase( aPos );
    }
};

// see comment in SwAccessibleMap::InvalidatePosOrSize()
// last case "else if(pParent)" for why this surprising hack exists
void SwAccessibleEventList_Impl::MoveMissingXAccToEnd()
{
    size_t nSize = size();
    if (nSize < 2 )
    {
        return;
    }
    SwAccessibleEventList_Impl lstEvent;
    for (auto li = begin(); li != end(); )
    {
        if (li->IsNoXaccParentFrame())
        {
            lstEvent.insert(lstEvent.end(), *li);
            li = erase(li);
        }
        else
            ++li;
    }
    assert(size() + lstEvent.size() == nSize);
    maEvents.insert(end(),lstEvent.begin(),lstEvent.end());
    assert(size() == nSize);
}

namespace {

struct SwAccessibleChildFunc
{
    bool operator()( const SwAccessibleChild& r1,
                         const SwAccessibleChild& r2 ) const
    {
        const void *p1 = r1.GetSwFrame()
                         ? static_cast < const void * >( r1.GetSwFrame())
                         : ( r1.GetDrawObject()
                             ? static_cast < const void * >( r1.GetDrawObject() )
                             : static_cast < const void * >( r1.GetWindow() ) );
        const void *p2 = r2.GetSwFrame()
                         ? static_cast < const void * >( r2.GetSwFrame())
                         : ( r2.GetDrawObject()
                             ? static_cast < const void * >( r2.GetDrawObject() )
                             : static_cast < const void * >( r2.GetWindow() ) );
        return p1 < p2;
    }
};

}

class SwAccessibleEventMap_Impl
{
public:
    typedef SwAccessibleChild                                           key_type;
    typedef std::list<SwAccessibleEvent_Impl>::iterator                 mapped_type;
    typedef std::pair<const key_type,mapped_type>                       value_type;
    typedef SwAccessibleChildFunc                                       key_compare;
    typedef std::map<key_type,mapped_type,key_compare>::iterator        iterator;
    typedef std::map<key_type,mapped_type,key_compare>::const_iterator  const_iterator;
private:
    std::map <key_type,mapped_type,key_compare> maMap;
public:
    iterator end() { return maMap.end(); }
    iterator find(const key_type& key) { return maMap.find(key); }
    template<class... Args>
    std::pair<iterator,bool> emplace(Args&&... args) { return maMap.emplace(std::forward<Args>(args)...); }
    iterator erase(const_iterator const & pos) { return maMap.erase(pos); }
};

namespace {

struct SwAccessibleParaSelection
{
    TextFrameIndex nStartOfSelection;
    TextFrameIndex nEndOfSelection;

    SwAccessibleParaSelection(const TextFrameIndex nStartOfSelection_,
                              const TextFrameIndex nEndOfSelection_)
        : nStartOfSelection(nStartOfSelection_)
        , nEndOfSelection(nEndOfSelection_)
    {}
};

struct SwXAccWeakRefComp
{
    bool operator()( const uno::WeakReference<XAccessible>& _rXAccWeakRef1,
                         const uno::WeakReference<XAccessible>& _rXAccWeakRef2 ) const
    {
        return _rXAccWeakRef1.get() < _rXAccWeakRef2.get();
    }
};

}

class SwAccessibleSelectedParas_Impl
{
public:
    typedef uno::WeakReference < XAccessible >                          key_type;
    typedef SwAccessibleParaSelection                                   mapped_type;
    typedef std::pair<const key_type,mapped_type>                       value_type;
    typedef SwXAccWeakRefComp                                           key_compare;
    typedef std::map<key_type,mapped_type,key_compare>::iterator        iterator;
    typedef std::map<key_type,mapped_type,key_compare>::const_iterator  const_iterator;
private:
    std::map<key_type,mapped_type,key_compare> maMap;
public:
    iterator begin() { return maMap.begin(); }
    iterator end() { return maMap.end(); }
    iterator find(const key_type& key) { return maMap.find(key); }
    template<class... Args>
    std::pair<iterator,bool> emplace(Args&&... args) { return maMap.emplace(std::forward<Args>(args)...); }
    iterator erase(const_iterator const & pos) { return maMap.erase(pos); }
};

// helper class that stores preview data
class SwAccPreviewData
{
    typedef std::vector<tools::Rectangle> Rectangles;
    Rectangles maPreviewRects;
    Rectangles maLogicRects;

    SwRect maVisArea;
    Fraction maScale;

    const SwPageFrame *mpSelPage;

    /** adjust logic page rectangle to its visible part

        @param _iorLogicPgSwRect
        input/output parameter - reference to the logic page rectangle, which
        has to be adjusted.

        @param _rPreviewPgSwRect
        input parameter - constant reference to the corresponding preview page
        rectangle; needed to determine the visible part of the logic page rectangle.

        @param _rPreviewWinSize
        input parameter - constant reference to the preview window size in TWIP;
        needed to determine the visible part of the logic page rectangle
    */
    static void AdjustLogicPgRectToVisibleArea( SwRect&         _iorLogicPgSwRect,
                                         const SwRect&   _rPreviewPgSwRect,
                                         const Size&     _rPreviewWinSize );

public:
    SwAccPreviewData();

    void Update( const SwAccessibleMap& rAccMap,
                 const std::vector<std::unique_ptr<PreviewPage>>& _rPreviewPages,
                 const Fraction&  _rScale,
                 const SwPageFrame* _pSelectedPageFrame,
                 const Size&      _rPreviewWinSize );

    void InvalidateSelection( const SwPageFrame* _pSelectedPageFrame );

    const SwRect& GetVisArea() const { return maVisArea;}

    /** Adjust the MapMode so that the preview page appears at the
     * proper position. rPoint identifies the page for which the
     * MapMode should be adjusted. If bFromPreview is true, rPoint is
     * a preview coordinate; else it's a document coordinate. */
    void AdjustMapMode( MapMode& rMapMode,
                        const Point& rPoint ) const;

    const SwPageFrame *GetSelPage() const { return mpSelPage; }

    void DisposePage(const SwPageFrame *pPageFrame );
};

SwAccPreviewData::SwAccPreviewData() :
    mpSelPage( nullptr )
{
}

void SwAccPreviewData::Update( const SwAccessibleMap& rAccMap,
                               const std::vector<std::unique_ptr<PreviewPage>>& _rPreviewPages,
                               const Fraction&  _rScale,
                               const SwPageFrame* _pSelectedPageFrame,
                               const Size&      _rPreviewWinSize )
{
    // store preview scaling, maximal preview page size and selected page
    maScale = _rScale;
    mpSelPage = _pSelectedPageFrame;

    // prepare loop on preview pages
    maPreviewRects.clear();
    maLogicRects.clear();
    SwAccessibleChild aPage;
    maVisArea.Clear();

    // loop on preview pages to calculate <maPreviewRects>, <maLogicRects> and
    // <maVisArea>
    for ( auto & rpPreviewPage : _rPreviewPages )
    {
        aPage = rpPreviewPage->pPage;

        // add preview page rectangle to <maPreviewRects>
        tools::Rectangle aPreviewPgRect( rpPreviewPage->aPreviewWinPos, rpPreviewPage->aPageSize );
        maPreviewRects.push_back( aPreviewPgRect );

        // add logic page rectangle to <maLogicRects>
        SwRect aLogicPgSwRect( aPage.GetBox( rAccMap ) );
        tools::Rectangle aLogicPgRect( aLogicPgSwRect.SVRect() );
        maLogicRects.push_back( aLogicPgRect );
        // union visible area with visible part of logic page rectangle
        if ( rpPreviewPage->bVisible )
        {
            if ( !rpPreviewPage->pPage->IsEmptyPage() )
            {
                AdjustLogicPgRectToVisibleArea( aLogicPgSwRect,
                                                SwRect( aPreviewPgRect ),
                                                _rPreviewWinSize );
            }
            if ( maVisArea.IsEmpty() )
                maVisArea = aLogicPgSwRect;
            else
                maVisArea.Union( aLogicPgSwRect );
        }
    }
}

void SwAccPreviewData::InvalidateSelection( const SwPageFrame* _pSelectedPageFrame )
{
    mpSelPage = _pSelectedPageFrame;
    assert(mpSelPage);
}

namespace {

struct ContainsPredicate
{
    const Point& mrPoint;
    explicit ContainsPredicate( const Point& rPoint ) : mrPoint(rPoint) {}
    bool operator() ( const tools::Rectangle& rRect ) const
    {
        return rRect.IsInside( mrPoint );
    }
};

}

void SwAccPreviewData::AdjustMapMode( MapMode& rMapMode,
                                      const Point& rPoint ) const
{
    // adjust scale
    rMapMode.SetScaleX( maScale );
    rMapMode.SetScaleY( maScale );

    // find proper rectangle
    Rectangles::const_iterator aBegin = maLogicRects.begin();
    Rectangles::const_iterator aEnd = maLogicRects.end();
    Rectangles::const_iterator aFound = std::find_if( aBegin, aEnd,
                                                 ContainsPredicate( rPoint ) );

    if( aFound != aEnd )
    {
        // found! set new origin
        Point aPoint = (maPreviewRects.begin() + (aFound - aBegin))->TopLeft();
        aPoint -= (maLogicRects.begin() + (aFound-aBegin))->TopLeft();
        rMapMode.SetOrigin( aPoint );
    }
    // else: don't adjust MapMode
}

void SwAccPreviewData::DisposePage(const SwPageFrame *pPageFrame )
{
    if( mpSelPage == pPageFrame )
        mpSelPage = nullptr;
}

// adjust logic page rectangle to its visible part
void SwAccPreviewData::AdjustLogicPgRectToVisibleArea(
                            SwRect&         _iorLogicPgSwRect,
                            const SwRect&   _rPreviewPgSwRect,
                            const Size&     _rPreviewWinSize )
{
    // determine preview window rectangle
    const SwRect aPreviewWinSwRect( Point( 0, 0 ), _rPreviewWinSize );
    // calculate visible preview page rectangle
    SwRect aVisPreviewPgSwRect( _rPreviewPgSwRect );
    aVisPreviewPgSwRect.Intersection( aPreviewWinSwRect );
    // adjust logic page rectangle
    SwTwips nTmpDiff;
    // left
    nTmpDiff = aVisPreviewPgSwRect.Left() - _rPreviewPgSwRect.Left();
    _iorLogicPgSwRect.AddLeft( nTmpDiff );
    // top
    nTmpDiff = aVisPreviewPgSwRect.Top() - _rPreviewPgSwRect.Top();
    _iorLogicPgSwRect.AddTop( nTmpDiff );
    // right
    nTmpDiff = _rPreviewPgSwRect.Right() - aVisPreviewPgSwRect.Right();
    _iorLogicPgSwRect.AddRight( - nTmpDiff );
    // bottom
    nTmpDiff = _rPreviewPgSwRect.Bottom() - aVisPreviewPgSwRect.Bottom();
    _iorLogicPgSwRect.AddBottom( - nTmpDiff );
}

static bool AreInSameTable( const uno::Reference< XAccessible >& rAcc,
                                  const SwFrame *pFrame )
{
    bool bRet = false;

    if( pFrame && pFrame->IsCellFrame() && rAcc.is() )
    {
        // Is it in the same table? We check that
        // by comparing the last table frame in the
        // follow chain, because that's cheaper than
        // searching the first one.
        SwAccessibleContext *pAccImpl =
            static_cast< SwAccessibleContext *>( rAcc.get() );
        if( pAccImpl->GetFrame()->IsCellFrame() )
        {
            const SwTabFrame *pTabFrame1 = pAccImpl->GetFrame()->FindTabFrame();
            if (pTabFrame1)
            {
                while (pTabFrame1->GetFollow())
                    pTabFrame1 = pTabFrame1->GetFollow();
            }

            const SwTabFrame *pTabFrame2 = pFrame->FindTabFrame();
            if (pTabFrame2)
            {
                while (pTabFrame2->GetFollow())
                    pTabFrame2 = pTabFrame2->GetFollow();
            }

            bRet = (pTabFrame1 == pTabFrame2);
        }
    }

    return bRet;
}

void SwAccessibleMap::FireEvent( const SwAccessibleEvent_Impl& rEvent )
{
    ::rtl::Reference < SwAccessibleContext > xAccImpl( rEvent.GetContext() );
    if (!xAccImpl.is() && rEvent.mpParentFrame != nullptr)
    {
        SwAccessibleContextMap_Impl::iterator aIter =
            mpFrameMap->find( rEvent.mpParentFrame );
        if( aIter != mpFrameMap->end() )
        {
            uno::Reference < XAccessible > xAcc( (*aIter).second );
            if (xAcc.is())
            {
                uno::Reference < XAccessibleContext >  xContext(xAcc,uno::UNO_QUERY);
                if (xContext.is() && xContext->getAccessibleRole() == AccessibleRole::PARAGRAPH)
                {
                    xAccImpl = static_cast< SwAccessibleContext *>( xAcc.get() );
                }
            }
        }
    }
    if( SwAccessibleEvent_Impl::SHAPE_SELECTION == rEvent.GetType() )
    {
        DoInvalidateShapeSelection();
    }
    else if( xAccImpl.is() && xAccImpl->GetFrame() )
    {
        if ( rEvent.GetType() != SwAccessibleEvent_Impl::DISPOSE &&
             rEvent.IsInvalidateTextAttrs() )
        {
            xAccImpl->InvalidateAttr();
        }
        switch( rEvent.GetType() )
        {
        case SwAccessibleEvent_Impl::INVALID_CONTENT:
            xAccImpl->InvalidateContent();
            break;
        case SwAccessibleEvent_Impl::POS_CHANGED:
            xAccImpl->InvalidatePosOrSize( rEvent.GetOldBox() );
            break;
        case SwAccessibleEvent_Impl::CHILD_POS_CHANGED:
            xAccImpl->InvalidateChildPosOrSize( rEvent.GetFrameOrObj(),
                                       rEvent.GetOldBox() );
            break;
        case SwAccessibleEvent_Impl::DISPOSE:
            assert(!"dispose event has been stored");
            break;
        case SwAccessibleEvent_Impl::INVALID_ATTR:
            // nothing to do here - handled above
            break;
        default:
            break;
        }
        if( SwAccessibleEvent_Impl::DISPOSE != rEvent.GetType() )
        {
            if( rEvent.IsUpdateCursorPos() )
                xAccImpl->InvalidateCursorPos();
            if( rEvent.IsInvalidateStates() )
                xAccImpl->InvalidateStates( rEvent.GetStates() );
            if( rEvent.IsInvalidateRelation() )
            {
                // both events CONTENT_FLOWS_FROM_RELATION_CHANGED and
                // CONTENT_FLOWS_TO_RELATION_CHANGED are possible
                if ( rEvent.GetAllStates() & AccessibleStates::RELATION_FROM )
                {
                    xAccImpl->InvalidateRelation(
                        AccessibleEventId::CONTENT_FLOWS_FROM_RELATION_CHANGED );
                }
                if ( rEvent.GetAllStates() & AccessibleStates::RELATION_TO )
                {
                    xAccImpl->InvalidateRelation(
                        AccessibleEventId::CONTENT_FLOWS_TO_RELATION_CHANGED );
                }
            }

            if ( rEvent.IsInvalidateTextSelection() )
            {
                xAccImpl->InvalidateTextSelection();
            }
        }
    }
}

void SwAccessibleMap::AppendEvent( const SwAccessibleEvent_Impl& rEvent )
{
    osl::MutexGuard aGuard( maEventMutex );

    if( !mpEvents )
        mpEvents.reset(new SwAccessibleEventList_Impl);
    if( !mpEventMap )
        mpEventMap.reset(new SwAccessibleEventMap_Impl);

    if( mpEvents->IsFiring() )
    {
        // While events are fired new ones are generated. They have to be fired
        // now. This does not work for DISPOSE events!
        OSL_ENSURE( rEvent.GetType() != SwAccessibleEvent_Impl::DISPOSE,
                "dispose event while firing events" );
        FireEvent( rEvent );
    }
    else
    {

        SwAccessibleEventMap_Impl::iterator aIter =
                                        mpEventMap->find( rEvent.GetFrameOrObj() );
        if( aIter != mpEventMap->end() )
        {
            SwAccessibleEvent_Impl aEvent( *(*aIter).second );
            assert( aEvent.GetType() != SwAccessibleEvent_Impl::DISPOSE &&
                    "dispose events should not be stored" );
            bool bAppendEvent = true;
            switch( rEvent.GetType() )
            {
            case SwAccessibleEvent_Impl::CARET_OR_STATES:
                // A CARET_OR_STATES event is added to any other
                // event only. It is broadcasted after any other event, so the
                // event should be put to the back.
                OSL_ENSURE( aEvent.GetType() != SwAccessibleEvent_Impl::CHILD_POS_CHANGED,
                        "invalid event combination" );
                aEvent.SetStates( rEvent.GetAllStates() );
                break;
            case SwAccessibleEvent_Impl::INVALID_CONTENT:
                // An INVALID_CONTENT event overwrites a CARET_OR_STATES
                // event (but keeps its flags) and it is contained in a
                // POS_CHANGED event.
                // Therefore, the event's type has to be adapted and the event
                // has to be put at the end.
                //
                // fdo#56031 An INVALID_CONTENT event overwrites a INVALID_ATTR
                // event and overwrites its flags
                OSL_ENSURE( aEvent.GetType() != SwAccessibleEvent_Impl::CHILD_POS_CHANGED,
                        "invalid event combination" );
                if( aEvent.GetType() == SwAccessibleEvent_Impl::CARET_OR_STATES )
                    aEvent.SetType( SwAccessibleEvent_Impl::INVALID_CONTENT );
                else if ( aEvent.GetType() == SwAccessibleEvent_Impl::INVALID_ATTR )
                {
                    aEvent.SetType( SwAccessibleEvent_Impl::INVALID_CONTENT );
                    aEvent.SetStates( rEvent.GetAllStates() );
                }

                break;
            case SwAccessibleEvent_Impl::POS_CHANGED:
                // A pos changed event overwrites CARET_STATES (keeping its
                // flags) as well as INVALID_CONTENT. The old box position
                // has to be stored however if the old event is not a
                // POS_CHANGED itself.
                OSL_ENSURE( aEvent.GetType() != SwAccessibleEvent_Impl::CHILD_POS_CHANGED,
                        "invalid event combination" );
                if( aEvent.GetType() != SwAccessibleEvent_Impl::POS_CHANGED )
                    aEvent.SetOldBox( rEvent.GetOldBox() );
                aEvent.SetType( SwAccessibleEvent_Impl::POS_CHANGED );
                break;
            case SwAccessibleEvent_Impl::CHILD_POS_CHANGED:
                // CHILD_POS_CHANGED events can only follow CHILD_POS_CHANGED
                // events. The only action that needs to be done again is
                // to put the old event to the back. The new one cannot be used,
                // because we are interested in the old frame bounds.
                OSL_ENSURE( aEvent.GetType() == SwAccessibleEvent_Impl::CHILD_POS_CHANGED,
                        "invalid event combination" );
                break;
            case SwAccessibleEvent_Impl::SHAPE_SELECTION:
                OSL_ENSURE( aEvent.GetType() == SwAccessibleEvent_Impl::SHAPE_SELECTION,
                        "invalid event combination" );
                break;
            case SwAccessibleEvent_Impl::DISPOSE:
                // DISPOSE events overwrite all others. They are not stored
                // but executed immediately to avoid broadcasting of
                // nonfunctional objects. So what needs to be done here is to
                // remove all events for the frame in question.
                bAppendEvent = false;
                break;
            case SwAccessibleEvent_Impl::INVALID_ATTR:
                OSL_ENSURE( aEvent.GetType() == SwAccessibleEvent_Impl::INVALID_ATTR,
                        "invalid event combination" );
                break;
            }
            if( bAppendEvent )
            {
                mpEvents->erase( (*aIter).second );
                (*aIter).second = mpEvents->insert( mpEvents->end(), aEvent );
            }
            else
            {
                mpEvents->erase( (*aIter).second );
                mpEventMap->erase( aIter );
            }
        }
        else if( SwAccessibleEvent_Impl::DISPOSE != rEvent.GetType() )
        {
            mpEventMap->emplace( rEvent.GetFrameOrObj(),
                    mpEvents->insert( mpEvents->end(), rEvent ) );
        }
    }
}

void SwAccessibleMap::InvalidateCursorPosition(
        const uno::Reference< XAccessible >& rAcc )
{
    SwAccessibleContext *pAccImpl =
        static_cast< SwAccessibleContext *>( rAcc.get() );
    assert(pAccImpl);
    assert(pAccImpl->GetFrame());
    if( GetShell()->ActionPend() )
    {
        SwAccessibleEvent_Impl aEvent( SwAccessibleEvent_Impl::CARET_OR_STATES,
                                       pAccImpl,
                                       SwAccessibleChild(pAccImpl->GetFrame()),
                                       AccessibleStates::CARET );
        AppendEvent( aEvent );
    }
    else
    {
        FireEvents();
        // While firing events the current frame might have
        // been disposed because it moved out of the visible area.
        // Setting the cursor for such frames is useless and even
        // causes asserts.
        if( pAccImpl->GetFrame() )
            pAccImpl->InvalidateCursorPos();
    }
}

void SwAccessibleMap::InvalidateShapeSelection()
{
    if( GetShell()->ActionPend() )
    {
        SwAccessibleEvent_Impl aEvent(
            SwAccessibleEvent_Impl::SHAPE_SELECTION );
        AppendEvent( aEvent );
    }
    else
    {
        FireEvents();
        DoInvalidateShapeSelection();
    }
}

//This method should implement the following functions:
//1.find the shape objects and set the selected state.
//2.find the Swframe objects and set the selected state.
//3.find the paragraph objects and set the selected state.
void SwAccessibleMap::InvalidateShapeInParaSelection()
{
    std::unique_ptr<SwAccessibleObjShape_Impl[]> pShapes;
    SwAccessibleObjShape_Impl *pSelShape = nullptr;
    size_t nShapes = 0;

    const SwViewShell *pVSh = GetShell();
    const SwFEShell *pFESh = dynamic_cast<const SwFEShell*>( pVSh) !=  nullptr ?
                            static_cast< const SwFEShell * >( pVSh ) : nullptr;
    SwPaM* pCursor = pFESh ? pFESh->GetCursor( false /* ??? */ ) : nullptr;

    //const size_t nSelShapes = pFESh ? pFESh->IsObjSelected() : 0;

    {
        osl::MutexGuard aGuard( maMutex );
        if( mpShapeMap )
            pShapes = mpShapeMap->Copy( nShapes, pFESh, &pSelShape );
    }

    bool bIsSelAll =IsDocumentSelAll();

    if( mpShapeMap )
    {
        //Checked for shapes.
        SwAccessibleShapeMap_Impl::const_iterator aIter = mpShapeMap->cbegin();
        SwAccessibleShapeMap_Impl::const_iterator aEndIter = mpShapeMap->cend();

        if( bIsSelAll)
        {
            while( aIter != aEndIter )
            {
                uno::Reference < XAccessible > xAcc( (*aIter).second );
                if( xAcc.is() )
                    static_cast < ::accessibility::AccessibleShape* >(xAcc.get())->SetState( AccessibleStateType::SELECTED );

                ++aIter;
            }
        }
        else
        {
            while( aIter != aEndIter )
            {
                const SwFrameFormat *pFrameFormat = (*aIter).first ? ::FindFrameFormat( (*aIter).first ) : nullptr;
                if( !pFrameFormat )
                {
                    ++aIter;
                    continue;
                }
                const SwFormatAnchor& rAnchor = pFrameFormat->GetAnchor();
                const SwPosition *pPos = rAnchor.GetContentAnchor();

                if(rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PAGE)
                {
                    uno::Reference < XAccessible > xAcc( (*aIter).second );
                    if(xAcc.is())
                        static_cast < ::accessibility::AccessibleShape* >(xAcc.get())->ResetState( AccessibleStateType::SELECTED );

                    ++aIter;
                    continue;
                }

                if( !pPos )
                {
                    ++aIter;
                    continue;
                }
                if( pPos->nNode.GetNode().GetTextNode() )
                {
                    int nIndex = pPos->nContent.GetIndex();
                    bool bMarked = false;
                    if( pCursor != nullptr )
                    {
                        const SwTextNode* pNode = pPos->nNode.GetNode().GetTextNode();
                        SwTextFrame const*const pFrame(static_cast<SwTextFrame*>(pNode->getLayoutFrame(pVSh->GetLayout())));
                        sal_uLong nFirstNode(pFrame->GetTextNodeFirst()->GetIndex());
                        sal_uLong nLastNode;
                        if (sw::MergedPara const*const pMerged = pFrame->GetMergedPara())
                        {
                            nLastNode = pMerged->pLastNode->GetIndex();
                        }
                        else
                        {
                            nLastNode = nFirstNode;
                        }

                        sal_uLong nHere = pNode->GetIndex();

                        for(SwPaM& rTmpCursor : pCursor->GetRingContainer())
                        {
                            // ignore, if no mark
                            if( rTmpCursor.HasMark() )
                            {
                                bMarked = true;
                                // check whether nHere is 'inside' pCursor
                                SwPosition* pStart = rTmpCursor.Start();
                                sal_uLong nStartIndex = pStart->nNode.GetIndex();
                                SwPosition* pEnd = rTmpCursor.End();
                                sal_uLong nEndIndex = pEnd->nNode.GetIndex();
                                if ((nStartIndex <= nLastNode) && (nFirstNode <= nEndIndex))
                                {
                                    if( rAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR )
                                    {
                                        if( ( ((nHere == nStartIndex) && (nIndex >= pStart->nContent.GetIndex())) || (nHere > nStartIndex) )
                                            &&( ((nHere == nEndIndex) && (nIndex < pEnd->nContent.GetIndex())) || (nHere < nEndIndex) ) )
                                        {
                                            uno::Reference < XAccessible > xAcc( (*aIter).second );
                                            if( xAcc.is() )
                                                static_cast < ::accessibility::AccessibleShape* >(xAcc.get())->SetState( AccessibleStateType::SELECTED );
                                        }
                                        else
                                        {
                                            uno::Reference < XAccessible > xAcc( (*aIter).second );
                                            if( xAcc.is() )
                                                static_cast < ::accessibility::AccessibleShape* >(xAcc.get())->ResetState( AccessibleStateType::SELECTED );
                                        }
                                    }
                                    else if( rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA )
                                    {
                                        uno::Reference<XAccessible> const xAcc((*aIter).second);
                                        if (xAcc.is())
                                        {
                                            if (IsSelectFrameAnchoredAtPara(*pPos, *pStart, *pEnd))
                                            {
                                                static_cast < ::accessibility::AccessibleShape* >(xAcc.get())->SetState( AccessibleStateType::SELECTED );
                                            }
                                            else
                                            {
                                                static_cast < ::accessibility::AccessibleShape* >(xAcc.get())->ResetState( AccessibleStateType::SELECTED );
                                            }
                                        }
                                    }
                                    else if (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR)
                                    {
                                        uno::Reference<XAccessible> const xAcc((*aIter).second);
                                        if (xAcc.is())
                                        {
                                            if (IsDestroyFrameAnchoredAtChar(*pPos, *pStart, *pEnd))
                                            {
                                                static_cast<::accessibility::AccessibleShape*>(xAcc.get())->SetState( AccessibleStateType::SELECTED );
                                            }
                                            else
                                            {
                                                static_cast<::accessibility::AccessibleShape*>(xAcc.get())->ResetState( AccessibleStateType::SELECTED );
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                    if( !bMarked )
                    {
                        SwAccessibleObjShape_Impl  *pShape = pShapes.get();
                        size_t nNumShapes = nShapes;
                        while( nNumShapes )
                        {
                            if( pShape < pSelShape && (pShape->first==(*aIter).first) )
                            {
                                uno::Reference < XAccessible > xAcc( (*aIter).second );
                                if(xAcc.is())
                                    static_cast < ::accessibility::AccessibleShape* >(xAcc.get())->ResetState( AccessibleStateType::SELECTED );
                            }
                            --nNumShapes;
                            ++pShape;
                        }
                    }
                }

                ++aIter;
            }//while( aIter != aEndIter )
        }//else
    }

    pShapes.reset();

    //Checked for FlyFrame
    if (mpFrameMap)
    {
        SwAccessibleContextMap_Impl::iterator aIter = mpFrameMap->begin();
        while( aIter != mpFrameMap->end() )
        {
            const SwFrame *pFrame = (*aIter).first;
            if(pFrame->IsFlyFrame())
            {
                uno::Reference < XAccessible > xAcc = (*aIter).second;

                if(xAcc.is())
                {
                    SwAccessibleFrameBase *pAccFrame = static_cast< SwAccessibleFrameBase * >(xAcc.get());
                    bool bFrameChanged = pAccFrame->SetSelectedState( true );
                    if (bFrameChanged)
                    {
                        const SwFlyFrame *pFlyFrame = static_cast< const SwFlyFrame * >( pFrame );
                        const SwFrameFormat *pFrameFormat = pFlyFrame->GetFormat();
                        if (pFrameFormat)
                        {
                            const SwFormatAnchor& rAnchor = pFrameFormat->GetAnchor();
                            if( rAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR )
                            {
                                uno::Reference< XAccessible > xAccParent = pAccFrame->getAccessibleParent();
                                if (xAccParent.is())
                                {
                                    uno::Reference< XAccessibleContext > xAccContext = xAccParent->getAccessibleContext();
                                    if(xAccContext.is() && xAccContext->getAccessibleRole() == AccessibleRole::PARAGRAPH)
                                    {
                                        SwAccessibleParagraph* pAccPara = static_cast< SwAccessibleParagraph *>(xAccContext.get());
                                        if(pAccFrame->IsSeletedInDoc())
                                        {
                                            m_setParaAdd.insert(pAccPara);
                                        }
                                        else if(m_setParaAdd.count(pAccPara) == 0)
                                        {
                                            m_setParaRemove.insert(pAccPara);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            ++aIter;
        }
    }

    typedef std::vector< SwAccessibleContext* > VEC_PARA;
    VEC_PARA vecAdd;
    VEC_PARA vecRemove;
    //Checked for Paras.
    bool bMarkChanged = false;
    SwAccessibleContextMap_Impl mapTemp;
    if( pCursor != nullptr )
    {
        for(SwPaM& rTmpCursor : pCursor->GetRingContainer())
        {
            if( rTmpCursor.HasMark() )
            {
                SwNodeIndex nStartIndex( rTmpCursor.Start()->nNode );
                SwNodeIndex nEndIndex( rTmpCursor.End()->nNode );
                for (; nStartIndex <= nEndIndex; ++nStartIndex)
                {
                    SwFrame *pFrame = nullptr;
                    if(nStartIndex.GetNode().IsContentNode())
                    {
                        SwContentNode* pCNd = static_cast<SwContentNode*>(&(nStartIndex.GetNode()));
                        pFrame = SwIterator<SwFrame, SwContentNode, sw::IteratorMode::UnwrapMulti>(*pCNd).First();
                        if (mapTemp.find(pFrame) != mapTemp.end())
                        {
                            continue; // sw_redlinehide: once is enough
                        }
                    }
                    else if( nStartIndex.GetNode().IsTableNode() )
                    {
                        SwTableNode * pTable = static_cast<SwTableNode *>(&(nStartIndex.GetNode()));
                        SwTableFormat* pFormat = pTable->GetTable().GetFrameFormat();
                        pFrame = SwIterator<SwFrame, SwTableFormat>(*pFormat).First();
                    }

                    if( pFrame && mpFrameMap)
                    {
                        SwAccessibleContextMap_Impl::iterator aIter = mpFrameMap->find( pFrame );
                        if( aIter != mpFrameMap->end() )
                        {
                            uno::Reference < XAccessible > xAcc = (*aIter).second;
                            bool isChanged = false;
                            if( xAcc.is() )
                            {
                                isChanged = static_cast< SwAccessibleContext * >(xAcc.get())->SetSelectedState( true );
                            }
                            if(!isChanged)
                            {
                                SwAccessibleContextMap_Impl::iterator aEraseIter = mpSeletedFrameMap->find( pFrame );
                                if(aEraseIter != mpSeletedFrameMap->end())
                                    mpSeletedFrameMap->erase(aEraseIter);
                            }
                            else
                            {
                                bMarkChanged = true;
                                vecAdd.push_back(static_cast< SwAccessibleContext * >(xAcc.get()));
                            }

                            mapTemp.emplace( pFrame, xAcc );
                        }
                    }
                }
            }
        }
    }
    if( !mpSeletedFrameMap )
        mpSeletedFrameMap.reset( new SwAccessibleContextMap_Impl );
    if( !mpSeletedFrameMap->empty() )
    {
        SwAccessibleContextMap_Impl::iterator aIter = mpSeletedFrameMap->begin();
        while( aIter != mpSeletedFrameMap->end() )
        {
            uno::Reference < XAccessible > xAcc = (*aIter).second;
            if(xAcc.is())
                static_cast< SwAccessibleContext * >(xAcc.get())->SetSelectedState( false );
            ++aIter;
            vecRemove.push_back(static_cast< SwAccessibleContext * >(xAcc.get()));
        }
        bMarkChanged = true;
        mpSeletedFrameMap->clear();
    }

    SwAccessibleContextMap_Impl::iterator aIter = mapTemp.begin();
    while( aIter != mapTemp.end() )
    {
        mpSeletedFrameMap->emplace( (*aIter).first, (*aIter).second );
        ++aIter;
    }
    mapTemp.clear();

    if( bMarkChanged && mpFrameMap)
    {
        for (SwAccessibleContext* pAccPara : vecAdd)
        {
            AccessibleEventObject aEvent;
            aEvent.EventId = AccessibleEventId::SELECTION_CHANGED;
            if (pAccPara)
            {
                pAccPara->FireAccessibleEvent( aEvent );
            }
        }
        for (SwAccessibleContext* pAccPara : vecRemove)
        {
            AccessibleEventObject aEvent;
            aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_REMOVE;
            if (pAccPara)
            {
                pAccPara->FireAccessibleEvent( aEvent );
            }
        }
    }
}

//Merge with DoInvalidateShapeFocus
void SwAccessibleMap::DoInvalidateShapeSelection(bool bInvalidateFocusMode /*=false*/)
{
    std::unique_ptr<SwAccessibleObjShape_Impl[]> pShapes;
    SwAccessibleObjShape_Impl *pSelShape = nullptr;
    size_t nShapes = 0;

    const SwViewShell *pVSh = GetShell();
    const SwFEShell *pFESh = dynamic_cast<const SwFEShell*>( pVSh) !=  nullptr ?
                            static_cast< const SwFEShell * >( pVSh ) : nullptr;
    const size_t nSelShapes = pFESh ? pFESh->IsObjSelected() : 0;

    //when InvalidateFocus Call this function ,and the current selected shape count is not 1 ,
    //return
    if (bInvalidateFocusMode && nSelShapes != 1)
    {
        return;
    }
    {
        osl::MutexGuard aGuard( maMutex );
        if( mpShapeMap )
            pShapes = mpShapeMap->Copy( nShapes, pFESh, &pSelShape );
    }

    if( !pShapes )
        return;

    typedef std::vector< ::rtl::Reference < ::accessibility::AccessibleShape >  >  VEC_SHAPE;
    VEC_SHAPE vecxShapeAdd;
    VEC_SHAPE vecxShapeRemove;
    int nCountSelectedShape=0;

    vcl::Window *pWin = GetShell()->GetWin();
    bool bFocused = pWin && pWin->HasFocus();
    SwAccessibleObjShape_Impl *pShape = pShapes.get();
    int nShapeCount = nShapes;
    while( nShapeCount )
    {
        if (pShape->second.is() && IsInSameLevel(pShape->first, pFESh))
        {
            if( pShape < pSelShape )
            {
                if(pShape->second->ResetState( AccessibleStateType::SELECTED ))
                {
                    vecxShapeRemove.push_back(pShape->second);
                }
                pShape->second->ResetState( AccessibleStateType::FOCUSED );
            }
        }
        --nShapeCount;
        ++pShape;
    }

    for (const auto& rpShape : vecxShapeRemove)
    {
        ::accessibility::AccessibleShape *pAccShape = rpShape.get();
        if (pAccShape)
        {
            pAccShape->CommitChange(AccessibleEventId::SELECTION_CHANGED_REMOVE, uno::Any(), uno::Any());
        }
    }

    pShape = pShapes.get();

    while( nShapes )
    {
        if (pShape->second.is() && IsInSameLevel(pShape->first, pFESh))
        {
            if( pShape >= pSelShape )
            {
                //first fire focus event
                if( bFocused && 1 == nSelShapes )
                    pShape->second->SetState( AccessibleStateType::FOCUSED );
                else
                    pShape->second->ResetState( AccessibleStateType::FOCUSED );

                if(pShape->second->SetState( AccessibleStateType::SELECTED ))
                {
                    vecxShapeAdd.push_back(pShape->second);
                }
                ++nCountSelectedShape;
            }
        }

        --nShapes;
        ++pShape;
    }

    const unsigned int SELECTION_WITH_NUM = 10;
    if (vecxShapeAdd.size() > SELECTION_WITH_NUM )
    {
         uno::Reference< XAccessible > xDoc = GetDocumentView( );
         SwAccessibleContext * pCont = static_cast<SwAccessibleContext *>(xDoc.get());
         if (pCont)
         {
             AccessibleEventObject aEvent;
             aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_WITHIN;
             pCont->FireAccessibleEvent(aEvent);
         }
    }
    else
    {
        short nEventID = AccessibleEventId::SELECTION_CHANGED_ADD;
        if (nCountSelectedShape <= 1 && vecxShapeAdd.size() == 1 )
        {
            nEventID = AccessibleEventId::SELECTION_CHANGED;
        }
        for (const auto& rpShape : vecxShapeAdd)
        {
            ::accessibility::AccessibleShape *pAccShape = rpShape.get();
            if (pAccShape)
            {
                pAccShape->CommitChange(nEventID, uno::Any(), uno::Any());
            }
        }
    }

    for (const auto& rpShape : vecxShapeAdd)
    {
        ::accessibility::AccessibleShape *pAccShape = rpShape.get();
        if (pAccShape)
        {
            SdrObject *pObj = GetSdrObjectFromXShape(pAccShape->GetXShape());
            SwFrameFormat *pFrameFormat = pObj ? FindFrameFormat( pObj ) : nullptr;
            if (pFrameFormat)
            {
                const SwFormatAnchor& rAnchor = pFrameFormat->GetAnchor();
                if( rAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR )
                {
                    uno::Reference< XAccessible > xPara = pAccShape->getAccessibleParent();
                    if (xPara.is())
                    {
                        uno::Reference< XAccessibleContext > xParaContext = xPara->getAccessibleContext();
                        if (xParaContext.is() && xParaContext->getAccessibleRole() == AccessibleRole::PARAGRAPH)
                        {
                            SwAccessibleParagraph* pAccPara = static_cast< SwAccessibleParagraph *>(xPara.get());
                            if (pAccPara)
                            {
                                m_setParaAdd.insert(pAccPara);
                            }
                        }
                    }
                }
            }
        }
    }
    for (const auto& rpShape : vecxShapeRemove)
    {
        ::accessibility::AccessibleShape *pAccShape = rpShape.get();
        if (pAccShape && !pAccShape->IsDisposed())
        {
            uno::Reference< XAccessible > xPara = pAccShape->getAccessibleParent();
            uno::Reference< XAccessibleContext > xParaContext = xPara->getAccessibleContext();
            if (xParaContext.is() && xParaContext->getAccessibleRole() == AccessibleRole::PARAGRAPH)
            {
                SwAccessibleParagraph* pAccPara = static_cast< SwAccessibleParagraph *>(xPara.get());
                if (m_setParaAdd.count(pAccPara) == 0 )
                {
                    m_setParaRemove.insert(pAccPara);
                }
            }
        }
    }
}

//Merge with DoInvalidateShapeSelection
/*
void SwAccessibleMap::DoInvalidateShapeFocus()
{
    const SwViewShell *pVSh = GetShell();
    const SwFEShell *pFESh = dynamic_cast<const SwFEShell*>( pVSh) !=  nullptr ?
                            static_cast< const SwFEShell * >( pVSh ) : nullptr;
    const size_t nSelShapes = pFESh ? pFESh->IsObjSelected() : 0;

    if( nSelShapes != 1 )
        return;

    SwAccessibleObjShape_Impl *pShapes = nullptr;
    SwAccessibleObjShape_Impl *pSelShape = nullptr;
    size_t nShapes = 0;

    {
        osl::MutexGuard aGuard( maMutex );
        if( mpShapeMap )
            pShapes = mpShapeMap->Copy( nShapes, pFESh, &pSelShape );
    }

    if( pShapes )
    {
        vcl::Window *pWin = GetShell()->GetWin();
        bool bFocused = pWin && pWin->HasFocus();
        SwAccessibleObjShape_Impl  *pShape = pShapes;
        while( nShapes )
        {
            if( pShape->second.is() )
            {
                if( bFocused && pShape >= pSelShape )
                    pShape->second->SetState( AccessibleStateType::FOCUSED );
                else
                    pShape->second->ResetState( AccessibleStateType::FOCUSED );
            }

            --nShapes;
            ++pShape;
        }

        delete[] pShapes;
    }
}

*/

SwAccessibleMap::SwAccessibleMap( SwViewShell *pSh ) :
    mpVSh( pSh ),
    mbShapeSelected( false ),
    maDocName(SwAccessibleContext::GetResource(STR_ACCESS_DOC_NAME))
{
    pSh->GetLayout()->AddAccessibleShell();
}

SwAccessibleMap::~SwAccessibleMap()
{
    DBG_TESTSOLARMUTEX();
    uno::Reference < XAccessible > xAcc;
    {
        osl::MutexGuard aGuard( maMutex );
        if( mpFrameMap )
        {
            const SwRootFrame *pRootFrame = GetShell()->GetLayout();
            SwAccessibleContextMap_Impl::iterator aIter = mpFrameMap->find( pRootFrame );
            if( aIter != mpFrameMap->end() )
                xAcc = (*aIter).second;
            if( !xAcc.is() )
                assert(false); // let's hope this can't happen? the vcl::Window apparently owns the top-level
                //xAcc = new SwAccessibleDocument(shared_from_this());
        }
    }

    if(xAcc.is())
    {
        SwAccessibleDocumentBase *const pAcc =
            static_cast<SwAccessibleDocumentBase *>(xAcc.get());
        pAcc->Dispose( true );
    }
#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
    if( mpFrameMap )
    {
        SwAccessibleContextMap_Impl::iterator aIter = mpFrameMap->begin();
        while( aIter != mpFrameMap->end() )
        {
            uno::Reference < XAccessible > xTmp = (*aIter).second;
            if( xTmp.is() )
            {
                SwAccessibleContext *pTmp = static_cast< SwAccessibleContext * >( xTmp.get() );
                assert(pTmp->GetMap() == nullptr); // must be disposed
            }
            ++aIter;
        }
    }
#endif
    {
        osl::MutexGuard aGuard( maMutex );
        assert((!mpFrameMap || mpFrameMap->empty()) &&
                "Frame map should be empty after disposing the root frame");
        assert((!mpShapeMap || mpShapeMap->empty()) &&
                "Object map should be empty after disposing the root frame");
        mpFrameMap.reset();
        mpShapeMap.reset();
        mvShapes.clear();
        mpSelectedParas.reset();
    }

    mpPreview.reset();

    {
        osl::MutexGuard aGuard( maEventMutex );
        assert(!mpEvents);
        assert(!mpEventMap);
        mpEventMap.reset();
        mpEvents.reset();
    }
    mpVSh->GetLayout()->RemoveAccessibleShell();
}

uno::Reference< XAccessible > SwAccessibleMap::GetDocumentView_(
    bool bPagePreview )
{
    uno::Reference < XAccessible > xAcc;
    bool bSetVisArea = false;

    {
        osl::MutexGuard aGuard( maMutex );

        if( !mpFrameMap )
        {
            mpFrameMap.reset(new SwAccessibleContextMap_Impl);
#if OSL_DEBUG_LEVEL > 0
            mpFrameMap->mbLocked = false;
#endif
        }

#if OSL_DEBUG_LEVEL > 0
        assert(!mpFrameMap->mbLocked);
        mpFrameMap->mbLocked = true;
#endif

        const SwRootFrame *pRootFrame = GetShell()->GetLayout();
        SwAccessibleContextMap_Impl::iterator aIter = mpFrameMap->find( pRootFrame );
        if( aIter != mpFrameMap->end() )
            xAcc = (*aIter).second;
        if( xAcc.is() )
        {
            bSetVisArea = true; // Set VisArea when map mutex is not locked
        }
        else
        {
            if( bPagePreview )
                xAcc = new SwAccessiblePreview(shared_from_this());
            else
                xAcc = new SwAccessibleDocument(shared_from_this());

            if( aIter != mpFrameMap->end() )
            {
                (*aIter).second = xAcc;
            }
            else
            {
                mpFrameMap->emplace( pRootFrame, xAcc );
            }
        }

#if OSL_DEBUG_LEVEL > 0
        mpFrameMap->mbLocked = false;
#endif
    }

    if( bSetVisArea )
    {
        SwAccessibleDocumentBase *pAcc =
            static_cast< SwAccessibleDocumentBase * >( xAcc.get() );
        pAcc->SetVisArea();
    }

    return xAcc;
}

uno::Reference< XAccessible > SwAccessibleMap::GetDocumentView( )
{
    return GetDocumentView_( false );
}

uno::Reference<XAccessible> SwAccessibleMap::GetDocumentPreview(
                                    const std::vector<std::unique_ptr<PreviewPage>>& _rPreviewPages,
                                    const Fraction&  _rScale,
                                    const SwPageFrame* _pSelectedPageFrame,
                                    const Size&      _rPreviewWinSize )
{
    // create & update preview data object
    if( mpPreview == nullptr )
        mpPreview.reset( new SwAccPreviewData() );
    mpPreview->Update( *this, _rPreviewPages, _rScale, _pSelectedPageFrame, _rPreviewWinSize );

    uno::Reference<XAccessible> xAcc = GetDocumentView_( true );
    return xAcc;
}

uno::Reference< XAccessible> SwAccessibleMap::GetContext( const SwFrame *pFrame,
                                                     bool bCreate )
{
    DBG_TESTSOLARMUTEX();
    uno::Reference < XAccessible > xAcc;
    uno::Reference < XAccessible > xOldCursorAcc;
    bool bOldShapeSelected = false;

    {
        osl::MutexGuard aGuard( maMutex );

        if( !mpFrameMap && bCreate )
            mpFrameMap.reset(new SwAccessibleContextMap_Impl);
        if( mpFrameMap )
        {
            SwAccessibleContextMap_Impl::iterator aIter = mpFrameMap->find( pFrame );
            if( aIter != mpFrameMap->end() )
                xAcc = (*aIter).second;

            if( !xAcc.is() && bCreate )
            {
                SwAccessibleContext *pAcc = nullptr;
                switch( pFrame->GetType() )
                {
                case SwFrameType::Txt:
                    pAcc = new SwAccessibleParagraph(shared_from_this(),
                                    static_cast< const SwTextFrame& >( *pFrame ) );
                    break;
                case SwFrameType::Header:
                    pAcc = new SwAccessibleHeaderFooter(shared_from_this(),
                                    static_cast< const SwHeaderFrame *>( pFrame ) );
                    break;
                case SwFrameType::Footer:
                    pAcc = new SwAccessibleHeaderFooter(shared_from_this(),
                                    static_cast< const SwFooterFrame *>( pFrame ) );
                    break;
                case SwFrameType::Ftn:
                    {
                        const SwFootnoteFrame *pFootnoteFrame =
                            static_cast < const SwFootnoteFrame * >( pFrame );
                        bool bIsEndnote =
                            SwAccessibleFootnote::IsEndnote( pFootnoteFrame );
                        pAcc = new SwAccessibleFootnote(shared_from_this(), bIsEndnote,
                                    /*(bIsEndnote ? mnEndnote++ : mnFootnote++),*/
                                    pFootnoteFrame );
                    }
                    break;
                case SwFrameType::Fly:
                    {
                        const SwFlyFrame *pFlyFrame =
                            static_cast < const SwFlyFrame * >( pFrame );
                        switch( SwAccessibleFrameBase::GetNodeType( pFlyFrame ) )
                        {
                        case SwNodeType::Grf:
                            pAcc = new SwAccessibleGraphic(shared_from_this(), pFlyFrame );
                            break;
                        case SwNodeType::Ole:
                            pAcc = new SwAccessibleEmbeddedObject(shared_from_this(), pFlyFrame );
                            break;
                        default:
                            pAcc = new SwAccessibleTextFrame(shared_from_this(), *pFlyFrame );
                            break;
                        }
                    }
                    break;
                case SwFrameType::Cell:
                    pAcc = new SwAccessibleCell(shared_from_this(),
                                    static_cast< const SwCellFrame *>( pFrame ) );
                    break;
                case SwFrameType::Tab:
                    pAcc = new SwAccessibleTable(shared_from_this(),
                                    static_cast< const SwTabFrame *>( pFrame ) );
                    break;
                case SwFrameType::Page:
                    OSL_ENSURE( GetShell()->IsPreview(),
                                "accessible page frames only in PagePreview" );
                    pAcc = new SwAccessiblePage(shared_from_this(), pFrame);
                    break;
                default: break;
                }
                xAcc = pAcc;
                assert(xAcc.is());

                if( aIter != mpFrameMap->end() )
                {
                    (*aIter).second = xAcc;
                }
                else
                {
                    mpFrameMap->emplace( pFrame, xAcc );
                }

                if( pAcc->HasCursor() &&
                    !AreInSameTable( mxCursorContext, pFrame ) )
                {
                    // If the new context has the focus, and if we know
                    // another context that had the focus, then the focus
                    // just moves from the old context to the new one. We
                    // then have to send a focus event and a caret event for
                    // the old context. We have to do that now,
                    // because after we have left this method, anyone might
                    // call getStates for the new context and will get a
                    // focused state then. Sending the focus changes event
                    // after that seems to be strange. However, we cannot
                    // send a focus event for the new context now, because
                    // no one except us knows it. In any case, we remember
                    // the new context as the one that has the focus
                    // currently.

                    xOldCursorAcc = mxCursorContext;
                    mxCursorContext = xAcc;

                    bOldShapeSelected = mbShapeSelected;
                    mbShapeSelected = false;
                }
            }
        }
    }

    // Invalidate focus for old object when map is not locked
    if( xOldCursorAcc.is() )
        InvalidateCursorPosition( xOldCursorAcc );
    if( bOldShapeSelected )
        InvalidateShapeSelection();

    return xAcc;
}

::rtl::Reference < SwAccessibleContext > SwAccessibleMap::GetContextImpl(
            const SwFrame *pFrame,
            bool bCreate )
{
    uno::Reference < XAccessible > xAcc( GetContext( pFrame, bCreate ) );

    ::rtl::Reference < SwAccessibleContext > xAccImpl(
         static_cast< SwAccessibleContext * >( xAcc.get() ) );

    return xAccImpl;
}

uno::Reference< XAccessible> SwAccessibleMap::GetContext(
        const SdrObject *pObj,
        SwAccessibleContext *pParentImpl,
        bool bCreate )
{
    uno::Reference < XAccessible > xAcc;
    uno::Reference < XAccessible > xOldCursorAcc;

    {
        osl::MutexGuard aGuard( maMutex );

        if( !mpShapeMap && bCreate )
            mpShapeMap.reset(new SwAccessibleShapeMap_Impl( this ));
        if( mpShapeMap )
        {
            SwAccessibleShapeMap_Impl::iterator aIter = mpShapeMap->find( pObj );
            if( aIter != mpShapeMap->end() )
                xAcc = (*aIter).second;

            if( !xAcc.is() && bCreate )
            {
                rtl::Reference< ::accessibility::AccessibleShape> pAcc;
                uno::Reference < drawing::XShape > xShape(
                    const_cast< SdrObject * >( pObj )->getUnoShape(),
                    uno::UNO_QUERY );
                if( xShape.is() )
                {
                    ::accessibility::ShapeTypeHandler& rShapeTypeHandler =
                                ::accessibility::ShapeTypeHandler::Instance();
                    uno::Reference < XAccessible > xParent( pParentImpl );
                    ::accessibility::AccessibleShapeInfo aShapeInfo(
                            xShape, xParent, this );

                    pAcc = rShapeTypeHandler.CreateAccessibleObject(
                                aShapeInfo, mpShapeMap->GetInfo() );
                }
                xAcc = pAcc.get();
                assert(xAcc.is());
                pAcc->Init();
                if( aIter != mpShapeMap->end() )
                {
                    (*aIter).second = xAcc;
                }
                else
                {
                    mpShapeMap->emplace( pObj, xAcc );
                }
                // TODO: focus!!!
                AddGroupContext(pObj, xAcc);
            }
        }
    }

    // Invalidate focus for old object when map is not locked
    if( xOldCursorAcc.is() )
        InvalidateCursorPosition( xOldCursorAcc );

    return xAcc;
}

bool SwAccessibleMap::IsInSameLevel(const SdrObject* pObj, const SwFEShell* pFESh)
{
    if (pFESh)
        return pFESh->IsObjSameLevelWithMarked(pObj);
    return false;
}

void SwAccessibleMap::AddShapeContext(const SdrObject *pObj, uno::Reference < XAccessible > const & xAccShape)
{
    osl::MutexGuard aGuard( maMutex );

    if( mpShapeMap )
    {
        mpShapeMap->emplace( pObj, xAccShape );
    }

}

//Added by yanjun for sym2_6407
void SwAccessibleMap::RemoveGroupContext(const SdrObject *pParentObj)
{
    osl::MutexGuard aGuard( maMutex );
    // TODO: Why are sub-shapes of group shapes even added to our map?
    //       Doesn't the AccessibleShape of the top-level shape create them
    //       on demand anyway? Why does SwAccessibleMap need to know them?
    // We cannot rely on getAccessibleChild here to remove the sub-shapes
    // from mpShapes because the top-level shape may not only be disposed here
    // but also by visibility checks in svx, then it doesn't return children.
    if (mpShapeMap && pParentObj && pParentObj->IsGroupObject())
    {
        SdrObjList *const pChildren(pParentObj->GetSubList());
        for (size_t i = 0; pChildren && i < pChildren->GetObjCount(); ++i)
        {
            SdrObject *const pChild(pChildren->GetObj(i));
            assert(pChild);
            RemoveContext(pChild);
        }
    }
}
//End

void SwAccessibleMap::AddGroupContext(const SdrObject *pParentObj, uno::Reference < XAccessible > const & xAccParent)
{
    osl::MutexGuard aGuard( maMutex );
    if( mpShapeMap )
    {
        //here get all the sub list.
        if (pParentObj->IsGroupObject())
        {
            if (xAccParent.is())
            {
                uno::Reference < XAccessibleContext > xContext = xAccParent->getAccessibleContext();
                if (xContext.is())
                {
                    sal_Int32 nChildren = xContext->getAccessibleChildCount();
                    for(sal_Int32 i = 0; i<nChildren; i++)
                    {
                        uno::Reference < XAccessible > xChild = xContext->getAccessibleChild(i);
                        if (xChild.is())
                        {
                            uno::Reference < XAccessibleContext > xChildContext = xChild->getAccessibleContext();
                            if (xChildContext.is())
                            {
                                short nRole = xChildContext->getAccessibleRole();
                                if (nRole == AccessibleRole::SHAPE)
                                {
                                    ::accessibility::AccessibleShape* pAccShape = static_cast < ::accessibility::AccessibleShape* >( xChild.get());
                                    uno::Reference < drawing::XShape > xShape = pAccShape->GetXShape();
                                    if (xShape.is())
                                    {
                                        SdrObject* pObj = GetSdrObjectFromXShape(xShape);
                                        AddShapeContext(pObj, xChild);
                                        AddGroupContext(pObj,xChild);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

::rtl::Reference < ::accessibility::AccessibleShape > SwAccessibleMap::GetContextImpl(
            const SdrObject *pObj,
            SwAccessibleContext *pParentImpl,
            bool bCreate )
{
    uno::Reference < XAccessible > xAcc( GetContext( pObj, pParentImpl, bCreate ) );

    ::rtl::Reference < ::accessibility::AccessibleShape > xAccImpl(
         static_cast< ::accessibility::AccessibleShape* >( xAcc.get() ) );

    return xAccImpl;
}

void SwAccessibleMap::RemoveContext( const SwFrame *pFrame )
{
    osl::MutexGuard aGuard( maMutex );

    if( mpFrameMap )
    {
        SwAccessibleContextMap_Impl::iterator aIter =
            mpFrameMap->find( pFrame );
        if( aIter != mpFrameMap->end() )
        {
            mpFrameMap->erase( aIter );

            // Remove reference to old caret object. Though mxCursorContext
            // is a weak reference and cleared automatically, clearing it
            // directly makes sure to not keep a non-functional object.
            uno::Reference < XAccessible > xOldAcc( mxCursorContext );
            if( xOldAcc.is() )
            {
                SwAccessibleContext *pOldAccImpl =
                    static_cast< SwAccessibleContext *>( xOldAcc.get() );
                OSL_ENSURE( pOldAccImpl->GetFrame(), "old caret context is disposed" );
                if( pOldAccImpl->GetFrame() == pFrame )
                {
                    xOldAcc.clear();    // get an empty ref
                    mxCursorContext = xOldAcc;
                }
            }

            if( mpFrameMap->empty() )
            {
                mpFrameMap.reset();
            }
        }
    }
}

void SwAccessibleMap::RemoveContext( const SdrObject *pObj )
{
    osl::MutexGuard aGuard( maMutex );

    if( mpShapeMap )
    {
        SwAccessibleShapeMap_Impl::iterator aIter = mpShapeMap->find( pObj );
        if( aIter != mpShapeMap->end() )
        {
            uno::Reference < XAccessible > xTempHold( (*aIter).second );
            mpShapeMap->erase( aIter );
            RemoveGroupContext(pObj);
            // The shape selection flag is not cleared, but one might do
            // so but has to make sure that the removed context is the one
            // that is selected.

            if( mpShapeMap && mpShapeMap->empty() )
            {
                mpShapeMap.reset();
            }
        }
    }
}

bool SwAccessibleMap::Contains(const SwFrame *pFrame) const
{
    return (pFrame && mpFrameMap && mpFrameMap->find(pFrame) != mpFrameMap->end());
}

void SwAccessibleMap::A11yDispose( const SwFrame *pFrame,
                                   const SdrObject *pObj,
                                   vcl::Window* pWindow,
                                   bool bRecursive,
                                   bool bCanSkipInvisible )
{
    SwAccessibleChild aFrameOrObj( pFrame, pObj, pWindow );

    // Indeed, the following assert checks the frame's accessible flag,
    // because that's the one that is evaluated in the layout. The frame
    // might not be accessible anyway. That's the case for cell frames that
    // contain further cells.
    OSL_ENSURE( !aFrameOrObj.GetSwFrame() || aFrameOrObj.GetSwFrame()->IsAccessibleFrame(),
            "non accessible frame should be disposed" );

    if (aFrameOrObj.IsAccessible(GetShell()->IsPreview())
               // fdo#87199 dispose the darn thing if it ever was accessible
            || Contains(pFrame))
    {
        ::rtl::Reference< SwAccessibleContext > xAccImpl;
        ::rtl::Reference< SwAccessibleContext > xParentAccImpl;
        ::rtl::Reference< ::accessibility::AccessibleShape > xShapeAccImpl;
        // get accessible context for frame
        {
            osl::MutexGuard aGuard( maMutex );

            // First of all look for an accessible context for a frame
            if( aFrameOrObj.GetSwFrame() && mpFrameMap )
            {
                SwAccessibleContextMap_Impl::iterator aIter =
                    mpFrameMap->find( aFrameOrObj.GetSwFrame() );
                if( aIter != mpFrameMap->end() )
                {
                    uno::Reference < XAccessible > xAcc( (*aIter).second );
                    xAccImpl = static_cast< SwAccessibleContext *>( xAcc.get() );
                }
            }
            if( !xAccImpl.is() && mpFrameMap )
            {
                // If there is none, look if the parent is accessible.
                const SwFrame *pParent =
                        SwAccessibleFrame::GetParent( aFrameOrObj,
                                                      GetShell()->IsPreview());

                if( pParent )
                {
                    SwAccessibleContextMap_Impl::iterator aIter =
                        mpFrameMap->find( pParent );
                    if( aIter != mpFrameMap->end() )
                    {
                        uno::Reference < XAccessible > xAcc( (*aIter).second );
                        xParentAccImpl =
                            static_cast< SwAccessibleContext *>( xAcc.get() );
                    }
                }
            }
            if( !xParentAccImpl.is() && !aFrameOrObj.GetSwFrame() && mpShapeMap )
            {
                SwAccessibleShapeMap_Impl::iterator aIter =
                    mpShapeMap->find( aFrameOrObj.GetDrawObject() );
                if( aIter != mpShapeMap->end() )
                {
                    uno::Reference < XAccessible > xAcc( (*aIter).second );
                    xShapeAccImpl =
                        static_cast< ::accessibility::AccessibleShape *>( xAcc.get() );
                }
            }
            if( pObj && GetShell()->ActionPend() &&
                (xParentAccImpl.is() || xShapeAccImpl.is()) )
            {
                // Keep a reference to the XShape to avoid that it
                // is deleted with a SwFrameFormat::Modify.
                uno::Reference < drawing::XShape > xShape(
                    const_cast< SdrObject * >( pObj )->getUnoShape(),
                    uno::UNO_QUERY );
                if( xShape.is() )
                {
                    mvShapes.push_back( xShape );
                }
            }
        }

        // remove events stored for the frame
        {
            osl::MutexGuard aGuard( maEventMutex );
            if( mpEvents )
            {
                SwAccessibleEventMap_Impl::iterator aIter =
                    mpEventMap->find( aFrameOrObj );
                if( aIter != mpEventMap->end() )
                {
                    SwAccessibleEvent_Impl aEvent(
                            SwAccessibleEvent_Impl::DISPOSE, aFrameOrObj );
                    AppendEvent( aEvent );
                }
            }
        }

        // If the frame is accessible and there is a context for it, dispose
        // the frame. If the frame is no context for it but disposing should
        // take place recursive, the frame's children have to be disposed
        // anyway, so we have to create the context then.
        if( xAccImpl.is() )
        {
            xAccImpl->Dispose( bRecursive );
        }
        else if( xParentAccImpl.is() )
        {
            // If the frame is a cell frame, the table must be notified.
            // If we are in an action, a table model change event will
            // be broadcasted at the end of the action to give the table
            // a chance to generate a single table change event.

            xParentAccImpl->DisposeChild( aFrameOrObj, bRecursive, bCanSkipInvisible );
        }
        else if( xShapeAccImpl.is() )
        {
            RemoveContext( aFrameOrObj.GetDrawObject() );
            xShapeAccImpl->dispose();
        }

        if( mpPreview && pFrame && pFrame->IsPageFrame() )
            mpPreview->DisposePage( static_cast< const SwPageFrame *>( pFrame ) );
    }
}

void SwAccessibleMap::InvalidatePosOrSize( const SwFrame *pFrame,
                                           const SdrObject *pObj,
                                           vcl::Window* pWindow,
                                           const SwRect& rOldBox )
{
    SwAccessibleChild aFrameOrObj( pFrame, pObj, pWindow );
    if( aFrameOrObj.IsAccessible( GetShell()->IsPreview() ) )
    {
        ::rtl::Reference< SwAccessibleContext > xAccImpl;
        ::rtl::Reference< SwAccessibleContext > xParentAccImpl;
        const SwFrame *pParent =nullptr;
        {
            osl::MutexGuard aGuard( maMutex );

            if( mpFrameMap )
            {
                if( aFrameOrObj.GetSwFrame() )
                {
                    SwAccessibleContextMap_Impl::iterator aIter =
                        mpFrameMap->find( aFrameOrObj.GetSwFrame() );
                    if( aIter != mpFrameMap->end() )
                    {
                        // If there is an accessible object already it is
                        // notified directly.
                        uno::Reference < XAccessible > xAcc( (*aIter).second );
                        xAccImpl =
                            static_cast< SwAccessibleContext *>( xAcc.get() );
                    }
                }
                if( !xAccImpl.is() )
                {
                    // Otherwise we look if the parent is accessible.
                    // If not, there is nothing to do.
                    pParent =
                        SwAccessibleFrame::GetParent( aFrameOrObj,
                                                      GetShell()->IsPreview());

                    if( pParent )
                    {
                        SwAccessibleContextMap_Impl::iterator aIter =
                            mpFrameMap->find( pParent );
                        if( aIter != mpFrameMap->end() )
                        {
                            uno::Reference < XAccessible > xAcc( (*aIter).second );
                            xParentAccImpl =
                                static_cast< SwAccessibleContext *>( xAcc.get() );
                        }
                    }
                }
            }
        }

        if( xAccImpl.is() )
        {
            if( GetShell()->ActionPend() )
            {
                SwAccessibleEvent_Impl aEvent(
                    SwAccessibleEvent_Impl::POS_CHANGED, xAccImpl.get(),
                    aFrameOrObj, rOldBox );
                AppendEvent( aEvent );
            }
            else
            {
                FireEvents();
                if (xAccImpl->GetFrame()) // not if disposed by FireEvents()
                {
                    xAccImpl->InvalidatePosOrSize(rOldBox);
                }
            }
        }
        else if( xParentAccImpl.is() )
        {
            if( GetShell()->ActionPend() )
            {
                assert(pParent);
                // tdf#99722 faster not to buffer events that won't be sent
                if (!SwAccessibleChild(pParent).IsVisibleChildrenOnly()
                    || xParentAccImpl->IsShowing(rOldBox)
                    || xParentAccImpl->IsShowing(*this, aFrameOrObj))
                {
                    SwAccessibleEvent_Impl aEvent(
                        SwAccessibleEvent_Impl::CHILD_POS_CHANGED,
                        xParentAccImpl.get(), aFrameOrObj, rOldBox );
                    AppendEvent( aEvent );
                }
            }
            else
            {
                FireEvents();
                xParentAccImpl->InvalidateChildPosOrSize( aFrameOrObj,
                                                          rOldBox );
            }
        }
        else if(pParent)
        {
/*
For child graphic and its parent paragraph,if split 2 graphic to 2 paragraph,
will delete one graphic swfrm and new create 1 graphic swfrm ,
then the new paragraph and the new graphic SwFrame will add .
but when add graphic SwFrame ,the accessible of the new Paragraph is not created yet.
so the new graphic accessible 'parent is NULL,
so run here: save the parent's SwFrame not the accessible object parent,
*/
            bool bIsValidFrame = false;
            bool bIsTextParent = false;
            if (aFrameOrObj.GetSwFrame())
            {
                if (SwFrameType::Fly == pFrame->GetType())
                {
                    bIsValidFrame =true;
                }
            }
            else if(pObj)
            {
                if (SwFrameType::Txt == pParent->GetType())
                {
                    bIsTextParent =true;
                }
            }
            if( bIsValidFrame || bIsTextParent )
            {
                if( GetShell()->ActionPend() )
                {
                    SwAccessibleEvent_Impl aEvent(
                        SwAccessibleEvent_Impl::CHILD_POS_CHANGED,
                        pParent, aFrameOrObj, rOldBox );
                    AppendEvent( aEvent );
                }
                else
                {
                    OSL_ENSURE(false,"");
                }
            }
        }
    }
}

void SwAccessibleMap::InvalidateContent( const SwFrame *pFrame )
{
    SwAccessibleChild aFrameOrObj( pFrame );
    if( aFrameOrObj.IsAccessible( GetShell()->IsPreview() ) )
    {
        uno::Reference < XAccessible > xAcc;
        {
            osl::MutexGuard aGuard( maMutex );

            if( mpFrameMap )
            {
                SwAccessibleContextMap_Impl::iterator aIter =
                    mpFrameMap->find( aFrameOrObj.GetSwFrame() );
                if( aIter != mpFrameMap->end() )
                    xAcc = (*aIter).second;
            }
        }

        if( xAcc.is() )
        {
            SwAccessibleContext *pAccImpl =
                static_cast< SwAccessibleContext *>( xAcc.get() );
            if( GetShell()->ActionPend() )
            {
                SwAccessibleEvent_Impl aEvent(
                    SwAccessibleEvent_Impl::INVALID_CONTENT, pAccImpl,
                    aFrameOrObj );
                AppendEvent( aEvent );
            }
            else
            {
                FireEvents();
                pAccImpl->InvalidateContent();
            }
        }
    }
}

void SwAccessibleMap::InvalidateAttr( const SwTextFrame& rTextFrame )
{
    SwAccessibleChild aFrameOrObj( &rTextFrame );
    if( aFrameOrObj.IsAccessible( GetShell()->IsPreview() ) )
    {
        uno::Reference < XAccessible > xAcc;
        {
            osl::MutexGuard aGuard( maMutex );

            if( mpFrameMap )
            {
                SwAccessibleContextMap_Impl::iterator aIter =
                    mpFrameMap->find( aFrameOrObj.GetSwFrame() );
                if( aIter != mpFrameMap->end() )
                    xAcc = (*aIter).second;
            }
        }

        if( xAcc.is() )
        {
            SwAccessibleContext *pAccImpl =
                static_cast< SwAccessibleContext *>( xAcc.get() );
            if( GetShell()->ActionPend() )
            {
                SwAccessibleEvent_Impl aEvent( SwAccessibleEvent_Impl::INVALID_ATTR,
                                               pAccImpl, aFrameOrObj );
                aEvent.SetStates( AccessibleStates::TEXT_ATTRIBUTE_CHANGED );
                AppendEvent( aEvent );
            }
            else
            {
                FireEvents();
                pAccImpl->InvalidateAttr();
            }
        }
    }
}

void SwAccessibleMap::InvalidateCursorPosition( const SwFrame *pFrame )
{
    SwAccessibleChild aFrameOrObj( pFrame );
    bool bShapeSelected = false;
    const SwViewShell *pVSh = GetShell();
    if( auto pCSh = dynamic_cast<const SwCursorShell*>(pVSh) )
    {
        if( pCSh->IsTableMode() )
        {
            while( aFrameOrObj.GetSwFrame() && !aFrameOrObj.GetSwFrame()->IsCellFrame() )
                aFrameOrObj = aFrameOrObj.GetSwFrame()->GetUpper();
        }
        else if( auto pFESh = dynamic_cast<const SwFEShell*>(pVSh) )
        {
            const SwFrame *pFlyFrame = pFESh->GetSelectedFlyFrame();
            if( pFlyFrame )
            {
                OSL_ENSURE( !pFrame || pFrame->FindFlyFrame() == pFlyFrame,
                        "cursor is not contained in fly frame" );
                aFrameOrObj = pFlyFrame;
            }
            else if( pFESh->IsObjSelected() > 0 )
            {
                bShapeSelected = true;
                aFrameOrObj = static_cast<const SwFrame *>( nullptr );
            }
        }
    }

    OSL_ENSURE( bShapeSelected || aFrameOrObj.IsAccessible(GetShell()->IsPreview()),
            "frame is not accessible" );

    uno::Reference < XAccessible > xOldAcc;
    uno::Reference < XAccessible > xAcc;
    bool bOldShapeSelected = false;

    {
        osl::MutexGuard aGuard( maMutex );

        xOldAcc = mxCursorContext;
        mxCursorContext = xAcc; // clear reference

        bOldShapeSelected = mbShapeSelected;
        mbShapeSelected = bShapeSelected;

        if( aFrameOrObj.GetSwFrame() && mpFrameMap )
        {
            SwAccessibleContextMap_Impl::iterator aIter =
                mpFrameMap->find( aFrameOrObj.GetSwFrame() );
            if( aIter != mpFrameMap->end() )
                xAcc = (*aIter).second;
            else
            {
                SwRect rcEmpty;
                const SwTabFrame* pTabFrame = aFrameOrObj.GetSwFrame()->FindTabFrame();
                if (pTabFrame)
                {
                    InvalidatePosOrSize(pTabFrame, nullptr, nullptr, rcEmpty);
                }
                else
                {
                    InvalidatePosOrSize(aFrameOrObj.GetSwFrame(), nullptr, nullptr, rcEmpty);
                }

                aIter = mpFrameMap->find( aFrameOrObj.GetSwFrame() );
                if( aIter != mpFrameMap->end() )
                {
                    xAcc = (*aIter).second;
                }
            }

            // For cells, some extra thoughts are necessary,
            // because invalidating the cursor for one cell
            // invalidates the cursor for all cells of the same
            // table. For this reason, we don't want to
            // invalidate the cursor for the old cursor object
            // and the new one if they are within the same table,
            // because this would result in doing the work twice.
            // Moreover, we have to make sure to invalidate the
            // cursor even if the current cell has no accessible object.
            // If the old cursor objects exists and is in the same
            // table, it's the best choice, because using it avoids
            // an unnecessary cursor invalidation cycle when creating
            // a new object for the current cell.
            if( aFrameOrObj.GetSwFrame()->IsCellFrame() )
            {
                if( xOldAcc.is() &&
                    AreInSameTable( xOldAcc, aFrameOrObj.GetSwFrame() ) )
                {
                    if( xAcc.is() )
                        xOldAcc = xAcc; // avoid extra invalidation
                    else
                        xAcc = xOldAcc; // make sure at least one
                }
                if( !xAcc.is() )
                    xAcc = GetContext( aFrameOrObj.GetSwFrame() );
            }
        }
        else if (bShapeSelected)
        {
            const SwFEShell *pFESh = static_cast< const SwFEShell * >( pVSh );
            const SdrMarkList *pMarkList = pFESh->GetMarkList();
            if (pMarkList != nullptr && pMarkList->GetMarkCount() == 1)
            {
                SdrObject *pObj = pMarkList->GetMark( 0 )->GetMarkedSdrObj();
                ::rtl::Reference < ::accessibility::AccessibleShape > pAccShapeImpl = GetContextImpl(pObj,nullptr,false);
                if (!pAccShapeImpl.is())
                {
                    while (pObj && pObj->getParentSdrObjectFromSdrObject())
                    {
                        pObj = pObj->getParentSdrObjectFromSdrObject();
                    }
                    if (pObj != nullptr)
                    {
                        const SwFrame *pParent = SwAccessibleFrame::GetParent( SwAccessibleChild(pObj), GetShell()->IsPreview() );
                        if( pParent )
                        {
                            ::rtl::Reference< SwAccessibleContext > xParentAccImpl = GetContextImpl(pParent,false);
                            if (!xParentAccImpl.is())
                            {
                                const SwTabFrame* pTabFrame = pParent->FindTabFrame();
                                if (pTabFrame)
                                {
                                    //The Table should not add in acc.because the "pParent" is not add to acc .
                                    uno::Reference< XAccessible>  xAccParentTab = GetContext(pTabFrame);//Should Create.

                                    const SwFrame *pParentRoot = SwAccessibleFrame::GetParent( SwAccessibleChild(pTabFrame), GetShell()->IsPreview() );
                                    if (pParentRoot)
                                    {
                                        ::rtl::Reference< SwAccessibleContext > xParentAccImplRoot = GetContextImpl(pParentRoot,false);
                                        if(xParentAccImplRoot.is())
                                        {
                                            AccessibleEventObject aEvent;
                                            aEvent.EventId = AccessibleEventId::CHILD;
                                            aEvent.NewValue <<= xAccParentTab;
                                            xParentAccImplRoot->FireAccessibleEvent( aEvent );
                                        }
                                    }

                                    //Get "pParent" acc again.
                                    xParentAccImpl = GetContextImpl(pParent,false);
                                }
                                else
                                {
                                    //directly create this acc para .
                                    xParentAccImpl = GetContextImpl(pParent);//Should Create.

                                    const SwFrame *pParentRoot = SwAccessibleFrame::GetParent( SwAccessibleChild(pParent), GetShell()->IsPreview() );

                                    ::rtl::Reference< SwAccessibleContext > xParentAccImplRoot = GetContextImpl(pParentRoot,false);
                                    if(xParentAccImplRoot.is())
                                    {
                                        AccessibleEventObject aEvent;
                                        aEvent.EventId = AccessibleEventId::CHILD;
                                        aEvent.NewValue <<= uno::Reference< XAccessible>(xParentAccImpl.get());
                                        xParentAccImplRoot->FireAccessibleEvent( aEvent );
                                    }
                                }
                            }
                            if (xParentAccImpl.is())
                            {
                                uno::Reference< XAccessible>  xAccShape =
                                    GetContext(pObj,xParentAccImpl.get());

                                AccessibleEventObject aEvent;
                                aEvent.EventId = AccessibleEventId::CHILD;
                                aEvent.NewValue <<= xAccShape;
                                xParentAccImpl->FireAccessibleEvent( aEvent );
                            }
                        }
                    }
                }
            }
        }
    }

    m_setParaAdd.clear();
    m_setParaRemove.clear();
    if( xOldAcc.is() && xOldAcc != xAcc )
        InvalidateCursorPosition( xOldAcc );
    if( bOldShapeSelected || bShapeSelected )
        InvalidateShapeSelection();
    if( xAcc.is() )
        InvalidateCursorPosition( xAcc );

    InvalidateShapeInParaSelection();

    for (SwAccessibleParagraph* pAccPara : m_setParaRemove)
    {
        if(pAccPara && pAccPara->getSelectedAccessibleChildCount() == 0 && pAccPara->getSelectedText().getLength() == 0)
        {
            if(pAccPara->SetSelectedState(false))
            {
                AccessibleEventObject aEvent;
                aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_REMOVE;
                pAccPara->FireAccessibleEvent( aEvent );
            }
        }
    }
    for (SwAccessibleParagraph* pAccPara : m_setParaAdd)
    {
        if(pAccPara && pAccPara->SetSelectedState(true))
        {
            AccessibleEventObject aEvent;
            aEvent.EventId = AccessibleEventId::SELECTION_CHANGED;
            pAccPara->FireAccessibleEvent( aEvent );
        }
    }
}

void SwAccessibleMap::InvalidateFocus()
{
    if(GetShell()->IsPreview())
    {
        uno::Reference<XAccessible> xAcc = GetDocumentView_( true );
        if (xAcc)
        {
            SwAccessiblePreview *pAccPreview = static_cast<SwAccessiblePreview *>(xAcc.get());
            if (pAccPreview)
            {
                pAccPreview->InvalidateFocus();
                return ;
            }
        }
    }
    uno::Reference < XAccessible > xAcc;
    {
        osl::MutexGuard aGuard( maMutex );

        xAcc = mxCursorContext;
    }

    if( xAcc.is() )
    {
        SwAccessibleContext *pAccImpl = static_cast< SwAccessibleContext *>( xAcc.get() );
        pAccImpl->InvalidateFocus();
    }
    else
    {
        DoInvalidateShapeSelection(true);
    }
}

void SwAccessibleMap::SetCursorContext(
        const ::rtl::Reference < SwAccessibleContext >& rCursorContext )
{
    osl::MutexGuard aGuard( maMutex );
    uno::Reference < XAccessible > xAcc( rCursorContext.get() );
    mxCursorContext = xAcc;
}

void SwAccessibleMap::InvalidateEditableStates( const SwFrame* _pFrame )
{
    // Start with the frame or the first upper that is accessible
    SwAccessibleChild aFrameOrObj( _pFrame );
    while( aFrameOrObj.GetSwFrame() &&
            !aFrameOrObj.IsAccessible( GetShell()->IsPreview() ) )
        aFrameOrObj = aFrameOrObj.GetSwFrame()->GetUpper();
    if( !aFrameOrObj.GetSwFrame() )
        aFrameOrObj = GetShell()->GetLayout();

    uno::Reference< XAccessible > xAcc( GetContext( aFrameOrObj.GetSwFrame() ) );
    SwAccessibleContext *pAccImpl = static_cast< SwAccessibleContext *>( xAcc.get() );
    if( GetShell()->ActionPend() )
    {
        SwAccessibleEvent_Impl aEvent( SwAccessibleEvent_Impl::CARET_OR_STATES,
                                       pAccImpl,
                                       SwAccessibleChild(pAccImpl->GetFrame()),
                                       AccessibleStates::EDITABLE );
        AppendEvent( aEvent );
    }
    else
    {
        FireEvents();
        pAccImpl->InvalidateStates( AccessibleStates::EDITABLE );
    }
}

void SwAccessibleMap::InvalidateRelationSet_( const SwFrame* pFrame,
                                              bool bFrom )
{
    // first, see if this frame is accessible, and if so, get the respective
    SwAccessibleChild aFrameOrObj( pFrame );
    if( aFrameOrObj.IsAccessible( GetShell()->IsPreview() ) )
    {
        uno::Reference < XAccessible > xAcc;
        {
            osl::MutexGuard aGuard( maMutex );

            if( mpFrameMap )
            {
                SwAccessibleContextMap_Impl::iterator aIter =
                                        mpFrameMap->find( aFrameOrObj.GetSwFrame() );
                if( aIter != mpFrameMap->end() )
                {
                    xAcc = (*aIter).second;
                }
            }
        }

        // deliver event directly, or queue event
        if( xAcc.is() )
        {
            SwAccessibleContext *pAccImpl =
                            static_cast< SwAccessibleContext *>( xAcc.get() );
            if( GetShell()->ActionPend() )
            {
                SwAccessibleEvent_Impl aEvent( SwAccessibleEvent_Impl::CARET_OR_STATES,
                                               pAccImpl, SwAccessibleChild(pFrame),
                                               ( bFrom
                                                 ? AccessibleStates::RELATION_FROM
                                                 : AccessibleStates::RELATION_TO ) );
                AppendEvent( aEvent );
            }
            else
            {
                FireEvents();
                pAccImpl->InvalidateRelation( bFrom
                        ? AccessibleEventId::CONTENT_FLOWS_FROM_RELATION_CHANGED
                        : AccessibleEventId::CONTENT_FLOWS_TO_RELATION_CHANGED );
            }
        }
    }
}

void SwAccessibleMap::InvalidateRelationSet( const SwFrame* pMaster,
                                             const SwFrame* pFollow )
{
    InvalidateRelationSet_( pMaster, false );
    InvalidateRelationSet_( pFollow, true );
}

// invalidation of CONTENT_FLOW_FROM/_TO relation of a paragraph
void SwAccessibleMap::InvalidateParaFlowRelation( const SwTextFrame& _rTextFrame,
                                                  const bool _bFrom )
{
    InvalidateRelationSet_( &_rTextFrame, _bFrom );
}

// invalidation of text selection of a paragraph
void SwAccessibleMap::InvalidateParaTextSelection( const SwTextFrame& _rTextFrame )
{
    // first, see if this frame is accessible, and if so, get the respective
    SwAccessibleChild aFrameOrObj( &_rTextFrame );
    if( aFrameOrObj.IsAccessible( GetShell()->IsPreview() ) )
    {
        uno::Reference < XAccessible > xAcc;
        {
            osl::MutexGuard aGuard( maMutex );

            if( mpFrameMap )
            {
                SwAccessibleContextMap_Impl::iterator aIter =
                                        mpFrameMap->find( aFrameOrObj.GetSwFrame() );
                if( aIter != mpFrameMap->end() )
                {
                    xAcc = (*aIter).second;
                }
            }
        }

        // deliver event directly, or queue event
        if( xAcc.is() )
        {
            SwAccessibleContext *pAccImpl =
                            static_cast< SwAccessibleContext *>( xAcc.get() );
            if( GetShell()->ActionPend() )
            {
                SwAccessibleEvent_Impl aEvent(
                    SwAccessibleEvent_Impl::CARET_OR_STATES,
                    pAccImpl,
                    SwAccessibleChild( &_rTextFrame ),
                    AccessibleStates::TEXT_SELECTION_CHANGED );
                AppendEvent( aEvent );
            }
            else
            {
                FireEvents();
                pAccImpl->InvalidateTextSelection();
            }
        }
    }
}

sal_Int32 SwAccessibleMap::GetChildIndex( const SwFrame& rParentFrame,
                                          vcl::Window& rChild ) const
{
    sal_Int32 nIndex( -1 );

    SwAccessibleChild aFrameOrObj( &rParentFrame );
    if( aFrameOrObj.IsAccessible( GetShell()->IsPreview() ) )
    {
        uno::Reference < XAccessible > xAcc;
        {
            osl::MutexGuard aGuard( maMutex );

            if( mpFrameMap )
            {
                SwAccessibleContextMap_Impl::iterator aIter =
                                        mpFrameMap->find( aFrameOrObj.GetSwFrame() );
                if( aIter != mpFrameMap->end() )
                {
                    xAcc = (*aIter).second;
                }
            }
        }

        if( xAcc.is() )
        {
            SwAccessibleContext *pAccImpl =
                            static_cast< SwAccessibleContext *>( xAcc.get() );

            nIndex = pAccImpl->GetChildIndex( const_cast<SwAccessibleMap&>(*this),
                                              SwAccessibleChild( &rChild ) );
        }
    }

    return nIndex;
}

void SwAccessibleMap::UpdatePreview( const std::vector<std::unique_ptr<PreviewPage>>& _rPreviewPages,
                                     const Fraction&  _rScale,
                                     const SwPageFrame* _pSelectedPageFrame,
                                     const Size&      _rPreviewWinSize )
{
    assert(GetShell()->IsPreview() && "no preview?");
    assert(mpPreview != nullptr && "no preview data?");

    mpPreview->Update( *this, _rPreviewPages, _rScale, _pSelectedPageFrame, _rPreviewWinSize );

    // propagate change of VisArea through the document's
    // accessibility tree; this will also send appropriate scroll
    // events
    SwAccessibleContext* pDoc =
        GetContextImpl( GetShell()->GetLayout() ).get();
    static_cast<SwAccessibleDocumentBase*>( pDoc )->SetVisArea();

    uno::Reference < XAccessible > xOldAcc;
    uno::Reference < XAccessible > xAcc;
    {
        osl::MutexGuard aGuard( maMutex );

        xOldAcc = mxCursorContext;

        const SwPageFrame *pSelPage = mpPreview->GetSelPage();
        if( pSelPage && mpFrameMap )
        {
            SwAccessibleContextMap_Impl::iterator aIter =
                mpFrameMap->find( pSelPage );
            if( aIter != mpFrameMap->end() )
                xAcc = (*aIter).second;
        }
    }

    if( xOldAcc.is() && xOldAcc != xAcc )
        InvalidateCursorPosition( xOldAcc );
    if( xAcc.is() )
        InvalidateCursorPosition( xAcc );
}

void SwAccessibleMap::InvalidatePreviewSelection( sal_uInt16 nSelPage )
{
    assert(GetShell()->IsPreview());
    assert(mpPreview != nullptr);

    mpPreview->InvalidateSelection( GetShell()->GetLayout()->GetPageByPageNum( nSelPage ) );

    uno::Reference < XAccessible > xOldAcc;
    uno::Reference < XAccessible > xAcc;
    {
        osl::MutexGuard aGuard( maMutex );

        xOldAcc = mxCursorContext;

        const SwPageFrame *pSelPage = mpPreview->GetSelPage();
        if( pSelPage && mpFrameMap )
        {
            SwAccessibleContextMap_Impl::iterator aIter = mpFrameMap->find( pSelPage );
            if( aIter != mpFrameMap->end() )
                xAcc = (*aIter).second;
        }
    }

    if( xOldAcc.is() && xOldAcc != xAcc )
        InvalidateCursorPosition( xOldAcc );
    if( xAcc.is() )
        InvalidateCursorPosition( xAcc );
}

bool SwAccessibleMap::IsPageSelected( const SwPageFrame *pPageFrame ) const
{
    return mpPreview && mpPreview->GetSelPage() == pPageFrame;
}

void SwAccessibleMap::FireEvents()
{
    {
        osl::MutexGuard aGuard( maEventMutex );
        if( mpEvents )
        {
            if (mpEvents->IsFiring())
            {
                return; // prevent recursive FireEvents()
            }

            mpEvents->SetFiring();
            mpEvents->MoveMissingXAccToEnd();
            for( auto const& aEvent : *mpEvents )
                 FireEvent(aEvent);

            mpEventMap.reset();
            mpEvents.reset();
        }
    }
    {
        osl::MutexGuard aGuard( maMutex );
        mvShapes.clear();
    }

}

tools::Rectangle SwAccessibleMap::GetVisibleArea() const
{
    MapMode aSrc( MapUnit::MapTwip );
    MapMode aDest( MapUnit::Map100thMM );
    return OutputDevice::LogicToLogic( GetVisArea().SVRect(), aSrc, aDest );
}

// Convert a MM100 value relative to the document root into a pixel value
// relative to the screen!
Point SwAccessibleMap::LogicToPixel( const Point& rPoint ) const
{
    MapMode aSrc( MapUnit::Map100thMM );
    MapMode aDest( MapUnit::MapTwip );

    Point aPoint = OutputDevice::LogicToLogic( rPoint, aSrc, aDest );
    if (const vcl::Window* pWin = GetShell()->GetWin())
    {
        MapMode aMapMode;
        GetMapMode( aPoint, aMapMode );
        aPoint = pWin->LogicToPixel( aPoint, aMapMode );
        aPoint = pWin->OutputToAbsoluteScreenPixel( aPoint );
    }

    return aPoint;
}

Size SwAccessibleMap::LogicToPixel( const Size& rSize ) const
{
    MapMode aSrc( MapUnit::Map100thMM );
    MapMode aDest( MapUnit::MapTwip );
    Size aSize( OutputDevice::LogicToLogic( rSize, aSrc, aDest ) );
    if (const OutputDevice* pWin = GetShell()->GetWin())
    {
        MapMode aMapMode;
        GetMapMode( Point(0,0), aMapMode );
        aSize = pWin->LogicToPixel( aSize, aMapMode );
    }

    return aSize;
}

bool SwAccessibleMap::ReplaceChild (
        ::accessibility::AccessibleShape* pCurrentChild,
        const uno::Reference< drawing::XShape >& _rxShape,
        const long /*_nIndex*/,
        const ::accessibility::AccessibleShapeTreeInfo& /*_rShapeTreeInfo*/
    )
{
    const SdrObject *pObj = nullptr;
    {
        osl::MutexGuard aGuard( maMutex );
        if( mpShapeMap )
        {
            SwAccessibleShapeMap_Impl::const_iterator aIter = mpShapeMap->cbegin();
            SwAccessibleShapeMap_Impl::const_iterator aEndIter = mpShapeMap->cend();
            while( aIter != aEndIter && !pObj )
            {
                uno::Reference < XAccessible > xAcc( (*aIter).second );
                ::accessibility::AccessibleShape *pAccShape =
                    static_cast < ::accessibility::AccessibleShape* >( xAcc.get() );
                if( pAccShape == pCurrentChild )
                {
                    pObj = (*aIter).first;
                }
                ++aIter;
            }
        }
    }
    if( !pObj )
        return false;

    uno::Reference < drawing::XShape > xShape( _rxShape );  // keep reference to shape, because
                                                            // we might be the only one that
                                                            // holds it.
    // Also get keep parent.
    uno::Reference < XAccessible > xParent( pCurrentChild->getAccessibleParent() );
    pCurrentChild = nullptr;  // will be released by dispose
    A11yDispose( nullptr, pObj, nullptr );

    {
        osl::MutexGuard aGuard( maMutex );

        if( !mpShapeMap )
            mpShapeMap.reset(new SwAccessibleShapeMap_Impl( this ));

        // create the new child
        ::accessibility::ShapeTypeHandler& rShapeTypeHandler =
                        ::accessibility::ShapeTypeHandler::Instance();
        ::accessibility::AccessibleShapeInfo aShapeInfo(
                                            xShape, xParent, this );
        rtl::Reference< ::accessibility::AccessibleShape> pReplacement(
            rShapeTypeHandler.CreateAccessibleObject (
                aShapeInfo, mpShapeMap->GetInfo() ));

        uno::Reference < XAccessible > xAcc( pReplacement.get() );
        if( xAcc.is() )
        {
            pReplacement->Init();

            SwAccessibleShapeMap_Impl::iterator aIter = mpShapeMap->find( pObj );
            if( aIter != mpShapeMap->end() )
            {
                (*aIter).second = xAcc;
            }
            else
            {
                mpShapeMap->emplace( pObj, xAcc );
            }
        }
    }

    SwRect aEmptyRect;
    InvalidatePosOrSize( nullptr, pObj, nullptr, aEmptyRect );

    return true;
}

//Get the accessible control shape from the model object, here model object is with XPropertySet type
::accessibility::AccessibleControlShape * SwAccessibleMap::GetAccControlShapeFromModel(css::beans::XPropertySet* pSet)
{
    if( mpShapeMap )
    {
        SwAccessibleShapeMap_Impl::const_iterator aIter = mpShapeMap->cbegin();
        SwAccessibleShapeMap_Impl::const_iterator aEndIter = mpShapeMap->cend();
        while( aIter != aEndIter)
        {
            uno::Reference < XAccessible > xAcc( (*aIter).second );
            ::accessibility::AccessibleShape *pAccShape =
                static_cast < ::accessibility::AccessibleShape* >( xAcc.get() );
            if(pAccShape && ::accessibility::ShapeTypeHandler::Instance().GetTypeId (pAccShape->GetXShape()) == ::accessibility::DRAWING_CONTROL)
            {
                ::accessibility::AccessibleControlShape *pCtlAccShape = static_cast < ::accessibility::AccessibleControlShape* >(pAccShape);
                if (pCtlAccShape->GetControlModel() == pSet)
                    return pCtlAccShape;
            }
            ++aIter;
        }
    }
    return nullptr;
}

css::uno::Reference< XAccessible >
    SwAccessibleMap::GetAccessibleCaption (const css::uno::Reference< css::drawing::XShape >&)
{
    return nullptr;
}

Point SwAccessibleMap::PixelToCore( const Point& rPoint ) const
{
    Point aPoint;
    if (const OutputDevice* pWin = GetShell()->GetWin())
    {
        MapMode aMapMode;
        GetMapMode( rPoint, aMapMode );
        aPoint = pWin->PixelToLogic( rPoint, aMapMode );
    }
    return aPoint;
}

static long lcl_CorrectCoarseValue(long aCoarseValue, long aFineValue,
                                          long aRefValue, bool bToLower)
{
    long aResult = aCoarseValue;

    if (bToLower)
    {
        if (aFineValue < aRefValue)
            aResult -= 1;
    }
    else
    {
        if (aFineValue > aRefValue)
            aResult += 1;
    }

    return aResult;
}

static void lcl_CorrectRectangle(tools::Rectangle & rRect,
                                        const tools::Rectangle & rSource,
                                        const tools::Rectangle & rInGrid)
{
    rRect.SetLeft( lcl_CorrectCoarseValue(rRect.Left(), rSource.Left(),
                                          rInGrid.Left(), false) );
    rRect.SetTop( lcl_CorrectCoarseValue(rRect.Top(), rSource.Top(),
                                         rInGrid.Top(), false) );
    rRect.SetRight( lcl_CorrectCoarseValue(rRect.Right(), rSource.Right(),
                                           rInGrid.Right(), true) );
    rRect.SetBottom( lcl_CorrectCoarseValue(rRect.Bottom(), rSource.Bottom(),
                                            rInGrid.Bottom(), true) );
}

tools::Rectangle SwAccessibleMap::CoreToPixel( const tools::Rectangle& rRect ) const
{
    tools::Rectangle aRect;
    if (const OutputDevice* pWin = GetShell()->GetWin())
    {
        MapMode aMapMode;
        GetMapMode( rRect.TopLeft(), aMapMode );
        aRect = pWin->LogicToPixel( rRect, aMapMode );

        tools::Rectangle aTmpRect = pWin->PixelToLogic( aRect, aMapMode );
        lcl_CorrectRectangle(aRect, rRect, aTmpRect);
    }

    return aRect;
}

/** get mapping mode for LogicToPixel and PixelToLogic conversions

    Method returns mapping mode of current output device and adjusts it,
    if the shell is in page/print preview.
    Necessary, because <PreviewAdjust(..)> changes mapping mode at current
    output device for mapping logic document positions to page preview window
    positions and vice versa and doesn't take care to recover its changes.
*/
void SwAccessibleMap::GetMapMode( const Point& _rPoint,
                                  MapMode&     _orMapMode ) const
{
    MapMode aMapMode = GetShell()->GetWin()->GetMapMode();
    if( GetShell()->IsPreview() )
    {
        assert(mpPreview != nullptr);
        mpPreview->AdjustMapMode( aMapMode, _rPoint );
    }
    _orMapMode = aMapMode;
}

Size SwAccessibleMap::GetPreviewPageSize(sal_uInt16 const nPreviewPageNum) const
{
    assert(mpVSh->IsPreview());
    assert(mpPreview != nullptr);
    return mpVSh->PagePreviewLayout()->GetPreviewPageSizeByPageNum(nPreviewPageNum);
}

/** method to build up a new data structure of the accessible paragraphs,
    which have a selection
    Important note: method has to be used inside a mutual exclusive section
*/
std::unique_ptr<SwAccessibleSelectedParas_Impl> SwAccessibleMap::BuildSelectedParas()
{
    // no accessible contexts, no selection
    if ( !mpFrameMap )
    {
        return nullptr;
    }

    // get cursor as an instance of its base class <SwPaM>
    SwPaM* pCursor( nullptr );
    {
        SwCursorShell* pCursorShell = dynamic_cast<SwCursorShell*>(GetShell());
        if ( pCursorShell )
        {
            SwFEShell* pFEShell = dynamic_cast<SwFEShell*>(pCursorShell);
            if ( !pFEShell ||
                 ( !pFEShell->IsFrameSelected() &&
                   pFEShell->IsObjSelected() == 0 ) )
            {
                // get cursor without updating an existing table cursor.
                pCursor = pCursorShell->GetCursor( false );
            }
        }
    }
    // no cursor, no selection
    if ( !pCursor )
    {
        return nullptr;
    }

    std::unique_ptr<SwAccessibleSelectedParas_Impl> pRetSelectedParas;

    // loop on all cursors
    SwPaM* pRingStart = pCursor;
    do {

        // for a selection the cursor has to have a mark.
        // for safety reasons assure that point and mark are in text nodes
        if ( pCursor->HasMark() &&
             pCursor->GetPoint()->nNode.GetNode().IsTextNode() &&
             pCursor->GetMark()->nNode.GetNode().IsTextNode() )
        {
            SwPosition* pStartPos = pCursor->Start();
            SwPosition* pEndPos = pCursor->End();
            // loop on all text nodes inside the selection
            SwNodeIndex aIdx( pStartPos->nNode );
            for ( ; aIdx.GetIndex() <= pEndPos->nNode.GetIndex(); ++aIdx )
            {
                SwTextNode* pTextNode( aIdx.GetNode().GetTextNode() );
                if ( pTextNode )
                {
                    // loop on all text frames registered at the text node.
                    SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pTextNode);
                    for( SwTextFrame* pTextFrame = aIter.First(); pTextFrame; pTextFrame = aIter.Next() )
                        {
                            uno::WeakReference < XAccessible > xWeakAcc;
                            SwAccessibleContextMap_Impl::iterator aMapIter =
                                                    mpFrameMap->find( pTextFrame );
                            if( aMapIter != mpFrameMap->end() )
                            {
                                xWeakAcc = (*aMapIter).second;
                                SwAccessibleParaSelection aDataEntry(
                                    sw::FrameContainsNode(*pTextFrame, pStartPos->nNode.GetIndex())
                                        ? pTextFrame->MapModelToViewPos(*pStartPos)
                                        : TextFrameIndex(0),

                                    sw::FrameContainsNode(*pTextFrame, pEndPos->nNode.GetIndex())
                                        ? pTextFrame->MapModelToViewPos(*pEndPos)
                                        : TextFrameIndex(COMPLETE_STRING));
                                if ( !pRetSelectedParas )
                                {
                                    pRetSelectedParas.reset(
                                            new SwAccessibleSelectedParas_Impl);
                                }
                                // sw_redlinehide: should be idempotent for multiple nodes in a merged para
                                pRetSelectedParas->emplace( xWeakAcc, aDataEntry );
                            }
                        }
                    }
                }
            }

        // prepare next turn: get next cursor in ring
        pCursor = pCursor->GetNext();
    } while ( pCursor != pRingStart );

    return pRetSelectedParas;
}

void SwAccessibleMap::InvalidateTextSelectionOfAllParas()
{
    osl::MutexGuard aGuard( maMutex );

    // keep previously known selected paragraphs
    std::unique_ptr<SwAccessibleSelectedParas_Impl> pPrevSelectedParas( std::move(mpSelectedParas) );

    // determine currently selected paragraphs
    mpSelectedParas = BuildSelectedParas();

    // compare currently selected paragraphs with the previously selected
    // paragraphs and submit corresponding TEXT_SELECTION_CHANGED events.
    // first, search for new and changed selections.
    // on the run remove selections from previously known ones, if they are
    // also in the current ones.
    if ( mpSelectedParas )
    {
        SwAccessibleSelectedParas_Impl::iterator aIter = mpSelectedParas->begin();
        for ( ; aIter != mpSelectedParas->end(); ++aIter )
        {
            bool bSubmitEvent( false );
            if ( !pPrevSelectedParas )
            {
                // new selection
                bSubmitEvent = true;
            }
            else
            {
                SwAccessibleSelectedParas_Impl::iterator aPrevSelected =
                                        pPrevSelectedParas->find( (*aIter).first );
                if ( aPrevSelected != pPrevSelectedParas->end() )
                {
                    // check, if selection has changed
                    if ( (*aIter).second.nStartOfSelection !=
                                    (*aPrevSelected).second.nStartOfSelection ||
                         (*aIter).second.nEndOfSelection !=
                                    (*aPrevSelected).second.nEndOfSelection )
                    {
                        // changed selection
                        bSubmitEvent = true;
                    }
                    pPrevSelectedParas->erase( aPrevSelected );
                }
                else
                {
                    // new selection
                    bSubmitEvent = true;
                }
            }

            if ( bSubmitEvent )
            {
                uno::Reference < XAccessible > xAcc( (*aIter).first );
                if ( xAcc.is() )
                {
                    ::rtl::Reference < SwAccessibleContext > xAccImpl(
                                static_cast<SwAccessibleContext*>( xAcc.get() ) );
                    if ( xAccImpl.is() && xAccImpl->GetFrame() )
                    {
                        const SwTextFrame* pTextFrame(
                            dynamic_cast<const SwTextFrame*>(xAccImpl->GetFrame()) );
                        OSL_ENSURE( pTextFrame,
                                "<SwAccessibleMap::_SubmitTextSelectionChangedEvents()> - unexpected type of frame" );
                        if ( pTextFrame )
                        {
                            InvalidateParaTextSelection( *pTextFrame );
                        }
                    }
                }
            }
        }
    }

    // second, handle previous selections - after the first step the data
    // structure of the previously known only contains the 'old' selections
    if ( pPrevSelectedParas )
    {
        SwAccessibleSelectedParas_Impl::iterator aIter = pPrevSelectedParas->begin();
        for ( ; aIter != pPrevSelectedParas->end(); ++aIter )
        {
            uno::Reference < XAccessible > xAcc( (*aIter).first );
            if ( xAcc.is() )
            {
                ::rtl::Reference < SwAccessibleContext > xAccImpl(
                            static_cast<SwAccessibleContext*>( xAcc.get() ) );
                if ( xAccImpl.is() && xAccImpl->GetFrame() )
                {
                    const SwTextFrame* pTextFrame(
                            dynamic_cast<const SwTextFrame*>(xAccImpl->GetFrame()) );
                    OSL_ENSURE( pTextFrame,
                            "<SwAccessibleMap::_SubmitTextSelectionChangedEvents()> - unexpected type of frame" );
                    if ( pTextFrame )
                    {
                        InvalidateParaTextSelection( *pTextFrame );
                    }
                }
            }
        }
    }
}

const SwRect& SwAccessibleMap::GetVisArea() const
{
    assert(!GetShell()->IsPreview() || (mpPreview != nullptr));

    return GetShell()->IsPreview()
           ? mpPreview->GetVisArea()
           : GetShell()->VisArea();
}

bool SwAccessibleMap::IsDocumentSelAll()
{
    return GetShell()->GetDoc()->IsPrepareSelAll();
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
