/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include <vcl/window.hxx>
#include <swtypes.hxx>

#include <com/sun/star/accessibility/XAccessible.hpp>
#include <com/sun/star/accessibility/XAccessibleStateSet.hpp>
#include <com/sun/star/accessibility/AccessibleStateType.hpp>
#include <com/sun/star/accessibility/AccessibleEventId.hpp>
#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
#include <osl/mutex.hxx>
#include <sal/log.hxx>
#include <vcl/svapp.hxx>
#include <vcl/settings.hxx>
#include <i18nlangtag/languagetag.hxx>
#include <unotools/accessiblestatesethelper.hxx>
#include <unotools/accessiblerelationsethelper.hxx>
#include <viewsh.hxx>
#include <crsrsh.hxx>
#include <fesh.hxx>
#include <wrtsh.hxx>
#include <txtfrm.hxx>
#include <ndtxt.hxx>
#include <pagefrm.hxx>
#include <flyfrm.hxx>
#include <dflyobj.hxx>
#include <pam.hxx>
#include <accmap.hxx>
#include "accfrmobjslist.hxx"
#include "acccontext.hxx"
#include <svx/AccessibleShape.hxx>
#include <comphelper/accessibleeventnotifier.hxx>
#include <cppuhelper/supportsservice.hxx>
#include <PostItMgr.hxx>

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

void SwAccessibleContext::InitStates()
{
    m_isShowingState = GetMap() && IsShowing( *(GetMap()) );

    SwViewShell *pVSh = GetMap()->GetShell();
    m_isEditableState = pVSh && IsEditable( pVSh );
    m_isOpaqueState = pVSh && IsOpaque( pVSh );
    m_isDefuncState = false;
}

void SwAccessibleContext::SetParent( SwAccessibleContext *pParent )
{
    osl::MutexGuard aGuard( m_Mutex );

    uno::Reference < XAccessible > xParent( pParent );
    m_xWeakParent = xParent;
}

uno::Reference< XAccessible > SwAccessibleContext::GetWeakParent() const
{
    osl::MutexGuard aGuard( m_Mutex );

    uno::Reference< XAccessible > xParent( m_xWeakParent );
    return xParent;
}

vcl::Window *SwAccessibleContext::GetWindow()
{
    vcl::Window *pWin = nullptr;

    if( GetMap() )
    {
        const SwViewShell *pVSh = GetMap()->GetShell();
        OSL_ENSURE( pVSh, "no view shell" );
        if( pVSh )
            pWin = pVSh->GetWin();

        OSL_ENSURE( pWin, "no window" );
    }

    return pWin;
}

// get SwViewShell from accessibility map, and cast to cursor shell
SwCursorShell* SwAccessibleContext::GetCursorShell()
{
    SwViewShell* pViewShell = GetMap() ? GetMap()->GetShell() : nullptr;
    OSL_ENSURE( pViewShell, "no view shell" );
    return dynamic_cast<SwCursorShell*>( pViewShell);
}

const SwCursorShell* SwAccessibleContext::GetCursorShell() const
{
    // just like non-const GetCursorShell
    const SwViewShell* pViewShell = GetMap() ? GetMap()->GetShell() : nullptr;
    OSL_ENSURE( pViewShell, "no view shell" );
    return dynamic_cast<const SwCursorShell*>( pViewShell);
}

namespace {

enum class Action { NONE, SCROLLED, SCROLLED_WITHIN,
                          SCROLLED_IN, SCROLLED_OUT };

}

void SwAccessibleContext::ChildrenScrolled( const SwFrame *pFrame,
                                            const SwRect& rOldVisArea )
{
    const SwRect& rNewVisArea = GetVisArea();
    const bool bVisibleChildrenOnly = SwAccessibleChild( pFrame ).IsVisibleChildrenOnly();

    const SwAccessibleChildSList aList( *pFrame, *(GetMap()) );
    SwAccessibleChildSList::const_iterator aIter( aList.begin() );
    while( aIter != aList.end() )
    {
        const SwAccessibleChild& rLower = *aIter;
        const SwRect aBox( rLower.GetBox( *(GetMap()) ) );
        if( rLower.IsAccessible( GetShell()->IsPreview() ) )
        {
            Action eAction = Action::NONE;
            if( aBox.IsOver( rNewVisArea ) )
            {
                if( aBox.IsOver( rOldVisArea ) )
                {
                    eAction = Action::SCROLLED_WITHIN;
                }
                else
                {
                    if ( bVisibleChildrenOnly &&
                         !rLower.AlwaysIncludeAsChild() )
                    {
                        eAction = Action::SCROLLED_IN;
                    }
                    else
                    {
                        eAction = Action::SCROLLED;
                    }
                }
            }
            else if( aBox.IsOver( rOldVisArea ) )
            {
                if ( bVisibleChildrenOnly &&
                     !rLower.AlwaysIncludeAsChild() )
                {
                    eAction = Action::SCROLLED_OUT;
                }
                else
                {
                    eAction = Action::SCROLLED;
                }
            }
            else if( !bVisibleChildrenOnly ||
                     rLower.AlwaysIncludeAsChild() )
            {
                // This wouldn't be required if the SwAccessibleFrame,
                // wouldn't know about the visible area.
                eAction = Action::SCROLLED;
            }
            if( Action::NONE != eAction )
            {
                if ( rLower.GetSwFrame() )
                {
                    OSL_ENSURE( !rLower.AlwaysIncludeAsChild(),
                            "<SwAccessibleContext::ChildrenScrolled(..)> - always included child not considered!" );
                    const SwFrame* pLower( rLower.GetSwFrame() );
                    ::rtl::Reference< SwAccessibleContext > xAccImpl =
                        GetMap()->GetContextImpl( pLower );
                    if( xAccImpl.is() )
                    {
                        switch( eAction )
                        {
                        case Action::SCROLLED:
                            xAccImpl->Scrolled( rOldVisArea );
                            break;
                        case Action::SCROLLED_WITHIN:
                            xAccImpl->ScrolledWithin( rOldVisArea );
                            break;
                        case Action::SCROLLED_IN:
                            xAccImpl->ScrolledIn();
                            break;
                        case Action::SCROLLED_OUT:
                            xAccImpl->ScrolledOut( rOldVisArea );
                            break;
                        case Action::NONE:
                            break;
                        }
                    }
                    else
                    {
                        ChildrenScrolled( pLower, rOldVisArea );
                    }
                }
                else if ( rLower.GetDrawObject() )
                {
                    OSL_ENSURE( !rLower.AlwaysIncludeAsChild(),
                            "<SwAccessibleContext::ChildrenScrolled(..)> - always included child not considered!" );
                    ::rtl::Reference< ::accessibility::AccessibleShape > xAccImpl =
                        GetMap()->GetContextImpl( rLower.GetDrawObject(),
                                                  this );
                    if( xAccImpl.is() )
                    {
                        switch( eAction )
                        {
                        case Action::SCROLLED:
                        case Action::SCROLLED_WITHIN:
                            xAccImpl->ViewForwarderChanged();
                            break;
                        case Action::SCROLLED_IN:
                            ScrolledInShape( xAccImpl.get() );
                            break;
                        case Action::SCROLLED_OUT:
                            {
                                xAccImpl->ViewForwarderChanged();
                                // this DisposeShape call was removed by
                                // IAccessibility2 implementation
                                // without giving any reason why
                                DisposeShape( rLower.GetDrawObject(),
                                          xAccImpl.get() );
                            }
                            break;
                        // coverity[dead_error_begin] - following conditions exist to avoid compiler warning
                        case Action::NONE:
                            break;
                        }
                    }
                }
                else if ( rLower.GetWindow() )
                {
                    // nothing to do - as such children are always included as children.
                    OSL_ENSURE( rLower.AlwaysIncludeAsChild(),
                            "<SwAccessibleContext::ChildrenScrolled(..)> - not always included child not considered!" );
                }
            }
        }
        else if ( rLower.GetSwFrame() &&
                  ( !bVisibleChildrenOnly ||
                    aBox.IsOver( rOldVisArea ) ||
                    aBox.IsOver( rNewVisArea ) ) )
        {
            // There are no unaccessible SdrObjects that need to be notified
            ChildrenScrolled( rLower.GetSwFrame(), rOldVisArea );
        }
        ++aIter;
    }
}

void SwAccessibleContext::Scrolled( const SwRect& rOldVisArea )
{
    SetVisArea( GetMap()->GetVisArea() );

    ChildrenScrolled( GetFrame(), rOldVisArea );

    bool bIsOldShowingState;
    bool bIsNewShowingState = IsShowing( *(GetMap()) );
    {
        osl::MutexGuard aGuard( m_Mutex );
        bIsOldShowingState = m_isShowingState;
        m_isShowingState = bIsNewShowingState;
    }

    if( bIsOldShowingState != bIsNewShowingState )
        FireStateChangedEvent( AccessibleStateType::SHOWING,
                               bIsNewShowingState  );
}

void SwAccessibleContext::ScrolledWithin( const SwRect& rOldVisArea )
{
    SetVisArea( GetMap()->GetVisArea() );

    ChildrenScrolled( GetFrame(), rOldVisArea );

    FireVisibleDataEvent();
}

void SwAccessibleContext::ScrolledIn()
{
    // This accessible should be freshly created, because it
    // was not visible before. Therefore, its visible area must already
    // reflect the scrolling.
    OSL_ENSURE( GetVisArea() == GetMap()->GetVisArea(),
            "Visible area of child is wrong. Did it exist already?" );

    // Send child event at parent. That's all we have to do here.
    const SwFrame* pParent = GetParent();
    ::rtl::Reference< SwAccessibleContext > xParentImpl(
         GetMap()->GetContextImpl( pParent, false ) );
    uno::Reference < XAccessibleContext > xThis( this );
    if( xParentImpl.is() )
    {
        SetParent( xParentImpl.get() );

        AccessibleEventObject aEvent;
        aEvent.EventId = AccessibleEventId::CHILD;
        aEvent.NewValue <<= xThis;

        xParentImpl->FireAccessibleEvent( aEvent );

        if( HasCursor() )
        {
            vcl::Window *pWin = GetWindow();
            if( pWin && pWin->HasFocus() )
            {
                FireStateChangedEvent( AccessibleStateType::FOCUSED, true );
            }
        }

    }
}

void SwAccessibleContext::ScrolledOut( const SwRect& rOldVisArea )
{
    SetVisArea( GetMap()->GetVisArea() );

    // First of all, update the children. That's required to dispose
    // all children that are existing only if they are visible. They
    // are not disposed by the recursive Dispose call that follows later on,
    // because this call will only dispose children that are in the
    // new visible area. The children we want to dispose however are in the
    // old visible area all.
    ChildrenScrolled( GetFrame(), rOldVisArea );

    // Broadcast a state changed event for the showing state.
    // It might be that the child is freshly created just to send
    // the child event. In this case no listener will exist.
    FireStateChangedEvent( AccessibleStateType::SHOWING, false );

    // this Dispose call was removed by IAccessibility2 implementation
    // without giving any reason why - without it we get stale
    // entries in SwAccessibleMap::mpFrameMap.
    Dispose(true);
}

// #i27301# - use new type definition for <_nStates>
void SwAccessibleContext::InvalidateChildrenStates( const SwFrame* _pFrame,
                                                    AccessibleStates _nStates )
{
    const SwAccessibleChildSList aVisList( GetVisArea(), *_pFrame, *(GetMap()) );

    SwAccessibleChildSList::const_iterator aIter( aVisList.begin() );
    while( aIter != aVisList.end() )
    {
        const SwAccessibleChild& rLower = *aIter;
        const SwFrame* pLower = rLower.GetSwFrame();
        if( pLower )
        {
            ::rtl::Reference< SwAccessibleContext > xAccImpl;
            if( rLower.IsAccessible( GetShell()->IsPreview() ) )
                xAccImpl = GetMap()->GetContextImpl( pLower, false );
            if( xAccImpl.is() )
                xAccImpl->InvalidateStates( _nStates );
            else
                InvalidateChildrenStates( pLower, _nStates );
        }
        else if ( rLower.GetDrawObject() )
        {
            // TODO: SdrObjects
        }
        else if ( rLower.GetWindow() )
        {
            // nothing to do ?
        }

        ++aIter;
    }
}

void SwAccessibleContext::DisposeChildren(const SwFrame *pFrame,
                                          bool bRecursive,
                                          bool bCanSkipInvisible)
{
    const SwAccessibleChildSList aVisList( GetVisArea(), *pFrame, *(GetMap()) );
    SwAccessibleChildSList::const_iterator aIter( aVisList.begin() );
    while( aIter != aVisList.end() )
    {
        const SwAccessibleChild& rLower = *aIter;
        const SwFrame* pLower = rLower.GetSwFrame();
        if( pLower )
        {
            // tdf#117601 dispose the darn thing if it ever was accessible
            ::rtl::Reference<SwAccessibleContext> xAccImpl = GetMap()->GetContextImpl(pLower, false);
            if( xAccImpl.is() )
                xAccImpl->Dispose( bRecursive );
            else
            {
                // it's possible that the xAccImpl *does* exist with a
                // ref-count of 0 and blocked in its dtor in another thread -
                // this call here could be from SwAccessibleMap dtor so
                // remove it from any maps now!
                GetMap()->RemoveContext(pLower);
                // in this case the context will check with a weak_ptr
                // that the map is still alive so it's not necessary
                // to clear its m_pMap here.
                if (bRecursive)
                {
                    DisposeChildren(pLower, bRecursive, bCanSkipInvisible);
                }
            }
        }
        else if ( rLower.GetDrawObject() )
        {
            ::rtl::Reference< ::accessibility::AccessibleShape > xAccImpl(
                    GetMap()->GetContextImpl( rLower.GetDrawObject(),
                                          this, false )  );
            if( xAccImpl.is() )
                DisposeShape( rLower.GetDrawObject(), xAccImpl.get() );
        }
        else if ( rLower.GetWindow() )
        {
            DisposeChild(rLower, false, bCanSkipInvisible);
        }
        ++aIter;
    }
}

void SwAccessibleContext::InvalidateContent_( bool )
{
}

void SwAccessibleContext::InvalidateCursorPos_()
{
}

void SwAccessibleContext::InvalidateFocus_()
{
}

void SwAccessibleContext::FireAccessibleEvent( AccessibleEventObject& rEvent )
{
    OSL_ENSURE( GetFrame(), "fire event for disposed frame?" );
    if( !GetFrame() )
        return;

    if( !rEvent.Source.is() )
    {
        uno::Reference < XAccessibleContext > xThis( this );
        rEvent.Source = xThis;
    }

    if (m_nClientId)
        comphelper::AccessibleEventNotifier::addEvent( m_nClientId, rEvent );
}

void SwAccessibleContext::FireVisibleDataEvent()
{
    AccessibleEventObject aEvent;
    aEvent.EventId = AccessibleEventId::VISIBLE_DATA_CHANGED;

    FireAccessibleEvent( aEvent );
}

void SwAccessibleContext::FireStateChangedEvent( sal_Int16 nState,
                                                 bool bNewState )
{
    AccessibleEventObject aEvent;

    aEvent.EventId = AccessibleEventId::STATE_CHANGED;
    if( bNewState )
        aEvent.NewValue <<= nState;
    else
        aEvent.OldValue <<= nState;

    FireAccessibleEvent( aEvent );
}

void SwAccessibleContext::GetStates(
        ::utl::AccessibleStateSetHelper& rStateSet )
{
    SolarMutexGuard aGuard;

    // SHOWING
    if (m_isShowingState)
        rStateSet.AddState( AccessibleStateType::SHOWING );

    // EDITABLE
    if (m_isEditableState)
    //Set editable state to graphic and other object when the document is editable
    {
        rStateSet.AddState( AccessibleStateType::EDITABLE );
        rStateSet.AddState( AccessibleStateType::RESIZABLE );
        rStateSet.AddState( AccessibleStateType::MOVEABLE );
    }
    // ENABLED
    rStateSet.AddState( AccessibleStateType::ENABLED );

    // OPAQUE
    if (m_isOpaqueState)
        rStateSet.AddState( AccessibleStateType::OPAQUE );

    // VISIBLE
    rStateSet.AddState( AccessibleStateType::VISIBLE );

    if (m_isDefuncState)
        rStateSet.AddState( AccessibleStateType::DEFUNC );
}

bool SwAccessibleContext::IsEditableState()
{
    bool bRet;
    {
        osl::MutexGuard aGuard( m_Mutex );
        bRet = m_isEditableState;
    }

    return bRet;
}

void SwAccessibleContext::ThrowIfDisposed()
{
    if (!(GetFrame() && GetMap()))
    {
        throw lang::DisposedException("object is nonfunctional",
                static_cast<cppu::OWeakObject*>(this));
    }
}

SwAccessibleContext::SwAccessibleContext(std::shared_ptr<SwAccessibleMap> const& pMap,
                                          sal_Int16 const nRole,
                                          const SwFrame *pF )
    : SwAccessibleFrame( pMap->GetVisArea().SVRect(), pF,
                         pMap->GetShell()->IsPreview() )
    , m_pMap(pMap.get())
    , m_wMap(pMap)
    , m_nClientId(0)
    , m_nRole(nRole)
    , m_isDisposing( false )
    , m_isRegisteredAtAccessibleMap( true )
    , m_isSelectedInDoc(false)
{
    InitStates();
}

SwAccessibleContext::~SwAccessibleContext()
{
    // must have for 2 reasons: 2. as long as this thread has SolarMutex
    // another thread cannot destroy the SwAccessibleMap so our temporary
    // taking a hard ref to SwAccessibleMap won't delay its destruction
    SolarMutexGuard aGuard;
    // must check with weak_ptr that m_pMap is still alive
    std::shared_ptr<SwAccessibleMap> pMap(m_wMap.lock());
    if (m_isRegisteredAtAccessibleMap && GetFrame() && pMap)
    {
        pMap->RemoveContext( GetFrame() );
    }
}

uno::Reference< XAccessibleContext > SAL_CALL
    SwAccessibleContext::getAccessibleContext()
{
    uno::Reference < XAccessibleContext > xRet( this );
    return xRet;
}

sal_Int32 SAL_CALL SwAccessibleContext::getAccessibleChildCount()
{
    SolarMutexGuard aGuard;

    ThrowIfDisposed();

    return m_isDisposing ? 0 : GetChildCount( *(GetMap()) );
}

uno::Reference< XAccessible> SAL_CALL
    SwAccessibleContext::getAccessibleChild( sal_Int32 nIndex )
{
    SolarMutexGuard aGuard;

    ThrowIfDisposed();

    const SwAccessibleChild aChild( GetChild( *(GetMap()), nIndex ) );
    if( !aChild.IsValid() )
    {
        uno::Reference < XAccessibleContext > xThis( this );
        lang::IndexOutOfBoundsException aExcept(
                "index out of bounds",
                xThis );
        throw aExcept;
    }

    uno::Reference< XAccessible > xChild;
    if( aChild.GetSwFrame() )
    {
        ::rtl::Reference < SwAccessibleContext > xChildImpl(
                GetMap()->GetContextImpl( aChild.GetSwFrame(), !m_isDisposing )  );
        if( xChildImpl.is() )
        {
            xChildImpl->SetParent( this );
            xChild = xChildImpl.get();
        }
    }
    else if ( aChild.GetDrawObject() )
    {
        ::rtl::Reference < ::accessibility::AccessibleShape > xChildImpl(
                GetMap()->GetContextImpl( aChild.GetDrawObject(),
                                          this, !m_isDisposing) );
        if( xChildImpl.is() )
            xChild = xChildImpl.get();
    }
    else if ( aChild.GetWindow() )
    {
        xChild = aChild.GetWindow()->GetAccessible();
    }

    return xChild;
}

css::uno::Sequence<uno::Reference<XAccessible>> SAL_CALL
    SwAccessibleContext::getAccessibleChildren()
{
    SolarMutexGuard aGuard;

    ThrowIfDisposed();

    std::list< sw::access::SwAccessibleChild > aChildren;
    GetChildren( *GetMap(), aChildren );

    std::vector<uno::Reference<XAccessible>> aRet;
    aRet.reserve(aChildren.size());
    for (const auto & rSwChild : aChildren)
    {
        uno::Reference< XAccessible > xChild;
        if( rSwChild.GetSwFrame() )
        {
            ::rtl::Reference < SwAccessibleContext > xChildImpl(
                    GetMap()->GetContextImpl( rSwChild.GetSwFrame(), !m_isDisposing )  );
            if( xChildImpl.is() )
            {
                xChildImpl->SetParent( this );
                xChild = xChildImpl.get();
            }
        }
        else if ( rSwChild.GetDrawObject() )
        {
            ::rtl::Reference < ::accessibility::AccessibleShape > xChildImpl(
                    GetMap()->GetContextImpl( rSwChild.GetDrawObject(),
                                              this, !m_isDisposing) );
            if( xChildImpl.is() )
                xChild = xChildImpl.get();
        }
        else if ( rSwChild.GetWindow() )
        {
            xChild = rSwChild.GetWindow()->GetAccessible();
        }
        aRet.push_back(xChild);
    }
    return comphelper::containerToSequence(aRet);
}

uno::Reference< XAccessible> SwAccessibleContext::getAccessibleParentImpl()
{
    SolarMutexGuard aGuard;

    const SwFrame *pUpper = GetParent();
    OSL_ENSURE( pUpper != nullptr || m_isDisposing, "no upper found" );

    uno::Reference< XAccessible > xAcc;
    if( pUpper )
        xAcc = GetMap()->GetContext( pUpper, !m_isDisposing );

    OSL_ENSURE( xAcc.is() || m_isDisposing, "no parent found" );

    // Remember the parent as weak ref.
    {
        osl::MutexGuard aWeakParentGuard( m_Mutex );
        m_xWeakParent = xAcc;
    }

    return xAcc;
}

uno::Reference< XAccessible> SAL_CALL SwAccessibleContext::getAccessibleParent()
{
    SolarMutexGuard aGuard;

    ThrowIfDisposed();

    return getAccessibleParentImpl();
}

sal_Int32 SAL_CALL SwAccessibleContext::getAccessibleIndexInParent()
{
    SolarMutexGuard aGuard;

    ThrowIfDisposed();

    const SwFrame *pUpper = GetParent();
    OSL_ENSURE( pUpper != nullptr || m_isDisposing, "no upper found" );

    sal_Int32 nIndex = -1;
    if( pUpper )
    {
        ::rtl::Reference < SwAccessibleContext > xAccImpl(
            GetMap()->GetContextImpl(pUpper, !m_isDisposing) );
        OSL_ENSURE( xAccImpl.is() || m_isDisposing, "no parent found" );
        if( xAccImpl.is() )
            nIndex = xAccImpl->GetChildIndex( *(GetMap()), SwAccessibleChild(GetFrame()) );
    }

    return nIndex;
}

sal_Int16 SAL_CALL SwAccessibleContext::getAccessibleRole()
{
    return m_nRole;
}

OUString SAL_CALL SwAccessibleContext::getAccessibleName()
{
    return m_sName;
}

uno::Reference< XAccessibleRelationSet> SAL_CALL
    SwAccessibleContext::getAccessibleRelationSet()
{
    // by default there are no relations
    uno::Reference< XAccessibleRelationSet> xRet( new utl::AccessibleRelationSetHelper() );
    return xRet;
}

uno::Reference<XAccessibleStateSet> SAL_CALL
    SwAccessibleContext::getAccessibleStateSet()
{
    SolarMutexGuard aGuard;

    ThrowIfDisposed();

    ::utl::AccessibleStateSetHelper *pStateSet =
        new ::utl::AccessibleStateSetHelper;

    if (m_isSelectedInDoc)
        pStateSet->AddState( AccessibleStateType::SELECTED );

    uno::Reference<XAccessibleStateSet> xStateSet( pStateSet );
    GetStates( *pStateSet );

    return xStateSet;
}

lang::Locale SAL_CALL SwAccessibleContext::getLocale()
{
    SolarMutexGuard aGuard;

    lang::Locale aLoc( Application::GetSettings().GetLanguageTag().getLocale() );
    return aLoc;
}

void SAL_CALL SwAccessibleContext::addAccessibleEventListener(
            const uno::Reference< XAccessibleEventListener >& xListener )
{
    if (xListener.is())
    {
        SolarMutexGuard aGuard;
        if (!m_nClientId)
            m_nClientId = comphelper::AccessibleEventNotifier::registerClient( );
        comphelper::AccessibleEventNotifier::addEventListener( m_nClientId, xListener );
    }
}

void SAL_CALL SwAccessibleContext::removeAccessibleEventListener(
            const uno::Reference< XAccessibleEventListener >& xListener )
{
    if (xListener.is() && m_nClientId)
    {
        SolarMutexGuard aGuard;
        sal_Int32 nListenerCount = comphelper::AccessibleEventNotifier::removeEventListener( m_nClientId, xListener );
        if ( !nListenerCount )
        {
            // no listeners anymore
            // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client),
            // and at least to us not firing any events anymore, in case somebody calls
            // NotifyAccessibleEvent, again
            comphelper::AccessibleEventNotifier::revokeClient( m_nClientId );
            m_nClientId = 0;
        }
    }
}

static bool lcl_PointInRectangle(const awt::Point & aPoint,
                                     const awt::Rectangle & aRect)
{
    long nDiffX = aPoint.X - aRect.X;
    long nDiffY = aPoint.Y - aRect.Y;

    return
        nDiffX >= 0 && nDiffX < aRect.Width && nDiffY >= 0 &&
        nDiffY < aRect.Height;

}

sal_Bool SAL_CALL SwAccessibleContext::containsPoint(
            const awt::Point& aPoint )
{
    awt::Rectangle aPixBounds = getBoundsImpl(true);
    aPixBounds.X = 0;
    aPixBounds.Y = 0;

    return lcl_PointInRectangle(aPoint, aPixBounds);
}

uno::Reference< XAccessible > SAL_CALL SwAccessibleContext::getAccessibleAtPoint(
                const awt::Point& aPoint )
{
    SolarMutexGuard aGuard;

    ThrowIfDisposed();

    uno::Reference< XAccessible > xAcc;

    vcl::Window *pWin = GetWindow();
    if (!pWin)
    {
        throw uno::RuntimeException("no Window", static_cast<cppu::OWeakObject*>(this));
    }

    Point aPixPoint( aPoint.X, aPoint.Y ); // px rel to parent
    if( !GetFrame()->IsRootFrame() )
    {
        SwRect aLogBounds( GetBounds( *(GetMap()), GetFrame() ) ); // twip rel to doc root
        Point aPixPos( GetMap()->CoreToPixel( aLogBounds.SVRect() ).TopLeft() );
        aPixPoint.setX(aPixPoint.getX() + aPixPos.getX());
        aPixPoint.setY(aPixPoint.getY() + aPixPos.getY());
    }

    const SwAccessibleChild aChild( GetChildAtPixel( aPixPoint, *(GetMap()) ) );
    if( aChild.GetSwFrame() )
    {
        xAcc = GetMap()->GetContext( aChild.GetSwFrame() );
    }
    else if( aChild.GetDrawObject() )
    {
        xAcc = GetMap()->GetContext( aChild.GetDrawObject(), this );
    }
    else if ( aChild.GetWindow() )
    {
        xAcc = aChild.GetWindow()->GetAccessible();
    }

    return xAcc;
}

/**
   Get bounding box.

   There are two modes.

   - relative

     Return bounding box relative to parent if parent is no root
     frame. Otherwise return the absolute bounding box.

   - absolute

     Return the absolute bounding box.

   @param bRelative
   true: Use relative mode.
   false: Use absolute mode.
*/
awt::Rectangle SwAccessibleContext::getBoundsImpl(bool bRelative)
{
    SolarMutexGuard aGuard;

    ThrowIfDisposed();

    const SwFrame *pParent = GetParent();
    OSL_ENSURE( pParent, "no Parent found" );
    vcl::Window *pWin = GetWindow();

    if (!pParent)
    {
        throw uno::RuntimeException("no Parent", static_cast<cppu::OWeakObject*>(this));
    }
    if (!pWin)
    {
        throw uno::RuntimeException("no Window", static_cast<cppu::OWeakObject*>(this));
    }

    SwRect aLogBounds( GetBounds( *(GetMap()), GetFrame() ) ); // twip relative to document root
    tools::Rectangle aPixBounds( 0, 0, 0, 0 );
    if( GetFrame()->IsPageFrame() &&
        static_cast < const SwPageFrame * >( GetFrame() )->IsEmptyPage() )
    {
        OSL_ENSURE( GetShell()->IsPreview(), "empty page accessible?" );
        if( GetShell()->IsPreview() )
        {
            // adjust method call <GetMap()->GetPreviewPageSize()>
            sal_uInt16 nPageNum =
                static_cast < const SwPageFrame * >( GetFrame() )->GetPhyPageNum();
            aLogBounds.SSize( GetMap()->GetPreviewPageSize( nPageNum ) );
        }
    }
    if( !aLogBounds.IsEmpty() )
    {
        aPixBounds = GetMap()->CoreToPixel( aLogBounds.SVRect() );
        if( !pParent->IsRootFrame() && bRelative)
        {
            SwRect aParentLogBounds( GetBounds( *(GetMap()), pParent ) ); // twip rel to doc root
            Point aParentPixPos( GetMap()->CoreToPixel( aParentLogBounds.SVRect() ).TopLeft() );
            aPixBounds.Move( -aParentPixPos.getX(), -aParentPixPos.getY() );
        }
    }

    awt::Rectangle aBox( aPixBounds.Left(), aPixBounds.Top(),
                         aPixBounds.GetWidth(), aPixBounds.GetHeight() );

    return aBox;
}

awt::Rectangle SAL_CALL SwAccessibleContext::getBounds()
{
    return getBoundsImpl(true);
}

awt::Point SAL_CALL SwAccessibleContext::getLocation()
{
    awt::Rectangle aRect = getBoundsImpl(true);
    awt::Point aPoint(aRect.X, aRect.Y);

    return aPoint;
}

awt::Point SAL_CALL SwAccessibleContext::getLocationOnScreen()
{
    awt::Rectangle aRect = getBoundsImpl(false);

    Point aPixPos(aRect.X, aRect.Y);

    vcl::Window *pWin = GetWindow();
    if (!pWin)
    {
        throw uno::RuntimeException("no Window", static_cast<cppu::OWeakObject*>(this));
    }

    aPixPos = pWin->OutputToAbsoluteScreenPixel(aPixPos);
    awt::Point aPoint(aPixPos.getX(), aPixPos.getY());

    return aPoint;
}

awt::Size SAL_CALL SwAccessibleContext::getSize()
{
    awt::Rectangle aRect = getBoundsImpl(false);
    awt::Size aSize( aRect.Width, aRect.Height );

    return aSize;
}

void SAL_CALL SwAccessibleContext::grabFocus()
{
    SolarMutexGuard aGuard;

    ThrowIfDisposed();

    if( GetFrame()->IsFlyFrame() )
    {
        const SdrObject *pObj =
            static_cast < const SwFlyFrame * >( GetFrame() )->GetVirtDrawObj();
        if( pObj )
            Select( const_cast < SdrObject * >( pObj ), false );
    }
    else
    {
        const SwContentFrame *pCFrame = nullptr;
        if( GetFrame()->IsContentFrame() )
            pCFrame = static_cast< const SwContentFrame * >( GetFrame() );
        else if( GetFrame()->IsLayoutFrame() )
            pCFrame = static_cast< const SwLayoutFrame * >( GetFrame() )->ContainsContent();

        if( pCFrame && pCFrame->IsTextFrame() )
        {
            const SwTextFrame *pTextFrame = static_cast< const SwTextFrame * >( pCFrame );
            const SwTextNode *pTextNd = pTextFrame->GetTextNodeFirst();
            assert(pTextNd); // can it actually be null? probably not=>simplify
            if( pTextNd )
            {
                // create pam for selection
                SwPosition const aStartPos(pTextFrame->MapViewToModelPos(pTextFrame->GetOffset()));
                SwPaM aPaM( aStartPos );

                // set PaM at cursor shell
                Select( aPaM );
            }
        }
    }
}

sal_Int32 SAL_CALL SwAccessibleContext::getForeground()
{
    return sal_Int32(COL_BLACK);
}

sal_Int32 SAL_CALL SwAccessibleContext::getBackground()
{
    return sal_Int32(COL_WHITE);
}

sal_Bool SAL_CALL SwAccessibleContext::supportsService (const OUString& ServiceName)
{
    return cppu::supportsService(this, ServiceName);
}

void SwAccessibleContext::DisposeShape( const SdrObject *pObj,
                                ::accessibility::AccessibleShape *pAccImpl )
{
    ::rtl::Reference< ::accessibility::AccessibleShape > xAccImpl( pAccImpl );
    if( !xAccImpl.is() )
        xAccImpl = GetMap()->GetContextImpl( pObj, this );

    AccessibleEventObject aEvent;
    aEvent.EventId = AccessibleEventId::CHILD;
    uno::Reference< XAccessible > xAcc( xAccImpl.get() );
    aEvent.OldValue <<= xAcc;
    FireAccessibleEvent( aEvent );

    GetMap()->RemoveContext( pObj );
    xAccImpl->dispose();
}

void SwAccessibleContext::ScrolledInShape( ::accessibility::AccessibleShape *pAccImpl )
{
    if(nullptr == pAccImpl)
    {
        return ;
    }
    AccessibleEventObject aEvent;
    aEvent.EventId = AccessibleEventId::CHILD;
    uno::Reference< XAccessible > xAcc( pAccImpl );
    aEvent.NewValue <<= xAcc;
    FireAccessibleEvent( aEvent );

    if( pAccImpl->GetState( AccessibleStateType::FOCUSED ) )
    {
        vcl::Window *pWin = GetWindow();
        if( pWin && pWin->HasFocus() )
        {
            AccessibleEventObject aStateChangedEvent;
            aStateChangedEvent.EventId = AccessibleEventId::STATE_CHANGED;
            aStateChangedEvent.NewValue <<= AccessibleStateType::FOCUSED;
            aStateChangedEvent.Source = xAcc;

            FireAccessibleEvent( aStateChangedEvent );
        }
    }
}

void SwAccessibleContext::Dispose(bool bRecursive, bool bCanSkipInvisible)
{
    SolarMutexGuard aGuard;

    OSL_ENSURE( GetFrame() && GetMap(), "already disposed" );
    OSL_ENSURE( GetMap()->GetVisArea() == GetVisArea(),
                "invalid visible area for dispose" );

    m_isDisposing = true;

    // dispose children
    if( bRecursive )
        DisposeChildren(GetFrame(), bRecursive, bCanSkipInvisible);

    // get parent
    uno::Reference< XAccessible > xParent( GetWeakParent() );
    uno::Reference < XAccessibleContext > xThis( this );

    // send child event at parent
    if( xParent.is() )
    {
        SwAccessibleContext *pAcc = static_cast<SwAccessibleContext *>(xParent.get());

        AccessibleEventObject aEvent;
        aEvent.EventId = AccessibleEventId::CHILD;
        aEvent.OldValue <<= xThis;
        pAcc->FireAccessibleEvent( aEvent );
    }

    // set defunc state (it's not required to broadcast a state changed
    // event if the object is disposed afterwards)
    {
        osl::MutexGuard aDefuncStateGuard( m_Mutex );
        m_isDefuncState = true;
    }

    // broadcast dispose event
    if (m_nClientId)
    {
        comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing( m_nClientId, *this );
        m_nClientId =  0;
    }

    RemoveFrameFromAccessibleMap();
    ClearFrame();
    m_pMap = nullptr;
    m_wMap.reset();

    m_isDisposing = false;
}

void SwAccessibleContext::DisposeChild( const SwAccessibleChild& rChildFrameOrObj,
                                        bool bRecursive, bool bCanSkipInvisible )
{
    SolarMutexGuard aGuard;

    if ( !bCanSkipInvisible ||
         rChildFrameOrObj.AlwaysIncludeAsChild() ||
         IsShowing( *(GetMap()), rChildFrameOrObj ) ||
         !SwAccessibleChild( GetFrame() ).IsVisibleChildrenOnly() )
    {
        // If the object could have existed before, then there is nothing to do,
        // because no wrapper exists now and therefore no one is interested to
        // get notified of the movement.
        if( rChildFrameOrObj.GetSwFrame() )
        {
            ::rtl::Reference< SwAccessibleContext > xAccImpl =
                    GetMap()->GetContextImpl( rChildFrameOrObj.GetSwFrame(), false );
            if (xAccImpl)
                xAccImpl->Dispose( bRecursive );
        }
        else if ( rChildFrameOrObj.GetDrawObject() )
        {
            ::rtl::Reference< ::accessibility::AccessibleShape > xAccImpl =
                    GetMap()->GetContextImpl( rChildFrameOrObj.GetDrawObject(),
                                              this, false );
            if (xAccImpl)
                DisposeShape( rChildFrameOrObj.GetDrawObject(),
                              xAccImpl.get() );
        }
        else if ( rChildFrameOrObj.GetWindow() )
        {
            AccessibleEventObject aEvent;
            aEvent.EventId = AccessibleEventId::CHILD;
            uno::Reference< XAccessible > xAcc =
                                    rChildFrameOrObj.GetWindow()->GetAccessible();
            aEvent.OldValue <<= xAcc;
            FireAccessibleEvent( aEvent );
        }
    }
    else if( bRecursive && rChildFrameOrObj.GetSwFrame() )
        DisposeChildren(rChildFrameOrObj.GetSwFrame(), bRecursive, bCanSkipInvisible);
}

void SwAccessibleContext::InvalidatePosOrSize( const SwRect& )
{
    SolarMutexGuard aGuard;

    OSL_ENSURE( GetFrame() && !GetFrame()->getFrameArea().IsEmpty(), "context should have a size" );

    bool bIsOldShowingState;
    bool bIsNewShowingState = IsShowing( *(GetMap()) );
    {
        osl::MutexGuard aShowingStateGuard( m_Mutex );
        bIsOldShowingState = m_isShowingState;
        m_isShowingState = bIsNewShowingState;
    }

    if( bIsOldShowingState != bIsNewShowingState )
    {
        FireStateChangedEvent( AccessibleStateType::SHOWING,
                               bIsNewShowingState  );
    }
    else if( bIsNewShowingState )
    {
        // The frame stays visible -> broadcast event
        FireVisibleDataEvent();
    }

    // note: InvalidatePosOrSize must call InvalidateContent_ so that
    // SwAccessibleParagraph updates its portions, or dispose it
    // (see accmap.cxx: INVALID_CONTENT is contained in POS_CHANGED)
    if( !bIsNewShowingState &&
        SwAccessibleChild( GetParent() ).IsVisibleChildrenOnly() )
    {
        // this Dispose call was removed by IAccessibility2 implementation
        // without giving any reason why - without it we get stale
        // entries in SwAccessibleMap::mpFrameMap.
        Dispose(true);
    }
    else
    {
        InvalidateContent_( true );
    }
}

void SwAccessibleContext::InvalidateChildPosOrSize(
                    const SwAccessibleChild& rChildFrameOrObj,
                    const SwRect& rOldFrame )
{
    SolarMutexGuard aGuard;

    // this happens during layout, e.g. when a page is deleted and next page's
    // header/footer moves backward such an event is generated
    SAL_INFO_IF(rChildFrameOrObj.GetSwFrame() &&
            rChildFrameOrObj.GetSwFrame()->getFrameArea().IsEmpty(),
            "sw.a11y", "child context should have a size");

    if ( rChildFrameOrObj.AlwaysIncludeAsChild() )
    {
        // nothing to do;
        return;
    }

    const bool bVisibleChildrenOnly = SwAccessibleChild( GetFrame() ).IsVisibleChildrenOnly();
    const bool bNew = rOldFrame.IsEmpty() ||
                     ( rOldFrame.Left() == 0 && rOldFrame.Top() == 0 );
    if( IsShowing( *(GetMap()), rChildFrameOrObj ) )
    {
        // If the object could have existed before, then there is nothing to do,
        // because no wrapper exists now and therefore no one is interested to
        // get notified of the movement.
        if( bNew || (bVisibleChildrenOnly && !IsShowing( rOldFrame )) )
        {
            if( rChildFrameOrObj.GetSwFrame() )
            {
                // The frame becomes visible. A child event must be send.
                ::rtl::Reference< SwAccessibleContext > xAccImpl =
                    GetMap()->GetContextImpl( rChildFrameOrObj.GetSwFrame() );
                xAccImpl->ScrolledIn();
            }
            else if ( rChildFrameOrObj.GetDrawObject() )
            {
                ::rtl::Reference< ::accessibility::AccessibleShape > xAccImpl =
                        GetMap()->GetContextImpl( rChildFrameOrObj.GetDrawObject(),
                                                  this );
                // #i37790#
                if ( xAccImpl.is() )
                {
                    ScrolledInShape( xAccImpl.get() );
                }
                else
                {
                    OSL_FAIL( "<SwAccessibleContext::InvalidateChildPosOrSize(..)> - no accessible shape found." );
                }
            }
            else if ( rChildFrameOrObj.GetWindow() )
            {
                AccessibleEventObject aEvent;
                aEvent.EventId = AccessibleEventId::CHILD;
                aEvent.NewValue <<= rChildFrameOrObj.GetWindow()->GetAccessible();
                FireAccessibleEvent( aEvent );
            }
        }
    }
    else
    {
        // If the frame was visible before, then a child event for the parent
        // needs to be send. However, there is no wrapper existing, and so
        // no notifications for grandchildren are required. If the are
        // grandgrandchildren, they would be notified by the layout.
        if( bVisibleChildrenOnly &&
            !bNew && IsShowing( rOldFrame ) )
        {
            if( rChildFrameOrObj.GetSwFrame() )
            {
                ::rtl::Reference< SwAccessibleContext > xAccImpl =
                    GetMap()->GetContextImpl( rChildFrameOrObj.GetSwFrame() );
                xAccImpl->SetParent( this );
                xAccImpl->Dispose( true );
            }
            else if ( rChildFrameOrObj.GetDrawObject() )
            {
                ::rtl::Reference< ::accessibility::AccessibleShape > xAccImpl =
                        GetMap()->GetContextImpl( rChildFrameOrObj.GetDrawObject(),
                                                  this );
                DisposeShape( rChildFrameOrObj.GetDrawObject(),
                          xAccImpl.get() );
            }
            else if ( rChildFrameOrObj.GetWindow() )
            {
                OSL_FAIL( "<SwAccessibleContext::InvalidateChildPosOrSize(..)> - not expected to handle dispose of child of type <vcl::Window>." );
            }
        }
    }
}

void SwAccessibleContext::InvalidateContent()
{
    SolarMutexGuard aGuard;

    InvalidateContent_( false );
}

void SwAccessibleContext::InvalidateCursorPos()
{
    SolarMutexGuard aGuard;

    InvalidateCursorPos_();
}

void SwAccessibleContext::InvalidateFocus()
{
    SolarMutexGuard aGuard;

    InvalidateFocus_();
}

// #i27301# - use new type definition for <_nStates>
void SwAccessibleContext::InvalidateStates( AccessibleStates _nStates )
{
    if( GetMap() )
    {
        SwViewShell *pVSh = GetMap()->GetShell();
        if( pVSh )
        {
            if( _nStates & AccessibleStates::EDITABLE )
            {
                bool bIsOldEditableState;
                bool bIsNewEditableState = IsEditable( pVSh );
                {
                    osl::MutexGuard aGuard( m_Mutex );
                    bIsOldEditableState = m_isEditableState;
                    m_isEditableState = bIsNewEditableState;
                }

                if( bIsOldEditableState != bIsNewEditableState )
                    FireStateChangedEvent( AccessibleStateType::EDITABLE,
                                           bIsNewEditableState  );
            }
            if( _nStates & AccessibleStates::OPAQUE )
            {
                bool bIsOldOpaqueState;
                bool bIsNewOpaqueState = IsOpaque( pVSh );
                {
                    osl::MutexGuard aGuard( m_Mutex );
                    bIsOldOpaqueState = m_isOpaqueState;
                    m_isOpaqueState = bIsNewOpaqueState;
                }

                if( bIsOldOpaqueState != bIsNewOpaqueState )
                    FireStateChangedEvent( AccessibleStateType::OPAQUE,
                                           bIsNewOpaqueState  );
            }
        }

        InvalidateChildrenStates( GetFrame(), _nStates );
    }
}

void SwAccessibleContext::InvalidateRelation( sal_uInt16 nType )
{
    AccessibleEventObject aEvent;
    aEvent.EventId = nType;

    FireAccessibleEvent( aEvent );
}

/** #i27301# - text selection has changed */
void SwAccessibleContext::InvalidateTextSelection()
{
    AccessibleEventObject aEvent;
    aEvent.EventId = AccessibleEventId::TEXT_SELECTION_CHANGED;

    FireAccessibleEvent( aEvent );
}

/** #i88069# - attributes has changed */
void SwAccessibleContext::InvalidateAttr()
{
    AccessibleEventObject aEvent;
    aEvent.EventId = AccessibleEventId::TEXT_ATTRIBUTE_CHANGED;

    FireAccessibleEvent( aEvent );
}

bool SwAccessibleContext::HasCursor()
{
    return false;
}

bool SwAccessibleContext::Select( SwPaM *pPaM, SdrObject *pObj,
                                  bool bAdd )
{
    SwCursorShell* pCursorShell = GetCursorShell();
    if( !pCursorShell )
        return false;

    SwFEShell* pFEShell = dynamic_cast<const SwFEShell*>( pCursorShell) !=  nullptr
                                ? static_cast<SwFEShell*>( pCursorShell )
                                : nullptr;
    // Get rid of activated OLE object
    if( pFEShell )
        pFEShell->FinishOLEObj();

    SwWrtShell* pWrtShell = dynamic_cast<const SwWrtShell*>( pCursorShell) !=  nullptr
                                ? static_cast<SwWrtShell*>( pCursorShell )
                                : nullptr;

    bool bRet = false;
    if( pObj )
    {
        if( pFEShell )
        {
            sal_uInt8 nFlags = bAdd ? SW_ADD_SELECT : 0;
            pFEShell->SelectObj( Point(), nFlags, pObj );
            bRet = true;
        }
    }
    else if( pPaM )
    {
        // Get rid of frame selection. If there is one, make text cursor
        // visible again.
        bool bCallShowCursor = false;
        if( pFEShell && (pFEShell->IsFrameSelected() ||
                         pFEShell->IsObjSelected()) )
        {
            Point aPt( LONG_MIN, LONG_MIN );
            pFEShell->SelectObj( aPt );
            bCallShowCursor = true;
        }
        pCursorShell->KillPams();
        if( pWrtShell && pPaM->HasMark() )
            // We have to do this or SwWrtShell can't figure out that it needs
            // to kill the selection later, when the user moves the cursor.
            pWrtShell->SttSelect();
        pCursorShell->SetSelection( *pPaM );
        if( pPaM->HasMark() && *pPaM->GetPoint() == *pPaM->GetMark())
            // Setting a "Selection" that starts and ends at the same spot
            // should remove the selection rather than create an empty one, so
            // that we get defined behavior if accessibility sets the cursor
            // later.
            pCursorShell->ClearMark();
        if( bCallShowCursor )
            pCursorShell->ShowCursor();
        bRet = true;
    }

    return bRet;
}

OUString SwAccessibleContext::GetResource(const char* pResId,
                                          const OUString *pArg1,
                                          const OUString *pArg2)
{
    OUString sStr = SwResId(pResId);

    if( pArg1 )
    {
        sStr = sStr.replaceFirst( "$(ARG1)", *pArg1 );
    }
    if( pArg2 )
    {
        sStr = sStr.replaceFirst( "$(ARG2)", *pArg2 );
    }

    return sStr;
}

void SwAccessibleContext::RemoveFrameFromAccessibleMap()
{
    assert(m_refCount > 0); // must be alive to do this without using m_wMap
    if (m_isRegisteredAtAccessibleMap && GetFrame() && GetMap())
        GetMap()->RemoveContext( GetFrame() );
}

bool SwAccessibleContext::HasAdditionalAccessibleChildren()
{
    bool bRet( false );

    if ( GetFrame()->IsTextFrame() )
    {
        SwPostItMgr* pPostItMgr = GetMap()->GetShell()->GetPostItMgr();
        if ( pPostItMgr && pPostItMgr->HasNotes() && pPostItMgr->ShowNotes() )
        {
            bRet = pPostItMgr->HasFrameConnectedSidebarWins( *(GetFrame()) );
        }
    }

    return bRet;
}

/** #i88070# - get additional accessible child by index */
vcl::Window* SwAccessibleContext::GetAdditionalAccessibleChild( const sal_Int32 nIndex )
{
    vcl::Window* pAdditionalAccessibleChild( nullptr );

    if ( GetFrame()->IsTextFrame() )
    {
        SwPostItMgr* pPostItMgr = GetMap()->GetShell()->GetPostItMgr();
        if ( pPostItMgr && pPostItMgr->HasNotes() && pPostItMgr->ShowNotes() )
        {
            pAdditionalAccessibleChild =
                    pPostItMgr->GetSidebarWinForFrameByIndex( *(GetFrame()), nIndex );
        }
    }

    return pAdditionalAccessibleChild;
}

/** #i88070# - get all additional accessible children */
void SwAccessibleContext::GetAdditionalAccessibleChildren( std::vector< vcl::Window* >* pChildren )
{
    if ( GetFrame()->IsTextFrame() )
    {
        SwPostItMgr* pPostItMgr = GetMap()->GetShell()->GetPostItMgr();
        if ( pPostItMgr && pPostItMgr->HasNotes() && pPostItMgr->ShowNotes() )
        {
            pPostItMgr->GetAllSidebarWinForFrame( *(GetFrame()), pChildren );
        }
    }
}

bool SwAccessibleContext::SetSelectedState(bool const bSelected)
{
    if (m_isSelectedInDoc != bSelected)
    {
        m_isSelectedInDoc = bSelected;
        FireStateChangedEvent( AccessibleStateType::SELECTED, bSelected );
        return true;
    }
    return false;
};

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