/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/




#include <hintids.hxx>
#include <editeng/brshitem.hxx>
#include <flyfrm.hxx>
#include <rootfrm.hxx>
#include <txtfrm.hxx>
#include <sectfrm.hxx>
#include <section.hxx>
#include <viewsh.hxx>
#include <viewopt.hxx>
#include <doc.hxx>
#include <frmatr.hxx>
#include <pagefrm.hxx>
#include <pagedesc.hxx>
#include <fmtanchr.hxx>
#include <fldbas.hxx>
#include <dcontact.hxx>
#include <accmap.hxx>
#include <accfrmobjslist.hxx>
#include <accfrmobjmap.hxx>
#include <accframe.hxx>

using namespace sw::access;

// Regarding visibilily (or in terms of accessibility: regarding the showing
// state): A frame is visible and therfor contained in the tree if its frame
// size overlaps with the visible area. The bounding box however is the
// frame's paint area.
/* static */ sal_Int32 SwAccessibleFrame::GetChildCount( SwAccessibleMap& rAccMap,
                                                         const SwRect& rVisArea,
                                                         const SwFrm *pFrm,
                                                         sal_Bool bInPagePreview )
{
    sal_Int32 nCount = 0;

    const SwAccessibleChildSList aVisList( rVisArea, *pFrm, rAccMap );
    SwAccessibleChildSList::const_iterator aIter( aVisList.begin() );
    while( aIter != aVisList.end() )
    {
        const SwAccessibleChild& rLower = *aIter;
        if( rLower.IsAccessible( bInPagePreview ) )
        {
            nCount++;
        }
        else if( rLower.GetSwFrm() )
        {
            // There are no unaccessible SdrObjects that count
            nCount += GetChildCount( rAccMap,
                                     rVisArea, rLower.GetSwFrm(),
                                     bInPagePreview );
        }
        ++aIter;
    }

    return nCount;
}

/* static */ SwAccessibleChild SwAccessibleFrame::GetChild(
                                                SwAccessibleMap& rAccMap,
                                                const SwRect& rVisArea,
                                                const SwFrm& rFrm,
                                                sal_Int32& rPos,
                                                sal_Bool bInPagePreview )
{
    SwAccessibleChild aRet;

    if( rPos >= 0 )
    {
        if( SwAccessibleChildMap::IsSortingRequired( rFrm ) )
        {
            // We need a sorted list here
            const SwAccessibleChildMap aVisMap( rVisArea, rFrm, rAccMap );
            SwAccessibleChildMap::const_iterator aIter( aVisMap.begin() );
            while( aIter != aVisMap.end() && !aRet.IsValid() )
            {
                const SwAccessibleChild& rLower = (*aIter).second;
                if( rLower.IsAccessible( bInPagePreview ) )
                {
                    if( 0 == rPos )
                        aRet = rLower;
                    else
                        rPos--;
                }
                else if( rLower.GetSwFrm() )
                {
                    // There are no unaccessible SdrObjects that count
                    aRet = GetChild( rAccMap,
                                     rVisArea, *(rLower.GetSwFrm()), rPos,
                                     bInPagePreview );
                }
                ++aIter;
            }
        }
        else
        {
            // The unsorted list is sorted enough, because it return lower
            // frames in the correct order.
            const SwAccessibleChildSList aVisList( rVisArea, rFrm, rAccMap );
            SwAccessibleChildSList::const_iterator aIter( aVisList.begin() );
            while( aIter != aVisList.end() && !aRet.IsValid() )
            {
                const SwAccessibleChild& rLower = *aIter;
                if( rLower.IsAccessible( bInPagePreview ) )
                {
                    if( 0 == rPos )
                        aRet = rLower;
                    else
                        rPos--;
                }
                else if( rLower.GetSwFrm() )
                {
                    // There are no unaccessible SdrObjects that count
                    aRet = GetChild( rAccMap,
                                     rVisArea, *(rLower.GetSwFrm()), rPos,
                                     bInPagePreview );
                }
                ++aIter;
            }
        }
    }

    return aRet;
}

/* static */ sal_Bool SwAccessibleFrame::GetChildIndex(
                                                SwAccessibleMap& rAccMap,
                                                const SwRect& rVisArea,
                                                const SwFrm& rFrm,
                                                const SwAccessibleChild& rChild,
                                                sal_Int32& rPos,
                                                sal_Bool bInPagePreview )
{
    sal_Bool bFound = sal_False;

    if( SwAccessibleChildMap::IsSortingRequired( rFrm ) )
    {
        // We need a sorted list here
        const SwAccessibleChildMap aVisMap( rVisArea, rFrm, rAccMap );
        SwAccessibleChildMap::const_iterator aIter( aVisMap.begin() );
        while( aIter != aVisMap.end() && !bFound )
        {
            const SwAccessibleChild& rLower = (*aIter).second;
            if( rLower.IsAccessible( bInPagePreview ) )
            {
                if( rChild == rLower )
                    bFound = sal_True;
                else
                    rPos++;
            }
            else if( rLower.GetSwFrm() )
            {
                // There are no unaccessible SdrObjects that count
                bFound = GetChildIndex( rAccMap,
                                        rVisArea, *(rLower.GetSwFrm()), rChild,
                                        rPos, bInPagePreview );
            }
            ++aIter;
        }
    }
    else
    {
        // The unsorted list is sorted enough, because it return lower
        // frames in the correct order.
        const SwAccessibleChildSList aVisList( rVisArea, rFrm, rAccMap );
        SwAccessibleChildSList::const_iterator aIter( aVisList.begin() );
        while( aIter != aVisList.end() && !bFound )
        {
            const SwAccessibleChild& rLower = *aIter;
            if( rLower.IsAccessible( bInPagePreview ) )
            {
                if( rChild == rLower )
                    bFound = sal_True;
                else
                    rPos++;
            }
            else if( rLower.GetSwFrm() )
            {
                // There are no unaccessible SdrObjects that count
                bFound = GetChildIndex( rAccMap,
                                        rVisArea, *(rLower.GetSwFrm()), rChild,
                                        rPos, bInPagePreview );
            }
            ++aIter;
        }
    }

    return bFound;
}

SwAccessibleChild SwAccessibleFrame::GetChildAtPixel( const SwRect& rVisArea,
                                          const SwFrm& rFrm,
                                          const Point& rPixPos,
                                          sal_Bool bInPagePreview,
                                          SwAccessibleMap& rAccMap )
{
    SwAccessibleChild aRet;

    if( SwAccessibleChildMap::IsSortingRequired( rFrm ) )
    {
        // We need a sorted list here, and we have to reverse iterate,
        // because objects in front should be returned.
        const SwAccessibleChildMap aVisMap( rVisArea, rFrm, rAccMap );
        SwAccessibleChildMap::const_reverse_iterator aRIter( aVisMap.rbegin() );
        while( aRIter != aVisMap.rend() && !aRet.IsValid() )
        {
            const SwAccessibleChild& rLower = (*aRIter).second;
            // A frame is returned if it's frame size is inside the visarea
            // and the positiion is inside the frame's paint area.
            if( rLower.IsAccessible( bInPagePreview ) )
            {
                SwRect aLogBounds( rLower.GetBounds( rAccMap ) );
                if( !aLogBounds.IsEmpty() )
                {
                    Rectangle aPixBounds( rAccMap.CoreToPixel( aLogBounds.SVRect() ) );
                    if( aPixBounds.IsInside( rPixPos ) )
                        aRet = rLower;
                }
            }
            else if( rLower.GetSwFrm() )
            {
                // There are no unaccessible SdrObjects that count
                aRet = GetChildAtPixel( rVisArea, *(rLower.GetSwFrm()), rPixPos,
                                        bInPagePreview, rAccMap );
            }
            ++aRIter;
        }
    }
    else
    {
        // The unsorted list is sorted enough, because it returns lower
        // frames in the correct order. Morover, we can iterate forward,
        // because the lowers don't overlap!
        const SwAccessibleChildSList aVisList( rVisArea, rFrm, rAccMap );
        SwAccessibleChildSList::const_iterator aIter( aVisList.begin() );
        while( aIter != aVisList.end() && !aRet.IsValid() )
        {
            const SwAccessibleChild& rLower = *aIter;
            // A frame is returned if it's frame size is inside the visarea
            // and the positiion is inside the frame's paint area.
            if( rLower.IsAccessible( bInPagePreview ) )
            {
                SwRect aLogBounds( rLower.GetBounds( rAccMap ) );
                if( !aLogBounds.IsEmpty() )
                {
                    Rectangle aPixBounds( rAccMap.CoreToPixel( aLogBounds.SVRect() ) );
                    if( aPixBounds.IsInside( rPixPos ) )
                        aRet = rLower;
                }
            }
            else if( rLower.GetSwFrm() )
            {
                // There are no unaccessible SdrObjects that count
                aRet = GetChildAtPixel( rVisArea, *(rLower.GetSwFrm()), rPixPos,
                                   bInPagePreview, rAccMap );
            }
            ++aIter;
        }
    }

    return aRet;
}

/* static */ void SwAccessibleFrame::GetChildren( SwAccessibleMap& rAccMap,
                                                  const SwRect& rVisArea,
                                                  const SwFrm& rFrm,
                                                  ::std::list< SwAccessibleChild >& rChildren,
                                                  sal_Bool bInPagePreview )
{
    if( SwAccessibleChildMap::IsSortingRequired( rFrm ) )
    {
        // We need a sorted list here
        const SwAccessibleChildMap aVisMap( rVisArea, rFrm, rAccMap );
        SwAccessibleChildMap::const_iterator aIter( aVisMap.begin() );
        while( aIter != aVisMap.end() )
        {
            const SwAccessibleChild& rLower = (*aIter).second;
            if( rLower.IsAccessible( bInPagePreview ) )
            {
                rChildren.push_back( rLower );
            }
            else if( rLower.GetSwFrm() )
            {
                // There are no unaccessible SdrObjects that count
                GetChildren( rAccMap, rVisArea, *(rLower.GetSwFrm()),
                             rChildren, bInPagePreview );
            }
            ++aIter;
        }
    }
    else
    {
        // The unsorted list is sorted enough, because it return lower
        // frames in the correct order.
        const SwAccessibleChildSList aVisList( rVisArea, rFrm, rAccMap );
        SwAccessibleChildSList::const_iterator aIter( aVisList.begin() );
        while( aIter != aVisList.end() )
        {
            const SwAccessibleChild& rLower = *aIter;
            if( rLower.IsAccessible( bInPagePreview ) )
            {
                rChildren.push_back( rLower );
            }
            else if( rLower.GetSwFrm() )
            {
                // There are no unaccessible SdrObjects that count
                GetChildren( rAccMap, rVisArea, *(rLower.GetSwFrm()),
                             rChildren, bInPagePreview );
            }
            ++aIter;
        }
    }
}

SwRect SwAccessibleFrame::GetBounds( const SwAccessibleMap& rAccMap,
                                     const SwFrm *pFrm )
{
    if( !pFrm )
        pFrm = GetFrm();

    SwAccessibleChild aFrm( pFrm );
    SwRect aBounds( aFrm.GetBounds( rAccMap ).Intersection( maVisArea ) );
    return aBounds;
}

sal_Bool SwAccessibleFrame::IsEditable( ViewShell *pVSh ) const
{
    const SwFrm *pFrm = GetFrm();
    if( !pFrm )
        return sal_False;

    OSL_ENSURE( pVSh, "no view shell" );
    if( pVSh && (pVSh->GetViewOptions()->IsReadonly() ||
                 pVSh->IsPreView()) )
        return sal_False;

    if( !pFrm->IsRootFrm() && pFrm->IsProtected() )
        return sal_False;

    return sal_True;
}

sal_Bool SwAccessibleFrame::IsOpaque( ViewShell *pVSh ) const
{
    SwAccessibleChild aFrm( GetFrm() );
    if( !aFrm.GetSwFrm() )
        return sal_False;

    OSL_ENSURE( pVSh, "no view shell" );
    if( !pVSh )
        return sal_False;

    const SwViewOption *pVOpt = pVSh->GetViewOptions();
    do
    {
        const SwFrm *pFrm = aFrm.GetSwFrm();
        if( pFrm->IsRootFrm() )
            return sal_True;

        if( pFrm->IsPageFrm() && !pVOpt->IsPageBack() )
            return sal_False;

        const SvxBrushItem &rBack = pFrm->GetAttrSet()->GetBackground();
        if( !rBack.GetColor().GetTransparency() ||
             rBack.GetGraphicPos() != GPOS_NONE )
            return sal_True;

        // If a fly frame has a transparent background color, we have to consider the background.
        // But a background color "no fill"/"auto fill" has *not* to be considered.
        if( pFrm->IsFlyFrm() &&
            (rBack.GetColor().GetTransparency() != 0) &&
            (rBack.GetColor() != COL_TRANSPARENT)
          )
            return sal_True;

        if( pFrm->IsSctFrm() )
        {
            const SwSection* pSection = ((SwSectionFrm*)pFrm)->GetSection();
            if( pSection && ( TOX_HEADER_SECTION == pSection->GetType() ||
                TOX_CONTENT_SECTION == pSection->GetType() ) &&
                !pVOpt->IsReadonly() &&
                SwViewOption::IsIndexShadings() )
                return sal_True;
        }
        if( pFrm->IsFlyFrm() )
            aFrm = static_cast<const SwFlyFrm*>(pFrm)->GetAnchorFrm();
        else
            aFrm = pFrm->GetUpper();
    } while( aFrm.GetSwFrm() && !aFrm.IsAccessible( IsInPagePreview() ) );

    return sal_False;
}

SwAccessibleFrame::SwAccessibleFrame( const SwRect& rVisArea,
                                      const SwFrm *pF,
                                      sal_Bool bIsPagePreview ) :
    maVisArea( rVisArea ),
    mpFrm( pF ),
    mbIsInPagePreview( bIsPagePreview )
{
}

SwAccessibleFrame::~SwAccessibleFrame()
{
}

/* static */ const SwFrm* SwAccessibleFrame::GetParent( const SwAccessibleChild& rFrmOrObj,
                                                        sal_Bool bInPagePreview )
{
    return rFrmOrObj.GetParent( bInPagePreview );
}

String SwAccessibleFrame::GetFormattedPageNumber() const
{
    sal_uInt16 nPageNum = GetFrm()->GetVirtPageNum();
    sal_uInt32 nFmt = GetFrm()->FindPageFrm()->GetPageDesc()
                              ->GetNumType().GetNumberingType();
    if( SVX_NUM_NUMBER_NONE == nFmt )
        nFmt = SVX_NUM_ARABIC;

    String sRet( FormatNumber( nPageNum, nFmt ) );
    return sRet;
}

sal_Int32 SwAccessibleFrame::GetChildCount( SwAccessibleMap& rAccMap ) const
{
    return GetChildCount( rAccMap, maVisArea, mpFrm, IsInPagePreview() );
}

sw::access::SwAccessibleChild SwAccessibleFrame::GetChild(
                                                SwAccessibleMap& rAccMap,
                                                sal_Int32 nPos ) const
{
    return SwAccessibleFrame::GetChild( rAccMap, maVisArea, *mpFrm, nPos, IsInPagePreview() );
}

sal_Int32 SwAccessibleFrame::GetChildIndex( SwAccessibleMap& rAccMap,
                                            const sw::access::SwAccessibleChild& rChild ) const
{
    sal_Int32 nPos = 0;
    return GetChildIndex( rAccMap, maVisArea, *mpFrm, rChild, nPos, IsInPagePreview() )
           ? nPos
           : -1L;
}

sw::access::SwAccessibleChild SwAccessibleFrame::GetChildAtPixel(
                                                const Point& rPos,
                                                SwAccessibleMap& rAccMap ) const
{
    return GetChildAtPixel( maVisArea, *mpFrm, rPos, IsInPagePreview(), rAccMap );
}

void SwAccessibleFrame::GetChildren( SwAccessibleMap& rAccMap,
                                     ::std::list< sw::access::SwAccessibleChild >& rChildren ) const
{
    GetChildren( rAccMap, maVisArea, *mpFrm, rChildren, IsInPagePreview() );
}

sal_Bool SwAccessibleFrame::IsShowing( const SwAccessibleMap& rAccMap,
                                       const sw::access::SwAccessibleChild& rFrmOrObj ) const
{
    return IsShowing( rFrmOrObj.GetBox( rAccMap ) );
}

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