 /*************************************************************************
 *
 * 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.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sw.hxx"



#include <hintids.hxx>
#include <editeng/brshitem.hxx>
#include <flyfrm.hxx>
#include <rootfrm.hxx>
#include <txtfrm.hxx>
#include <sectfrm.hxx>
#include <pagefrm.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;

	ASSERT( 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;

	ASSERT( 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;

        /// OD 20.08.2002 #99657#
        ///     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 ) );
}

