/* This file is part of the KDE project
   Copyright (C) 1998, 1999, 2000 Reginald Stadlbauer <reggie@kde.org>
   Copyright (C) 2000-2006 David Faure <faure@kde.org>
   Copyright (C) 2005 Thomas Zander <zander@kde.org>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This library 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
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
*/

#include "KWFrame.h"
#include "KWFrameSet.h"
#include "KWFrameList.h"
#include "KWDocument.h"
#include "KWPageManager.h"
#include "KWTextFrameSet.h"
#include "KWViewMode.h"
#include "KWCanvas.h"

#include <KoOasisContext.h>
#include <KoXmlNS.h>
#include <KoXmlWriter.h>
#include <KoStyleStack.h>

#include <kcommand.h>
#include <kdebug.h>

#include <float.h> // for DBL_DIG

//#define DEBUG_DRAW

/******************************************************************/
/* Class: ZOrderedFrameList                                       */
/******************************************************************/

int ZOrderedFrameList::compareItems(TQPtrCollection::Item a, TQPtrCollection::Item b)
{
    int za = ((KWFrame *)a)->zOrder();
    int zb = ((KWFrame *)b)->zOrder();
    if (za == zb) return 0;
    if (za < zb) return -1;
    return 1;
}


/******************************************************************/
/* Class: KWFrame                                                 */
/******************************************************************/

KWFrame::KWFrame(KWFrame * frame)
{
    m_runAround = RA_NO;
    //kdDebug(32001) << "KWFrame::KWFrame this=" << this << " frame=" << frame << endl;
    copySettings( frame );
    m_minFrameHeight=0;
    m_frameStack = 0; // lazy initialisation.
}

KWFrame::KWFrame(KWFrameSet *fs, double left, double top, double width, double height, RunAround ra )
    : KoRect( left, top, width, height ),
      // Initialize member vars here. This ensures they are all initialized, since it's
      // easier to compare this list with the member vars list (compiler ensures order).
      m_sheetSide( AnySide ),
      m_runAround( ra ),
      m_runAroundSide( RA_BIGGEST ),
      m_frameBehavior( AutoExtendFrame ),
      m_newFrameBehavior( ( fs && fs->type() == FT_TEXT ) ? Reconnect : NoFollowup ),
      m_bCopy( false ),
      m_drawFootNoteLine( false ),
      m_runAroundLeft( 1.0 ),
      m_runAroundRight( 1.0 ),
      m_runAroundTop( 1.0 ),
      m_runAroundBottom( 1.0 ),
      m_paddingLeft( 0 ),
      m_paddingRight( 0 ),
      m_paddingTop( 0 ),
      m_paddingBottom( 0 ),
      m_minFrameHeight( 0.01 ), // not 0, since AutoExtendFrame means min-height in odt
      m_internalY( 0 ),
      m_zOrder( 0 ),
      m_backgroundColor( (fs && (fs->type() == FT_PICTURE || fs->type() == FT_PART)) ? TQBrush( TQColor(), TQt::NoBrush) : TQBrush( TQColor() ) ), // valid brush with invalid color ( default )
      m_borderLeft( TQColor(), KoBorder::SOLID, 0 ),
      m_borderRight( TQColor(), KoBorder::SOLID, 0 ),
      m_borderTop( TQColor(), KoBorder::SOLID, 0 ),
      m_borderBottom( TQColor(), KoBorder::SOLID, 0 ),
      m_frameSet( fs )
{
    //kdDebug(32001) << "KWFrame::KWFrame " << this << " left=" << left << " top=" << top << endl;
    m_frameStack = 0; // lazy initialisation.
}

KWFrame::~KWFrame()
{
    //kdDebug(32001) << "KWFrame::~KWFrame " << this << endl;
    delete m_frameStack;
    m_frameStack = 0;
}

void KWFrame::setBackgroundColor( const TQBrush &color )
{
    m_backgroundColor = color;
}


int KWFrame::pageNumber() const
{
    Q_ASSERT( m_frameSet );
    if( !m_frameSet ) {
        kdDebug() << k_funcinfo << this << " has no frameset!" << endl;
        return 0;
    }
    if( !m_frameSet->pageManager() ) {
        kdWarning() << k_funcinfo << this << " is not a frame that is in use; misses a pageManager!" << endl;
        return -1;
    }
    return frameSet()->pageManager()->pageNumber(this);
}

int KWFrame::pageNumber( KWDocument* doc ) const
{
    return doc->pageManager()->pageNumber(this);
}

KWFrame *KWFrame::getCopy() {
    /* returns a deep copy of self */
    return new KWFrame(this);
}

void KWFrame::copySettings(KWFrame *frm)
{
    setFrameSet( frm->frameSet() ); // do this first in case of debug output in the methods below
    setRect(frm->x(), frm->y(), frm->width(), frm->height());
    // Keep order identical as member var order (and init in ctor)
    setSheetSide(frm->sheetSide());
    setRunAround(frm->runAround());
    setRunAroundSide(frm->runAroundSide());
    setFrameBehavior(frm->frameBehavior());
    setNewFrameBehavior(frm->newFrameBehavior());
    setRunAroundGap(frm->runAroundLeft(), frm->runAroundRight(), frm->runAroundTop(), frm->runAroundBottom());
    setPaddingLeft(frm->paddingLeft());
    setPaddingRight(frm->paddingRight());
    setPaddingTop(frm->paddingTop());
    setPaddingBottom(frm->paddingBottom());
    setMinimumFrameHeight(frm->minimumFrameHeight());
    m_internalY = 0; // internal Y is recalculated
    setZOrder(frm->zOrder());
    setCopy(frm->isCopy());
    m_drawFootNoteLine = false; // recalculated
    setBackgroundColor( frm->backgroundColor() );
    setLeftBorder(frm->leftBorder());
    setRightBorder(frm->rightBorder());
    setTopBorder(frm->topBorder());
    setBottomBorder(frm->bottomBorder());
}

void KWFrame::frameBordersChanged() {
    if (frameSet()->isFloating())
        frameSet()->anchorFrameset()->invalidate();
}


void KWFrame::updateRulerHandles(){
// TODO
#if 0
    if(! isSelected())
    {
        KWDocument *doc = frameSet()->kWordDocument();
        if(doc)
            doc->updateRulerFrameStartEnd();
    }
#endif
}

TQRect KWFrame::outerRect( KWViewMode* viewMode ) const
{
    KWDocument *doc = m_frameSet->kWordDocument();
    TQRect outerRect( doc->zoomRect( *this ) );
    if ( viewMode && !m_frameSet->groupmanager() ) {
        int minBorder = viewMode->drawFrameBorders() ? 1 : 0;
        KWFrame* settingsFrame = m_frameSet->settingsFrame( this );
        outerRect.rLeft() -= KoBorder::zoomWidthX( settingsFrame->leftBorder().width(), doc, minBorder );
        outerRect.rTop() -= KoBorder::zoomWidthY( settingsFrame->topBorder().width(), doc, minBorder );
        outerRect.rRight() += KoBorder::zoomWidthX( settingsFrame->rightBorder().width(), doc, minBorder );
        outerRect.rBottom() += KoBorder::zoomWidthY( settingsFrame->bottomBorder().width(), doc, minBorder );
    }
    return outerRect;
}

KoRect KWFrame::outerKoRect() const
{
    KoRect outerRect = *this;
    KWDocument *doc = m_frameSet->kWordDocument();
    KWFrame* settingsFrame = m_frameSet->settingsFrame( this );
    outerRect.rLeft() -= KoBorder::zoomWidthX( settingsFrame->leftBorder().width(), doc, 1 ) / doc->zoomedResolutionX();
    outerRect.rTop() -= KoBorder::zoomWidthY( settingsFrame->topBorder().width(), doc, 1 ) / doc->zoomedResolutionY();
    outerRect.rRight() += KoBorder::zoomWidthX( settingsFrame->rightBorder().width(), doc, 1 ) / doc->zoomedResolutionX();
    outerRect.rBottom() += KoBorder::zoomWidthY( settingsFrame->bottomBorder().width(), doc, 1 ) / doc->zoomedResolutionY();
    return outerRect;
}

KoRect KWFrame::runAroundRect() const
{
    KoRect raRect = outerKoRect();
    raRect.rLeft() -= m_runAroundLeft;
    raRect.rRight() += m_runAroundRight;
    raRect.rTop() -= m_runAroundTop;
    raRect.rBottom() += m_runAroundBottom;
    return raRect;
}

void KWFrame::save( TQDomElement &frameElem )
{
    // setAttribute( double ) uses a default precision of 6, and this seems
    // to be 6 digits, even like '123.123' !
    frameElem.setAttribute( "left", TQString::number( left(), 'g', DBL_DIG ) );
    frameElem.setAttribute( "top", TQString::number( top(), 'g', DBL_DIG ) );
    frameElem.setAttribute( "right", TQString::number( right(), 'g', DBL_DIG ) );
    frameElem.setAttribute( "bottom", TQString::number( bottom(), 'g', DBL_DIG ) );
    if ( minimumFrameHeight() > 0 )
        frameElem.setAttribute( "min-height", TQString::number( minimumFrameHeight(), 'g', DBL_DIG ) );

    if ( !m_frameSet->isHeaderOrFooter() && !m_frameSet->isMainFrameset() )
    {
        if(runAround()!=RA_NO)
        {
            frameElem.setAttribute( "runaround", static_cast<int>( runAround() ) );
            if (runAround() == RA_BOUNDINGRECT)
            {
                if (runAroundSide()==RA_LEFT)
                    frameElem.setAttribute( "runaroundSide", "left" );
                else if (runAroundSide()==RA_RIGHT)
                    frameElem.setAttribute( "runaroundSide", "right" );
                else
                    frameElem.setAttribute( "runaroundSide", "biggest" );
            }
        }
        if(runAroundLeft()!=0 || runAroundRight()!=0 || runAroundTop()!=0 || runAroundBottom()!=0) {
            frameElem.setAttribute( "runaroundLeft", m_runAroundLeft );
            frameElem.setAttribute( "runaroundRight", m_runAroundRight );
            frameElem.setAttribute( "runaroundTop", m_runAroundTop );
            frameElem.setAttribute( "runaroundBottom", m_runAroundBottom );
            // The old file format had only one value, keep compat
            double runAroundGap = TQMAX( TQMAX( m_runAroundLeft, m_runAroundRight ), TQMAX( m_runAroundTop, m_runAroundBottom ) );
            frameElem.setAttribute( "runaroundGap", runAroundGap );
        }
    }

    if(leftBorder().penWidth()!=0)
        frameElem.setAttribute( "lWidth", leftBorder().penWidth() );

    if(leftBorder().color.isValid())
    {
        frameElem.setAttribute( "lRed", leftBorder().color.red() );
        frameElem.setAttribute( "lGreen", leftBorder().color.green() );
        frameElem.setAttribute( "lBlue", leftBorder().color.blue() );
    }
    if(leftBorder().getStyle() != KoBorder::SOLID)
        frameElem.setAttribute( "lStyle", static_cast<int>( leftBorder().getStyle()) );

    if(rightBorder().penWidth()!=0)
        frameElem.setAttribute( "rWidth", rightBorder().penWidth() );

    if(rightBorder().color.isValid())
    {
        frameElem.setAttribute( "rRed", rightBorder().color.red() );
        frameElem.setAttribute( "rGreen", rightBorder().color.green() );
        frameElem.setAttribute( "rBlue", rightBorder().color.blue() );
    }
    if(rightBorder().getStyle() != KoBorder::SOLID)
        frameElem.setAttribute( "rStyle", static_cast<int>( rightBorder().getStyle() ) );

    if(topBorder().penWidth()!=0)
        frameElem.setAttribute( "tWidth", topBorder().penWidth() );

    if(topBorder().color.isValid())
    {
        frameElem.setAttribute( "tRed", topBorder().color.red() );
        frameElem.setAttribute( "tGreen", topBorder().color.green() );
        frameElem.setAttribute( "tBlue", topBorder().color.blue() );
    }
    if(topBorder().getStyle() != KoBorder::SOLID)
        frameElem.setAttribute( "tStyle", static_cast<int>( topBorder().getStyle() ) );

    if(bottomBorder().penWidth()!=0) {
        frameElem.setAttribute( "bWidth", bottomBorder().penWidth() );
    }
    if(bottomBorder().color.isValid()) {
        frameElem.setAttribute( "bRed", bottomBorder().color.red() );
        frameElem.setAttribute( "bGreen", bottomBorder().color.green() );
        frameElem.setAttribute( "bBlue", bottomBorder().color.blue() );
    }
    if(bottomBorder().getStyle() != KoBorder::SOLID)
        frameElem.setAttribute( "bStyle", static_cast<int>( bottomBorder().getStyle() ) );

    if(backgroundColor().color().isValid())
    {
        frameElem.setAttribute( "bkRed", backgroundColor().color().red() );
        frameElem.setAttribute( "bkGreen", backgroundColor().color().green() );
        frameElem.setAttribute( "bkBlue", backgroundColor().color().blue() );
        frameElem.setAttribute( "bkStyle", (int)backgroundColor().style ());
    }
    if(paddingLeft() != 0)
        frameElem.setAttribute( "bleftpt", paddingLeft() );

    if(paddingRight()!=0)
        frameElem.setAttribute( "brightpt", paddingRight() );

    if(paddingTop()!=0)
        frameElem.setAttribute( "btoppt", paddingTop() );

    if(paddingBottom()!=0)
        frameElem.setAttribute( "bbottompt", paddingBottom() );

    if(frameBehavior()!=AutoCreateNewFrame)
        frameElem.setAttribute( "autoCreateNewFrame", static_cast<int>( frameBehavior()) );

    //if(newFrameBehavior()!=Reconnect) // always save this one, since the default value depends on the type of frame, etc.
    frameElem.setAttribute( "newFrameBehavior", static_cast<int>( newFrameBehavior()) );

    //same reason
    frameElem.setAttribute( "copy", static_cast<int>( m_bCopy ) );

    if(sheetSide()!= AnySide)
        frameElem.setAttribute( "sheetSide", static_cast<int>( sheetSide()) );

    frameElem.setAttribute( "z-index", zOrder() );
}

void KWFrame::load( TQDomElement &frameElem, KWFrameSet* frameSet, int syntaxVersion )
{
    m_minFrameHeight = KWDocument::getAttribute( frameElem, "min-height", 0.0 );
    m_runAround = static_cast<RunAround>( KWDocument::getAttribute( frameElem, "runaround", RA_NO ) );
    TQString str = frameElem.attribute( "runaroundSide" );
    if ( str == "left" )
        m_runAroundSide = RA_LEFT;
    else if ( str == "right" )
        m_runAroundSide = RA_RIGHT;
    else
        m_runAroundSide = RA_BIGGEST;

    double runAroundGap = ( frameElem.hasAttribute( "runaroundGap" ) )
                          ? frameElem.attribute( "runaroundGap" ).toDouble()
                          : frameElem.attribute( "runaGapPT" ).toDouble();
    setRunAroundGap( KWDocument::getAttribute( frameElem, "runaroundLeft", runAroundGap ),
                     KWDocument::getAttribute( frameElem, "runaroundRight", runAroundGap ),
                     KWDocument::getAttribute( frameElem, "runaroundTop", runAroundGap ),
                     KWDocument::getAttribute( frameElem, "runaroundBottom", runAroundGap ) );

    m_sheetSide = static_cast<SheetSide>( KWDocument::getAttribute( frameElem, "sheetSide", AnySide ) );
    m_frameBehavior = static_cast<FrameBehavior>( KWDocument::getAttribute( frameElem, "autoCreateNewFrame", AutoCreateNewFrame ) );
    // Old documents had no "NewFrameBehavior" for footers/headers -> default to Copy.
    NewFrameBehavior defaultValue = frameSet->isHeaderOrFooter() ? Copy : Reconnect;
    // for old document we used the British spelling (newFrameBehaviour), so this is for backwards compatibility.
    defaultValue = static_cast<NewFrameBehavior>( KWDocument::getAttribute( frameElem, "newFrameBehaviour", defaultValue ) );
    m_newFrameBehavior = static_cast<NewFrameBehavior>( KWDocument::getAttribute( frameElem, "newFrameBehavior", defaultValue ) );
    if ( frameSet->isFootEndNote() ) // note that isFootNote/isEndNote are not possible yet
        m_newFrameBehavior = NoFollowup;

    KoBorder l, r, t, b;
    l.setPenWidth( KWDocument::getAttribute( frameElem, "lWidth", 0.0 ));
    r.setPenWidth(KWDocument::getAttribute( frameElem, "rWidth", 0.0 ));
    t.setPenWidth(KWDocument::getAttribute( frameElem, "tWidth", 0.0 ));
    b.setPenWidth(KWDocument::getAttribute( frameElem, "bWidth", 0.0 ));
    if ( frameElem.hasAttribute("lRed") )
        l.color.setRgb(
            KWDocument::getAttribute( frameElem, "lRed", 0 ),
            KWDocument::getAttribute( frameElem, "lGreen", 0 ),
            KWDocument::getAttribute( frameElem, "lBlue", 0 ) );
    if ( frameElem.hasAttribute("rRed") )
        r.color.setRgb(
            KWDocument::getAttribute( frameElem, "rRed", 0 ),
            KWDocument::getAttribute( frameElem, "rGreen", 0 ),
            KWDocument::getAttribute( frameElem, "rBlue", 0 ) );
    if ( frameElem.hasAttribute("tRed") )
        t.color.setRgb(
            KWDocument::getAttribute( frameElem, "tRed", 0 ),
            KWDocument::getAttribute( frameElem, "tGreen", 0 ),
            KWDocument::getAttribute( frameElem, "tBlue", 0 ) );
    if ( frameElem.hasAttribute("bRed") )
        b.color.setRgb(
            KWDocument::getAttribute( frameElem, "bRed", 0 ),
            KWDocument::getAttribute( frameElem, "bGreen", 0 ),
            KWDocument::getAttribute( frameElem, "bBlue", 0 ) );
    l.setStyle(static_cast<KoBorder::BorderStyle>( KWDocument::getAttribute( frameElem, "lStyle", KoBorder::SOLID ) ));
    r.setStyle(static_cast<KoBorder::BorderStyle>( KWDocument::getAttribute( frameElem, "rStyle", KoBorder::SOLID ) ));
    t.setStyle(static_cast<KoBorder::BorderStyle>( KWDocument::getAttribute( frameElem, "tStyle", KoBorder::SOLID ) ));
    b.setStyle( static_cast<KoBorder::BorderStyle>( KWDocument::getAttribute( frameElem, "bStyle", KoBorder::SOLID ) ));
    TQColor c;
    if ( frameElem.hasAttribute("bkRed") )
        c.setRgb(
            KWDocument::getAttribute( frameElem, "bkRed", 0 ),
            KWDocument::getAttribute( frameElem, "bkGreen", 0 ),
            KWDocument::getAttribute( frameElem, "bkBlue", 0 ) );

    if ( syntaxVersion < 2 ) // Activate old "white border == no border" conversion
    {
        if(c==l.color && l.penWidth()==1 && l.getStyle()==0 )
            l.setPenWidth(0);
        if(c==r.color  && r.penWidth()==1 && r.getStyle()==0)
            r.setPenWidth(0);
        if(c==t.color && t.penWidth()==1 && t.getStyle()==0 )
            t.setPenWidth(0);
        if(c==b.color && b.penWidth()==1 && b.getStyle()==0 )
            b.setPenWidth(0);
    }
    m_borderLeft = l;
    m_borderRight = r;
    m_borderTop = t;
    m_borderBottom = b;
    m_backgroundColor = TQBrush( c );


    if( frameElem.hasAttribute("bkStyle"))
        m_backgroundColor.setStyle (static_cast<TQt::BrushStyle>(KWDocument::getAttribute( frameElem, "bkStyle", TQt::SolidPattern )));

    m_paddingLeft = frameElem.attribute( "bleftpt" ).toDouble();
    m_paddingRight = frameElem.attribute( "brightpt" ).toDouble();
    m_paddingTop = frameElem.attribute( "btoppt" ).toDouble();
    m_paddingBottom = frameElem.attribute( "bbottompt" ).toDouble();
    m_bCopy = KWDocument::getAttribute( frameElem, "copy", frameSet->isHeaderOrFooter() /* default to true for h/f */ );
    m_zOrder = frameElem.attribute( "z-index" ).toInt();
}

// This is shared with table cells - so, no runaround and newframebehavior etc.
// Only background, borders, padding.
void KWFrame::loadBorderProperties( KoStyleStack& styleStack )
{
    // padding. fo:padding for 4 values or padding-left/right/top/bottom
    m_paddingLeft = KoUnit::parseValue( styleStack.attributeNS( KoXmlNS::fo, "padding", "left" ) );
    m_paddingRight = KoUnit::parseValue( styleStack.attributeNS( KoXmlNS::fo, "padding", "right" ) );
    m_paddingTop = KoUnit::parseValue( styleStack.attributeNS( KoXmlNS::fo, "padding", "top" ) );
    m_paddingBottom = KoUnit::parseValue( styleStack.attributeNS( KoXmlNS::fo, "padding", "bottom" ) );

    // background color
    if ( styleStack.hasAttributeNS( KoXmlNS::fo, "background-color" ) ) {
        TQString color = styleStack.attributeNS( KoXmlNS::fo, "background-color" );
        if ( color == "transparent" )
            m_backgroundColor = TQBrush( TQColor(), TQt::NoBrush );
        else
        {
            m_backgroundColor = TQBrush( TQColor( color ) /*, brush style is a dead feature, ignored */ );
        }
    }
    // OOo compatibility: it uses background-transparency=100% instead of background-color="transparent"
    if ( styleStack.hasAttributeNS( KoXmlNS::fo, "background-transparency" ) ) {
        TQString transp = styleStack.attributeNS( KoXmlNS::fo, "background-transparency" );
        if ( transp == "100%" )
            m_backgroundColor.setStyle( TQt::NoBrush );
    }

    // borders (3.11.27)
    // can be none/hidden, solid and double. General form is the XSL/FO "width|style|color"
    {
        m_borderLeft.loadFoBorder( styleStack.attributeNS( KoXmlNS::fo, "border", "left") );
        m_borderRight.loadFoBorder( styleStack.attributeNS( KoXmlNS::fo, "border", "right") );
        m_borderTop.loadFoBorder( styleStack.attributeNS( KoXmlNS::fo, "border", "top") );
        m_borderBottom.loadFoBorder( styleStack.attributeNS( KoXmlNS::fo, "border", "bottom") );
    }
    // TODO more refined border spec for double borders (3.11.28)
}

void KWFrame::loadCommonOasisProperties( KoOasisContext& context, KWFrameSet* frameSet, const char* typeProperties )
{
    KoStyleStack& styleStack = context.styleStack();
    styleStack.setTypeProperties( typeProperties );

    loadBorderProperties( styleStack );

    // Background color is now done with fill-color.
    // loadBorderProperties loads fo:background-color for compatibility (and for table cells),
    // but the correct way for text boxes is draw:fill-color
    if ( styleStack.hasAttributeNS( KoXmlNS::draw, "fill-color" ) ) {
        TQString color = styleStack.attributeNS( KoXmlNS::draw, "fill-color" );
        if ( color == "transparent" )
            m_backgroundColor = TQBrush( TQColor(), TQt::NoBrush );
        else
        {
            m_backgroundColor = TQBrush( TQColor( color ) /*, brush style is a dead feature, ignored */ );
        }
    }

#if 0 // not allowed in the current OASIS spec
    // margins, i.e. runAroundGap. fo:margin for 4 values or padding-left/right/top/bottom
    m_runAroundLeft = KoUnit::parseValue( styleStack.attributeNS( KoXmlNS::fo, "margin", "left" ) );
    m_runAroundRight = KoUnit::parseValue( styleStack.attributeNS( KoXmlNS::fo, "margin", "right" ) );
    m_runAroundTop = KoUnit::parseValue( styleStack.attributeNS( KoXmlNS::fo, "margin", "top" ) );
    m_runAroundBottom = KoUnit::parseValue( styleStack.attributeNS( KoXmlNS::fo, "margin", "bottom" ) );
#endif
    // margins, i.e. runAroundGap. fo:margin-left/right/top/bottom
    m_runAroundLeft = KoUnit::parseValue( styleStack.attributeNS( KoXmlNS::fo, "margin-left" ) );
    m_runAroundRight = KoUnit::parseValue( styleStack.attributeNS( KoXmlNS::fo, "margin-right" ) );
    m_runAroundTop = KoUnit::parseValue( styleStack.attributeNS( KoXmlNS::fo, "margin-top" ) );
    m_runAroundBottom = KoUnit::parseValue( styleStack.attributeNS( KoXmlNS::fo, "margin-bottom" ) );

    // This attribute isn't part of the OASIS spec. Doesn't matter since it doesn't affect rendering
    // of existing documents, only editing (and only KWord has this kind of option until now).
    const TQCString frameBehaviorOnNewPage = styleStack.attributeNS( KoXmlNS::koffice, "frame-behavior-on-new-page" ).latin1();
    if ( frameBehaviorOnNewPage == "followup" )
        m_newFrameBehavior = Reconnect;
    else if ( frameBehaviorOnNewPage == "copy" )
        m_newFrameBehavior = Copy;
    else if ( frameBehaviorOnNewPage == "none" )
        m_newFrameBehavior = NoFollowup;
    else { // Defaults for OASIS documents not created by KWord
        m_newFrameBehavior = frameSet->isHeaderOrFooter() ? Copy : NoFollowup;
        if ( !frameBehaviorOnNewPage.isEmpty() )
            kdWarning(32001) << "Unknown value for koffice:frame-behavior-on-new-page: " << frameBehaviorOnNewPage << endl;
    }
    // Footnotes and endnotes are handled in a special way.
    if ( frameSet->isFootEndNote() ) // note that isFootNote/isEndNote are not possible yet
        m_newFrameBehavior = NoFollowup;

    KWFrame::RunAround runAround = KWFrame::RA_BOUNDINGRECT;
    KWFrame::RunAroundSide runAroundSide = KWFrame::RA_BIGGEST;
    const TQCString oowrap = styleStack.attributeNS( KoXmlNS::style, "wrap" ).latin1();
    if ( oowrap == "none" )        // 'no wrap' means 'avoid horizontal space'
        runAround = KWFrame::RA_SKIP;
    else if ( oowrap == "left" )
        runAroundSide = KWFrame::RA_LEFT;
    else if ( oowrap == "right" )
        runAroundSide= KWFrame::RA_RIGHT;
    else if ( oowrap == "run-through" )
        runAround = KWFrame::RA_NO;
    //if ( oowrap == "biggest" ) // OASIS extension
    // ->( KWFrame::RA_BOUNDINGRECT, KWFrame::RA_BIGGEST ), already set above
    //if ( oowrap == "parallel" || oowrap == "dynamic" )
    // dynamic is called "optimal" in the OO GUI. It's different from biggest because it can lead to parallel.
    // Those are not supported in KWord, let's use biggest instead
    setRunAround( runAround );
    setRunAroundSide( runAroundSide );
}

void KWFrame::startOasisFrame( KoXmlWriter &writer, KoGenStyles& mainStyles, const TQString& name, const TQString& lastFrameName ) const
{
    writer.startElement( "draw:frame" );
    writer.addAttribute( "draw:name", name );
    writer.addAttribute( "draw:style-name", saveOasisFrameStyle( mainStyles ) );

    if ( !frameSet()->isFloating() )
    { // non-inline frame, anchored to page
        const int pgNum = pageNumber();
        const double yInPage = top() - frameSet()->pageManager()->topOfPage(pgNum);
        writer.addAttributePt( "svg:x", left() );
        writer.addAttributePt( "svg:y", yInPage );
        writer.addAttribute( "text:anchor-type", "page" );
        writer.addAttribute( "text:anchor-page-number", pgNum );
        writer.addAttribute( "draw:z-index", zOrder() );
    }
    writer.addAttributePt( "svg:width", width() );
    writer.addAttributePt( "svg:height", height() );
    if ( isCopy() )
        writer.addAttribute( "draw:copy-of", lastFrameName );
}

// shared between startOasisFrame and table cells.
// Only background, borders, padding.
void KWFrame::saveBorderProperties( KoGenStyle& frameStyle ) const
{
    // Background: color and transparency
    // OOo seems to use style:background-transparency="100%", but the schema allows background-color=transparent
    if ( m_backgroundColor.style() == TQt::NoBrush )
        frameStyle.addProperty( "fo:background-color", "transparent" );
    else if ( m_backgroundColor.color().isValid() )
        frameStyle.addProperty( "fo:background-color", m_backgroundColor.color().name() );

    // Borders
    if (  ( m_borderLeft == m_borderRight )
          && ( m_borderLeft == m_borderTop )
          && ( m_borderLeft == m_borderBottom ) )
    {
        frameStyle.addProperty( "fo:border", m_borderLeft.saveFoBorder() );
    }
    else
    {
        frameStyle.addProperty( "fo:border-left", m_borderLeft.saveFoBorder() );
        frameStyle.addProperty( "fo:border-right", m_borderRight.saveFoBorder() );
        frameStyle.addProperty( "fo:border-top", m_borderTop.saveFoBorder() );
        frameStyle.addProperty( "fo:border-bottom", m_borderBottom.saveFoBorder() );
    }

    if ( m_paddingLeft != 0 && ( ( m_paddingLeft == m_paddingRight )
                                 && ( m_paddingLeft == m_paddingTop )
                                 && ( m_paddingLeft == m_paddingBottom ) ) )
        frameStyle.addPropertyPt( "fo:padding", m_paddingLeft );
    else
    {
        if ( m_paddingLeft != 0 )
            frameStyle.addPropertyPt( "fo:padding-left", m_paddingLeft );
        if ( m_paddingRight != 0 )
            frameStyle.addPropertyPt( "fo:padding-right", m_paddingRight );
        if ( m_paddingTop != 0 )
            frameStyle.addPropertyPt( "fo:padding-top", m_paddingTop );
        if ( m_paddingBottom != 0 )
            frameStyle.addPropertyPt( "fo:padding-bottom", m_paddingBottom );
    }
}

void KWFrame::saveMarginAttributes( KoXmlWriter &writer ) const
{
    if ( m_runAroundLeft != 0 )
        writer.addAttributePt( "fo:margin-left", m_runAroundLeft );
    if ( m_runAroundRight != 0 )
        writer.addAttributePt( "fo:margin-right", m_runAroundRight );
    if ( m_runAroundTop != 0 )
        writer.addAttributePt( "fo:margin-top", m_runAroundTop );
    if ( m_runAroundBottom != 0 )
        writer.addAttributePt( "fo:margin-bottom", m_runAroundBottom );
}

void KWFrame::saveMarginProperties( KoGenStyle& frameStyle ) const
{
#if 0 // not allowed in the current OASIS spec
    if ( m_runAroundLeft != 0 && ( ( m_runAroundLeft == m_runAroundRight )
                                 && ( m_runAroundLeft == m_runAroundTop )
                                 && ( m_runAroundLeft == m_runAroundBottom ) ) )
        frameStyle.addPropertyPt( "fo:margin", m_runAroundLeft );
    else
    {
#endif
        if ( m_runAroundLeft != 0 )
            frameStyle.addPropertyPt( "fo:margin-left", m_runAroundLeft );
        if ( m_runAroundRight != 0 )
            frameStyle.addPropertyPt( "fo:margin-right", m_runAroundRight );
        if ( m_runAroundTop != 0 )
            frameStyle.addPropertyPt( "fo:margin-top", m_runAroundTop );
        if ( m_runAroundBottom != 0 )
            frameStyle.addPropertyPt( "fo:margin-bottom", m_runAroundBottom );
#if 0 // not allowed in the current OASIS spec
    }
#endif
}

TQString KWFrame::saveOasisFrameStyle( KoGenStyles& mainStyles ) const
{
    KoGenStyle frameStyle( KWDocument::STYLE_FRAME_AUTO, "graphic" );
    TQString protect;
    if ( frameSet()->protectContent() )
        protect = "content";
    if ( frameSet()->isProtectSize() ) // ## should be moved for frame
    {
        if ( !protect.isEmpty() )
            protect+=" ";
        protect+="size";
    }
    if ( !protect.isEmpty() )
        frameStyle.addProperty( "style:protect", protect );

    if ( !frameSet()->isFloating() )
    { // non-inline frame, anchored to page
        frameStyle.addProperty( "style:horizontal-rel", "page" );
        frameStyle.addProperty( "style:vertical-rel", "page" );
        frameStyle.addProperty( "style:horizontal-pos", "from-left" );
        frameStyle.addProperty( "style:vertical-pos", "from-top" );
    }

    // Background (KWFrame::saveBorderProperties saves as fo:background-color, but OOo-2.0.x uses draw:fill-color)
    // So now we use draw:fill-color too, the text background color is in fact a paragraph feature.
    if ( m_backgroundColor.style() == TQt::NoBrush )
        frameStyle.addProperty( "draw:fill-color", "transparent" );
    else if ( m_backgroundColor.color().isValid() )
        frameStyle.addProperty( "draw:fill-color", m_backgroundColor.color().name() );

    saveBorderProperties( frameStyle );
    saveMarginProperties( frameStyle );

    if ( runAround() == KWFrame::RA_SKIP )
        frameStyle.addProperty( "style:wrap", "none" );
    else if ( runAround() == KWFrame::RA_NO )
        frameStyle.addProperty( "style:wrap", "run-through" );
    else // RA_BOUNDINGRECT
    {
        if ( runAroundSide() ==  KWFrame::RA_LEFT )
            frameStyle.addProperty( "style:wrap", "left" );
        else if ( runAroundSide() == KWFrame::RA_RIGHT )
            frameStyle.addProperty( "style:wrap", "right" );
        else if ( runAroundSide() == KWFrame::RA_BIGGEST )
            frameStyle.addProperty( "style:wrap", "biggest" );
    }

    // This attribute isn't part of the OASIS spec. Doesn't matter since it doesn't affect rendering
    // of existing documents, only editing (and only KWord has this kind of option until now).
    NewFrameBehavior defaultNfb = frameSet()->isHeaderOrFooter() ? Copy : NoFollowup;
    if ( m_newFrameBehavior != defaultNfb ) {
        const char* value = "none";
        if ( m_newFrameBehavior == Reconnect )
            value = "followup";
        else if ( m_newFrameBehavior == Copy )
            value = "copy";
        else if ( m_newFrameBehavior == NoFollowup )
            value = "none";
        frameStyle.addProperty( "koffice:frame-behavior-on-new-page", value );
    }

    // The loading code for this one is in kwtextframeset, maybe this should be moved there too
    const char* frameBehav = 0;
    if ( m_frameBehavior == KWFrame::Ignore )
        frameBehav = "clip";
    else if ( m_frameBehavior == KWFrame::AutoCreateNewFrame )
        frameBehav = "auto-create-new-frame";
    // the third case, AutoExtendFrame is handled by min-height
    if ( frameBehav )
        frameStyle.addProperty( "style:overflow-behavior", frameBehav );

    return mainStyles.lookup( frameStyle, "fr" );
}

bool KWFrame::frameAtPos( const TQPoint& point, bool borderOfFrameOnly) const {
    // Forwarded to KWFrameSet to make it virtual
    return frameSet()->isFrameAtPos( this, point, borderOfFrameOnly );
}

KoRect KWFrame::innerRect() const
{
    KoRect inner( this->normalize());
    inner.moveBy( paddingLeft(), paddingTop() );
    inner.setWidth( innerWidth() );
    inner.setHeight( innerHeight() );
    return inner;
}

double KWFrame::innerWidth() const
{
    return KMAX( 0.0, width() - m_paddingLeft - m_paddingRight );
}

double KWFrame::innerHeight() const
{
    return KMAX( 0.0, height() - m_paddingTop - m_paddingBottom );
}

void KWFrame::setFramePadding( double left, double top, double right, double bottom)
{
    m_paddingLeft = left;
    m_paddingTop = top;
    m_paddingRight = right;
    m_paddingBottom = bottom;
}

bool KWFrame::compareFrameZOrder(KWFrame *f1, KWFrame *f2)
{
    return f1->zOrder() < f2->zOrder();
}
