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

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.

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

#include <qpainter.h>
#include <qpixmap.h>
#include <qimage.h>
#include <qbitmap.h>
#include <qapplication.h>
#include <qstyle.h>

#include <kstddirs.h>
#include <kglobal.h>
#include <kdebug.h>
#include <kiconloader.h>
#include <kmimetype.h>
#include <kglobalsettings.h>
#include <kcursor.h>
#include <kapp.h>
#include <kipc.h>
#include <qwidgetlist.h>

#include "kicker.h"
#include "panel.h"

#include "panelbuttonbase.h"
#include "panelbuttonbase.moc"

static ZoomButton* zoomButton = 0;
static int zoomDisabled = 0;

ZoomButton::ZoomButton()
	: PanelButtonBase( 0, 0, WStyle_Customize | WX11BypassWM | WMouseNoMask)
{
    zooming = FALSE;
    locked = 0;
    hideTimer = 0;
    qApp->installEventFilter( this );
    setMouseTracking( TRUE );
    connect( Kicker::kicker(), SIGNAL( configurationChanged() ),
	     this, SLOT(reconfigure() ) );
    reconfigure();
}
ZoomButton::~ZoomButton()
{
}

void ZoomButton::reconfigure()
{
    KConfig* config = KGlobal::config();
    config->setGroup("buttons");
    zooming = config->readBoolEntry("EnableIconZoom", false);
}


bool ZoomButton::isWatching( PanelButtonBase* btn )
{
    return ( btn == watch );
}

void ZoomButton::watchMe( PanelButtonBase* btn, const QPoint& center, const QPixmap& pm)
{
    PanelButtonBase* watch_old = watch;
    watch = btn;
    _icon = pm;
    if ( watch_old )
	watch_old->update();

    hide();
    resize( pm.size() );
    int offset = 5;
    QPoint p = center - rect().center();
    if ( p.x() < offset )
	p.rx() = offset;
    if ( p.y() < offset )
	p.ry() = offset;
    if ( p.x() + width() > qApp->desktop()->width() - offset)
	p.rx() = qApp->desktop()->width() - width() - offset;
    if ( p.y() + height() > qApp->desktop()->height() - offset)
	p.ry() = qApp->desktop()->height() - height() - offset;
    move( p );
    mypos = p;
    setDown( false );
    watch->repaint();
    QApplication::flushX();
    qApp->processEvents(100);
    QPixmap pixm =
	QPixmap::grabWindow(QApplication::desktop()->screen(x11Screen())->winId(),
			    p.x(), p.y(), width(), height());
    setBackgroundPixmap(pixm);
    clearMask();
    if (pm.mask())
	setMask(*pm.mask());
    raise();
    show();
}

void ZoomButton::drawButtonLabel(QPainter *p, const QPixmap &)
{
    if ( isDown() || isOn() )
	move ( mypos + QPoint(2,2) );
    else
	move ( mypos );
    if ( !_icon.isNull()) {
	int x = (width() - _icon.width())/2;
	int y = (height() - _icon.height())/2;
	p->drawPixmap(x, y, _icon);
    }
}

/* This event filter is very tricky and relies on Qt
   internals. It's written this way to make all panel buttons work
   without modification and to keep advanced functionality like tool
   tips for the buttons alive.

   Don't hack around in this filter unless you REALLY know what you
   are doing. In case of doubt, ask ettrich@kde.org.
*/
bool ZoomButton::eventFilter( QObject *o, QEvent * e)
{
    if ( zoomButton != this )
	return FALSE;

    if ( !watch )
	return FALSE;
    if ( e == locked )
	return FALSE;
    if ( !isVisible() )
	return FALSE;
    if ( e->type() == QEvent::Enter ) {
	QPoint globalPos = QCursor::pos();
	if ( geometry().contains( globalPos, true ) ) {
	    if (_changeCursorOverItem)
		setCursor(KCursor().handCursor());
	}
    }
    if ( e->type() == QEvent::Leave ) {
	if ( !hideTimer )
	    hideTimer = startTimer( 200 );
    }


    if ( o == this ) {
	if ( e->type() == QEvent::Timer && ((QTimerEvent*)e)->timerId() == hideTimer ) {
	    if ( watch && watch->isDown() )
		return TRUE;
	    QPoint globalPos = QCursor::pos();
	    if ( !geometry().contains( globalPos ) ) {
		killTimer( hideTimer );
		hideTimer = 0;
		hide();
                PanelButtonBase* btn = watch;
                watch = 0;
		if ( btn )
		    btn->update();
		if(!_changeCursorOverItem)
		    setCursor(_oldCursor);
	    }
	    return TRUE;
	}

	if ( e->type() == QEvent::MouseButtonPress )
	    raise();

	(void) event( e ); // send to us directly , below we'll  hide the event from the tooltip filters


	if ( e->type() == QEvent::MouseButtonPress ||
	     e->type() == QEvent::MouseButtonRelease ||
	     e->type() == QEvent::MouseMove ) {
	    QMouseEvent* ev = (QMouseEvent*) e;
	    if ( rect().contains( ev->pos() ) && watch && !watch->rect().contains( ev->pos() ) )
		ev = new QMouseEvent( ev->type(),
				      watch->rect().center(),
				      ev->globalPos(),
				      ev->button(),
				      ev->state() );
	    locked = e;
	    QApplication::sendEvent( watch, ev );
	    if ( ev != e )
		delete ev;
	    locked = 0;
	}

	if ( e->type() == QEvent::Enter || e->type() == QEvent::Leave ) {
	    locked = e;
	    QApplication::sendEvent( watch, e );
	    locked = 0;
	}



	return TRUE;
    }
    if ( watch == o && e != locked ) {
	if ( e->type() == QEvent::MouseButtonPress ||
	     e->type() == QEvent::MouseButtonRelease ||
	     e->type() == QEvent::MouseMove ||
	     e->type() == QEvent::Enter ||
	     e->type() == QEvent::Leave ) {
	    QApplication::sendEvent( this, e );
	    return TRUE;
	}
    }
    return FALSE;
}

PanelButtonBase::PanelButtonBase(QWidget *parent, const char *name, WFlags f)
  : QButton(parent, name, f)
  , _drawArrow(false)
  , _highlight(false)
  , _changeCursorOverItem(true)
  , _hasAcceptedDrag(false)
  , _tile(QString::null)
  , _iconName(QString::null)
  , _arrowDirection(::Bottom)
  , _popupDirection(::dUp)
  , _orientation(Horizontal)
{
    setBackgroundMode( PaletteBackground );
    _oldCursor = cursor();

    slotSettingsChanged(KApplication::SETTINGS_MOUSE);
    connect(kapp, SIGNAL(settingsChanged(int)), SLOT(slotSettingsChanged(int)));
    kapp->addKipcEventMask(KIPC::SettingsChanged);

    setAcceptDrops( !Kicker::kicker()->isImmutable() );
}

void PanelButtonBase::slotSettingsChanged(int category)
{
    if (category != KApplication::SETTINGS_MOUSE) return;

    _changeCursorOverItem = KGlobalSettings::changeCursorOverIcon();

    if(!_changeCursorOverItem)
	setCursor(_oldCursor);
}

void PanelButtonBase::setTile(const QString& tile)
{
    if( tile != _tile ) {
        _tile = tile;
        loadTiles();
        update();
    }
}

void PanelButtonBase::setIcon(const QString& icon)
{
    if( icon != _iconName ) {
        _iconName = icon;
        loadIcons();
        update();
    }
}

void PanelButtonBase::loadIcons()
{
    KIcon::StdSizes sz;
    KIcon::StdSizes zoom_sz;

    if ( orientation() == Horizontal ) {
	if ( height() < 32 ) sz = KIcon::SizeSmall;
	else if ( height() < 48 ) sz = KIcon::SizeMedium;
	else sz = KIcon::SizeLarge;
    }
    else {
	if ( width() < 32 ) sz = KIcon::SizeSmall;
	else if ( width() < 48 ) sz = KIcon::SizeMedium;
	else sz = KIcon::SizeLarge;
    }

    if (sz == KIcon::SizeSmall)
       zoom_sz = KIcon::SizeMedium;
    else
       zoom_sz = KIcon::SizeLarge;

    KIconLoader * ldr = KGlobal::iconLoader();
    QString nm = _iconName;

    _icon = ldr->loadIcon(nm, KIcon::Panel, sz, KIcon::DefaultState, 0L, true);

    if (_icon.isNull()) {
        nm = defaultIcon();
	_icon = ldr->loadIcon(nm, KIcon::Panel, sz, KIcon::DefaultState);
    }

    _iconh = ldr->loadIcon(nm, KIcon::Panel, sz, KIcon::ActiveState, 0L, true);
    _iconz = ldr->loadIcon(nm, KIcon::Panel, zoom_sz, KIcon::ActiveState, 0L, true );
}

void PanelButtonBase::setIconURL(const KURL & u)
{
    QString name = KMimeType::iconForURL(u, 0);
    setIcon( name );
}

void PanelButtonBase::setTitle(const QString & t)
{
    _title = t;
}

void PanelButtonBase::setDrawArrow(bool v)
{
    if (_drawArrow != v) {
	_drawArrow = v;
	update();
    }
}

void PanelButtonBase::slotSetPopupDirection(Direction d)
{
    _popupDirection = d;

    switch (_popupDirection) {
    case ::dUp:    setArrowDirection(::Top);    break;
    case ::dDown:  setArrowDirection(::Bottom); break;
    case ::dLeft:  setArrowDirection(::Left);   break;
    case ::dRight: setArrowDirection(::Right);  break;
    }
}

void PanelButtonBase::setArrowDirection(Position dir)
{
    if (_arrowDirection != dir) {
	_arrowDirection = dir;
	update();
    }
}

void PanelButtonBase::setBackground()
{
    KConfig* config = KGlobal::config();
    config->setGroup("General");
    if (config->readBoolEntry("UseBackgroundTheme", false))
    {
	// Get the pixmap from the container area object
        QWidget* pContainer = Panel::the();
	QPalette pal = pContainer->palette();
	QBrush bgBrush = pal.brush( QPalette::Active, QColorGroup::Background );
	QPixmap* containerBG = bgBrush.pixmap();

	// Make sure the background pixmap exists
	if ( containerBG && parent() ) //parent() is null for ZoomButtons
	{
	    // Create a pixmap the same size as the button to use as the bg
	    QPixmap bgPix( width(), height() );

	    // Calculate which part of the container area background should be copied
	    QWidget* p = (QWidget*) parent();

	    int srcx, srcy;
	    if ( _arrowDirection == ::Bottom || _arrowDirection == ::Top )
	    {
		srcx = p->x() % containerBG->width();
		srcy = 0;
	    }
	    else
	    {
		srcx = 0;
		srcy = p->y() % containerBG->height();
	    }

	    // Check if we need to take care of a wrap-around
	    if ( srcx + p->width() <= containerBG->width() &&
		    srcy + p->height() <= containerBG->height() )
	    {
		// Nothing funny going on with overlaps - just copy

		QPoint dp( 0, 0 ); // destination point
		QRect sr( srcx, srcy, width(), height() );
		bitBlt( &bgPix, dp, containerBG, sr, CopyROP );
	    }
	    else
	    {
		// Need to do 2 seperate bitBlts to take care of the overlap

		// Copy the left/top of the image first up to the wrap-
		// around point
		int x = 0;
		int y = 0;
		int w = containerBG->width() - srcx;
		int h = containerBG->height() - srcy;
		QPoint dp( x, y ); // destination point
		QRect sr( srcx, srcy, w, h );
		bitBlt( &bgPix, dp, containerBG, sr, CopyROP );

		// Now copy the wrap-around bit
		if ( _arrowDirection == ::Bottom || _arrowDirection == ::Top )
		{
		    x = containerBG->width() - srcx;
		    y = 0;
		    w = srcx + p->width() - containerBG->width();
		    h = p->height();
		}
		else
		{
		    x = 0;
		    y = containerBG->height() - srcy;
		    w = p->width();
		    h = srcy + p->height() - containerBG->height();
		}
		dp = QPoint( x, y );
		sr = QRect( 0, 0, w, h );
		bitBlt( &bgPix, dp, containerBG, sr, CopyROP );
	    }

	    _bg = bgPix;
	}
	else
	{
	    // Conatainer palette pixmap not set yet
	    _bg = QPixmap();
	}
    }
    else
    {
	// No background pixmap to use
	_bg = (backgroundPixmap() && backgroundPixmap()->size()==size()) ? *backgroundPixmap() :  QPixmap();
    }
}

void PanelButtonBase::resizeEvent(QResizeEvent*)
{
    // optimize: reload only when size really changes
    loadTiles();
    loadIcons();
}

void PanelButtonBase::setZoomEnabled(bool b)
{
    if (!b)
      zoomDisabled++;
    else
      zoomDisabled--;

    if (zoomDisabled)
    {
       delete zoomButton;
       zoomButton = 0;
    }
}

void PanelButtonBase::enterEvent(QEvent* e)
{
    if (_changeCursorOverItem)
	setCursor(KCursor().handCursor());

    if (!zoomDisabled)
    {
        if (!zoomButton)
            zoomButton = new ZoomButton;

        if (zoomButton->isZoomingEnabled() && /* _icon.width() < 32 && */
          !_iconz.isNull() && _iconz.width() > _icon.width() &&
          !mouseGrabber() && !qApp->activePopupWidget() )
        { // we can and should zoom
            if ( !zoomButton->isWatching( this ) ) {
                zoomButton->watchMe( this, mapToGlobal( rect().center() ), _iconz );
                update();
            }
            return;
        }
    }

    _highlight = true;
    repaint(false);
    QButton::enterEvent(e);
}

void PanelButtonBase::leaveEvent(QEvent* e)
{
    if (_changeCursorOverItem)
	setCursor(_oldCursor);

    if (_highlight) {
	_highlight = false;
	repaint(false);
    }

    QButton::leaveEvent(e);
}

void PanelButtonBase::dragEnterEvent(QDragEnterEvent* e)
{
    if (e->isAccepted())
	_hasAcceptedDrag = true;
    update();
    QButton::dragEnterEvent( e );
}

void PanelButtonBase::dragLeaveEvent(QDragLeaveEvent* e)
{
    _hasAcceptedDrag = false;
    update();
    QButton::dragLeaveEvent( e );
}

void PanelButtonBase::dropEvent(QDropEvent* e)
{
    _hasAcceptedDrag = false;
    update();
    QButton::dropEvent( e );
}

void PanelButtonBase::loadTiles()
{
    if( _tile == QString::null ) {
	_up = _down = QPixmap();
	return;
    }

    QString uptile, downtile;

    if ( height() < 42 ) {
	uptile = _tile + "_tiny_up.png";
	downtile = _tile + "_tiny_down.png";
    }
    else if ( height() < 54 ) {
	uptile = _tile + "_normal_up.png";
	downtile = _tile + "_normal_down.png";
    }
    else {
	uptile = _tile + "_large_up.png";
	downtile = _tile + "_large_down.png";
    }

    _up = QPixmap( KGlobal::dirs()->findResource("tiles", uptile) );
    _down = QPixmap ( KGlobal::dirs()->findResource("tiles", downtile) );

    // scale if size does not match exactly
    if ( !_up.isNull() ) {
	if ( _up.width() != width() || _up.height() != height() ) {
	    QImage upI = _up.convertToImage();
	    _up.convertFromImage( upI.smoothScale( width(), height() ) );
	}
    }

    // scale if size does not match exactly
    if ( !_down.isNull() ) {
	if ( _down.width() != width() || _down.height() != height() ) {
	    QImage downI = _down.convertToImage();
	    _down.convertFromImage( downI.smoothScale( width(), height() ) );
	}
    }
}

void PanelButtonBase::drawButton(QPainter *p)
{
    setBackground();

    QPixmap bg(size());
    QPainter pbg;
    pbg.begin(&bg);

    // Draw the background. This is always needed, even when using tiles,
    // because they don't have to cover the entire button.
    if ( !_bg.isNull() )
	pbg.drawPixmap( 0, 0, _bg );
    else
	pbg.fillRect(rect(), colorGroup().brush(QColorGroup::Background));

    QPixmap tile = (isDown() || isOn()) ? _down : _up;
    if (!tile.isNull()) { // Draw the tile
	int x = (width() -  tile.width() )/2;
	int y = (height() - tile.height())/2;
	pbg.drawPixmap(x, y, tile);
    }
    else if (isDown() || isOn()) { // Draw shapes to indicate the down state.
	pbg.setPen(Qt::black);
	pbg.drawLine(0, 0, width()-1, 0);
	pbg.drawLine(0, 0, 0, height()-1);
	pbg.setPen(colorGroup().light());
	pbg.drawLine(0, height()-1, width()-1, height()-1);
	pbg.drawLine(width()-1, 0, width()-1, height()-1);
    }

    pbg.end();
    p->drawPixmap(0,0,bg);
    drawButtonLabel(p);

    if ( hasFocus() || _hasAcceptedDrag ) {
	int x1, y1, x2, y2;
	rect().coords( &x1, &y1, &x2, &y2 );// get coordinates
	QRect r(x1+2, y1+2, x2-x1-3, y2-y1-3);
	style().drawPrimitive( QStyle::PE_FocusRect, p, r, colorGroup(),
                               QStyle::Style_Default, colorGroup().button() );
    }
}

void PanelButtonBase::drawButtonLabel(QPainter *p)
{
    p->save();

    if(isDown() || isOn())
	p->translate(2,2);

    QPixmap icon = _highlight ? _iconh : _icon;

    if ( (!zoomButton || !zoomButton->isWatching( this )) && !icon.isNull()) {
	int x = (width()  - icon.width() )/2;
	int y = (height() - icon.height())/2;
	p->drawPixmap(x, y, icon);
    }

    if(_drawArrow) {
	QStyle::PrimitiveElement e = QStyle::PE_ArrowUp;
	QRect r( QPoint( 0, 0 ), QSize( 8, 8 ) );

	switch(_arrowDirection) {
	case ::Top:
	    e = QStyle::PE_ArrowUp;
	    break;
	case ::Bottom:
	    e = QStyle::PE_ArrowDown;
	    r.moveBy( 0, height() - 8 );
	    break;
	case ::Right:
	    e = QStyle::PE_ArrowRight;
	    r.moveBy( width() - 8 , 0 );
	    break;
	case ::Left:
	    e = QStyle::PE_ArrowLeft;
	    break;
	}

	int flags = QStyle::Style_Enabled;
	if ( isDown() || isOn() )
	    flags |= QStyle::Style_Down;
	style().drawPrimitive( e, p, r, colorGroup(), flags );
    }

    p->restore();
}
