/****************************************************************************
** $Id: qt/qeffects.cpp   3.2.3   edited Sep 29 16:09 $
**
** Implementation of QEffects functions
**
** Created : 000621
**
** Copyright (C) 2000 Trolltech AS.  All rights reserved.
**
** This file is part of the widgets module of the Qt GUI Toolkit.
**
** This file may be distributed under the terms of the Q Public License
** as defined by Trolltech AS of Norway and appearing in the file
** LICENSE.QPL included in the packaging of this file.
**
** 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 Qt Enterprise Edition or Qt Professional Edition
** licenses may use this file in accordance with the Qt 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.trolltech.com/pricing.html or email sales@trolltech.com for
**   information about Qt Commercial License Agreements.
** See http://www.trolltech.com/qpl/ for QPL licensing information.
** See http://www.trolltech.com/gpl/ for GPL licensing information.
**
** Contact info@trolltech.com if any conditions of this licensing are
** not clear to you.
**
**********************************************************************/

//#include <qapplication.h>
#ifndef QT_EFF
#define QT_EFF
#include "baghira.h"
#undef QT_EFF
#endif // QT_H
#ifndef QT_H
#include <qnamespace.h>
#endif // QT_H
#include <qapplication.h>
#include <qwidget.h>
#include <qpixmap.h>
#include <qimage.h>
#include <qtimer.h>
#include <qdatetime.h>
#include <qguardedptr.h>
#include <qpopupmenu.h>
#include <qscrollview.h>



/*
  Internal class to get access to protected QWidget-members
*/

class QAccessWidget : public QWidget
{
    friend class AlphaWidget;
    friend class RollEffect;
public:
    QAccessWidget( QWidget* parent=0, const char* name=0, WFlags f = 0 )
	: QWidget( parent, name, f ) {}
};

/*
  Internal class AlphaWidget.

  The AlphaWidget is shown while the animation lasts
  and displays the pixmap resulting from the alpha blending.
*/

class AlphaWidget: public QWidget, private Effects
{
    Q_OBJECT
public:
    AlphaWidget( QWidget* w, QPixmap* pix, WFlags f = 0, OptionHandler* oh = NULL, const QPopupMenu* p = NULL, PopupStuff *popupStuff = NULL, int swh = 0);

    void run( int time );

protected:
    void paintEvent( QPaintEvent* e );
    void closeEvent( QCloseEvent* );
    bool eventFilter( QObject* o, QEvent* e );
    void alphaBlend();

protected slots:
    void render();

private:
    OptionHandler* oHandler;
    const QPopupMenu* popup;
    PopupStuff* popStuff;
    int shadowWH;
    QPixmap* backPix;
    QPixmap pm;
    double alpha;
    QImage back;
    QImage front;
    QImage mixed;
    QGuardedPtr<QAccessWidget> widget;
    int duration;
    int elapsed;
    bool showWidget;
    QTimer anim;
    QTime checkTime;
};

static AlphaWidget* q_blend = 0;

/*
  Constructs a AlphaWidget.
*/
AlphaWidget::AlphaWidget( QWidget* w, QPixmap* pix, WFlags f, OptionHandler* oh, const QPopupMenu* p, PopupStuff *popupStuff, int swh)
    : QWidget( 0, "qt internal alpha effect widget", f ){
    setEnabled( FALSE );
    
    oHandler = oh;
    popup = p;
    popStuff = popupStuff;
    shadowWH = swh;
    
    backPix = pix;
    pm.setOptimization( QPixmap::BestOptim );
    setBackgroundMode( NoBackground );
    widget = (QAccessWidget*)w;
    setErasePixmap(*pix);
    alpha = 0;
//    connect( this, SIGNAL(destroyed()), oHandler, SLOT(warningSlot()));
}

/*
  \reimp
*/
bool AlphaWidget::eventFilter( QObject* o, QEvent* e ){
	if (o != widget){
		return false;
	}
	switch ( e->type() ) {
//		case QEvent::MouseButtonDblClick: 
// 		case QEvent::MouseButtonRelease:
// 		case QEvent::KeyPress: widget->releaseKeyboard();
// 		case QEvent::KeyRelease:
// 		case QEvent::MouseButtonPress: {
		// hardcoded to --enable-final
		case 1:
		case 2:
		case 3:
		case 5:
		case 6:{
			widget->releaseMouse();
			widget->hide();
			widget->close();
			if (widget) delete widget;
			return true;
		}
		default:
//			return QWidget::eventFilter( o, e );
		return false;
	}
	return false;
//	return QWidget::eventFilter( o, e );
}

/*
  Internal class RollEffect

  The RollEffect widget is shown while the animation lasts
  and displays a scrolling pixmap.
*/
class RollEffect : public QWidget, private Effects
{
    Q_OBJECT
public:
    RollEffect( QWidget* w, QPixmap* pix, WFlags f, DirFlags orient, OptionHandler* oh = NULL, const QPopupMenu* p = NULL, PopupStuff *popupStuff = NULL, int swh = 0);

    void run( int time );

protected:
    void paintEvent( QPaintEvent* e );
    void closeEvent( QCloseEvent* );
    bool eventFilter( QObject* o, QEvent* e );

protected slots:
    void scroll();
    //void render();

private:

    OptionHandler* oHandler;
    const QPopupMenu* popup;
    PopupStuff* popStuff;
    int shadowWH;
    
    QPixmap* backPix;
    QPixmap pm;
    
    int currentHeight;
    int currentWidth;
    int totalHeight;
    int totalWidth;
    
    QGuardedPtr<QAccessWidget> widget;
    
    int duration;
    int elapsed;
    bool showWidget;
    int orientation;
    bool done;
        
    QTimer anim;    
    QTime checkTime;
};

static RollEffect* q_roll = 0;

/*
  Construct a RollEffect widget.
*/
RollEffect::RollEffect( QWidget* w, QPixmap* pix, WFlags f, DirFlags orient, OptionHandler* oh, const QPopupMenu* p, PopupStuff *popupStuff, int swh)
    : QWidget( 0, "qt internal roll effect widget", f ), orientation(orient)
{
    setEnabled( FALSE );

    oHandler = oh;
    popup = p;
    popStuff = popupStuff;
    shadowWH = swh;
    
    if (w->erasePixmap() && !w->erasePixmap()->isNull())
    	pm = *(w->erasePixmap());
    else{
    	pm = QPixmap(w->width(), w->height());
	pm.fill(w->eraseColor());
    }
    
    pm.setOptimization( QPixmap::BestOptim );
//    setBackgroundMode( NoBackground );
    widget = (QAccessWidget*)w;
    setErasePixmap(*pix);
    
 //   if ( widget->testWState( WState_Resized ) ) {
	totalWidth = widget->width();
	totalHeight = widget->height();
//    } else {
//	totalWidth = widget->sizeHint().width();
//	totalHeight = widget->sizeHint().height();
//    }

    currentHeight = totalHeight;
    currentWidth = totalWidth;

    if ( orientation & (RightScroll|LeftScroll) )
	currentWidth = 0;
    if ( orientation & (DownScroll|UpScroll) )
	currentHeight = 0;
    
}

/*
  \reimp
*/
bool RollEffect::eventFilter( QObject* o, QEvent* e )
{
	if (o != widget)
		return false;
	switch ( e->type() ) {
// 		case QEvent::MouseButtonRelease:
//  		case QEvent::MouseButtonDblClick: 
// 		case QEvent::KeyPress:
// 			widget->releaseKeyboard();
// 		case QEvent::KeyRelease:
// 		case QEvent::MouseButtonPress:{
		// hardcoded to --enable-final
		case 1:
		case 2:
		case 3:
		case 5:
		case 6:{
			widget->releaseMouse();
			widget->hide();
			widget->close();
			if (widget) delete widget;
			return true;
		}
		default:
//			return QWidget::eventFilter( o, e );
		return false;
	}
	return false;
}

//#include <X11/X.h>
//#include <X11/Xlib.h>

/*
  \reimp
*/
void AlphaWidget::paintEvent( QPaintEvent* )
{
//    bitBlt( this, QPoint(0,0), &pm );
}

/*
  Starts the alphablending animation.
  The animation will take about \a time ms
*/
void AlphaWidget::run( int time )
{
    duration = time;

    if ( duration < 0 )
	duration = 150;

    if ( !widget )
	return;

    elapsed = 0;
    checkTime.start();

    showWidget = TRUE;
//    qApp->installEventFilter( this );

    widget->setWState( WState_Visible );

    move( widget->geometry().x(),widget->geometry().y() );
    resize( widget->size().width(), widget->size().height() );

    front = QImage( widget->size(), 32 );
    front = QPixmap::grabWidget( widget );

    back = QImage(widget->size(), 32 );
    back = *backPix;
    
    if ( !back.isNull() && checkTime.elapsed() < duration / 2 ) {
        mixed = back.copy();
	pm = mixed;
	show();
//	XSelectInput(qt_xdisplay(), winId(), StructureNotifyMask );
//	XMapWindow(qt_xdisplay(), winId());
	//===================
	connect( &anim, SIGNAL(timeout()), this, SLOT(render()));
	anim.start( 1 );
    } else {
	duration = 0;
	render();
    }
}

/*
  \reimp
*/
void AlphaWidget::closeEvent( QCloseEvent *e )
{
    e->accept();
    if ( !q_blend )
	return;

    showWidget = FALSE;
    render();

    QWidget::closeEvent( e );
}

/*
  Render alphablending for the time elapsed.

  Show the blended widget and free all allocated source
  if the blending is finished.
*/
void AlphaWidget::render()
{
    int tempel = checkTime.elapsed();
    if ( elapsed >= tempel )
        elapsed++;
    else
        elapsed = tempel;

    if ( duration != 0 )
	alpha = tempel / double(duration);
    else
	alpha = 1;
    if ( alpha >= 1 || !showWidget) {
	anim.stop();
	qApp->removeEventFilter( this );
	if (oHandler && popup && popStuff && !(popup->parent() && popup->parent()->inherits("KPoupMenu"))) oHandler->createShadowWindows(popup, popStuff, shadowWH);

	if ( widget ) {
	    if ( !showWidget ) {
		//direectly accessing X to not get confused on popup position updates...
		// it's faster, btw ;-)
//		XUnmapWindow(qt_xdisplay(), widget->winId());
//		XFlush(qt_xdisplay());
		hide();
//		widget->setWState( WState_ForceHide );
//		widget->clearWState( WState_Visible );
		widget->close();
		if (widget) delete widget;
	    } else if ( duration ) {
		BackgroundMode bgm = widget->backgroundMode();
		QColor erc = widget->eraseColor();
		const QPixmap *erp = widget->erasePixmap();

		widget->clearWState( WState_Visible );
		widget->setBackgroundMode( NoBackground );
		//direectly accessing X to not get confused on popup position updates...
		// it's faster, btw ;-)
//		XSelectInput(qt_xdisplay(), widget->winId(), StructureNotifyMask );
//		XMapWindow(qt_xdisplay(), widget->winId());
		show();
		if ( bgm != FixedColor && bgm != FixedPixmap ) {
		    widget->clearWState( WState_Visible ); // prevent update in setBackgroundMode
		    widget->setBackgroundMode( bgm );
		    widget->setWState( WState_Visible );
		}
		if ( erc.isValid() ) {
		    widget->setEraseColor( erc );
		} else if ( erp ) {
		    widget->setErasePixmap( *erp );
		}
	    } else {
		widget->clearWState( WState_Visible );
		//direectly accessing X to not get confused on popup position updates...
		// it's faster, btw ;-)
//		XSelectInput(qt_xdisplay(), widget->winId(), StructureNotifyMask );
//		XMapWindow(qt_xdisplay(), widget->winId());
		show();
	    }
	}
	q_blend = 0;
	deleteLater();
    } else {
	if (widget)
	    widget->clearWState( WState_ForceHide );
	alphaBlend();
	pm = mixed;
	// no show - no paintevent - so bitblt here =)
	bitBlt( this, QPoint(0,0), &pm );
	repaint( FALSE );
    }
}

/*
  Calculate an alphablended image.
*/
void AlphaWidget::alphaBlend()
{
    const double ia = 1-alpha;
    const int sw = front.width();
    const int sh = front.height();
    switch( front.depth() ) {
    case 32:
	{
	    Q_UINT32** md = (Q_UINT32**)mixed.jumpTable();
	    Q_UINT32** bd = (Q_UINT32**)back.jumpTable();
	    Q_UINT32** fd = (Q_UINT32**)front.jumpTable();

	    for (int sy = 0; sy < sh; sy++ ) {
		Q_UINT32* bl = ((Q_UINT32*)bd[sy]);
		Q_UINT32* fl = ((Q_UINT32*)fd[sy]);
		for (int sx = 0; sx < sw; sx++ ) {
		    Q_UINT32 bp = bl[sx];
		    Q_UINT32 fp = fl[sx];

		    ((Q_UINT32*)(md[sy]))[sx] =  qRgb(int (qRed(bp)*ia + qRed(fp)*alpha),
						    int (qGreen(bp)*ia + qGreen(fp)*alpha),
						    int (qBlue(bp)*ia + qBlue(fp)*alpha) );
		}
	    }
	}
    default:
	break;
    }
}


//============================================

// moved stuff upwards, as the inclusion of the
// X headers lead to compiler errors

//============================================


/*
  \reimp
*/
void RollEffect::paintEvent( QPaintEvent* )
{
 /*   int x = orientation & RightScroll ? QMIN(0, currentWidth - totalWidth) : 0;
    int y = orientation & DownScroll ? QMIN(0, currentHeight - totalHeight) : 0;

    bitBlt( this, x, y, &pm, 0, 0, pm.width(), pm.height(), CopyROP, TRUE );*/
}



/*
  \reimp
*/
void RollEffect::closeEvent( QCloseEvent *e )
{

    e->accept();
    if ( !q_roll )
	return;

    showWidget = FALSE;
    done = TRUE;
    scroll();

    QWidget::closeEvent( e );
}

/*
  Start the animation.

  The animation will take about \a time ms, or is
  calculated if \a time is negative
*/
void RollEffect::run( int time )
{
    duration = time;

    if ( duration < 0 )
	duration = 150;

    if ( !widget )
	return;

    elapsed = 0;
    
    if ( duration < 0 ) {
	int dist = 0;
	if ( orientation & (RightScroll|LeftScroll) )
	    dist += totalWidth - currentWidth;
	if ( orientation & (DownScroll|UpScroll) ) 
	    dist += totalHeight - currentHeight;
	duration = QMIN( QMAX( dist/3, 50 ), 120 );
    }

    checkTime.start();

    showWidget = TRUE;
    done = FALSE;
    qApp->installEventFilter( this );

    widget->setWState( WState_Visible );

    move( widget->geometry().x(),widget->geometry().y() );
    resize( widget->size().width(), widget->size().height() );

       
    if ( checkTime.elapsed() < duration / 2 ) {

	show();
//	XSelectInput(qt_xdisplay(), winId(), StructureNotifyMask );
//	XMapWindow(qt_xdisplay(), winId());
	//===================

	connect( &anim, SIGNAL(timeout()), this, SLOT(scroll()));
	anim.start( 1 );
    } else {
	duration = 0;
	scroll();
    }
}

/*
  Roll according to the time elapsed.
*/
void RollEffect::scroll(){
    int tempel = checkTime.elapsed();
    if ( elapsed >= tempel )
        elapsed++;
    else
        elapsed = tempel;

    if ( done || !showWidget) {
	anim.stop();
	qApp->removeEventFilter( this );
	if (oHandler && popup && popStuff && !(popup->parent() && popup->parent()->inherits("KPoupMenu"))) oHandler->createShadowWindows(popup, popStuff, shadowWH);

	if ( widget ) {
//		XUnmapWindow(qt_xdisplay(), widget->winId());
//		XFlush(qt_xdisplay());
		widget->hide();
		widget->close();
		if (widget) delete widget;
//		widget->setWState( WState_ForceHide );
//		widget->clearWState( WState_Visible );
/*	
	if ( widget ) {
	    if ( !showWidget ) {
		//direectly accessing X to not get confused on popup position updates...
		// it's faster, btw ;-)
		qWarning("hiding");
		XUnmapWindow(qt_xdisplay(), widget->winId());
		XFlush(qt_xdisplay());
		widget->setWState( WState_ForceHide );
		widget->clearWState( WState_Visible );
	    } else if ( duration ) {
	    qWarning("showing (1)");
		BackgroundMode bgm = widget->backgroundMode();
		QColor erc = widget->eraseColor();
		const QPixmap *erp = widget->erasePixmap();

		widget->clearWState( WState_Visible );
		widget->setBackgroundMode( NoBackground );
		//direectly accessing X to not get confused on popup position updates...
		// it's faster, btw ;-)
		qWarning("showing (2)");
		XSelectInput(qt_xdisplay(), widget->winId(), StructureNotifyMask );
		XMapWindow(qt_xdisplay(), widget->winId());
		if ( bgm != FixedColor && bgm != FixedPixmap ) {
		    widget->clearWState( WState_Visible ); // prevent update in setBackgroundMode
		    widget->setBackgroundMode( bgm );
		    widget->setWState( WState_Visible );
		}
		if ( erc.isValid() ) {
		    widget->setEraseColor( erc );
		} else if ( erp ) {
		    widget->setErasePixmap( *erp );
		}
	    } else {
	    qWarning("showing (3)");
		widget->clearWState( WState_Visible );
		//direectly accessing X to not get confused on popup position updates...
		// it's faster, btw ;-)
		XSelectInput(qt_xdisplay(), widget->winId(), StructureNotifyMask );
		XMapWindow(qt_xdisplay(), widget->winId());
	    }*/
	}
	q_roll = 0;
	deleteLater();
    } else {
	if (widget)
	    widget->clearWState( WState_ForceHide );
	    

	// no show - no paintevent - so bitblt here =)
	if ( currentWidth < totalWidth ) {
	    currentWidth = totalWidth * (elapsed/duration) 
	    	+ ( 2 * totalWidth * (elapsed%duration) + duration )
	    	/ ( 2 * duration );
	    // equiv. to int( (totalWidth*elapsed) / duration + 0.5 )
//	    done = (currentWidth >= totalWidth);
	}
	if ( currentHeight < totalHeight ) {
	    currentHeight = totalHeight * (elapsed/duration)
	    	+ ( 2 * totalHeight * (elapsed%duration) + duration )
	    	/ ( 2 * duration );
	    // equiv. to int( (totalHeight*elapsed) / duration + 0.5 )
//	    done = (currentHeight >= totalHeight);
	}
	done = ( currentHeight >= totalHeight ) &&
	       ( currentWidth >= totalWidth );
	       
        int w = totalWidth;
	int h = totalHeight;
//	int x_from = 0;
//	int y_from = 0;
	int x_to = 0;
	int y_to = 0;

	
	if ( orientation & RightScroll){
		w = QMIN( currentWidth, totalWidth );
//		x_from = 0;/*QMAX(0, totalWidth - currentWidth );*/
	}
	if (orientation & LeftScroll ){
		w = QMIN( currentWidth, totalWidth );
		x_to = QMAX( 0, totalWidth - currentWidth );
	}
	if ( orientation & DownScroll){
		h = QMIN( currentHeight, totalHeight );
//		y_from = 0;/*QMAX( 0, totalHeight - currentHeight );*/
	}
	if ( orientation & UpScroll ){
		y_to = QMAX( 0, totalHeight - currentHeight );
		h = QMIN( currentHeight, totalHeight );
	}
	/*OptionHandler::saveB*/bitBlt( /*(QPixmap *)*/this, x_to, y_to, &pm, x_to, y_to, w, h/*, "scroll"*/ );
	repaint( FALSE );
    }
}


/*
  Delete this after timeout
*/

#include "qeffects.moc"

/*!
    Scroll widget \a w in \a time ms. \a orient may be 1 (vertical), 2
    (horizontal) or 3 (diagonal).
*/
void OptionHandler::ScrollEffect( QWidget* w, QPixmap* pix, Effects::DirFlags orient, int time, OptionHandler* oh, const QPopupMenu* p, PopupStuff *popupStuff, int swh)
{
    if ( q_roll ) {
	delete q_roll;
	q_roll = 0;
    }

    qApp->sendPostedEvents( w, QEvent::Move );
    qApp->sendPostedEvents( w, QEvent::Resize );
    
    // those can popups - they would steal the focus, but are disabled
    q_roll = new RollEffect( w, pix, Qt::WStyle_Customize | Qt::WType_Popup | Qt::WX11BypassWM |
			      Qt::WNoAutoErase | Qt::WStyle_StaysOnTop, orient, oh, p, popupStuff, swh );

    q_roll->run( time );
}

/*!
    Fade in widget \a w in \a time ms.
*/
void OptionHandler::FadeEffect( QWidget* w, QPixmap* pix, int time, OptionHandler* oh, const QPopupMenu* p, PopupStuff *popupStuff, int swh )
{
    if ( q_blend ) {
	delete q_blend;
	q_blend = 0;
    }

    qApp->sendPostedEvents( w, QEvent::Move );
    qApp->sendPostedEvents( w, QEvent::Resize );

    // those can popups - they would steal the focus, but are disabled
    q_blend = new AlphaWidget( w, pix, Qt::WStyle_Customize | Qt::WType_Popup | Qt::WX11BypassWM | Qt::WNoAutoErase | Qt::WStyle_StaysOnTop, oh, p, popupStuff, swh);
    //q_blend = new AlphaWidget( w, pix, Qt::WStyle_Customize | Qt::WType_Popup | Qt::WX11BypassWM | Qt::WStyle_StaysOnTop);

    q_blend->run( time );
}
