/* -*- Mode: C++ -*-
   KD Tools - a set of useful widgets for TQt
*/

/****************************************************************************
** Copyright (C) 2001-2003 Klarälvdalens Datakonsult AB.  All rights reserved.
**
** This file is part of the KD Tools library.
**
** This file may be distributed and/or modified under the terms of the
** GNU General Public License version 2 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.
**
** Licensees holding valid commercial KD Tools licenses may use this file in
** accordance with the KD Tools Commercial License Agreement provided with
** the Software.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
** See http://www.klaralvdalens-datakonsult.se/?page=products for
**   information about KD Tools Commercial License Agreements.
**
** Contact info@klaralvdalens-datakonsult.se if any conditions of this
** licensing are not clear to you.
**
**********************************************************************/

#include <tqpainter.h>
#include <tqbrush.h>

#include <KDFrame.h>
#include <KDFrameProfileSection.h>
#include <KDXMLTools.h>

#if defined( SUN7 ) || defined( _SGIAPI )
#include <math.h>
#else
#include <cmath>
#endif
#ifdef TQ_WS_WIN
#define M_PI 3.14159265358979323846
#endif


KDFrame::~KDFrame()
{
    _profileSections.clear(); // is ignored if auto-deletion is disabled
}

KDFrame::KDFrameCorner::~KDFrameCorner()
{
    // Intentionally left blank for now.
}





void KDFrame::paintBackground( TQPainter& painter, const TQRect& innerRect ) const
{
    /* first draw the brush (may contain a pixmap)*/
    if( TQt::NoBrush != _background.style() ) {
        TQPen   oldPen(   painter.pen() );
        TQPoint oldOrig(  painter.brushOrigin() );
        TQBrush oldBrush( painter.brush() );
        painter.setPen( TQt::NoPen );
        const TQPoint newTopLeft( painter.xForm( innerRect.topLeft() ) );
        painter.setBrushOrigin( newTopLeft.x(), newTopLeft.y() );
        painter.setBrush( _background );
        painter.drawRect( innerRect.x(), innerRect.y(),
                          innerRect.width(), innerRect.height() );
        painter.setPen(         oldPen );
        painter.setBrushOrigin( oldOrig );
        painter.setBrush(       oldBrush );
    }
    /* next draw the backPixmap over the brush */
    if( ! _backPixmap.isNull() ) {
        TQPoint ol = innerRect.topLeft();
        if( PixCentered == _backPixmapMode )
        {
            ol.setX( innerRect.center().x() - _backPixmap.width() / 2 );
            ol.setY( innerRect.center().y() - _backPixmap.height()/ 2 );
            bitBlt( painter.device(), ol, &_backPixmap );
        } else {
            TQWMatrix m;
            double zW = (double)innerRect.width()  / (double)_backPixmap.width();
            double zH = (double)innerRect.height() / (double)_backPixmap.height();
            switch ( _backPixmapMode ) {
                case PixCentered:
                    break;
                case PixScaled: {
                                    double z;
                                    z = TQMIN( zW, zH );
                                    m.scale( z, z );
                                }
                                break;
                case PixStretched:
                                m.scale( zW, zH );
                                break;
            }
            TQPixmap pm = _backPixmap.xForm( m );
            ol.setX( innerRect.center().x() - pm.width() / 2 );
            ol.setY( innerRect.center().y() - pm.height()/ 2 );
            bitBlt( painter.device(), ol, &pm );
        }
    }
}


void KDFrame::paintEdges( TQPainter& painter, const TQRect& innerRect ) const
    {

/*
    Note: The following code OF COURSE is only dummy-code and will be replaced.

          At the moment it is used to draw the simple frames which were set by
          the setSimpleFrame() function.
*/
    if( !_topProfile.isEmpty() ){

        KDFrameProfileSection* section;
        for ( section = ((KDFrameProfile)_topProfile).last(); section; section = ((KDFrameProfile)_topProfile).prev() ){
            const TQPen   oldPen   = painter.pen();
            const TQBrush oldBrush = painter.brush();
            TQPen thePen;
            thePen = section->pen();
            int penWidth = TQMAX(thePen.width(), 1) * TQMAX(section->width(), 1);
//tqDebug("paintEdges: thePen.width() = %i", thePen.width());
//tqDebug("paintEdges: section->width() = %i", section->width());
//tqDebug("paintEdges: penWidth = %i", penWidth);
            thePen.setWidth( penWidth );
            painter.setPen( thePen );
            painter.setBrush( TQt::NoBrush );
            painter.drawRect( innerRect.x()-penWidth,
                              innerRect.y()-penWidth,
                              innerRect.width()  +2*penWidth,
                              innerRect.height() +2*penWidth );
            painter.setBrush( oldBrush );
            painter.setPen( oldPen );
        }
    }
}


void KDFrame::paintCorners( TQPainter& /*painter*/, const TQRect& /*innerRect*/ ) const
{
    // At the moment nothing is done here since the setSimpleFrame() function
    // does not allow specifying corners: all predefined frames have normal
    // (default) corners.
    ;
}


void KDFrame::paint( TQPainter* painter,
        KDFramePaintSteps steps,
        TQRect innerRect ) const
{
    if( painter ) {
        const TQRect& rect = innerRect.isValid() ? innerRect : _innerRect;
        switch( steps ) {
            case PaintBackground:
                paintBackground( *painter, rect );
                break;
            case PaintEdges:
                paintEdges(      *painter, rect );
                break;
            case PaintCorners:
                paintCorners(    *painter, rect );
                break;
            case PaintBorder:
                paintEdges(      *painter, rect );
                paintCorners(    *painter, rect );
                break;
            case PaintAll:
                paintBackground( *painter, rect );
                paintEdges(      *painter, rect );
                paintCorners(    *painter, rect );
                break;
        }
    }
}


void KDFrame::clearProfile( ProfileName name )
{
    switch( name ) {
        case ProfileTop:   _topProfile.clear();
                           break;
        case ProfileRight: _rightProfile.clear();
                           break;
        case ProfileBottom:_bottomProfile.clear();
                           break;
        case ProfileLeft:  _leftProfile.clear();
                           break;
    }
}

void KDFrame::addProfileSection( ProfileName      name,
                                 int              wid,
                                 TQPen             pen,
                                 KDFrameProfileSection::Direction dir,
                                 KDFrameProfileSection::Curvature curv )
{
    switch( name ) {
        case ProfileTop:   _topProfile.append(   new KDFrameProfileSection( dir, curv, wid, pen ) );
                           break;
        case ProfileRight: _rightProfile.append( new KDFrameProfileSection( dir, curv, wid, pen ) );
                           break;
        case ProfileBottom:_bottomProfile.append(new KDFrameProfileSection( dir, curv, wid, pen ) );
                           break;
        case ProfileLeft:  _leftProfile.append(  new KDFrameProfileSection( dir, curv, wid, pen ) );
                           break;
    }
}

void KDFrame::setProfile( ProfileName name, const KDFrameProfile& profile )
{
    switch( name ) {
        case ProfileTop:   _topProfile    = profile;
                           break;
        case ProfileRight: _rightProfile  = profile;
                           break;
        case ProfileBottom:_bottomProfile = profile;
                           break;
        case ProfileLeft:  _leftProfile   = profile;
                           break;
    }
}

const KDFrameProfile& KDFrame::profile( ProfileName name ) const
{
    switch( name ) {
        case ProfileTop:   return _topProfile;
                           break;
        case ProfileRight: return _rightProfile;
                           break;
        case ProfileBottom:return _bottomProfile;
                           break;
        default:           return _leftProfile;
    }

    return _leftProfile;
}


void KDFrame::setSimpleFrame( SimpleFrame frame,
                              int         lineWidth,
                              int         midLineWidth,
                              TQPen        pen,
                              TQBrush      background,
                              const TQPixmap* backPixmap,
                              BackPixmapMode backPixmapMode )
{
    _profileSections.clear();
    _topProfile.clear();
    _rightProfile.clear();
    _bottomProfile.clear();
    _leftProfile.clear();
    _background = background;
    _backPixmap = backPixmap ? *backPixmap : TQPixmap();
    _backPixmapMode = backPixmapMode;
    if( FrameFlat == frame ) {
        //tqDebug("_profileSections.count() before = %i", _profileSections.count());
        KDFrameProfileSection* newsection =
            new KDFrameProfileSection( KDFrameProfileSection::DirPlain,
                    KDFrameProfileSection::CvtPlain,
                    lineWidth, pen );
        _profileSections.append( newsection );
        //tqDebug( "_profileSections.count() after = %i,    lineWidth = %i",
        //        _profileSections.count(),
        //        lineWidth );
        _topProfile.append( newsection );
        _rightProfile  = _topProfile;
        _bottomProfile = _topProfile;
        _leftProfile   = _topProfile;
    }
    else {
        switch( frame ) {
            case FrameElegance: {
                                    int line1 = lineWidth / 8;
                                    int line2 = lineWidth / 16;
                                    int line3 = line2;
                                    int gap2  = line2 * 4;
                                    int gap1  = lineWidth - line1 - line2 - line3 - gap2;
                                    TQPen noP( TQt::NoPen );
                                    KDFrameProfileSection* newsection;
                                    newsection = new KDFrameProfileSection( KDFrameProfileSection::DirPlain,
                                            KDFrameProfileSection::CvtPlain,
                                            line1, pen );
                                    _profileSections.append( newsection );
                                    _topProfile.append( newsection );
                                    newsection = new KDFrameProfileSection( KDFrameProfileSection::DirPlain,
                                            KDFrameProfileSection::CvtPlain,
                                            gap1,  noP );
                                    _profileSections.append( newsection );
                                    _topProfile.append( newsection );
                                    newsection = new KDFrameProfileSection( KDFrameProfileSection::DirPlain,
                                            KDFrameProfileSection::CvtPlain,
                                            line2, pen );
                                    _profileSections.append( newsection );
                                    _topProfile.append( newsection );
                                    newsection = new KDFrameProfileSection( KDFrameProfileSection::DirPlain,
                                            KDFrameProfileSection::CvtPlain,
                                            gap2,  noP );
                                    _profileSections.append( newsection );
                                    _topProfile.append( newsection );
                                    newsection =  new KDFrameProfileSection( KDFrameProfileSection::DirPlain,
                                            KDFrameProfileSection::CvtPlain,
                                            line3, pen );
                                    _profileSections.append( newsection );
                                    _topProfile.append( newsection );
                                }
                                break;
            case FrameBoxRaized:
                                {
                                    KDFrameProfileSection* newsection;
                                    newsection = new KDFrameProfileSection( KDFrameProfileSection::DirRaising,
                                            KDFrameProfileSection::CvtPlain,
                                            lineWidth, pen );
                                    _profileSections.append( newsection );
                                    _topProfile.append( newsection );
                                    newsection = new KDFrameProfileSection( KDFrameProfileSection::DirPlain,
                                            KDFrameProfileSection::CvtPlain,
                                            midLineWidth, pen );
                                    _profileSections.append( newsection );
                                    _topProfile.append( newsection );
                                    newsection =  new KDFrameProfileSection( KDFrameProfileSection::DirSinking,
                                            KDFrameProfileSection::CvtPlain,
                                            lineWidth, pen );
                                    _profileSections.append( newsection );
                                    _topProfile.append( newsection );
                                    break;
                                }
            case FrameBoxSunken:
                                {
                                    KDFrameProfileSection* newsection;
                                    newsection = new KDFrameProfileSection( KDFrameProfileSection::DirSinking,
                                            KDFrameProfileSection::CvtPlain,
                                            lineWidth, pen );
                                    _profileSections.append( newsection );
                                    _topProfile.append( newsection );
                                    newsection = new KDFrameProfileSection( KDFrameProfileSection::DirPlain,
                                            KDFrameProfileSection::CvtPlain,
                                            midLineWidth, pen );
                                    _profileSections.append( newsection );
                                    _topProfile.append( newsection );
                                    newsection = new KDFrameProfileSection( KDFrameProfileSection::DirRaising,
                                            KDFrameProfileSection::CvtPlain,
                                            lineWidth, pen );
                                    _profileSections.append( newsection );
                                    _topProfile.append( newsection );
                                }
                                break;
            case FramePanelRaized:
                                {
                                    KDFrameProfileSection* newsection;
                                    newsection =  new KDFrameProfileSection( KDFrameProfileSection::DirRaising,
                                            KDFrameProfileSection::CvtPlain,
                                            lineWidth, pen );
                                    _profileSections.append( newsection );
                                    _topProfile.append( newsection );
                                    break;
                                }
            case FramePanelSunken:
                                {
                                    KDFrameProfileSection* newsection;
                                    newsection =  new KDFrameProfileSection( KDFrameProfileSection::DirSinking,
                                            KDFrameProfileSection::CvtPlain,
                                            lineWidth, pen );
                                    _profileSections.append( newsection );
                                    _topProfile.append( newsection );
                                }
                                break;
            case FrameSemicircular:
                                {
                                    KDFrameProfileSection* newsection;
                                    newsection = new KDFrameProfileSection( KDFrameProfileSection::DirRaising,
                                            KDFrameProfileSection::CvtConvex,
                                            lineWidth, pen );
                                    _profileSections.append( newsection );
                                    _topProfile.append( newsection );
                                    newsection =  new KDFrameProfileSection( KDFrameProfileSection::DirPlain,
                                            KDFrameProfileSection::CvtPlain,
                                            midLineWidth, pen );
                                    _profileSections.append( newsection );
                                    _topProfile.append( newsection );
                                    newsection = new KDFrameProfileSection( KDFrameProfileSection::DirSinking,
                                            KDFrameProfileSection::CvtConcave,
                                            lineWidth, pen );
                                    _profileSections.append( newsection );
                                    _topProfile.append( newsection );
                                }
                                break;
            default:
                                break;
        }
    }
    _rightProfile  = _topProfile;
    _bottomProfile = _topProfile;
    _leftProfile   = _topProfile;
    setCorners( CornerNormal, 0 );
}


void KDFrame::createFrameNode( TQDomDocument& document, TQDomNode& parent,
        const TQString& elementName,
        const KDFrame& frame )
{
    TQDomElement frameElement = document.createElement( elementName );
    parent.appendChild( frameElement );
    KDXML::createIntNode( document, frameElement, "ShadowWidth",
            frame._shadowWidth );
    KDXML::createStringNode( document, frameElement, "CornerName",
            cornerNameToString( frame._sunPos ) );

    KDXML::createBrushNode( document, frameElement, "Background",
            frame._background );
    KDXML::createPixmapNode( document, frameElement, "BackPixmap",
            frame._backPixmap );
    KDXML::createStringNode( document, frameElement, "BackPixmapMode",
            backPixmapModeToString( frame._backPixmapMode ) );

    KDXML::createRectNode( document, frameElement, "InnerRect",
            frame._innerRect );
    createFrameProfileNode( document, frameElement, "TopProfile",
            frame._topProfile );
    createFrameProfileNode( document, frameElement, "RightProfile",
            frame._rightProfile );
    createFrameProfileNode( document, frameElement, "BottomProfile",
            frame._bottomProfile );
    createFrameProfileNode( document, frameElement, "LeftProfile",
            frame._leftProfile );
    KDFrameCorner::createFrameCornerNode( document, frameElement, "CornerTL",
            frame._cornerTL );
    KDFrameCorner::createFrameCornerNode( document, frameElement, "CornerTR",
            frame._cornerTR );
    KDFrameCorner::createFrameCornerNode( document, frameElement, "CornerBL",
            frame._cornerBL );
    KDFrameCorner::createFrameCornerNode( document, frameElement, "CornerBR",
            frame._cornerBR );
}

void KDFrame::createFrameProfileNode( TQDomDocument& document, TQDomNode& parent,
        const TQString& elementName,
        KDFrameProfile profile )
{
    TQDomElement profileElement = document.createElement( elementName );
    parent.appendChild( profileElement );
    for( const KDFrameProfileSection* section = profile.first(); section != 0;
            section = profile.next() )
        KDFrameProfileSection::createFrameProfileSectionNode( document,
                profileElement,
                "ProfileSection",
                section );
}


void KDFrame::KDFrameCorner::createFrameCornerNode( TQDomDocument& document,
        TQDomNode& parent,
        const TQString& elementName,
        const KDFrameCorner& corner )
{
    TQDomElement cornerElement = document.createElement( elementName );
    parent.appendChild( cornerElement );
    KDXML::createStringNode( document, cornerElement, "Style",
            KDFrame::cornerStyleToString( corner._style ) );
    KDXML::createIntNode( document, cornerElement, "Width",
            corner._width );
    createFrameProfileNode( document, cornerElement, "Profile",
            corner._profile );
}

bool KDFrame::readFrameNode( const TQDomElement& element, KDFrame& frame )
{
    bool ok = true;
    int tempShadowWidth;
    CornerName tempCornerName=CornerTopLeft;
    TQBrush tempBackground;
    TQPixmap tempBackPixmap;
    BackPixmapMode tempBackPixmapMode=PixStretched;
    TQRect tempInnerRect;
    KDFrameProfile tempTopProfile, tempRightProfile,
    tempBottomProfile, tempLeftProfile;
    KDFrameCorner tempCornerTL, tempCornerTR, tempCornerBL, tempCornerBR;
    TQDomNode node = element.firstChild();
    while( !node.isNull() ) {
        TQDomElement element = node.toElement();
        if( !element.isNull() ) { // was really an element
            TQString tagName = element.tagName();
            if( tagName == "ShadowWidth" ) {
                ok = ok & KDXML::readIntNode( element, tempShadowWidth );
            } else if( tagName == "CornerName" ) {
                TQString value;
                ok = ok & KDXML::readStringNode( element, value );
                tempCornerName = stringToCornerName( value );
            } else if( tagName == "Background" ) {
                ok = ok & KDXML::readBrushNode( element, tempBackground );
            } else if( tagName == "BackPixmap" ) {
                ok = ok & KDXML::readPixmapNode( element, tempBackPixmap );
            } else if( tagName == "BackPixmapMode" ) {
                TQString value;
                ok = ok & KDXML::readStringNode( element, value );
                tempBackPixmapMode = stringToBackPixmapMode( value );
            } else if( tagName == "InnerRect" ) {
                ok = ok & KDXML::readRectNode( element, tempInnerRect );
            } else if( tagName == "TopProfile" ) {
                ok = ok & readFrameProfileNode( element, tempTopProfile );
            } else if( tagName == "RightProfile" ) {
                ok = ok & readFrameProfileNode( element, tempRightProfile );
            } else if( tagName == "BottomProfile" ) {
                ok = ok & readFrameProfileNode( element, tempBottomProfile );
            } else if( tagName == "LeftProfile" ) {
                ok = ok & readFrameProfileNode( element, tempLeftProfile );
            } else if( tagName == "CornerTL" ) {
                ok = ok & KDFrameCorner::readFrameCornerNode( element,
                        tempCornerTL );
            } else if( tagName == "CornerTR" ) {
                ok = ok & KDFrameCorner::readFrameCornerNode( element,
                        tempCornerTR );
            } else if( tagName == "CornerBL" ) {
                ok = ok & KDFrameCorner::readFrameCornerNode( element,
                        tempCornerBL );
            } else if( tagName == "CornerBR" ) {
                ok = ok & KDFrameCorner::readFrameCornerNode( element,
                        tempCornerBR );
            } else {
                tqDebug( "Unknown tag in frame" );
            }
        }
        node = node.nextSibling();
    }

    if( ok ) {
        frame._shadowWidth = tempShadowWidth;
        frame._sunPos = tempCornerName;
        frame._background = tempBackground;
        frame._backPixmap = tempBackPixmap;
        frame._backPixmapMode = tempBackPixmapMode;
        frame._innerRect = tempInnerRect;
        frame._topProfile = tempTopProfile;
        frame._rightProfile = tempRightProfile;
        frame._bottomProfile = tempBottomProfile;
        frame._leftProfile = tempLeftProfile;
        frame._cornerTL = tempCornerTL;
        frame._cornerTR = tempCornerTR;
        frame._cornerBL = tempCornerBL;
        frame._cornerBR = tempCornerBR;
    }

    return ok;
}


bool KDFrame::readFrameProfileNode( const TQDomElement& element,
        KDFrameProfile& profile )
{
    TQDomNode node = element.firstChild();
    while( !node.isNull() ) {
        TQDomElement element = node.toElement();
        if( !element.isNull() ) { // was really an element
            TQString tagName = element.tagName();
            if( tagName == "ProfileSection" ) {
                KDFrameProfileSection* section = new KDFrameProfileSection();
                KDFrameProfileSection::readFrameProfileSectionNode( element,
                        section );
                profile.append( section );
            } else {
                tqDebug( "Unknown tag in double map" );
                return false;
            }
        }
        node = node.nextSibling();
    }

    return true;
}


bool KDFrame::KDFrameCorner::readFrameCornerNode( const TQDomElement& element,
        KDFrameCorner& corner )
{
    bool ok = true;
    CornerStyle tempStyle=CornerNormal;
    int tempWidth;
    KDFrameProfile tempProfile;
    TQDomNode node = element.firstChild();
    while( !node.isNull() ) {
        TQDomElement element = node.toElement();
        if( !element.isNull() ) { // was really an element
            TQString tagName = element.tagName();
            if( tagName == "Style" ) {
                TQString value;
                ok = ok & KDXML::readStringNode( element, value );
                tempStyle = stringToCornerStyle( value );
            } else if( tagName == "Width" ) {
                ok = ok & KDXML::readIntNode( element, tempWidth );
            } else if( tagName == "Profile" ) {
                KDFrameProfile profile;
                ok = ok & readFrameProfileNode( element, profile );
            } else {
                tqDebug( "Unknown tag in frame" );
            }
        }
        node = node.nextSibling();
    }

    if( ok ) {
        corner._style = tempStyle;
        corner._width = tempWidth;
        corner._profile = tempProfile;
    }

    return ok;
}

#include "KDFrame.moc"
