/**************************************************************
 * 
 * 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
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sw.hxx"
#include <anchoredobjectposition.hxx>
#ifndef _ENVIRONMENTOFANCHOREDOBJECT
#include <environmentofanchoredobject.hxx>
#endif
#include <flyfrm.hxx>
#include <flyfrms.hxx>
#include <txtfrm.hxx>
#include <pagefrm.hxx>
#include <frmtool.hxx>
#ifndef _SVX_SVDOBJ_HXX
#include <svx/svdobj.hxx>
#endif
#include <dflyobj.hxx>
#include <dcontact.hxx>
#include <frmfmt.hxx>
#include <fmtornt.hxx>
// --> OD 2006-03-15 #i62875#
#include <fmtfollowtextflow.hxx>
// <--
#include <editeng/lrspitem.hxx>
#include <editeng/ulspitem.hxx>
#include <ndtxt.hxx>
#include <IDocumentSettingAccess.hxx>

using namespace ::com::sun::star;
using namespace objectpositioning;

// **************************************************************************
// constructor, destructor, initialization
// **************************************************************************
SwAnchoredObjectPosition::SwAnchoredObjectPosition( SdrObject& _rDrawObj )
    : mrDrawObj( _rDrawObj ),
      mbIsObjFly( false ),
      mpAnchoredObj( 0 ),
      mpAnchorFrm( 0 ),
      mpContact( 0 ),
      // --> OD 2006-03-15 #i62875#
      mbFollowTextFlow( false ),
      mbDoNotCaptureAnchoredObj( false )
      // <--
{
#if OSL_DEBUG_LEVEL > 1
    // assert, if object isn't of excepted type
    const bool bObjOfExceptedType =
            mrDrawObj.ISA(SwVirtFlyDrawObj) || // object representing fly frame
            mrDrawObj.ISA(SwDrawVirtObj)    || // 'virtual' drawing object
            ( !mrDrawObj.ISA(SdrVirtObj) &&    // 'master' drawing object
              !mrDrawObj.ISA(SwFlyDrawObj) );  // - indirectly checked
    (void) bObjOfExceptedType;
    ASSERT( bObjOfExceptedType,
            "SwAnchoredObjectPosition(..) - object of unexcepted type!" );
#endif

    _GetInfoAboutObj();
}

/** determine information about object

    OD 30.07.2003 #110978#
    members <mbIsObjFly>, <mpFrmOfObj>, <mpAnchorFrm>, <mpContact>,
    <mbFollowTextFlow> and <mbDoNotCaptureAnchoredObj> are set

    @author OD
*/
void SwAnchoredObjectPosition::_GetInfoAboutObj()
{
    // determine, if object represents a fly frame
    {
        mbIsObjFly = mrDrawObj.ISA(SwVirtFlyDrawObj);
    }

    // determine contact object
    {
        mpContact = static_cast<SwContact*>(GetUserCall( &mrDrawObj ));
        ASSERT( mpContact,
                "SwAnchoredObjectPosition::_GetInfoAboutObj() - missing SwContact-object." );
    }

    // determine anchored object, the object belongs to
    {
        // OD 2004-03-30 #i26791#
        mpAnchoredObj = mpContact->GetAnchoredObj( &mrDrawObj );
        ASSERT( mpAnchoredObj,
                "SwAnchoredObjectPosition::_GetInfoAboutObj() - missing anchored object." );
    }

    // determine frame, the object is anchored at
    {
        // OD 2004-03-23 #i26791#
        mpAnchorFrm = mpAnchoredObj->AnchorFrm();
        ASSERT( mpAnchorFrm,
                "SwAnchoredObjectPosition::_GetInfoAboutObj() - missing anchor frame." );
    }

    // determine format the object belongs to
    {
        // --> OD 2004-07-01 #i28701#
        mpFrmFmt = &mpAnchoredObj->GetFrmFmt();
        ASSERT( mpFrmFmt,
                "<SwAnchoredObjectPosition::_GetInfoAboutObj() - missing frame format." );
    }

    // --> OD 2006-03-15 #i62875#
    // determine attribute value of <Follow-Text-Flow>
    {
        mbFollowTextFlow = mpFrmFmt->GetFollowTextFlow().GetValue();
    }

    // determine, if anchored object has not to be captured on the page.
    // the following conditions must be hold to *not* capture it:
    // - corresponding document compatibility flag is set
    // - it's a drawing object
    // - it doesn't follow the text flow
    {
        mbDoNotCaptureAnchoredObj = !mbIsObjFly && !mbFollowTextFlow &&
                                    mpFrmFmt->getIDocumentSettingAccess()->get(IDocumentSettingAccess::DO_NOT_CAPTURE_DRAW_OBJS_ON_PAGE);
    }
    // <--
}

SwAnchoredObjectPosition::~SwAnchoredObjectPosition()
{}

bool SwAnchoredObjectPosition::IsAnchoredToChar() const
{
    return false;
}

const SwFrm* SwAnchoredObjectPosition::ToCharOrientFrm() const
{
    return NULL;
}

const SwRect* SwAnchoredObjectPosition::ToCharRect() const
{
    return NULL;
}

// OD 12.11.2003 #i22341#
SwTwips SwAnchoredObjectPosition::ToCharTopOfLine() const
{
    return 0L;
}

/** helper method to determine top of a frame for the vertical
    object positioning

    OD 2004-03-11 #i11860#

    @author OD
*/
SwTwips SwAnchoredObjectPosition::_GetTopForObjPos( const SwFrm& _rFrm,
                                                    const SwRectFn& _fnRect,
                                                    const bool _bVert ) const
{
    SwTwips nTopOfFrmForObjPos = (_rFrm.Frm().*_fnRect->fnGetTop)();

    if ( _rFrm.IsTxtFrm() )
    {
        const SwTxtFrm& rTxtFrm = static_cast<const SwTxtFrm&>(_rFrm);
        if ( _bVert )
        {
            nTopOfFrmForObjPos -=
                rTxtFrm.GetUpperSpaceAmountConsideredForPrevFrmAndPageGrid();
        }
        else
        {
            nTopOfFrmForObjPos +=
                rTxtFrm.GetUpperSpaceAmountConsideredForPrevFrmAndPageGrid();
        }
    }

    return nTopOfFrmForObjPos;
}

void SwAnchoredObjectPosition::_GetVertAlignmentValues(
                                        const SwFrm& _rVertOrientFrm,
                                        const SwFrm& _rPageAlignLayFrm,
                                        const sal_Int16 _eRelOrient,
                                        SwTwips&      _orAlignAreaHeight,
                                        SwTwips&      _orAlignAreaOffset ) const
{
    SwTwips nHeight = 0;
    SwTwips nOffset = 0;
    SWRECTFN( (&_rVertOrientFrm) )
    // OD 2004-03-11 #i11860# - top of <_rVertOrientFrm> for object positioning
    const SwTwips nVertOrientTop = _GetTopForObjPos( _rVertOrientFrm, fnRect, bVert );
    // OD 2004-03-11 #i11860# - upper space amount of <_rVertOrientFrm> considered
    // for previous frame
    const SwTwips nVertOrientUpperSpaceForPrevFrmAndPageGrid =
            _rVertOrientFrm.IsTxtFrm()
            ? static_cast<const SwTxtFrm&>(_rVertOrientFrm).
                        GetUpperSpaceAmountConsideredForPrevFrmAndPageGrid()
            : 0;
    switch ( _eRelOrient )
    {
        case text::RelOrientation::FRAME:
        {
            // OD 2004-03-11 #i11860# - consider upper space of previous frame
            nHeight = (_rVertOrientFrm.Frm().*fnRect->fnGetHeight)() -
                      nVertOrientUpperSpaceForPrevFrmAndPageGrid;
            nOffset = 0;
        }
        break;
        case text::RelOrientation::PRINT_AREA:
        {
            nHeight = (_rVertOrientFrm.Prt().*fnRect->fnGetHeight)();
            // OD 2004-03-11 #i11860# - consider upper space of previous frame
            nOffset = (_rVertOrientFrm.*fnRect->fnGetTopMargin)() -
                      nVertOrientUpperSpaceForPrevFrmAndPageGrid;
            // if aligned to page in horizontal layout, consider header and
            // footer frame height appropriately.
            if( _rVertOrientFrm.IsPageFrm() && !bVert )
            {
                const SwFrm* pPrtFrm =
                        static_cast<const SwPageFrm&>(_rVertOrientFrm).Lower();
                while( pPrtFrm )
                {
                    if( pPrtFrm->IsHeaderFrm() )
                    {
                        nHeight -= pPrtFrm->Frm().Height();
                        nOffset += pPrtFrm->Frm().Height();
                    }
                    else if( pPrtFrm->IsFooterFrm() )
                    {
                        nHeight -= pPrtFrm->Frm().Height();
                    }
                    pPrtFrm = pPrtFrm->GetNext();
                }
            }
        }
        break;
        case text::RelOrientation::PAGE_FRAME:
        {
            nHeight = (_rPageAlignLayFrm.Frm().*fnRect->fnGetHeight)();
            nOffset = (*fnRect->fnYDiff)(
                        (_rPageAlignLayFrm.Frm().*fnRect->fnGetTop)(),
                        nVertOrientTop );
        }
        break;
        case text::RelOrientation::PAGE_PRINT_AREA:
        {
            nHeight = (_rPageAlignLayFrm.Prt().*fnRect->fnGetHeight)();
            nOffset = (_rPageAlignLayFrm.*fnRect->fnGetTopMargin)() +
                      (*fnRect->fnYDiff)(
                        (_rPageAlignLayFrm.Frm().*fnRect->fnGetTop)(),
                        nVertOrientTop );
            // if aligned to page in horizontal layout, consider header and
            // footer frame height appropriately.
            if( _rPageAlignLayFrm.IsPageFrm() && !bVert )
            {
                const SwFrm* pPrtFrm =
                        static_cast<const SwPageFrm&>(_rPageAlignLayFrm).Lower();
                while( pPrtFrm )
                {
                    if( pPrtFrm->IsHeaderFrm() )
                    {
                        nHeight -= pPrtFrm->Frm().Height();
                        nOffset += pPrtFrm->Frm().Height();
                    }
                    else if( pPrtFrm->IsFooterFrm() )
                    {
                        nHeight -= pPrtFrm->Frm().Height();
                    }
                    pPrtFrm = pPrtFrm->GetNext();
                }
            }
        }
        break;
        // OD 12.11.2003 #i22341# - vertical alignment at top of line
        case text::RelOrientation::TEXT_LINE:
        {
            if ( IsAnchoredToChar() )
            {
                nHeight = 0;
                nOffset = (*fnRect->fnYDiff)( ToCharTopOfLine(), nVertOrientTop );
            }
            else
            {
                ASSERT( false,
                        "<SwAnchoredObjectPosition::_GetVertAlignmentValues(..)> - invalid relative alignment" );
            }
        }
        break;
        case text::RelOrientation::CHAR:
        {
            if ( IsAnchoredToChar() )
            {
                nHeight = (ToCharRect()->*fnRect->fnGetHeight)();
                nOffset = (*fnRect->fnYDiff)( (ToCharRect()->*fnRect->fnGetTop)(),
                                              nVertOrientTop );
            }
            else
            {
                ASSERT( false,
                        "<SwAnchoredObjectPosition::_GetVertAlignmentValues(..)> - invalid relative alignment" );
            }
        }
        break;
        // no break here, because text::RelOrientation::CHAR is invalid, if !mbAnchorToChar
        default:
        //case text::RelOrientation::PAGE_LEFT:     not valid for vertical alignment
        //case text::RelOrientation::PAGE_RIGHT:    not valid for vertical alignment
        //case text::RelOrientation::FRAME_LEFT:    not valid for vertical alignment
        //case text::RelOrientation::FRAME_RIGHT:   not valid for vertical alignment
        {
            ASSERT( false,
                    "<SwAnchoredObjectPosition::_GetVertAlignmentValues(..)> - invalid relative alignment" );
        }
    }

    _orAlignAreaHeight = nHeight;
    _orAlignAreaOffset = nOffset;
}

// --> OD 2004-06-17 #i26791# - add output parameter <_roVertOffsetToFrmAnchorPos>
SwTwips SwAnchoredObjectPosition::_GetVertRelPos(
                                    const SwFrm& _rVertOrientFrm,
                                    const SwFrm& _rPageAlignLayFrm,
                                    const sal_Int16 _eVertOrient,
                                    const sal_Int16 _eRelOrient,
                                    const SwTwips          _nVertPos,
                                    const SvxLRSpaceItem& _rLRSpacing,
                                    const SvxULSpaceItem& _rULSpacing,
                                    SwTwips& _roVertOffsetToFrmAnchorPos ) const
{
    SwTwips nRelPosY = 0;
    SWRECTFN( (&_rVertOrientFrm) );

    SwTwips nAlignAreaHeight;
    SwTwips nAlignAreaOffset;
    _GetVertAlignmentValues( _rVertOrientFrm, _rPageAlignLayFrm,
                             _eRelOrient, nAlignAreaHeight, nAlignAreaOffset );

    nRelPosY = nAlignAreaOffset;
    const SwRect aObjBoundRect( GetAnchoredObj().GetObjRect() );
    const SwTwips nObjHeight = (aObjBoundRect.*fnRect->fnGetHeight)();

    switch ( _eVertOrient )
    {
        case text::VertOrientation::NONE:
        {
            // 'manual' vertical position
            nRelPosY += _nVertPos;
        }
        break;
        case text::VertOrientation::TOP:
        {
            //Badaa: 2008-04-18 * Support for Classical Mongolian Script (SCMS) joint with Jiayanmin
              nRelPosY +=   bVert
                            ? ( bVertL2R
                                ? _rLRSpacing.GetLeft()
                                : _rLRSpacing.GetRight() )
                            : _rULSpacing.GetUpper();
        }
        break;
        case text::VertOrientation::CENTER:
        {
            nRelPosY += (nAlignAreaHeight / 2) - (nObjHeight / 2);
        }
        break;
        case text::VertOrientation::BOTTOM:
        {
            //Badaa: 2008-04-18 * Support for Classical Mongolian Script (SCMS) joint with Jiayanmin
            nRelPosY += nAlignAreaHeight -
                        ( nObjHeight + ( bVert
                                         ? ( bVertL2R
                                             ? _rLRSpacing.GetRight()
                                             : _rLRSpacing.GetLeft() )
                                         : _rULSpacing.GetLower() ) );
        }
        break;
        default:
        {
            ASSERT( false,
                    "<SwAnchoredObjectPosition::_GetVertRelPos(..) - invalid vertical positioning" );
        }
    }

    // --> OD 2004-06-17 #i26791#
    _roVertOffsetToFrmAnchorPos = nAlignAreaOffset;

    return nRelPosY;
}

/** adjust calculated vertical in order to keep object inside
    'page' alignment layout frame.

    OD 2004-07-01 #i28701# - parameter <nTopOfAnch> and <bVert> added
    OD 2004-07-22 #i31805# - add parameter <bCheckBottom>
    OD 2004-10-08 #i26945# - add parameter <bFollowTextFlow>
    OD 2006-03-15 #i62875# - method now private and renamed.
    OD 2009-09-01 #mongolianlayout# - add parameter <bVertL2R>

    @author OD
*/
SwTwips SwAnchoredObjectPosition::_ImplAdjustVertRelPos( const SwTwips nTopOfAnch,
                                                         const bool bVert,
                                                         const bool bVertL2R,
                                                         const SwFrm& rPageAlignLayFrm,
                                                         const SwTwips nProposedRelPosY,
                                                         const bool bFollowTextFlow,
                                                         const bool bCheckBottom ) const
{
    SwTwips nAdjustedRelPosY = nProposedRelPosY;

    const Size aObjSize( GetAnchoredObj().GetObjRect().SSize() );

    // determine the area of 'page' alignment frame, to which the vertical
    // position is restricted.
    // --> OD 2004-07-06 #i28701# - Extend restricted area for the vertical
    // position to area of the page frame, if wrapping style influence is
    // considered on object positioning. Needed to avoid layout loops in the
    // object positioning algorithm considering the wrapping style influence
    // caused by objects, which follow the text flow and thus are restricted
    // to its environment (e.g. page header/footer).
    SwRect aPgAlignArea;
    {
        // --> OD 2004-10-08 #i26945# - no extension of restricted area, if
        // object's attribute follow text flow is set and its inside a table
        if ( GetFrmFmt().getIDocumentSettingAccess()->get(IDocumentSettingAccess::CONSIDER_WRAP_ON_OBJECT_POSITION) &&
             ( !bFollowTextFlow ||
               !GetAnchoredObj().GetAnchorFrm()->IsInTab() ) )
        {
            aPgAlignArea = rPageAlignLayFrm.FindPageFrm()->Frm();
        }
        else
        {
            aPgAlignArea = rPageAlignLayFrm.Frm();
        }
    }

    if ( bVert )
    {
        // --> OD 2009-09-01 #mongolianlayout#
        if ( !bVertL2R )
        // <--
        {
            if ( bCheckBottom &&
                 nTopOfAnch - nAdjustedRelPosY - aObjSize.Width() <
                    aPgAlignArea.Left() )
            {
                nAdjustedRelPosY = aPgAlignArea.Left() +
                                   nTopOfAnch -
                                   aObjSize.Width();
            }
            if ( nTopOfAnch - nAdjustedRelPosY > aPgAlignArea.Right() )
            {
                nAdjustedRelPosY = nTopOfAnch - aPgAlignArea.Right();
            }
        }
        // --> OD 2009-09-01 #mongolianlayout#
        else
        {
            if ( bCheckBottom &&
                 nTopOfAnch + nAdjustedRelPosY + aObjSize.Width() >
                    aPgAlignArea.Right() )
            {
                nAdjustedRelPosY = aPgAlignArea.Right() -
                                   nTopOfAnch -
                                   aObjSize.Width();
            }
            if ( nTopOfAnch + nAdjustedRelPosY < aPgAlignArea.Left() )
            {
                nAdjustedRelPosY = aPgAlignArea.Left() - nTopOfAnch;
            }
        }
        // <--
    }
    else
    {
        if ( bCheckBottom &&
             nTopOfAnch + nAdjustedRelPosY + aObjSize.Height() >
                aPgAlignArea.Top() + aPgAlignArea.Height() )
        {
            nAdjustedRelPosY = aPgAlignArea.Top() + aPgAlignArea.Height() -
                               nTopOfAnch -
                               aObjSize.Height();
        }
        if ( nTopOfAnch + nAdjustedRelPosY < aPgAlignArea.Top() )
        {
            nAdjustedRelPosY = aPgAlignArea.Top() - nTopOfAnch;
        }
    }

    return nAdjustedRelPosY;
}

/** adjust calculated horizontal in order to keep object inside
    'page' alignment layout frame.

    OD 2006-03-15 #i62875# - method now private and renamed.

    @author OD
*/
SwTwips SwAnchoredObjectPosition::_ImplAdjustHoriRelPos(
                                        const SwFrm&  _rPageAlignLayFrm,
                                        const SwTwips _nProposedRelPosX ) const
{
    SwTwips nAdjustedRelPosX = _nProposedRelPosX;

    const SwFrm& rAnchorFrm = GetAnchorFrm();
    const bool bVert = rAnchorFrm.IsVertical();

    const Size aObjSize( GetAnchoredObj().GetObjRect().SSize() );

    if( bVert )
    {
        if ( rAnchorFrm.Frm().Top() + nAdjustedRelPosX + aObjSize.Height() >
                _rPageAlignLayFrm.Frm().Bottom() )
        {
            nAdjustedRelPosX = _rPageAlignLayFrm.Frm().Bottom() -
                               rAnchorFrm.Frm().Top() -
                               aObjSize.Height();
        }
        if ( rAnchorFrm.Frm().Top() + nAdjustedRelPosX <
                _rPageAlignLayFrm.Frm().Top() )
        {
            nAdjustedRelPosX = _rPageAlignLayFrm.Frm().Top() -
                               rAnchorFrm.Frm().Top();
        }
    }
    else
    {
        if ( rAnchorFrm.Frm().Left() + nAdjustedRelPosX + aObjSize.Width() >
                _rPageAlignLayFrm.Frm().Right() )
        {
            nAdjustedRelPosX = _rPageAlignLayFrm.Frm().Right() -
                               rAnchorFrm.Frm().Left() -
                               aObjSize.Width();
        }
        if ( rAnchorFrm.Frm().Left() + nAdjustedRelPosX <
                _rPageAlignLayFrm.Frm().Left() )
        {
            nAdjustedRelPosX = _rPageAlignLayFrm.Frm().Left() -
                               rAnchorFrm.Frm().Left();
        }
    }

    return nAdjustedRelPosX;
}

/** determine alignment value for horizontal position of object

    @author OD
*/
void SwAnchoredObjectPosition::_GetHoriAlignmentValues( const SwFrm&  _rHoriOrientFrm,
                                                        const SwFrm&  _rPageAlignLayFrm,
                                                        const sal_Int16 _eRelOrient,
                                                        const bool    _bObjWrapThrough,
                                                        SwTwips&      _orAlignAreaWidth,
                                                        SwTwips&      _orAlignAreaOffset,
                                                        bool&         _obAlignedRelToPage ) const
{
    SwTwips nWidth = 0;
    SwTwips nOffset = 0;
    SWRECTFN( (&_rHoriOrientFrm) )
    switch ( _eRelOrient )
    {
        case text::RelOrientation::PRINT_AREA:
        {
            nWidth = (_rHoriOrientFrm.Prt().*fnRect->fnGetWidth)();
            nOffset = (_rHoriOrientFrm.*fnRect->fnGetLeftMargin)();
            if ( _rHoriOrientFrm.IsTxtFrm() )
            {
                // consider movement of text frame left
                nOffset += static_cast<const SwTxtFrm&>(_rHoriOrientFrm).GetBaseOfstForFly( !_bObjWrapThrough );
            }
            else if ( _rHoriOrientFrm.IsPageFrm() && bVert )
            {
                // for to-page anchored objects, consider header/footer frame
                // in vertical layout
                const SwFrm* pPrtFrm =
                        static_cast<const SwPageFrm&>(_rHoriOrientFrm).Lower();
                while( pPrtFrm )
                {
                    if( pPrtFrm->IsHeaderFrm() )
                    {
                        nWidth -= pPrtFrm->Frm().Height();
                        nOffset += pPrtFrm->Frm().Height();
                    }
                    else if( pPrtFrm->IsFooterFrm() )
                    {
                        nWidth -= pPrtFrm->Frm().Height();
                    }
                    pPrtFrm = pPrtFrm->GetNext();
                }
            }
            break;
        }
        case text::RelOrientation::PAGE_LEFT:
        {
            // align at left border of page frame/fly frame/cell frame
            nWidth = (_rPageAlignLayFrm.*fnRect->fnGetLeftMargin)();
            nOffset = (*fnRect->fnXDiff)(
                      (_rPageAlignLayFrm.Frm().*fnRect->fnGetLeft)(),
                      (_rHoriOrientFrm.Frm().*fnRect->fnGetLeft)() );
            _obAlignedRelToPage = true;
        }
        break;
        case text::RelOrientation::PAGE_RIGHT:
        {
            // align at right border of page frame/fly frame/cell frame
            nWidth = (_rPageAlignLayFrm.*fnRect->fnGetRightMargin)();
            nOffset = (*fnRect->fnXDiff)(
                      (_rPageAlignLayFrm.*fnRect->fnGetPrtRight)(),
                      (_rHoriOrientFrm.Frm().*fnRect->fnGetLeft)() );
            _obAlignedRelToPage = true;
        }
        break;
        case text::RelOrientation::FRAME_LEFT:
        {
            // align at left border of anchor frame
            nWidth = (_rHoriOrientFrm.*fnRect->fnGetLeftMargin)();
            nOffset = 0;
        }
        break;
        case text::RelOrientation::FRAME_RIGHT:
        {
            // align at right border of anchor frame
            // OD 19.08.2003 #110978# - unify and simplify
            nWidth = (_rHoriOrientFrm.*fnRect->fnGetRightMargin)();
            //nOffset = (_rHoriOrientFrm.Frm().*fnRect->fnGetWidth)() -
            //          nWidth;
            nOffset = (_rHoriOrientFrm.Prt().*fnRect->fnGetRight)();
        }
        break;
        case text::RelOrientation::CHAR:
        {
            // alignment relative to character - assure, that corresponding
            // character rectangle is set.
            if ( IsAnchoredToChar() )
            {
                nWidth = 0;
                nOffset = (*fnRect->fnXDiff)(
                            (ToCharRect()->*fnRect->fnGetLeft)(),
                            (ToCharOrientFrm()->Frm().*fnRect->fnGetLeft)() );
                break;
            }
            // no break!
        }
        case text::RelOrientation::PAGE_PRINT_AREA:
        {
            nWidth = (_rPageAlignLayFrm.Prt().*fnRect->fnGetWidth)();
            nOffset = (*fnRect->fnXDiff)(
                        (_rPageAlignLayFrm.*fnRect->fnGetPrtLeft)(),
                        (_rHoriOrientFrm.Frm().*fnRect->fnGetLeft)() );
            if ( _rHoriOrientFrm.IsPageFrm() && bVert )
            {
                // for to-page anchored objects, consider header/footer frame
                // in vertical layout
                const SwFrm* pPrtFrm =
                        static_cast<const SwPageFrm&>(_rHoriOrientFrm).Lower();
                while( pPrtFrm )
                {
                    if( pPrtFrm->IsHeaderFrm() )
                    {
                        nWidth -= pPrtFrm->Frm().Height();
                        nOffset += pPrtFrm->Frm().Height();
                    }
                    else if( pPrtFrm->IsFooterFrm() )
                    {
                        nWidth -= pPrtFrm->Frm().Height();
                    }
                    pPrtFrm = pPrtFrm->GetNext();
                }
            }
            _obAlignedRelToPage = true;
            break;
        }
        case text::RelOrientation::PAGE_FRAME:
        {
            nWidth = (_rPageAlignLayFrm.Frm().*fnRect->fnGetWidth)();
            nOffset = (*fnRect->fnXDiff)(
                        (_rPageAlignLayFrm.Frm().*fnRect->fnGetLeft)(),
                        (_rHoriOrientFrm.Frm().*fnRect->fnGetLeft)() );
            _obAlignedRelToPage = true;
            break;
        }
        default:
        // case text::RelOrientation::FRAME:
        {
            nWidth = (_rHoriOrientFrm.Frm().*fnRect->fnGetWidth)();
            nOffset = _rHoriOrientFrm.IsTxtFrm() ?
                   static_cast<const SwTxtFrm&>(_rHoriOrientFrm).GetBaseOfstForFly( !_bObjWrapThrough ) :
                   0;
            break;
        }
    }

    _orAlignAreaWidth = nWidth;
    _orAlignAreaOffset = nOffset;
}

/** toggle given horizontal orientation and relative alignment

    @author OD
*/
void SwAnchoredObjectPosition::_ToggleHoriOrientAndAlign(
                                        const bool _bToggleLeftRight,
                                        sal_Int16& _ioeHoriOrient,
                                        sal_Int16& _iopeRelOrient
                                      ) const
{
    if( _bToggleLeftRight )
    {
        // toggle orientation
        switch ( _ioeHoriOrient )
        {
            case text::HoriOrientation::RIGHT :
                {
                    _ioeHoriOrient = text::HoriOrientation::LEFT;
                }
                break;
            case text::HoriOrientation::LEFT :
                {
                    _ioeHoriOrient = text::HoriOrientation::RIGHT;
                }
                break;
            default:
                break;
        }

        // toggle relative alignment
        switch ( _iopeRelOrient )
        {
            case text::RelOrientation::PAGE_RIGHT :
                {
                    _iopeRelOrient = text::RelOrientation::PAGE_LEFT;
                }
                break;
            case text::RelOrientation::PAGE_LEFT :
                {
                    _iopeRelOrient = text::RelOrientation::PAGE_RIGHT;
                }
                break;
            case text::RelOrientation::FRAME_RIGHT :
                {
                    _iopeRelOrient = text::RelOrientation::FRAME_LEFT;
                }
                break;
            case text::RelOrientation::FRAME_LEFT :
                {
                    _iopeRelOrient = text::RelOrientation::FRAME_RIGHT;
                }
                break;
            default:
                break;
        }
    }
}

/** calculate relative horizontal position

    @author OD
*/
SwTwips SwAnchoredObjectPosition::_CalcRelPosX(
                                const SwFrm& _rHoriOrientFrm,
                                const SwEnvironmentOfAnchoredObject& _rEnvOfObj,
                                const SwFmtHoriOrient& _rHoriOrient,
                                const SvxLRSpaceItem& _rLRSpacing,
                                const SvxULSpaceItem& _rULSpacing,
                                const bool _bObjWrapThrough,
                                const SwTwips _nRelPosY,
                                SwTwips& _roHoriOffsetToFrmAnchorPos
                              ) const
{
    // determine 'page' alignment layout frame
    const SwFrm& rPageAlignLayFrm =
            _rEnvOfObj.GetHoriEnvironmentLayoutFrm( _rHoriOrientFrm );

    const bool bEvenPage = !rPageAlignLayFrm.OnRightPage();
    const bool bToggle = _rHoriOrient.IsPosToggle() && bEvenPage;

    // determine orientation and relative alignment
    sal_Int16 eHoriOrient = _rHoriOrient.GetHoriOrient();
    sal_Int16 eRelOrient = _rHoriOrient.GetRelationOrient();
    // toggle orientation and relative alignment
    _ToggleHoriOrientAndAlign( bToggle, eHoriOrient, eRelOrient );

    // determine alignment parameter
    // <nWidth>:  'width' of alignment area
    // <nOffset>: offset of alignment area, relative to 'left' of anchor frame
    SwTwips nWidth = 0;
    SwTwips nOffset = 0;
    bool bAlignedRelToPage = false;
    _GetHoriAlignmentValues( _rHoriOrientFrm, rPageAlignLayFrm,
                             eRelOrient, _bObjWrapThrough,
                             nWidth, nOffset, bAlignedRelToPage );

    const SwFrm& rAnchorFrm = GetAnchorFrm();
    SWRECTFN( (&_rHoriOrientFrm) )
    SwTwips nObjWidth = (GetAnchoredObj().GetObjRect().*fnRect->fnGetWidth)();
    SwTwips nRelPosX = nOffset;
    if ( _rHoriOrient.GetHoriOrient() == text::HoriOrientation::NONE )
    {
        // 'manual' horizonal position
        const bool bR2L = rAnchorFrm.IsRightToLeft();
        if( IsAnchoredToChar() && text::RelOrientation::CHAR == eRelOrient )
        {
            if( bR2L )
                nRelPosX -= _rHoriOrient.GetPos();
            else
                nRelPosX += _rHoriOrient.GetPos();
        }
        else if ( bToggle || ( !_rHoriOrient.IsPosToggle() && bR2L ) )
        {
            // OD 04.08.2003 #110978# - correction: consider <nOffset> also for
            // toggling from left to right.
            nRelPosX += nWidth - nObjWidth - _rHoriOrient.GetPos();
        }
        else
        {
            nRelPosX += _rHoriOrient.GetPos();
        }
    }
    else if ( text::HoriOrientation::CENTER == eHoriOrient )
        nRelPosX += (nWidth / 2) - (nObjWidth / 2);
    else if ( text::HoriOrientation::RIGHT == eHoriOrient )
        nRelPosX += nWidth -
                    ( nObjWidth +
                      ( bVert ? _rULSpacing.GetLower() : _rLRSpacing.GetRight() ) );
    else
        nRelPosX += bVert ? _rULSpacing.GetUpper() : _rLRSpacing.GetLeft();

    // adjust relative position by distance between anchor frame and
    // the frame, the object is oriented at.
    if ( &rAnchorFrm != &_rHoriOrientFrm )
    {
        SwTwips nLeftOrient = (_rHoriOrientFrm.Frm().*fnRect->fnGetLeft)();
        SwTwips nLeftAnchor = (rAnchorFrm.Frm().*fnRect->fnGetLeft)();
        nRelPosX += (*fnRect->fnXDiff)( nLeftOrient, nLeftAnchor );
    }

    // OD 2004-05-21 #i28701# - deactivate follow code
//    // adjust relative horizontal position, if object is manual horizontal
//    // positioned (not 'page' aligned) and orients not at the anchor frame,
//    // but it overlaps anchor frame.
//    if ( _rHoriOrient.GetHoriOrient() == text::HoriOrientation::NONE && !bAlignedRelToPage &&
//         &rAnchorFrm != &_rHoriOrientFrm )
//    {
//        // E.g.: consider a columned page/section with an horizontal
//        //       negative positioned object.
//        // OD 2004-03-23 #i26791#
//        const SwRect& rObjRect = GetAnchoredObj().GetObjRect();
//        if( bVert )
//        {
//            if( _rHoriOrientFrm.Frm().Top() > rAnchorFrm.Frm().Bottom() &&
//                rObjRect.Right() > rAnchorFrm.Frm().Left() )
//            {
//                const SwTwips nProposedPosX = nRelPosX + rAnchorFrm.Frm().Top();
//                if ( nProposedPosX < rAnchorFrm.Frm().Bottom() )
//                    nRelPosX = rAnchorFrm.Frm().Height() + 1;
//            }
//        }
//        else
//        {
//            if( _rHoriOrientFrm.Frm().Left() > rAnchorFrm.Frm().Right() &&
//                rObjRect.Top() < rAnchorFrm.Frm().Bottom() )
//            {
//                // OD 04.08.2003 #110978# - correction: use <nRelPosX>
//                // instead of <aRelPos.X()>
//                const SwTwips nProposedPosX = nRelPosX + rAnchorFrm.Frm().Left();
//                if ( nProposedPosX < rAnchorFrm.Frm().Right() )
//                    nRelPosX = rAnchorFrm.Frm().Width() + 1;
//            }
//        }
//    }
    // adjust calculated relative horizontal position, in order to
    // keep object inside 'page' alignment layout frame
    const SwFrm& rEnvironmentLayFrm =
            _rEnvOfObj.GetHoriEnvironmentLayoutFrm( _rHoriOrientFrm );
    nRelPosX = _AdjustHoriRelPos( rEnvironmentLayFrm, nRelPosX );

    // if object is a Writer fly frame and it's anchored to a content and
    // it is horizontal positioned left or right, but not relative to character,
    // it has to be drawn aside another object, which have the same horizontal
    // position and lay below it.
    if ( GetAnchoredObj().ISA(SwFlyFrm) &&
         ( GetContact().ObjAnchoredAtPara() || GetContact().ObjAnchoredAtChar() ) &&
         ( eHoriOrient == text::HoriOrientation::LEFT || eHoriOrient == text::HoriOrientation::RIGHT ) &&
         eRelOrient != text::RelOrientation::CHAR )
    {
        nRelPosX = _AdjustHoriRelPosForDrawAside( _rHoriOrientFrm,
                                                  nRelPosX, _nRelPosY,
                                                  eHoriOrient, eRelOrient,
                                                  _rLRSpacing, _rULSpacing,
                                                  bEvenPage );
    }

    // --> OD 2004-06-17 #i26791#
    _roHoriOffsetToFrmAnchorPos = nOffset;

    return nRelPosX;
}

// **************************************************************************
// method incl. helper methods for adjusting proposed horizontal position,
// if object has to draw aside another object.
// **************************************************************************
/** adjust calculated horizontal position in order to draw object
    aside other objects with same positioning

    @author OD
*/
SwTwips SwAnchoredObjectPosition::_AdjustHoriRelPosForDrawAside(
                                            const SwFrm&  _rHoriOrientFrm,
                                            const SwTwips _nProposedRelPosX,
                                            const SwTwips _nRelPosY,
                                            const sal_Int16 _eHoriOrient,
                                            const sal_Int16 _eRelOrient,
                                            const SvxLRSpaceItem& _rLRSpacing,
                                            const SvxULSpaceItem& _rULSpacing,
                                            const bool _bEvenPage
                                          ) const
{
    // OD 2004-03-23 #i26791#
    if ( !GetAnchorFrm().ISA(SwTxtFrm) ||
         !GetAnchoredObj().ISA(SwFlyAtCntFrm) )
    {
        ASSERT( false,
                "<SwAnchoredObjectPosition::_AdjustHoriRelPosForDrawAside(..) - usage for wrong anchor type" );
        return _nProposedRelPosX;
    }

    const SwTxtFrm& rAnchorTxtFrm = static_cast<const SwTxtFrm&>(GetAnchorFrm());
    // OD 2004-03-23 #i26791#
    const SwFlyAtCntFrm& rFlyAtCntFrm =
                        static_cast<const SwFlyAtCntFrm&>(GetAnchoredObj());
    const SwRect aObjBoundRect( GetAnchoredObj().GetObjRect() );
    SWRECTFN( (&_rHoriOrientFrm) )

    SwTwips nAdjustedRelPosX = _nProposedRelPosX;

    // determine proposed object bound rectangle
    Point aTmpPos = (rAnchorTxtFrm.Frm().*fnRect->fnGetPos)();
    if( bVert )
    {
        aTmpPos.X() -= _nRelPosY + aObjBoundRect.Width();
        aTmpPos.Y() += nAdjustedRelPosX;
    }
    else
    {
        aTmpPos.X() += nAdjustedRelPosX;
        aTmpPos.Y() += _nRelPosY;
    }
    SwRect aTmpObjRect( aTmpPos, aObjBoundRect.SSize() );

    const sal_uInt32 nObjOrdNum = GetObject().GetOrdNum();
    const SwPageFrm* pObjPage = rFlyAtCntFrm.FindPageFrm();
    const SwFrm* pObjContext = ::FindKontext( &rAnchorTxtFrm, FRM_COLUMN );
    sal_uLong nObjIndex = rAnchorTxtFrm.GetTxtNode()->GetIndex();
    SwOrderIter aIter( pObjPage, sal_True );
    const SwFlyFrm* pFly = ((SwVirtFlyDrawObj*)aIter.Bottom())->GetFlyFrm();
    while ( pFly && nObjOrdNum > pFly->GetVirtDrawObj()->GetOrdNumDirect() )
    {
        if ( _DrawAsideFly( pFly, aTmpObjRect, pObjContext, nObjIndex,
                           _bEvenPage, _eHoriOrient, _eRelOrient ) )
        {
            if( bVert )
            {
                const SvxULSpaceItem& rOtherUL = pFly->GetFmt()->GetULSpace();
                const SwTwips nOtherTop = pFly->Frm().Top() - rOtherUL.GetUpper();
                const SwTwips nOtherBot = pFly->Frm().Bottom() + rOtherUL.GetLower();
                if ( nOtherTop <= aTmpObjRect.Bottom() + _rULSpacing.GetLower() &&
                     nOtherBot >= aTmpObjRect.Top() - _rULSpacing.GetUpper() )
                {
                    if ( _eHoriOrient == text::HoriOrientation::LEFT )
                    {
                        SwTwips nTmp = nOtherBot + 1 + _rULSpacing.GetUpper() -
                                       rAnchorTxtFrm.Frm().Top();
                        if ( nTmp > nAdjustedRelPosX &&
                             rAnchorTxtFrm.Frm().Top() + nTmp +
                             aObjBoundRect.Height() + _rULSpacing.GetLower()
                             <= pObjPage->Frm().Height() + pObjPage->Frm().Top() )
                        {
                            nAdjustedRelPosX = nTmp;
                        }
                    }
                    else if ( _eHoriOrient == text::HoriOrientation::RIGHT )
                    {
                        SwTwips nTmp = nOtherTop - 1 - _rULSpacing.GetLower() -
                                       aObjBoundRect.Height() -
                                       rAnchorTxtFrm.Frm().Top();
                        if ( nTmp < nAdjustedRelPosX &&
                             rAnchorTxtFrm.Frm().Top() + nTmp - _rULSpacing.GetUpper()
                              >= pObjPage->Frm().Top() )
                        {
                            nAdjustedRelPosX = nTmp;
                        }
                    }
                    aTmpObjRect.Pos().Y() = rAnchorTxtFrm.Frm().Top() +
                                            nAdjustedRelPosX;
                }
            }
            else
            {
                const SvxLRSpaceItem& rOtherLR = pFly->GetFmt()->GetLRSpace();
                const SwTwips nOtherLeft = pFly->Frm().Left() - rOtherLR.GetLeft();
                const SwTwips nOtherRight = pFly->Frm().Right() + rOtherLR.GetRight();
                if( nOtherLeft <= aTmpObjRect.Right() + _rLRSpacing.GetRight() &&
                    nOtherRight >= aTmpObjRect.Left() - _rLRSpacing.GetLeft() )
                {
                    if ( _eHoriOrient == text::HoriOrientation::LEFT )
                    {
                        SwTwips nTmp = nOtherRight + 1 + _rLRSpacing.GetLeft() -
                                       rAnchorTxtFrm.Frm().Left();
                        if ( nTmp > nAdjustedRelPosX &&
                             rAnchorTxtFrm.Frm().Left() + nTmp +
                             aObjBoundRect.Width() + _rLRSpacing.GetRight()
                             <= pObjPage->Frm().Width() + pObjPage->Frm().Left() )
                        {
                            nAdjustedRelPosX = nTmp;
                        }
                    }
                    else if ( _eHoriOrient == text::HoriOrientation::RIGHT )
                    {
                        SwTwips nTmp = nOtherLeft - 1 - _rLRSpacing.GetRight() -
                                       aObjBoundRect.Width() -
                                       rAnchorTxtFrm.Frm().Left();
                        if ( nTmp < nAdjustedRelPosX &&
                             rAnchorTxtFrm.Frm().Left() + nTmp - _rLRSpacing.GetLeft()
                             >= pObjPage->Frm().Left() )
                        {
                            nAdjustedRelPosX = nTmp;
                        }
                    }
                    aTmpObjRect.Pos().X() = rAnchorTxtFrm.Frm().Left() +
                                            nAdjustedRelPosX;
                }
            } // end of <if (bVert)>
        } // end of <if _DrawAsideFly(..)>

        pFly = ((SwVirtFlyDrawObj*)aIter.Next())->GetFlyFrm();
    } // end of <loop on fly frames

    return nAdjustedRelPosX;
}

/** detemine, if object has to draw aside given fly frame

    method used by <_AdjustHoriRelPosForDrawAside(..)>

    @author OD
*/
bool SwAnchoredObjectPosition::_DrawAsideFly( const SwFlyFrm* _pFly,
                                              const SwRect&   _rObjRect,
                                              const SwFrm*    _pObjContext,
                                              const sal_uLong     _nObjIndex,
                                              const bool      _bEvenPage,
                                              const sal_Int16 _eHoriOrient,
                                              const sal_Int16 _eRelOrient
                                            ) const
{
    bool bRetVal = false;

    SWRECTFN( (&GetAnchorFrm()) )

    if ( _pFly->IsFlyAtCntFrm() &&
         (_pFly->Frm().*fnRect->fnBottomDist)( (_rObjRect.*fnRect->fnGetTop)() ) < 0 &&
         (_rObjRect.*fnRect->fnBottomDist)( (_pFly->Frm().*fnRect->fnGetTop)() ) < 0 &&
         ::FindKontext( _pFly->GetAnchorFrm(), FRM_COLUMN ) == _pObjContext )
    {
        sal_uLong nOtherIndex =
            static_cast<const SwTxtFrm*>(_pFly->GetAnchorFrm())->GetTxtNode()->GetIndex();
        if( _nObjIndex >= nOtherIndex )
        {
            const SwFmtHoriOrient& rHori = _pFly->GetFmt()->GetHoriOrient();
            sal_Int16 eOtherRelOrient = rHori.GetRelationOrient();
            if( text::RelOrientation::CHAR != eOtherRelOrient )
            {
                sal_Int16 eOtherHoriOrient = rHori.GetHoriOrient();
                _ToggleHoriOrientAndAlign( _bEvenPage && rHori.IsPosToggle(),
                                           eOtherHoriOrient,
                                           eOtherRelOrient );
                if ( eOtherHoriOrient == _eHoriOrient &&
                    _Minor( _eRelOrient, eOtherRelOrient, text::HoriOrientation::LEFT == _eHoriOrient ) )
                {
                    bRetVal = true;
                }
            }
        }
    }

    return bRetVal;
}

/** determine, if object has to draw aside another object

    the different alignments of the objects determines, if one has
    to draw aside another one. Thus, the given alignment are checked
    against each other, which one has to be drawn aside the other one.
    depending on parameter _bLeft check is done for left or right
    positioning.
    method used by <_DrawAsideFly(..)>

    @author OD
*/
bool SwAnchoredObjectPosition::_Minor( sal_Int16 _eRelOrient1,
                                       sal_Int16 _eRelOrient2,
                                       bool             _bLeft ) const
{
    bool bRetVal;

    // draw aside order for left horizontal position
    //! one array entry for each value in text::RelOrientation
    static sal_uInt16 __READONLY_DATA aLeft[ 10 ] =
        { 5, 6, 0, 1, 8, 4, 7, 2, 3, 9 };
    // draw aside order for right horizontal position
    //! one array entry for each value in text::RelOrientation
    static sal_uInt16 __READONLY_DATA aRight[ 10 ] =
        { 5, 6, 0, 8, 1, 7, 4, 2, 3, 9 };

    // decide depending on given order, which frame has to draw aside another frame
    if( _bLeft )
        bRetVal = aLeft[ _eRelOrient1 ] >= aLeft[ _eRelOrient2 ];
    else
        bRetVal = aRight[ _eRelOrient1 ] >= aRight[ _eRelOrient2 ];

    return bRetVal;
}
