/*****************************************************************

Copyright (c) 1996-2000 the kicker authors. See file AUTHORS.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

******************************************************************/

//#define QT_CLEAN_NAMESPACE
#include <qbitmap.h>
#include <qdrawutil.h>
#include <qapplication.h>

#include <kconfig.h>
#include <kwinmodule.h>
#include <kwin.h>
#include <netwm.h>
#include <kwinmodule.h>
#include <kpixmapeffect.h>
#include <kglobal.h>
#include <kglobalsettings.h>
#include <kiconloader.h>
#include <klocale.h>
#include <kapp.h>
#include <dcopclient.h>
#include <krootpixmap.h>

#include "kasbar.h"
#include "kasitem.h"

//
// Bitmap data used for the window state indicators
//
static unsigned char min_bits[] = {
    0x00, 0xff, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00};
static unsigned char max_bits[] = {
    0xff, 0xff, 0xc3, 0xc3, 0xc3, 0xc3, 0xff, 0xff};
static unsigned char shade_bits[] = {
    0x06, 0x1e, 0x7e, 0xfe, 0xfe, 0x7e, 0x1e, 0x06};

const int BTN_EXTENT = 52;

KasBar::KasBar(KConfig* config, Orientation o, QWidget* parent, const char* name )
    : QWidget(parent, name)
{
    setOrientation(o);
    setFont(QFont("Helvetica", 10));

    //
    // Read configuration
    //
    config->setGroup("kasbar");

    maxBoxes = config->readUnsignedNumEntry( "MaxBoxes", 0 );
    currentDesktopOnly = config->readBoolEntry( "CurrentDesktopOnly", false );

    transparent = config->readBoolEntry( "Transparent", false );
    fillBg = config->readBoolEntry( "FillIconBackgrounds", true );
    enableTint = config->readBoolEntry( "EnableTint", false );
    tintColour = config->readColorEntry( "TintColor", &Qt::black );
    tintAmount = config->readDoubleNumEntry( "TintAmount", 0.1 );

    //
    // Setup transparency
    //
    if ( transparent ) {
        rootPix = new KRootPixmap( this );

        if ( enableTint )
            rootPix->setFadeEffect( tintAmount, tintColour );
        rootPix->start();
    }
    else {
        rootPix = 0;
    }

    //
    // Calculate the maximum number of boxes per row/column
    //
    QRect geom = QApplication::desktop()->geometry(); // ### TODO
    if(!maxBoxes){
        maxBoxes = orient == Horizontal ?
            (geom.width()-20)/BTN_EXTENT :
            (geom.height()-20)/BTN_EXTENT;
    }

    //
    // Initialise bitmaps and gradient fills
    //
    actBg = new KPixmap;
    actBg->resize(BTN_EXTENT-4, BTN_EXTENT-13-4);
    KPixmapEffect::gradient(*actBg, colorGroup().light(), colorGroup().mid(),
                            KPixmapEffect::DiagonalGradient);

    inactBg = new KPixmap;
    inactBg->resize(BTN_EXTENT-4, BTN_EXTENT-13-4);
    KPixmapEffect::gradient(*inactBg, colorGroup().mid(), colorGroup().dark(),
                            KPixmapEffect::DiagonalGradient);

    minPix = new QBitmap(8, 8, min_bits, true);
    minPix->setMask(*minPix);
    maxPix = new QBitmap(8, 8, max_bits, true);
    maxPix->setMask(*maxPix);
    shadePix = new QBitmap(8, 8, shade_bits, true);
    shadePix->setMask(*shadePix);

    //
    // Connect to KWin
    //
    kwin_module = new KWinModule(this);

    connect( kwin_module, SIGNAL( windowAdded(WId) ), this, SLOT( windowAdded(WId) ) );
    connect( kwin_module, SIGNAL( windowRemoved(WId) ), this, SLOT( windowRemoved(WId) ) );
    connect( kwin_module, SIGNAL( windowChanged(WId, unsigned int) ),
             this, SLOT( windowChanged(WId, unsigned int) ) );
    connect( kwin_module, SIGNAL( activeWindowChanged(WId) ), this, SLOT( activeWindowChanged(WId) ) );
    connect( kwin_module, SIGNAL( workAreaChanged() ), this, SIGNAL( layoutChanged() ) );
    connect( kwin_module, SIGNAL( currentDesktopChanged(int) ), this, SLOT( desktopChanged() ) );
    //
    // Register existing windows
    //
    rescanWindows();
}

void KasBar::rescanWindows()
{
    items.clear();

    const QValueList<WId> windows = kwin_module->windows();
    QValueList<WId>::ConstIterator it;

    for ( it=windows.begin(); it!=windows.end(); ++it )
        windowAdded( *it );
}

void KasBar::desktopChanged()
{
    if ( currentDesktopOnly ) {
        rescanWindows();
    }
}

KasItem* KasBar::findItem( WId w )
{
    for ( KasItem* i = items.first(); i; i = items.next() ) {
        if ( i->window() == w )
            return i;
    }
    return 0;
}

void KasBar::windowAdded( WId w )
{
    NETWinInfo info ( qt_xdisplay(),  w, qt_xrootwin(), NET::WMWindowType | NET::WMPid );
    if ( info.windowType() != NET::Normal && info.windowType() != NET::Unknown )
        return;

    KasItem* i = new KasItem(w);

    if ( currentDesktopOnly )
        if ( ( i->desktop() != kwin_module->currentDesktop() ) && ( i->desktop() != -1 ) )
            return;

    items.append(i);
    updateLayout();
}

void KasBar::windowRemoved(WId w )
{
    if ( w == topLevelWidget()->winId() )
        return;

    KasItem* i = findItem( w );
    if ( i ) {
        items.removeRef(i);
        delete i;
    }
    updateLayout();
}

void KasBar::windowChanged(WId w, unsigned int dirty)
{
    QPainter p(this);
    KasItem* i = findItem( w );

    if (i) {
        if ( dirty & NET::WMIcon )
            i->refreshIcon();
        i->refresh();

        if ( currentDesktopOnly ) {
            if ( ( i->desktop() != kwin_module->currentDesktop() ) &&
                 ( i->desktop() != -1 ) )
                windowRemoved( w );
        }
        repaintItem(i);
    }
    else if ( currentDesktopOnly )
        windowAdded( w );
}

void KasBar::activeWindowChanged(WId w)
{
    static WId oldWin = 0;
    QPainter p(this);
    if ( oldWin ) {
        repaintItem(findItem(oldWin));
        repaintItem(findItem(w));
    }
    else
        repaint(transparent);
    oldWin = w;
}

void KasBar::showWindowMenuAt( WId id, int x, int y )
{
    QByteArray data;
    QDataStream arg(data, IO_WriteOnly);
    arg << id;
    arg << x;
    arg << y;
    if ( !(kapp->dcopClient()->send( "kwin", "KWinInterface",
                                     "showWindowMenuAt(unsigned long,int,int)",
                                     data)))
        qDebug("kasbar: There was some error using DCOP.");
}

void KasBar::resizeEvent(QResizeEvent *ev)
{
    QWidget::resizeEvent(ev);
    emit layoutChanged();
}

void KasBar::mousePressEvent(QMouseEvent *ev)
{
    KasItem *i = itemAt(ev->pos());

    if ( i ) {
        WId id = i->window();

        if ( ev->button() == LeftButton ) {
            if ( kwin_module->activeWindow() == id )
                KWin::iconifyWindow( id );
            else {
                if ( i->isMinimized() )
                    KWin::deIconifyWindow( id );
                KWin::setActiveWindow( id );
            }
        }
        else if ( ev->button() == RightButton ) {
            showWindowMenuAt( id, ev->globalX(), ev->globalY() );
        }
    }
}

QSize KasBar::sizeHint( Orientation o, QSize /*max*/ )
{
    QSize s;

    if ( !items.count() ) {
        s.setWidth( BTN_EXTENT );
        s.setHeight( BTN_EXTENT );
        return s;
    }

    unsigned int r, c;
    if( items.count() > maxBoxes ) {
        r = items.count()/maxBoxes;
        c = maxBoxes;
    }
    else {
        r = 1;
        c = items.count();
    }

    if( r*c < items.count() ) // remainders
        ++r;

    if( o == Horizontal ) {
        s.setWidth( c*BTN_EXTENT );
        s.setHeight( r*BTN_EXTENT );
    }
    else {
        s.setWidth( r*BTN_EXTENT );
        s.setHeight( c*BTN_EXTENT );
    }

    return s;
}

void KasBar::updateLayout()
{
    static unsigned int oldR = 0, oldC = 0;
    if ( !items.count() ) {
        resize(BTN_EXTENT, BTN_EXTENT);
        repaint(transparent);
        return;
    }

    unsigned int r, c;
    if(items.count() > maxBoxes){
        r = items.count()/maxBoxes;
        c = maxBoxes;
    }
    else{
        r = 1;
        c = items.count();
    }
    if(r*c < items.count()) // remainders
        ++r;

    if(oldR != r || oldC != c){
        if(orientation() == Horizontal)
            resize(c*BTN_EXTENT, r*BTN_EXTENT);
        else
            resize(r*BTN_EXTENT, c*BTN_EXTENT);
        oldR = r;
        oldC = c;
    }
    repaint(transparent);
}

void KasBar::drawItem(QPainter *p, KasItem *i, int x, int y)
{
    //
    // Draw item frame
    //
    p->setPen(Qt::black);
    p->drawRect(x, y, BTN_EXTENT, BTN_EXTENT);

    qDrawShadePanel(p, x+1, y+1, BTN_EXTENT-2, BTN_EXTENT-2, colorGroup(), false,
                    1);

    //
    // Draw item label
    //
    p->fillRect( x+2, y+2, BTN_EXTENT-4, 13, QBrush( Qt::black ) );

    p->setPen( Qt::white );
    if ( fontMetrics().width( i->text() ) > BTN_EXTENT-4 )
        p->drawText( x+2, y+2, BTN_EXTENT-4, 12, AlignLeft | AlignVCenter,
                     i->text() );
    else
        p->drawText( x+2, y+2, BTN_EXTENT-4, 12, AlignCenter, i->text() );

    //
    // Draw background fill and window icon
    //
    if ( !transparent || fillBg ) {
        p->drawPixmap( x+2, y+15, kwin_module->activeWindow() == i->window()
                       ? *actBg : *inactBg);
    }

    p->drawPixmap(x+4, y+16, i->icon());

    //
    // Draw desktop number
    //
    p->setPen( kwin_module->activeWindow() == i->window()
               ? Qt::black : Qt::lightGray );

    QString deskStr;
    if ( i->desktop() >= 0 )
        deskStr.setNum( i->desktop() );
    else
        deskStr = i18n("All");
    p->drawText( x+BTN_EXTENT-fontMetrics().width(deskStr)-3,
                 y+16+fontMetrics().ascent(), deskStr );

    //
    // Draw state icon
    //
    if( i->isMinimized() )
        p->drawPixmap(x+BTN_EXTENT-11, y+BTN_EXTENT-11, *minPix);
    else if ( i->isShaded() )
        p->drawPixmap(x+BTN_EXTENT-11, y+BTN_EXTENT-11, *shadePix);
    else
        p->drawPixmap(x+BTN_EXTENT-11, y+BTN_EXTENT-11, *maxPix);
}

void KasBar::paintEvent(QPaintEvent *ev)
{
    QRect cr;
    QPainter p(this);
    KasItem *i;
    int r=0, c=0;

    int totalcols = width()/BTN_EXTENT;
    int totalrows = height()/BTN_EXTENT;

    if ( items.count() ) {
        if ( orientation() == Horizontal ) {
            for ( i = items.first(); i; i = items.next() ) {
                if ( c >= totalcols ) {
                    c = 0; ++r;
                }
                cr.setRect(c*BTN_EXTENT, r*BTN_EXTENT, BTN_EXTENT, BTN_EXTENT);
                if ( ev->rect().intersects(cr) )
                    drawItem(&p, i, c*BTN_EXTENT, r*BTN_EXTENT);
                ++c;
            }
        }
        else {
            for ( i = items.first(); i; i = items.next() ) {
                if ( r >= totalrows ) {
                    r = 0; ++c;
                }
                cr.setRect(c*BTN_EXTENT, r*BTN_EXTENT, BTN_EXTENT, BTN_EXTENT);
                if ( ev->rect().intersects(cr) )
                    drawItem(&p, i, c*BTN_EXTENT, r*BTN_EXTENT);
                ++r;
            }
        }
    }

    if ( !transparent ) {
        if( orientation() == Horizontal && c < totalcols ) {
            p.fillRect( c*BTN_EXTENT, r*BTN_EXTENT, width()-c*BTN_EXTENT,
                        BTN_EXTENT, colorGroup().brush(QColorGroup::Mid) );
        }
        else if( r < totalrows ) {
            p.fillRect( c*BTN_EXTENT, r*BTN_EXTENT, BTN_EXTENT,
                        height()-r*BTN_EXTENT, colorGroup().brush(QColorGroup::Mid) );
        }
    }
}

void KasBar::repaintItem(KasItem *item)
{
    int totalcols = width()/BTN_EXTENT;
    int totalrows = height()/BTN_EXTENT;
    int index = items.find( item );
    if ( index == -1 )
        return;

    int r = 0;
    int c = 0;
    if ( orientation() == Horizontal ) {
        r = index / totalcols;
        c = index % totalcols;
    }
    else {
        c = index / totalrows;
        r = index % totalrows;
    }

    if ( transparent )
        erase( QRect( c*BTN_EXTENT, r*BTN_EXTENT, BTN_EXTENT, BTN_EXTENT ) );
    QPainter p(this);
    drawItem(&p, item, c*BTN_EXTENT, r*BTN_EXTENT);
}

KasItem* KasBar::itemAt(const QPoint &p)
{
    QRect cr;
    KasItem *i;
    int r=0, c=0;
    int totalcols = width()/BTN_EXTENT;
    int totalrows = height()/BTN_EXTENT;

    if(orientation() == Horizontal){
        for (i = items.first(); i; i = items.next()){
            if(c >= totalcols){
                c = 0;
                ++r;
            }
            cr.setRect(c*BTN_EXTENT, r*BTN_EXTENT, BTN_EXTENT, BTN_EXTENT);
            if(cr.contains(p))
                return(i);
            ++c;
        }
    }
    else{
        for (i = items.first(); i; i = items.next()){
            if(r >= totalrows){
                r = 0; ++c;
            }
            cr.setRect(c*BTN_EXTENT, r*BTN_EXTENT, BTN_EXTENT, BTN_EXTENT);
            if(cr.contains(p))
                return(i);
            ++r;
        }
    }

    return 0;
}


#include "kasbar.moc"


