/********************************************************************
 KWin - the KDE window manager
 This file is part of the KDE project.

 Copyright (C) 2008 Martin Gräßlin <ubuntu@martin-graesslin.com>

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

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

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "cube.h"

#include <kaction.h>
#include <kactioncollection.h>
#include <klocale.h>
#include <kwinconfig.h>
#include <kconfiggroup.h>
#include <kcolorscheme.h>
#include <kglobal.h>
#include <kstandarddirs.h>
#include <kdebug.h>

#include <QColor>
#include <QRect>
#include <QEvent>
#include <QKeyEvent>

#include <math.h>

#include <GL/gl.h>

namespace KWin
{

KWIN_EFFECT( cube, CubeEffect )
KWIN_EFFECT_SUPPORTED( cube, CubeEffect::supported() )

CubeEffect::CubeEffect()
    : activated( false )
    , mousePolling( false )
    , cube_painting( false )
    , keyboard_grab( false )
    , schedule_close( false )
    , frontDesktop( 0 )
    , cubeOpacity( 1.0 )
    , opacityDesktopOnly( true )
    , displayDesktopName( false )
    , desktopNameFrame( EffectFrame::Styled )
    , reflection( true )
    , rotating( false )
    , desktopChangedWhileRotating( false )
    , paintCaps( true )
    , rotationDirection( Left )
    , verticalRotationDirection( Upwards )
    , verticalPosition( Normal )
    , wallpaper( NULL )
    , texturedCaps( true )
    , capTexture( NULL )
    , manualAngle( 0.0 )
    , manualVerticalAngle( 0.0 )
    , currentShape( TimeLine::EaseInOutCurve )
    , start( false )
    , stop( false )
    , reflectionPainting( false )
    , activeScreen( 0 )
    , bottomCap( false )
    , closeOnMouseRelease( false )
    , zoom( 0.0 )
    , zPosition( 0.0 )
    , useForTabBox( false )
    , tabBoxMode( false )
    , shortcutsRegistered( false )
    , mode( Cube )
    , useShaders( false )
    , cylinderShader( 0 )
    , sphereShader( 0 )
    , zOrderingFactor( 0.0f )
    , mAddedHeightCoeff1( 0.0f )
    , mAddedHeightCoeff2( 0.0f )
    , capListCreated( false )
    , recompileList( true )
    , glList( 0 )
    {
    desktopNameFont.setBold( true );
    desktopNameFont.setPointSize( 14 );
    desktopNameFrame.setFont( desktopNameFont );

    reconfigure( ReconfigureAll );
    }

bool CubeEffect::supported()
    {
    return effects->compositingType() == OpenGLCompositing;
    }

void CubeEffect::reconfigure( ReconfigureFlags )
    {
    loadConfig( "Cube" );
    }

void CubeEffect::loadConfig( QString config )
    {
    KConfigGroup conf = effects->effectConfig( config );
    foreach( ElectricBorder border, borderActivate )
        {
        effects->unreserveElectricBorder( border );
        }
    foreach( ElectricBorder border, borderActivateCylinder )
        {
        effects->unreserveElectricBorder( border );
        }
    foreach( ElectricBorder border, borderActivateSphere )
        {
        effects->unreserveElectricBorder( border );
        }
    borderActivate.clear();
    borderActivateCylinder.clear();
    borderActivateSphere.clear();
    QList<int> borderList = QList<int>();
    borderList.append( int( ElectricNone ) );
    borderList = conf.readEntry( "BorderActivate", borderList );
    foreach( int i, borderList )
        {
        borderActivate.append( ElectricBorder( i ) );
        effects->reserveElectricBorder( ElectricBorder( i ) );
        }
    borderList.clear();
    borderList.append( int( ElectricNone ) );
    borderList = conf.readEntry( "BorderActivateCylinder", borderList );
    foreach( int i, borderList )
        {
        borderActivateCylinder.append( ElectricBorder( i ) );
        effects->reserveElectricBorder( ElectricBorder( i ) );
        }
    borderList.clear();
    borderList.append( int( ElectricNone ) );
    borderList = conf.readEntry( "BorderActivateSphere", borderList );
    foreach( int i, borderList )
        {
        borderActivateSphere.append( ElectricBorder( i ) );
        effects->reserveElectricBorder( ElectricBorder( i ) );
        }

    cubeOpacity = (float)conf.readEntry( "Opacity", 80 )/100.0f;
    opacityDesktopOnly = conf.readEntry( "OpacityDesktopOnly", false );
    displayDesktopName = conf.readEntry( "DisplayDesktopName", true );
    reflection = conf.readEntry( "Reflection", true );
    rotationDuration = animationTime( conf, "RotationDuration", 500 );
    backgroundColor = conf.readEntry( "BackgroundColor", QColor( Qt::black ) );
    capColor = conf.readEntry( "CapColor", KColorScheme( QPalette::Active, KColorScheme::Window ).background().color() );
    paintCaps = conf.readEntry( "Caps", true );
    closeOnMouseRelease = conf.readEntry( "CloseOnMouseRelease", false );
    float defaultZPosition = 100.0f;
    if( config == "Sphere" )
        defaultZPosition = 450.0f;
    zPosition = conf.readEntry( "ZPosition", defaultZPosition );
    useForTabBox = conf.readEntry( "TabBox", false );
    invertKeys = conf.readEntry( "InvertKeys", false );
    invertMouse = conf.readEntry( "InvertMouse", false );
    capDeformationFactor = conf.readEntry( "CapDeformation", 0 )/100.0f;
    useZOrdering = conf.readEntry( "ZOrdering", false );
    QString file = conf.readEntry( "Wallpaper", QString("") );
    if( wallpaper )
        wallpaper->discard();
    delete wallpaper;
    wallpaper = NULL;
    if( !file.isEmpty() )
        {
        QImage img = QImage( file );
        if( !img.isNull() )
            {
            wallpaper = new GLTexture( img );
            }
        }
    delete capTexture;
    capTexture = NULL;
    texturedCaps = conf.readEntry( "TexturedCaps", true );
    if( texturedCaps )
        {
        QString capPath = conf.readEntry( "CapPath", KGlobal::dirs()->findResource( "appdata", "cubecap.png" ) );
        QImage img = QImage( capPath );
        if( !img.isNull() )
            {
            capTexture = new GLTexture( img );
            capTexture->setFilter( GL_LINEAR );
            capTexture->setWrapMode( GL_CLAMP_TO_BORDER );
            }
        }

    timeLine.setCurveShape( TimeLine::EaseInOutCurve );
    timeLine.setDuration( rotationDuration );

    verticalTimeLine.setCurveShape( TimeLine::EaseInOutCurve );
    verticalTimeLine.setDuration( rotationDuration );

    // do not connect the shortcut if we use cylinder or sphere
    if( !shortcutsRegistered )
        {
        KActionCollection* actionCollection = new KActionCollection( this );
        KAction* cubeAction = static_cast< KAction* >( actionCollection->addAction( "Cube" ));
        cubeAction->setText( i18n("Desktop Cube" ));
        cubeAction->setGlobalShortcut( KShortcut( Qt::CTRL + Qt::Key_F11 ));
        cubeShortcut = cubeAction->globalShortcut();
        KAction* cylinderAction = static_cast< KAction* >( actionCollection->addAction( "Cylinder" ));
        cylinderAction->setText( i18n("Desktop Cylinder" ));
        cylinderAction->setGlobalShortcut( KShortcut(), KAction::ActiveShortcut);
        cylinderShortcut = cylinderAction->globalShortcut();
        KAction* sphereAction = static_cast< KAction* >( actionCollection->addAction( "Sphere" ));
        sphereAction->setText( i18n("Desktop Sphere" ));
        sphereAction->setGlobalShortcut( KShortcut(), KAction::ActiveShortcut);
        sphereShortcut = sphereAction->globalShortcut();
        connect( cubeAction, SIGNAL( triggered( bool )), this, SLOT( toggleCube()));
        connect( cylinderAction, SIGNAL( triggered( bool )), this, SLOT( toggleCylinder()));
        connect( sphereAction, SIGNAL( triggered( bool )), this, SLOT( toggleSphere()));
        connect( cubeAction, SIGNAL( globalShortcutChanged( QKeySequence )), this, SLOT( cubeShortcutChanged(QKeySequence)));
        connect( cylinderAction, SIGNAL( globalShortcutChanged( QKeySequence )), this, SLOT( cylinderShortcutChanged(QKeySequence)));
        connect( sphereAction, SIGNAL( globalShortcutChanged( QKeySequence )), this, SLOT( sphereShortcutChanged(QKeySequence)));
        shortcutsRegistered = true;
        }
    }

CubeEffect::~CubeEffect()
    {
    foreach( ElectricBorder border, borderActivate )
        {
        effects->unreserveElectricBorder( border );
        }
    foreach( ElectricBorder border, borderActivateCylinder )
        {
        effects->unreserveElectricBorder( border );
        }
    foreach( ElectricBorder border, borderActivateSphere )
        {
        effects->unreserveElectricBorder( border );
        }
    delete wallpaper;
    delete capTexture;
    delete cylinderShader;
    delete sphereShader;
    }

bool CubeEffect::loadShader()
    {
    if( !(GLShader::fragmentShaderSupported() &&
            (effects->compositingType() == OpenGLCompositing)))
        return false;
    QString fragmentshader       =  KGlobal::dirs()->findResource( "data", "kwin/cylinder.frag" );
    QString cylinderVertexshader =  KGlobal::dirs()->findResource( "data", "kwin/cylinder.vert" );
    QString sphereVertexshader   = KGlobal::dirs()->findResource( "data", "kwin/sphere.vert" );
    if( fragmentshader.isEmpty() || cylinderVertexshader.isEmpty() || sphereVertexshader.isEmpty() )
        {
        kError(1212) << "Couldn't locate shader files" << endl;
        return false;
        }

    cylinderShader = new GLShader(cylinderVertexshader, fragmentshader);
    if( !cylinderShader->isValid() )
        {
        kError(1212) << "The cylinder shader failed to load!" << endl;
        return false;
        }
    else
        {
        cylinderShader->bind();
        cylinderShader->setUniform( "winTexture", 0 );
        QRect rect = effects->clientArea( FullArea, activeScreen, effects->currentDesktop() );
        cylinderShader->setUniform( "width", (float)rect.width() );
        cylinderShader->unbind();
        }
    sphereShader = new GLShader( sphereVertexshader, fragmentshader );
    if( !sphereShader->isValid() )
        {
        kError(1212) << "The sphere shader failed to load!" << endl;
        return false;
        }
    else
        {
        sphereShader->bind();
        sphereShader->setUniform( "winTexture", 0 );
        QRect rect = effects->clientArea( FullArea, activeScreen, effects->currentDesktop() );
        sphereShader->setUniform( "width", (float)rect.width() );
        sphereShader->setUniform( "height", (float)rect.height() );
        sphereShader->unbind();
        }
    return true;
    }

void CubeEffect::prePaintScreen( ScreenPrePaintData& data, int time )
    {
    if( activated )
        {
        data.mask |= PAINT_SCREEN_TRANSFORMED | Effect::PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS | PAINT_SCREEN_BACKGROUND_FIRST;

        if( rotating || start || stop )
            {
            timeLine.addTime( time );
            recompileList = true;
            }
        if( verticalRotating )
            {
            verticalTimeLine.addTime( time );
            recompileList = true;
            }
        }
    effects->prePaintScreen( data, time );
    }

void CubeEffect::paintScreen( int mask, QRegion region, ScreenPaintData& data )
    {
    if( activated )
        {
        if( recompileList )
            {
            recompileList = false;
            glPushMatrix();
            glNewList( glList, GL_COMPILE );
            rotateCube();
            glEndList();
            glPopMatrix();
            }

        // compile List for cube
        if( useList )
            {
            glNewList( glList + 1, GL_COMPILE );
            glPushMatrix();
            paintCube( mask, region, data );
            glPopMatrix();
            glEndList();
            }

        QRect rect = effects->clientArea( FullArea, activeScreen, effects->currentDesktop() );

        // background
        float clearColor[4];
        glGetFloatv( GL_COLOR_CLEAR_VALUE, clearColor );
        glClearColor( backgroundColor.redF(), backgroundColor.greenF(), backgroundColor.blueF(), 1.0 );
        glClear( GL_COLOR_BUFFER_BIT );
        glClearColor( clearColor[0], clearColor[1], clearColor[2], clearColor[3] );

        // wallpaper
        if( wallpaper )
            {
            wallpaper->bind();
            wallpaper->render( region, rect );
            wallpaper->unbind();
            }

        glPushAttrib( GL_CURRENT_BIT | GL_ENABLE_BIT );
        glEnable( GL_BLEND );
        glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );

        // some veriables needed for painting the caps
        float cubeAngle = (float)((float)(effects->numberOfDesktops() - 2 )/(float)effects->numberOfDesktops() * 180.0f);
        float point = rect.width()/2*tan(cubeAngle*0.5f*M_PI/180.0f);
        float zTranslate = zPosition + zoom;
        if( start )
            zTranslate *= timeLine.value();
        if( stop )
            zTranslate *= ( 1.0 - timeLine.value() );
        // reflection
        if( reflection && mode != Sphere )
            {
            // restrict painting the reflections to the current screen
            PaintClipper::push( QRegion( rect ));
            glPushMatrix();
            // we can use a huge scale factor (needed to calculate the rearground vertices)
            // as we restrict with a PaintClipper painting on the current screen
            float scaleFactor = 1000000 * tan( 60.0 * M_PI / 360.0f )/rect.height();
            glScalef( 1.0, -1.0, 1.0 );

            // TODO reflection is not correct when mixing manual (mouse) rotating with rotation by cursor keys
            // there's also a small bug when zooming
            float addedHeight1 = -sin( asin( float( rect.height() ) / mAddedHeightCoeff1 ) + fabs( manualVerticalAngle ) * M_PI / 180.0f ) * mAddedHeightCoeff1;
            float addedHeight2 = -sin( asin( float( rect.height() ) / mAddedHeightCoeff2 ) + fabs( manualVerticalAngle ) * M_PI / 180.0f ) * mAddedHeightCoeff2 - addedHeight1;
            if( manualVerticalAngle > 0.0f && effects->numberOfDesktops() & 1 )
                glTranslatef( 0.0, cos( fabs( manualAngle ) * M_PI / 360.0f * float( effects->numberOfDesktops() ) ) * addedHeight2 + addedHeight1 - float( rect.height() ), 0.0 );
            else
                glTranslatef( 0.0, sin( fabs( manualAngle ) * M_PI / 360.0f * float( effects->numberOfDesktops() ) ) * addedHeight2 + addedHeight1 - float( rect.height() ), 0.0 );


            glEnable( GL_CLIP_PLANE0 );
            reflectionPainting = true;
            glEnable( GL_CULL_FACE );
            // caps
            if( paintCaps && ( effects->numberOfDesktops() >= 2 ) )
                {
                glPushMatrix();
                glCallList( glList );
                glTranslatef( rect.width()/2, 0.0, -point-zTranslate );
                glRotatef( (1-frontDesktop)*360.0f / effects->numberOfDesktops(), 0.0, 1.0, 0.0 );
                glTranslatef( 0.0, rect.height(), 0.0 );
                glCullFace( GL_FRONT );

                // bottom texture has to be mirrored
                glMatrixMode( GL_TEXTURE );
                glPushMatrix();
                glLoadIdentity();
                glScalef( 1.0f, -1.0f, 1.0f );
                glTranslatef( 0.0f, -1.0f, 0.0f );
                glMatrixMode( GL_MODELVIEW );

                glCallList( glList + 2 );

                glMatrixMode( GL_TEXTURE );
                glPopMatrix();
                glMatrixMode( GL_MODELVIEW );

                glTranslatef( 0.0, -rect.height(), 0.0 );
                glCullFace( GL_BACK );
                glCallList( glList + 2 );
                glPopMatrix();
                }

            // cube
            glCullFace( GL_FRONT );
            if( mode == Cylinder )
                {
                cylinderShader->bind();
                cylinderShader->setUniform( "front", 1.0f );
                cylinderShader->unbind();
                }
            glPushMatrix();
            glCallList( glList );
            if( useList )
                glCallList( glList + 1 );
            else
                {
                glPushMatrix();
                paintCube( mask, region, data );
                glPopMatrix();
                }
            glPopMatrix();
            glCullFace( GL_BACK );
            if( mode == Cylinder )
                {
                cylinderShader->bind();
                cylinderShader->setUniform( "front", -1.0f );
                cylinderShader->unbind();
                }
            glPushMatrix();
            glCallList( glList );
            if( useList )
                glCallList( glList + 1 );
            else
                {
                glPushMatrix();
                paintCube( mask, region, data );
                glPopMatrix();
                }
            glPopMatrix();

            // cap
            if( paintCaps && ( effects->numberOfDesktops() >= 2 ) )
                {
                glPushMatrix();
                glCallList( glList );
                glTranslatef( rect.width()/2, 0.0, -point-zTranslate );
                glRotatef( (1-frontDesktop)*360.0f / effects->numberOfDesktops(), 0.0, 1.0, 0.0 );
                glTranslatef( 0.0, rect.height(), 0.0 );
                glCullFace( GL_BACK );

                // bottom texture has to be mirrored
                glMatrixMode( GL_TEXTURE );
                glPushMatrix();
                glLoadIdentity();
                glScalef( 1.0f, -1.0f, 1.0f );
                glTranslatef( 0.0f, -1.0f, 0.0f );
                glMatrixMode( GL_MODELVIEW );
                glCallList( glList + 2 );

                glMatrixMode( GL_TEXTURE );
                glPopMatrix();
                glMatrixMode( GL_MODELVIEW );

                glTranslatef( 0.0, -rect.height(), 0.0 );
                glCullFace( GL_FRONT );
                glCallList( glList + 2 );
                glPopMatrix();
                }
            glDisable( GL_CULL_FACE );
            reflectionPainting = false;
            glDisable( GL_CLIP_PLANE0 );

            glPopMatrix();
            glPushMatrix();
            glTranslatef( rect.x() + rect.width()*0.5f, 0.0, 0.0 );
            float vertices[] = {
                -rect.width()*0.5f, rect.height(), 0.0,
                rect.width()*0.5f, rect.height(), 0.0,
                (float)rect.width()*scaleFactor, rect.height(), -5000,
                -(float)rect.width()*scaleFactor, rect.height(), -5000 };
            // foreground
            float alpha = 0.7;
            if( start )
                alpha = 0.3 + 0.4 * timeLine.value();
            if( stop )
                alpha = 0.3 + 0.4 * ( 1.0 - timeLine.value() );
            glColor4f( 0.0, 0.0, 0.0, alpha );
            glBegin( GL_POLYGON );
            glVertex3f( vertices[0], vertices[1], vertices[2] );
            glVertex3f( vertices[3], vertices[4], vertices[5] );
            // rearground
            alpha = -1.0;
            glColor4f( 0.0, 0.0, 0.0, alpha );
            glVertex3f( vertices[6], vertices[7], vertices[8] );
            glVertex3f( vertices[9], vertices[10], vertices[11] );
            glEnd();
            glPopMatrix();
            PaintClipper::pop( QRegion( rect ));
            }
        glEnable( GL_CULL_FACE );
        // caps
        if( paintCaps && ( effects->numberOfDesktops() >= 2 ))
            {
            glPushMatrix();
            glCallList( glList );
            glTranslatef( rect.width()/2, 0.0, -point-zTranslate );
            glRotatef( (1-frontDesktop)*360.0f / effects->numberOfDesktops(), 0.0, 1.0, 0.0 );
            glTranslatef( 0.0, rect.height(), 0.0 );
            glCullFace( GL_BACK );
            if( mode == Sphere )
                {
                glPushMatrix();
                glScalef( 1.0, -1.0, 1.0 );
                }

            // bottom texture has to be mirrored
            glMatrixMode( GL_TEXTURE );
            glPushMatrix();
            glLoadIdentity();
            glScalef( 1.0f, -1.0f, 1.0f );
            glTranslatef( 0.0f, -1.0f, 0.0f );
            glMatrixMode( GL_MODELVIEW );

            glCallList( glList + 2 );

            glMatrixMode( GL_TEXTURE );
            glPopMatrix();
            glMatrixMode( GL_MODELVIEW );
            if( mode == Sphere )
                glPopMatrix();
            glTranslatef( 0.0, -rect.height(), 0.0 );
            glCullFace( GL_FRONT );
            glCallList( glList + 2 );
            glPopMatrix();
            }

        // cube
        glCullFace( GL_BACK );
        if( mode == Cylinder )
            {
            cylinderShader->bind();
            cylinderShader->setUniform( "front", -1.0f );
            cylinderShader->unbind();
            }
        if( mode == Sphere )
            {
            sphereShader->bind();
            sphereShader->setUniform( "front", -1.0f );
            sphereShader->unbind();
            }
        glPushMatrix();
        glCallList( glList );
        if( useList )
            glCallList( glList + 1 );
        else
            {
            glPushMatrix();
            paintCube( mask, region, data );
            glPopMatrix();
            }
        glPopMatrix();
        glCullFace( GL_FRONT );
        if( mode == Cylinder )
            {
            cylinderShader->bind();
            cylinderShader->setUniform( "front", 1.0f );
            cylinderShader->unbind();
            }
        if( mode == Sphere )
            {
            sphereShader->bind();
            sphereShader->setUniform( "front", 1.0f );
            sphereShader->unbind();
            }
        glPushMatrix();
        glCallList( glList );
        if( useList )
            glCallList( glList + 1 );
        else
            {
            glPushMatrix();
            paintCube( mask, region, data );
            glPopMatrix();
            }
        glPopMatrix();
        // we painted once without glList, now it's safe to paint using lists
        useList = true;

        // cap
        if( paintCaps && ( effects->numberOfDesktops() >= 2 ))
            {
            glPushMatrix();
            glCallList( glList );
            glTranslatef( rect.width()/2, 0.0, -point-zTranslate );
            glRotatef( (1-frontDesktop)*360.0f / effects->numberOfDesktops(), 0.0, 1.0, 0.0 );
            glTranslatef( 0.0, rect.height(), 0.0 );
            glCullFace( GL_FRONT );
            if( mode == Sphere )
                {
                glPushMatrix();
                glScalef( 1.0, -1.0, 1.0 );
                }

            // bottom texture has to be mirrored
            glMatrixMode( GL_TEXTURE );
            glPushMatrix();
            glLoadIdentity();
            glScalef( 1.0f, -1.0f, 1.0f );
            glTranslatef( 0.0f, -1.0f, 0.0f );
            glMatrixMode( GL_MODELVIEW );

            glCallList( glList + 2 );

            glMatrixMode( GL_TEXTURE );
            glPopMatrix();
            glMatrixMode( GL_MODELVIEW );

            if( mode == Sphere )
                glPopMatrix();
            glTranslatef( 0.0, -rect.height(), 0.0 );
            glCullFace( GL_BACK );
            glCallList( glList + 2 );
            glPopMatrix();
            }
        glDisable( GL_CULL_FACE );

        glDisable( GL_BLEND );
        glPopAttrib();

        // desktop name box - inspired from coverswitch
        if( displayDesktopName )
            {
            double opacity = 1.0;
            if( start )
                opacity = timeLine.value();
            if( stop )
                opacity = 1.0 - timeLine.value();
            QRect screenRect = effects->clientArea( ScreenArea, activeScreen, frontDesktop );
            QRect frameRect = QRect( screenRect.width() * 0.33f + screenRect.x(), screenRect.height() * 0.95f + screenRect.y(),
                screenRect.width() * 0.34f, QFontMetrics( desktopNameFont ).height() );
            desktopNameFrame.setGeometry( frameRect );
            desktopNameFrame.setText( effects->desktopName( frontDesktop ) );
            desktopNameFrame.render( region, opacity );
            }
        }
    else
        {
        effects->paintScreen( mask, region, data );
        }
    }

void CubeEffect::rotateCube()
    {
    QRect rect = effects->clientArea( FullArea, activeScreen, effects->currentDesktop() );

    float internalCubeAngle = 360.0f / effects->numberOfDesktops();
    float zTranslate = zPosition + zoom;
    if( start )
        zTranslate *= timeLine.value();
    if( stop )
        zTranslate *= ( 1.0 - timeLine.value() );
    // Rotation of the cube
    float cubeAngle = (float)((float)(effects->numberOfDesktops() - 2 )/(float)effects->numberOfDesktops() * 180.0f);
    float point = rect.width()/2*tan(cubeAngle*0.5f*M_PI/180.0f);
    if( verticalRotating || verticalPosition != Normal || manualVerticalAngle != 0.0 )
        {
        // change the verticalPosition if manualVerticalAngle > 90 or < -90 degrees
        if( manualVerticalAngle <= -90.0 )
            {
            manualVerticalAngle += 90.0;
            if( verticalPosition == Normal )
                verticalPosition = Down;
            if( verticalPosition == Up )
                verticalPosition = Normal;
            }
        if( manualVerticalAngle >= 90.0 )
            {
            manualVerticalAngle -= 90.0;
            if( verticalPosition == Normal )
                verticalPosition = Up;
            if( verticalPosition == Down )
                verticalPosition = Normal;
            }
        float angle = 0.0;
        if( verticalPosition == Up )
            {
            angle = 90.0;
            if( !verticalRotating)
                {
                if( manualVerticalAngle < 0.0 )
                    angle += manualVerticalAngle;
                else
                    manualVerticalAngle = 0.0;
                }
            }
        else if( verticalPosition == Down )
            {
            angle = -90.0;
            if( !verticalRotating)
                {
                if( manualVerticalAngle > 0.0 )
                    angle += manualVerticalAngle;
                else
                    manualVerticalAngle = 0.0;
                }
            }
        else
            {
            angle = manualVerticalAngle;
            }
        if( verticalRotating )
            {
            angle *= verticalTimeLine.value();
            if( verticalPosition == Normal && verticalRotationDirection == Upwards )
                angle = -90.0 + 90*verticalTimeLine.value();
            if( verticalPosition == Normal && verticalRotationDirection == Downwards )
                angle = 90.0 - 90*verticalTimeLine.value();
            angle += manualVerticalAngle * (1.0-verticalTimeLine.value());
            }
        if( stop )
            angle *= (1.0 - timeLine.value());
        glTranslatef( rect.width()/2, rect.height()/2, -point-zTranslate );
        glRotatef( angle, 1.0, 0.0, 0.0 );
        glTranslatef( -rect.width()/2, -rect.height()/2, point+zTranslate );
        }
    if( rotating || (manualAngle != 0.0) )
        {
        int tempFrontDesktop = frontDesktop;
        if( manualAngle > internalCubeAngle * 0.5f )
            {
            manualAngle -= internalCubeAngle;
            tempFrontDesktop--;
            if( tempFrontDesktop == 0 )
                tempFrontDesktop = effects->numberOfDesktops();
            }
        if( manualAngle < -internalCubeAngle * 0.5f )
            {
            manualAngle += internalCubeAngle;
            tempFrontDesktop++;
            if( tempFrontDesktop > effects->numberOfDesktops() )
                tempFrontDesktop = 1;
            }
        float rotationAngle = internalCubeAngle * timeLine.value();
        if( rotationAngle > internalCubeAngle * 0.5f )
            {
            rotationAngle -= internalCubeAngle;
            if( !desktopChangedWhileRotating )
                {
                desktopChangedWhileRotating = true;
                if( rotationDirection == Left )
                    {
                    tempFrontDesktop++;
                    }
                else if( rotationDirection == Right )
                    {
                    tempFrontDesktop--;
                    }
                if( tempFrontDesktop > effects->numberOfDesktops() )
                    tempFrontDesktop = 1;
                else if( tempFrontDesktop == 0 )
                    tempFrontDesktop = effects->numberOfDesktops();
                }
            }
        // don't change front desktop during stop animation as this would break some logic
        if( !stop )
            frontDesktop = tempFrontDesktop;
        if( rotationDirection == Left )
            {
            rotationAngle *= -1;
            }
        if( stop )
            rotationAngle = manualAngle * (1.0 - timeLine.value());
        else
            rotationAngle += manualAngle * (1.0 - timeLine.value());
        glTranslatef( rect.width()/2, rect.height()/2, -point-zTranslate );
        glRotatef( rotationAngle, 0.0, 1.0, 0.0 );
        glTranslatef( -rect.width()/2, -rect.height()/2, point+zTranslate );
        }
    }

void CubeEffect::paintCube( int mask, QRegion region, ScreenPaintData& data )
    {
    QRect rect = effects->clientArea( FullArea, activeScreen, effects->currentDesktop() );
    float internalCubeAngle = 360.0f / effects->numberOfDesktops();
    cube_painting = true;
    float zTranslate = zPosition + zoom;
    if( start )
        zTranslate *= timeLine.value();
    if( stop )
        zTranslate *= ( 1.0 - timeLine.value() );

    // Rotation of the cube
    float cubeAngle = (float)((float)(effects->numberOfDesktops() - 2 )/(float)effects->numberOfDesktops() * 180.0f);
    float point = rect.width()/2*tan(cubeAngle*0.5f*M_PI/180.0f);

    for( int i=0; i<effects->numberOfDesktops(); i++ )
        {
        // start painting the cube
        painting_desktop = (i + frontDesktop )%effects->numberOfDesktops();
        if( painting_desktop == 0 )
            {
            painting_desktop = effects->numberOfDesktops();
            }
        ScreenPaintData newData = data;
        RotationData rot = RotationData();
        rot.axis = RotationData::YAxis;
        rot.angle = internalCubeAngle * i;
        rot.xRotationPoint = rect.width()/2;
        rot.zRotationPoint = -point;
        newData.rotation = &rot;
        newData.zTranslate = -zTranslate;
        effects->paintScreen( mask, region, newData );
        }
    cube_painting = false;
    painting_desktop = effects->currentDesktop();
    }

void CubeEffect::paintCap()
    {
    if( ( !paintCaps ) || effects->numberOfDesktops() <= 2 )
        return;

    if( !capListCreated )
        {
        capListCreated = true;
        glNewList( glList + 2, GL_COMPILE );
        glColor4f( capColor.redF(), capColor.greenF(), capColor.blueF(), cubeOpacity );
        if( texturedCaps && effects->numberOfDesktops() > 3 && capTexture )
            {
            // modulate the cap texture: cap color should be background for translucent pixels
            // cube opacity should be used for all pixels
            // blend with cap color
            float color[4] = { capColor.redF(), capColor.greenF(), capColor.blueF(), cubeOpacity };
            glActiveTexture( GL_TEXTURE0 );
            capTexture->bind();
            glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL );
            glColor4fv( color );

            // set Opacity to cube opacity
            // TODO: change opacity during start/stop animation
            glActiveTexture( GL_TEXTURE1 );
            capTexture->bind();
            glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE );
            glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE );
            glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS );
            glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE );
            glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_CONSTANT );
            glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color );
            glActiveTexture( GL_TEXTURE0 );
            glTexParameterfv( GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, color );
            }
        glPushMatrix();
        switch( mode )
            {
            case Cube:
                paintCubeCap();
                break;
            case Cylinder:
                paintCylinderCap();
                break;
            case Sphere:
                paintSphereCap();
                break;
            default:
                // impossible
                break;
            }
        glPopMatrix();
        if( texturedCaps && effects->numberOfDesktops() > 3 && capTexture )
            {
            glActiveTexture( GL_TEXTURE1 );
            glDisable( capTexture->target() );
            glActiveTexture( GL_TEXTURE0 );
            glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
            glColor4f( 0.0f, 0.0f, 0.0f, 0.0f );
            capTexture->unbind();
            }
        glEndList();
        }
    }

void CubeEffect::paintCubeCap()
    {
    QRect rect = effects->clientArea( FullArea, activeScreen, effects->currentDesktop() );
    float cubeAngle = (float)((float)(effects->numberOfDesktops() - 2 )/(float)effects->numberOfDesktops() * 180.0f);
    float z = rect.width()/2*tan(cubeAngle*0.5f*M_PI/180.0f);
    float zTexture = rect.width()/2*tan(45.0f*M_PI/180.0f);
    float angle = 360.0f/effects->numberOfDesktops();
    bool texture = texturedCaps && effects->numberOfDesktops() > 3 && capTexture;
    for( int i=0; i<effects->numberOfDesktops(); i++ )
        {
        int triangleRows = effects->numberOfDesktops()*5;
        float zTriangleDistance = z/(float)triangleRows;
        float widthTriangle = tan( angle*0.5 * M_PI/180.0 ) * zTriangleDistance;
        float currentWidth = 0.0;
        glBegin( GL_TRIANGLES );
        float cosValue = cos( i*angle  * M_PI/180.0 );
        float sinValue = sin( i*angle  * M_PI/180.0 );
        for( int j=0; j<triangleRows; j++ )
            {
            float previousWidth = currentWidth;
            currentWidth = tan( angle*0.5 * M_PI/180.0 ) * zTriangleDistance * (j+1);
            int evenTriangles = 0;
            int oddTriangles = 0;
            for( int k=0; k<floor(currentWidth/widthTriangle*2-1+0.5f); k++ )
                {
                float x1 = -previousWidth;
                float x2 = -currentWidth;
                float x3 = 0.0;
                float z1 = 0.0;
                float z2 = 0.0;
                float z3 = 0.0;
                if( k%2 == 0 )
                    {
                    x1 += evenTriangles*widthTriangle*2;
                    x2 += evenTriangles*widthTriangle*2;
                    x3 = x2+widthTriangle*2;
                    z1 = j*zTriangleDistance;
                    z2 = (j+1)*zTriangleDistance;
                    z3 = (j+1)*zTriangleDistance;
                    float xRot = cosValue * x1 - sinValue * z1;
                    float zRot = sinValue * x1 + cosValue * z1;
                    x1 = xRot;
                    z1 = zRot;
                    xRot = cosValue * x2 - sinValue * z2;
                    zRot = sinValue * x2 + cosValue * z2;
                    x2 = xRot;
                    z2 = zRot;
                    xRot = cosValue * x3 - sinValue * z3;
                    zRot = sinValue * x3 + cosValue * z3;
                    x3 = xRot;
                    z3 = zRot;
                    evenTriangles++;
                    }
                else
                    {
                    x1 += oddTriangles*widthTriangle*2;
                    x2 += (oddTriangles+1)*widthTriangle*2;
                    x3 = x1+widthTriangle*2;
                    z1 = j*zTriangleDistance;
                    z2 = (j+1)*zTriangleDistance;
                    z3 = j*zTriangleDistance;
                    float xRot = cosValue * x1 - sinValue * z1;
                    float zRot = sinValue * x1 + cosValue * z1;
                    x1 = xRot;
                    z1 = zRot;
                    xRot = cosValue * x2 - sinValue * z2;
                    zRot = sinValue * x2 + cosValue * z2;
                    x2 = xRot;
                    z2 = zRot;
                    xRot = cosValue * x3 - sinValue * z3;
                    zRot = sinValue * x3 + cosValue * z3;
                    x3 = xRot;
                    z3 = zRot;
                    oddTriangles++;
                    }
                float texX1 = 0.0;
                float texX2 = 0.0;
                float texX3 = 0.0;
                float texY1 = 0.0;
                float texY2 = 0.0;
                float texY3 = 0.0;
                if( texture )
                    {
                    texX1 = x1/(rect.width())+0.5;
                    texY1 = 0.5 - z1/zTexture * 0.5;
                    texX2 = x2/(rect.width())+0.5;
                    texY2 = 0.5 - z2/zTexture * 0.5;
                    texX3 = x3/(rect.width())+0.5;
                    texY3 = 0.5 - z3/zTexture * 0.5;
                    glTexCoord2f( texX1, texY1 );
                    }
                glVertex3f( x1, 0.0, z1 );
                if( texture )
                    {
                    glTexCoord2f( texX2, texY2 );
                    }
                glVertex3f( x2, 0.0, z2 );
                if( texture )
                    {
                    glTexCoord2f( texX3, texY3 );
                    }
                glVertex3f( x3, 0.0, z3 );
                }
            }
        glEnd();
        }
    }

void CubeEffect::paintCylinderCap()
    {
    QRect rect = effects->clientArea( FullArea, activeScreen, effects->currentDesktop() );
    float cubeAngle = (float)((float)(effects->numberOfDesktops() - 2 )/(float)effects->numberOfDesktops() * 180.0f);

    float radian = (cubeAngle*0.5)*M_PI/180;
    float radius = (rect.width()*0.5)*tan(radian);
    float segment = radius/30.0f;

    bool texture = texturedCaps && effects->numberOfDesktops() > 3 && capTexture;
    for( int i=1; i<=30; i++ )
        {
        glBegin( GL_TRIANGLE_STRIP );
        int steps =  72;
        for( int j=0; j<=steps; j++ )
            {
            float azimuthAngle = (j*(360.0f/steps))*M_PI/180.0f;
            float x1 = segment*(i-1) * sin( azimuthAngle );
            float x2 = segment*i * sin( azimuthAngle );
            float z1 = segment*(i-1) * cos( azimuthAngle );
            float z2 = segment*i * cos( azimuthAngle );
            if( texture )
                glTexCoord2f( (radius+x1)/(radius*2.0f), 1.0f - (z1+radius)/(radius*2.0f) );
            glVertex3f( x1, 0.0, z1 );
            if( texture )
                glTexCoord2f( (radius+x2)/(radius*2.0f), 1.0f - (z2+radius)/(radius*2.0f) );
            glVertex3f( x2, 0.0, z2 );
            }
        glEnd();
        }
    }

void CubeEffect::paintSphereCap()
    {
    QRect rect = effects->clientArea( FullArea, activeScreen, effects->currentDesktop() );
    float cubeAngle = (float)((float)(effects->numberOfDesktops() - 2 )/(float)effects->numberOfDesktops() * 180.0f);
    float zTexture = rect.width()/2*tan(45.0f*M_PI/180.0f);
    float radius = (rect.width()*0.5)/cos(cubeAngle*0.5*M_PI/180.0);
    float angle = acos( (rect.height()*0.5)/radius )*180.0/M_PI;
    angle /= 30;
    bool texture = texturedCaps && effects->numberOfDesktops() > 3 && capTexture;
    glPushMatrix();
    glTranslatef( 0.0, -rect.height()*0.5, 0.0 );
    glBegin( GL_QUADS );
    for( int i=0; i<30; i++ )
        {
        float topAngle = angle*i*M_PI/180.0;
        float bottomAngle = angle*(i+1)*M_PI/180.0;
        float yTop = rect.height() - radius * cos( topAngle );
        yTop -= (yTop-rect.height()*0.5)*capDeformationFactor;
        float yBottom = rect.height() -radius * cos( bottomAngle );
        yBottom -= (yBottom-rect.height()*0.5)*capDeformationFactor;
        for( int j=0; j<36; j++ )
            {
            float x = radius * sin( topAngle ) * sin( (90.0+j*10.0)*M_PI/180.0 );
            float z = radius * sin( topAngle ) * cos( (90.0+j*10.0)*M_PI/180.0 );
            if( texture )
                glTexCoord2f( x/(rect.width())+0.5, 0.5 - z/zTexture * 0.5 );
            glVertex3f( x, yTop, z );
            x = radius * sin( bottomAngle ) * sin( (90.0+j*10.0)*M_PI/180.00 );
            z = radius * sin( bottomAngle ) * cos( (90.0+j*10.0)*M_PI/180.0 );
            if( texture )
                glTexCoord2f( x/(rect.width())+0.5, 0.5 - z/zTexture * 0.5 );
            glVertex3f( x, yBottom, z );
            x = radius * sin( bottomAngle ) * sin( (90.0+(j+1)*10.0)*M_PI/180.0 );
            z = radius * sin( bottomAngle ) * cos( (90.0+(j+1)*10.0)*M_PI/180.0 );
            if( texture )
                glTexCoord2f( x/(rect.width())+0.5, 0.5 - z/zTexture * 0.5 );
            glVertex3f( x, yBottom, z );
            x = radius * sin( topAngle ) * sin( (90.0+(j+1)*10.0)*M_PI/180.0 );
            z = radius * sin( topAngle ) * cos( (90.0+(j+1)*10.0)*M_PI/180.0 );
            if( texture )
                glTexCoord2f( x/(rect.width())+0.5, 0.5 - z/zTexture * 0.5 );
            glVertex3f( x, yTop, z );
            }
        }
    glEnd();
    glPopMatrix();
    }

void CubeEffect::postPaintScreen()
    {
    effects->postPaintScreen();
    if( activated )
        {
        if( start )
            {
            if( timeLine.value() == 1.0 )
                {
                start = false;
                timeLine.setProgress(0.0);
                // more rotations?
                if( !rotations.empty() )
                    {
                    rotationDirection = rotations.dequeue();
                    rotating = true;
                    // change the curve shape if current shape is not easeInOut
                    if( currentShape != TimeLine::EaseInOutCurve )
                        {
                        // more rotations follow -> linear curve
                        if( !rotations.empty() )
                            {
                            currentShape = TimeLine::LinearCurve;
                            }
                        // last rotation step -> easeOut curve
                        else
                            {
                            currentShape = TimeLine::EaseOutCurve;
                            }
                        timeLine.setCurveShape( currentShape );
                        }
                    else
                        {
                        // if there is at least one more rotation, we can change to easeIn
                        if( !rotations.empty() )
                            {
                            currentShape = TimeLine::EaseInCurve;
                            timeLine.setCurveShape( currentShape );
                            }
                        }
                    }
                }
            effects->addRepaintFull();
            return; // schedule_close could have been called, start has to finish first
            }
        if( stop )
            {
            if( timeLine.value() == 1.0 )
                {
                effects->setCurrentDesktop( frontDesktop );
                stop = false;
                timeLine.setProgress(0.0);
                activated = false;
                // set the new desktop
                if( keyboard_grab )
                    effects->ungrabKeyboard();
                keyboard_grab = false;
                effects->destroyInputWindow( input );

                effects->setActiveFullScreenEffect( 0 );

                // delete the GL lists
                glDeleteLists( glList, 3 );
                }
            effects->addRepaintFull();
            }
        if( rotating || verticalRotating )
            {
            if( rotating && timeLine.value() == 1.0 )
                {
                timeLine.setProgress(0.0);
                rotating = false;
                desktopChangedWhileRotating = false;
                manualAngle = 0.0;
                // more rotations?
                if( !rotations.empty() )
                    {
                    rotationDirection = rotations.dequeue();
                    rotating = true;
                    // change the curve shape if current shape is not easeInOut
                    if( currentShape != TimeLine::EaseInOutCurve )
                        {
                        // more rotations follow -> linear curve
                        if( !rotations.empty() )
                            {
                            currentShape = TimeLine::LinearCurve;
                            }
                        // last rotation step -> easeOut curve
                        else
                            {
                            currentShape = TimeLine::EaseOutCurve;
                            }
                        timeLine.setCurveShape( currentShape );
                        }
                    else
                        {
                        // if there is at least one more rotation, we can change to easeIn
                        if( !rotations.empty() )
                            {
                            currentShape = TimeLine::EaseInCurve;
                            timeLine.setCurveShape( currentShape );
                            }
                        }
                    }
                else
                    {
                    // reset curve shape if there are no more rotations
                    if( currentShape != TimeLine::EaseInOutCurve )
                        {
                        currentShape = TimeLine::EaseInOutCurve;
                        timeLine.setCurveShape( currentShape );
                        }
                    }
                }
            if( verticalRotating && verticalTimeLine.value() == 1.0 )
                {
                verticalTimeLine.setProgress(0.0);
                verticalRotating = false;
                manualVerticalAngle = 0.0;
                // more rotations?
                if( !verticalRotations.empty() )
                    {
                    verticalRotationDirection = verticalRotations.dequeue();
                    verticalRotating = true;
                    if( verticalRotationDirection == Upwards )
                        {
                        if( verticalPosition == Normal )
                            verticalPosition = Up;
                        if( verticalPosition == Down )
                            verticalPosition = Normal;
                        }
                    if( verticalRotationDirection == Downwards )
                        {
                        if( verticalPosition == Normal )
                            verticalPosition = Down;
                        if( verticalPosition == Up )
                            verticalPosition = Normal;
                        }
                    }
                }
            effects->addRepaintFull();
            return; // rotation has to end before cube is closed
            }
        if( schedule_close )
            {
            schedule_close = false;
            stop = true;
            effects->addRepaintFull();
            }
        }
    }

void CubeEffect::prePaintWindow( EffectWindow* w, WindowPrePaintData& data, int time )
    {
    if( activated )
        {
        if( cube_painting )
            {
            if( mode == Cylinder || mode == Sphere )
                {
                int leftDesktop = frontDesktop -1;
                int rightDesktop = frontDesktop + 1;
                if( leftDesktop == 0 )
                    leftDesktop = effects->numberOfDesktops();
                if( rightDesktop > effects->numberOfDesktops() )
                    rightDesktop = 1;
                if( painting_desktop == frontDesktop )
                    data.quads = data.quads.makeGrid( 40 );
                else if( painting_desktop == leftDesktop || painting_desktop == rightDesktop )
                    data.quads = data.quads.makeGrid( 100 );
                else
                    data.quads = data.quads.makeGrid( 250 );
                }
            if( w->isOnDesktop( painting_desktop ))
                {
                QRect rect = effects->clientArea( FullArea, activeScreen, painting_desktop );
                if( w->x() < rect.x() )
                    {
                    data.quads = data.quads.splitAtX( -w->x() );
                    }
                if( w->x() + w->width() > rect.x() + rect.width() )
                    {
                    data.quads = data.quads.splitAtX( rect.width() - w->x() );
                    }
                if( w->y() < rect.y() )
                    {
                    data.quads = data.quads.splitAtY( -w->y() );
                    }
                if( w->y() + w->height() > rect.y() + rect.height() )
                    {
                    data.quads = data.quads.splitAtY( rect.height() - w->y() );
                    }
                if( useZOrdering && !w->isDesktop() && !w->isDock() )
                    data.setTransformed();
                w->enablePainting( EffectWindow::PAINT_DISABLED_BY_DESKTOP );
                }
            else
                {
                // check for windows belonging to the previous desktop
                int prev_desktop = painting_desktop -1;
                if( prev_desktop == 0 )
                    prev_desktop = effects->numberOfDesktops();
                if( w->isOnDesktop( prev_desktop ) && mode == Cube )
                    {
                    QRect rect = effects->clientArea( FullArea, activeScreen, prev_desktop);
                    if( w->x()+w->width() > rect.x() + rect.width() )
                        {
                        w->enablePainting( EffectWindow::PAINT_DISABLED_BY_DESKTOP );
                        data.quads = data.quads.splitAtX( rect.width() - w->x() );
                        if( w->y() < rect.y() )
                            {
                            data.quads = data.quads.splitAtY( -w->y() );
                            }
                        if( w->y() + w->height() > rect.y() + rect.height() )
                            {
                            data.quads = data.quads.splitAtY( rect.height() - w->y() );
                            }
                        data.setTransformed();
                        effects->prePaintWindow( w, data, time );
                        return;
                        }
                    }
                // check for windows belonging to the next desktop
                int next_desktop = painting_desktop +1;
                if( next_desktop > effects->numberOfDesktops() )
                    next_desktop = 1;
                if( w->isOnDesktop( next_desktop ) && mode == Cube )
                    {
                    QRect rect = effects->clientArea( FullArea, activeScreen, next_desktop);
                    if( w->x() < rect.x() )
                        {
                        w->enablePainting( EffectWindow::PAINT_DISABLED_BY_DESKTOP );
                        data.quads = data.quads.splitAtX( -w->x() );
                        if( w->y() < rect.y() )
                            {
                            data.quads = data.quads.splitAtY( -w->y() );
                            }
                        if( w->y() + w->height() > rect.y() + rect.height() )
                            {
                            data.quads = data.quads.splitAtY( rect.height() - w->y() );
                            }
                        data.setTransformed();
                        effects->prePaintWindow( w, data, time );
                        return;
                        }
                    }
                w->disablePainting( EffectWindow::PAINT_DISABLED_BY_DESKTOP );
                }
            }
        }
    effects->prePaintWindow( w, data, time );
    }

void CubeEffect::paintWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data )
    {
    if( activated && cube_painting )
        {
        if( mode == Cylinder )
            {
            cylinderShader->bind();
            cylinderShader->setUniform( "xCoord", (float)w->x() );
            cylinderShader->setUniform( "cubeAngle", (effects->numberOfDesktops() - 2 )/(float)effects->numberOfDesktops() * 180.0f );
            cylinderShader->setUniform( "useTexture", 1.0f );
            float factor = 0.0f;
            if( start )
                factor = 1.0f - timeLine.value();
            if( stop )
                factor = timeLine.value();
            cylinderShader->setUniform( "timeLine", factor );
            data.shader = cylinderShader;
            }
        if( mode == Sphere )
            {
            sphereShader->bind();
            sphereShader->setUniform( "xCoord", (float)w->x() );
            sphereShader->setUniform( "yCoord", (float)w->y() );
            sphereShader->setUniform( "cubeAngle", (effects->numberOfDesktops() - 2 )/(float)effects->numberOfDesktops() * 180.0f );
            sphereShader->setUniform( "useTexture", 1.0f );
            float factor = 0.0f;
            if( start )
                factor = 1.0f - timeLine.value();
            if( stop )
                factor = timeLine.value();
            sphereShader->setUniform( "timeLine", factor );
            data.shader = sphereShader;
            }
        if( data.shader )
            {
            int texw = w->width();
            int texh = w->height();
            if( !GLTexture::NPOTTextureSupported() )
                {
                kWarning( 1212 ) << "NPOT textures not supported, wasting some memory" ;
                texw = nearestPowerOfTwo(texw);
                texh = nearestPowerOfTwo(texh);
                }
            data.shader->setTextureWidth( texw );
            data.shader->setTextureHeight( texh );
            }
        //kDebug(1212) << w->caption();
        float opacity = cubeOpacity;
        if( start )
            {
            opacity = 1.0 - (1.0 - opacity)*timeLine.value();
            if( reflectionPainting )
                opacity = 0.5 + ( cubeOpacity - 0.5 )*timeLine.value();
            // fade in windows belonging to different desktops
            if( painting_desktop == effects->currentDesktop() && (!w->isOnDesktop( painting_desktop )) )
                opacity = timeLine.value() * cubeOpacity;
            }
        if( stop )
            {
            opacity = 1.0 - (1.0 - opacity)*( 1.0 - timeLine.value() );
            if( reflectionPainting )
                opacity = 0.5 + ( cubeOpacity - 0.5 )*( 1.0 - timeLine.value() );
            // fade out windows belonging to different desktops
            if( painting_desktop == effects->currentDesktop() && (!w->isOnDesktop( painting_desktop )) )
                opacity = cubeOpacity * (1.0 - timeLine.value());
            }
        // z-Ordering
        if( !w->isDesktop() && !w->isDock() && useZOrdering )
            {
            float zOrdering = (effects->stackingOrder().indexOf( w )+1)*zOrderingFactor;
            if( start )
                zOrdering *= timeLine.value();
            if( stop )
                zOrdering *= (1.0 - timeLine.value());
            data.zTranslate += zOrdering;
            }
        // check for windows belonging to the previous desktop
        int prev_desktop = painting_desktop -1;
        if( prev_desktop == 0 )
            prev_desktop = effects->numberOfDesktops();
        int next_desktop = painting_desktop +1;
        if( next_desktop > effects->numberOfDesktops() )
            next_desktop = 1;
        glPushMatrix();
        if( w->isOnDesktop( prev_desktop ) && ( mask & PAINT_WINDOW_TRANSFORMED ) )
            {
            QRect rect = effects->clientArea( FullArea, activeScreen, prev_desktop);
            WindowQuadList new_quads;
            foreach( const WindowQuad &quad, data.quads )
                {
                if( quad.right() > rect.width() - w->x() )
                    {
                    new_quads.append( quad );
                    }
                }
            data.quads = new_quads;
            RotationData rot = RotationData();
            rot.axis = RotationData::YAxis;
            rot.xRotationPoint = rect.width() - w->x();
            rot.angle = 360.0f / effects->numberOfDesktops();
            data.rotation = &rot;
            float cubeAngle = (float)((float)(effects->numberOfDesktops() - 2 )/(float)effects->numberOfDesktops() * 180.0f);
            float point = rect.width()/2*tan(cubeAngle*0.5f*M_PI/180.0f);
            glTranslatef( rect.width()/2, 0.0, -point );
            glRotatef( -360.0f / effects->numberOfDesktops(), 0.0, 1.0, 0.0 );
            glTranslatef( -rect.width()/2, 0.0, point );
            }
        if( w->isOnDesktop( next_desktop ) && ( mask & PAINT_WINDOW_TRANSFORMED ) )
            {
            QRect rect = effects->clientArea( FullArea, activeScreen, next_desktop);
            WindowQuadList new_quads;
            foreach( const WindowQuad &quad, data.quads )
                {
                if( w->x() + quad.right() <= rect.x() )
                    {
                    new_quads.append( quad );
                    }
                }
            data.quads = new_quads;
            RotationData rot = RotationData();
            rot.axis = RotationData::YAxis;
            rot.xRotationPoint = -w->x();
            rot.angle = -360.0f / effects->numberOfDesktops();
            data.rotation = &rot;
            float cubeAngle = (float)((float)(effects->numberOfDesktops() - 2 )/(float)effects->numberOfDesktops() * 180.0f);
            float point = rect.width()/2*tan(cubeAngle*0.5f*M_PI/180.0f);
            glTranslatef( rect.width()/2, 0.0, -point );
            glRotatef( 360.0f / effects->numberOfDesktops(), 0.0, 1.0, 0.0 );
            glTranslatef( -rect.width()/2, 0.0, point );
            }
        QRect rect = effects->clientArea( FullArea, activeScreen, painting_desktop );

        if( start || stop )
            {
            // we have to change opacity values for fade in/out of windows which are shown on front-desktop
            if( prev_desktop == effects->currentDesktop() && w->x() < rect.x() )
                {
                if( start )
                    opacity = timeLine.value() * cubeOpacity;
                if( stop )
                    opacity = cubeOpacity * (1.0 - timeLine.value());
                }
            if( next_desktop == effects->currentDesktop() && w->x() + w->width() > rect.x() + rect.width() )
                {
                if( start )
                    opacity = timeLine.value() * cubeOpacity;
                if( stop )
                    opacity = cubeOpacity * (1.0 - timeLine.value());
                }
            }
        // HACK set opacity to 0.99 in case of fully opaque to ensure that windows are painted in correct sequence
        // bug #173214
        if( opacity > 0.99f )
            opacity = 0.99f;
        if( opacityDesktopOnly && !w->isDesktop() )
            opacity = 0.99f;
        data.opacity *= opacity;

        if( w->isOnDesktop(painting_desktop) && w->x() < rect.x() )
            {
            WindowQuadList new_quads;
            foreach( const WindowQuad &quad, data.quads )
                {
                if( quad.right() > -w->x() )
                    {
                    new_quads.append( quad );
                    }
                }
            data.quads = new_quads;
            }
        if( w->isOnDesktop(painting_desktop) && w->x() + w->width() > rect.x() + rect.width() )
            {
            WindowQuadList new_quads;
            foreach( const WindowQuad &quad, data.quads )
                {
                if( quad.right() <= rect.width() - w->x() )
                    {
                    new_quads.append( quad );
                    }
                }
            data.quads = new_quads;
            }
        if( w->y() < rect.y() )
            {
            WindowQuadList new_quads;
            foreach( const WindowQuad &quad, data.quads )
                {
                if( quad.bottom() > -w->y() )
                    {
                    new_quads.append( quad );
                    }
                }
            data.quads = new_quads;
            }
        if( w->y() + w->height() > rect.y() + rect.height() )
            {
            WindowQuadList new_quads;
            foreach( const WindowQuad &quad, data.quads )
                {
                if( quad.bottom() <= rect.height() - w->y() )
                    {
                    new_quads.append( quad );
                    }
                }
            data.quads = new_quads;
            }
        }
    effects->paintWindow( w, mask, region, data );
    if( activated && cube_painting )
        {
        if( w->isDesktop() && effects->numScreens() > 1 && paintCaps )
            {
            QRect rect = effects->clientArea( FullArea, activeScreen, painting_desktop );
            QRegion paint = QRegion( rect );
            for( int i=0; i<effects->numScreens(); i++ )
                {
                if( i == w->screen() )
                    continue;
                paint = paint.subtracted( QRegion( effects->clientArea( ScreenArea, i, painting_desktop )));
                }
            paint = paint.subtracted( QRegion( w->geometry()));
            // in case of free area in multiscreen setup fill it with cap color
            if( !paint.isEmpty() )
                {
                if( mode == Cylinder )
                    {
                    cylinderShader->setUniform( "useTexture", -1.0f );
                    cylinderShader->setUniform( "xCoord", 0.0f );
                    }
                if( mode == Sphere )
                    {
                    sphereShader->setUniform( "useTexture", -1.0f );
                    sphereShader->setUniform( "xCoord", 0.0f );
                    sphereShader->setUniform( "yCoord", 0.0f );
                    }
                glColor4f( capColor.redF(), capColor.greenF(), capColor.blueF(), cubeOpacity );
                glPushAttrib( GL_CURRENT_BIT | GL_ENABLE_BIT );
                glEnable( GL_BLEND );
                glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
                glBegin( GL_QUADS );
                float quadSize = 0.0f;
                int leftDesktop = frontDesktop -1;
                int rightDesktop = frontDesktop + 1;
                if( leftDesktop == 0 )
                    leftDesktop = effects->numberOfDesktops();
                if( rightDesktop > effects->numberOfDesktops() )
                    rightDesktop = 1;
                if( painting_desktop == frontDesktop )
                    quadSize = 100.0f;
                else if( painting_desktop == leftDesktop || painting_desktop == rightDesktop )
                    quadSize = 150.0f;
                else
                    quadSize = 250.0f;
                foreach( const QRect &paintRect, paint.rects() )
                    {
                    for( int i=0; i<=(paintRect.height()/quadSize); i++ )
                        {
                        for( int j=0; j<=(paintRect.width()/quadSize); j++ )
                            {
                            glVertex2f( paintRect.x()+j*quadSize, paintRect.y()+i*quadSize );
                            glVertex2f( qMin( paintRect.x()+(j+1)*quadSize, (float)paintRect.x() + paintRect.width() ),
                                paintRect.y()+i*quadSize );
                            glVertex2f( qMin( paintRect.x()+(j+1)*quadSize, (float)paintRect.x() + paintRect.width() ),
                                qMin( paintRect.y() + (i+1)*quadSize, (float)paintRect.y() + paintRect.height() ) );
                            glVertex2f( paintRect.x()+j*quadSize,
                                qMin( paintRect.y() + (i+1)*quadSize, (float)paintRect.y() + paintRect.height() ) );
                            }
                        }
                    }
                glEnd();
                glDisable( GL_BLEND );
                glPopAttrib();
                }
            }
        glPopMatrix();
        if( mode == Cylinder )
            cylinderShader->unbind();
        if( mode == Sphere )
            sphereShader->unbind();
        }
    }

bool CubeEffect::borderActivated( ElectricBorder border )
    {
    if( !borderActivate.contains( border ) &&
        !borderActivateCylinder.contains( border ) &&
        !borderActivateSphere.contains( border ) )
        return false;
    if( effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this )
        return false;
    if( borderActivate.contains( border ) )
        {
        if( !activated || ( activated && mode == Cube ) )
            toggleCube();
        else
            return false;
        }
    if( borderActivateCylinder.contains( border ) )
        {
        if( !activated || ( activated && mode == Cylinder ) )
            toggleCylinder();
        else
            return false;
        }
    if( borderActivateSphere.contains( border ) )
        {
        if( !activated || ( activated && mode == Sphere ) )
            toggleSphere();
        else
            return false;
        }
    return true;
    }

void CubeEffect::toggleCube()
    {
    kDebug(1212) << "toggle cube";
    toggle( Cube );
    }

void CubeEffect::toggleCylinder()
    {
    kDebug(1212) << "toggle cylinder";
    if( !useShaders )
        useShaders = loadShader();
    if( useShaders )
        toggle( Cylinder );
    else
        kError( 1212 ) << "Sorry shaders are not available - cannot activate Cylinder";
    }

void CubeEffect::toggleSphere()
    {
    kDebug(1212) << "toggle sphere";
    if( !useShaders )
        useShaders = loadShader();
    if( useShaders )
        toggle( Sphere );
    else
        kError( 1212 ) << "Sorry shaders are not available - cannot activate Sphere";
    }

void CubeEffect::toggle( CubeMode newMode )
    {
    if( ( effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this ) ||
        effects->numberOfDesktops() < 2 )
        return;
    if( !activated )
        {
        mode = newMode;
        setActive( true );
        }
    else
        {
        setActive( false );
        }
    }

void CubeEffect::grabbedKeyboardEvent( QKeyEvent* e )
    {
    if( stop )
        return;
    // taken from desktopgrid.cpp
    if( e->type() == QEvent::KeyPress )
        {
        // check for global shortcuts
        // HACK: keyboard grab disables the global shortcuts so we have to check for global shortcut (bug 156155)
        if( mode == Cube && cubeShortcut.contains( e->key() + e->modifiers() ) )
            {
            toggleCube();
            return;
            }
        if( mode == Cylinder && cylinderShortcut.contains( e->key() + e->modifiers() ) )
            {
            toggleCylinder();
            return;
            }
        if( mode == Sphere && sphereShortcut.contains( e->key() + e->modifiers() ) )
            {
            toggleSphere();
            return;
            }

        int desktop = -1;
        // switch by F<number> or just <number>
        if( e->key() >= Qt::Key_F1 && e->key() <= Qt::Key_F35 )
            desktop = e->key() - Qt::Key_F1 + 1;
        else if( e->key() >= Qt::Key_0 && e->key() <= Qt::Key_9 )
            desktop = e->key() == Qt::Key_0 ? 10 : e->key() - Qt::Key_0;
        if( desktop != -1 )
            {
            if( desktop <= effects->numberOfDesktops())
                {
                // we have to rotate to chosen desktop
                // and end effect when rotation finished
                rotateToDesktop( desktop );
                setActive( false );
                }
            return;
            }
        switch( e->key())
            { // wrap only on autorepeat
            case Qt::Key_Left:
                // rotate to previous desktop
                kDebug(1212) << "left";
                if( !rotating && !start )
                    {
                    rotating = true;
                    if( invertKeys )
                        rotationDirection = Right;
                    else
                        rotationDirection = Left;
                    }
                else
                    {
                    if( rotations.count() < effects->numberOfDesktops() )
                        {
                        if( invertKeys )
                            rotations.enqueue( Right );
                        else
                            rotations.enqueue( Left );
                        }
                    }
                break;
            case Qt::Key_Right:
                // rotate to next desktop
                kDebug(1212) << "right";
                if( !rotating && !start )
                    {
                    rotating = true;
                    if( invertKeys )
                        rotationDirection = Left;
                    else
                        rotationDirection = Right;
                    }
                else
                    {
                    if( rotations.count() < effects->numberOfDesktops() )
                        {
                        if( invertKeys )
                            rotations.enqueue( Left );
                        else
                            rotations.enqueue( Right );
                        }
                    }
                break;
            case Qt::Key_Up:
                kDebug(1212) << "up";
                if( invertKeys )
                    {
                    if( verticalPosition != Down )
                        {
                        if( !verticalRotating )
                            {
                            verticalRotating = true;
                            verticalRotationDirection = Downwards;
                            if( verticalPosition == Normal )
                                verticalPosition = Down;
                            if( verticalPosition == Up )
                                verticalPosition = Normal;
                            }
                        else
                            {
                            verticalRotations.enqueue( Downwards );
                            }
                        }
                    else if( manualVerticalAngle > 0.0 && !verticalRotating )
                        {
                        // rotate to down position from the manual position
                        verticalRotating = true;
                        verticalRotationDirection = Downwards;
                        verticalPosition = Down;
                        manualVerticalAngle -= 90.0;
                        }
                    }
                else
                    {
                    if( verticalPosition != Up )
                        {
                        if( !verticalRotating )
                            {
                            verticalRotating = true;
                            verticalRotationDirection = Upwards;
                            if( verticalPosition == Normal )
                                verticalPosition = Up;
                            if( verticalPosition == Down )
                                verticalPosition = Normal;
                            }
                        else
                            {
                            verticalRotations.enqueue( Upwards );
                            }
                        }
                    else if( manualVerticalAngle < 0.0 && !verticalRotating )
                        {
                        // rotate to up position from the manual position
                        verticalRotating = true;
                        verticalRotationDirection = Upwards;
                        verticalPosition = Up;
                        manualVerticalAngle += 90.0;
                        }
                    }
                break;
            case Qt::Key_Down:
                kDebug(1212) << "down";
                if( invertKeys )
                    {
                    if( verticalPosition != Up )
                        {
                        if( !verticalRotating )
                            {
                            verticalRotating = true;
                            verticalRotationDirection = Upwards;
                            if( verticalPosition == Normal )
                                verticalPosition = Up;
                            if( verticalPosition == Down )
                                verticalPosition = Normal;
                            }
                        else
                            {
                            verticalRotations.enqueue( Upwards );
                            }
                        }
                    else if( manualVerticalAngle < 0.0 && !verticalRotating )
                        {
                        // rotate to up position from the manual position
                        verticalRotating = true;
                        verticalRotationDirection = Upwards;
                        verticalPosition = Up;
                        manualVerticalAngle += 90.0;
                        }
                    }
                else
                    {
                    if( verticalPosition != Down )
                        {
                        if( !verticalRotating )
                            {
                            verticalRotating = true;
                            verticalRotationDirection = Downwards;
                            if( verticalPosition == Normal )
                                verticalPosition = Down;
                            if( verticalPosition == Up )
                                verticalPosition = Normal;
                            }
                        else
                            {
                            verticalRotations.enqueue( Downwards );
                            }
                        }
                    else if( manualVerticalAngle > 0.0 && !verticalRotating )
                        {
                        // rotate to down position from the manual position
                        verticalRotating = true;
                        verticalRotationDirection = Downwards;
                        verticalPosition = Down;
                        manualVerticalAngle -= 90.0;
                        }
                    }
                break;
            case Qt::Key_Escape:
                rotateToDesktop( effects->currentDesktop() );
                setActive( false );
                return;
            case Qt::Key_Enter:
            case Qt::Key_Return:
            case Qt::Key_Space:
                setActive( false );
                return;
            case Qt::Key_Plus:
                zoom -= 10.0;
                zoom = qMax( -zPosition, zoom );
                recompileList = true;
                break;
            case Qt::Key_Minus:
                zoom += 10.0f;
                recompileList = true;
                break;
            default:
                break;
            }
        effects->addRepaintFull();
        }
    }

void CubeEffect::rotateToDesktop( int desktop )
    {
    int tempFrontDesktop = frontDesktop;
    if( !rotations.empty() )
        {
        // all scheduled rotations will be removed as a speed up
        rotations.clear();
        }
    if( rotating && !desktopChangedWhileRotating )
        {
        // front desktop will change during the actual rotation - this has to be considered
        if( rotationDirection == Left )
            {
            tempFrontDesktop++;
            }
        else if( rotationDirection == Right )
            {
            tempFrontDesktop--;
            }
        if( tempFrontDesktop > effects->numberOfDesktops() )
            tempFrontDesktop = 1;
        else if( tempFrontDesktop == 0 )
            tempFrontDesktop = effects->numberOfDesktops();
        }
    // find the fastest rotation path from tempFrontDesktop to desktop
    int rightRotations = tempFrontDesktop - desktop;
    if( rightRotations < 0 )
        rightRotations += effects->numberOfDesktops();
    int leftRotations = desktop - tempFrontDesktop;
    if( leftRotations < 0 )
        leftRotations += effects->numberOfDesktops();
    if( leftRotations <= rightRotations )
        {
        for( int i=0; i<leftRotations; i++ )
            {
            rotations.enqueue( Left );
            }
        }
    else
        {
        for( int i=0; i<rightRotations; i++ )
            {
            rotations.enqueue( Right );
            }
        }
    if( !start && !rotating && !rotations.empty() )
        {
        rotating = true;
        rotationDirection = rotations.dequeue();
        }
    // change timeline curve if more rotations are following
    if( !rotations.empty() )
        {
        currentShape = TimeLine::EaseInCurve;
        timeLine.setCurveShape( currentShape );
        }
    }

void CubeEffect::setActive( bool active )
    {
    if( active )
        {
        if( !mousePolling )
            {
            effects->startMousePolling();
            mousePolling = true;
            }
        activated = true;
        activeScreen = effects->activeScreen();
        keyboard_grab = effects->grabKeyboard( this );
        input = effects->createInputWindow( this, 0, 0, displayWidth(), displayHeight(),
            Qt::OpenHandCursor );
        frontDesktop = effects->currentDesktop();
        zoom = 0.0;
        zOrderingFactor = zPosition / ( effects->stackingOrder().count() - 1 );
        start = true;
        effects->setActiveFullScreenEffect( this );
        kDebug(1212) << "Cube is activated";
        verticalPosition = Normal;
        verticalRotating = false;
        manualAngle = 0.0;
        manualVerticalAngle = 0.0;
        if( reflection )
            {
            // clip parts above the reflection area
            double eqn[4] = {0.0, 1.0, 0.0, 0.0};
            glPushMatrix();
            QRect rect = effects->clientArea( FullArea, activeScreen, effects->currentDesktop());
            glTranslatef( 0.0, rect.height(), 0.0 );
            glClipPlane( GL_CLIP_PLANE0, eqn );
            glPopMatrix();
            float temporaryCoeff = float( rect.width() ) / tan( M_PI / float( effects->numberOfDesktops() ) );
            mAddedHeightCoeff1 = sqrt( float( rect.height() ) * float( rect.height() ) + temporaryCoeff * temporaryCoeff );
            mAddedHeightCoeff2 = sqrt( float( rect.height() ) * float( rect.height() ) + float( rect.width() ) * float( rect.width() ) + temporaryCoeff * temporaryCoeff );
            }
        // create the needed GL lists
        glList = glGenLists(3);
        capListCreated = false;
        recompileList = true;
        useList = false;
        // create the capList
        if( paintCaps )
            paintCap();
        effects->addRepaintFull();
        }
    else
        {
        if( mousePolling )
            {
            effects->stopMousePolling();
            mousePolling = false;
            }
        schedule_close = true;
        desktopNameFrame.free();
        // we have to add a repaint, to start the deactivating
        effects->addRepaintFull();
        }
    }

void CubeEffect::mouseChanged( const QPoint& pos, const QPoint& oldpos, Qt::MouseButtons buttons, 
            Qt::MouseButtons oldbuttons, Qt::KeyboardModifiers, Qt::KeyboardModifiers )
    {
    if( !activated )
        return;
    if( tabBoxMode )
        return;
    if( stop )
        return;
    QRect rect = effects->clientArea( FullArea, activeScreen, effects->currentDesktop() );
    if( buttons.testFlag( Qt::LeftButton ) )
        {
        bool repaint = false;
        // vertical movement only if there is not a rotation
        if( !verticalRotating )
            {
            // display height corresponds to 180*
            int deltaY = pos.y() - oldpos.y();
            float deltaVerticalDegrees = (float)deltaY/rect.height()*180.0f;
            if( invertMouse )
                manualVerticalAngle += deltaVerticalDegrees;
            else
                manualVerticalAngle -= deltaVerticalDegrees;
            if( deltaVerticalDegrees != 0.0 )
                repaint = true;
            }
        // horizontal movement only if there is not a rotation
        if( !rotating )
            {
            // display width corresponds to sum of angles of the polyhedron
            int deltaX = oldpos.x() - pos.x();
            float deltaDegrees = (float)deltaX/rect.width() * 360.0f;
            if( deltaX == 0 )
                {
                if( pos.x() == 0 )
                    deltaDegrees = 5.0f;
                if( pos.x() == displayWidth() -1 )
                    deltaDegrees = -5.0f;
                }
            if( invertMouse )
                manualAngle += deltaDegrees;
            else
                manualAngle -= deltaDegrees;
            if( deltaDegrees != 0.0 )
                repaint = true;
            }
        if( repaint )
            {
            recompileList = true;
            effects->addRepaintFull();
            }
        }
    if( !oldbuttons.testFlag( Qt::LeftButton ) && buttons.testFlag( Qt::LeftButton ) )
        {
        XDefineCursor( display(), input, QCursor( Qt::ClosedHandCursor).handle() );
        }
    if( oldbuttons.testFlag( Qt::LeftButton) && !buttons.testFlag( Qt::LeftButton ) )
        {
        XDefineCursor( display(), input, QCursor( Qt::OpenHandCursor).handle() );
        if( closeOnMouseRelease )
            setActive( false );
        }
    if( oldbuttons.testFlag( Qt::RightButton) && !buttons.testFlag( Qt::RightButton ) )
        {
        // end effect on right mouse button
        setActive( false );
        }
    }

void CubeEffect::windowInputMouseEvent( Window w, QEvent* e )
    {
    assert( w == input );
    QMouseEvent *mouse = dynamic_cast< QMouseEvent* >( e );
    if( mouse && mouse->type() == QEvent::MouseButtonRelease )
        {
        if( mouse->button() == Qt::XButton1 )
            {
            if( !rotating && !start )
                {
                rotating = true;
                if( invertMouse )
                    rotationDirection = Right;
                else
                    rotationDirection = Left;
                }
            else
                {
                if( rotations.count() < effects->numberOfDesktops() )
                    {
                    if( invertMouse )
                        rotations.enqueue( Right );
                    else
                        rotations.enqueue( Left );
                    }
                }
            effects->addRepaintFull();
            }
        if( mouse->button() == Qt::XButton2 )
            {
            if( !rotating && !start )
                {
                rotating = true;
                if( invertMouse )
                    rotationDirection = Left;
                else
                    rotationDirection = Right;
                }
            else
                {
                if( rotations.count() < effects->numberOfDesktops() )
                    {
                    if( invertMouse )
                        rotations.enqueue( Left );
                    else
                        rotations.enqueue( Right );
                    }
                }
            effects->addRepaintFull();
            }
        }
    }

void CubeEffect::tabBoxAdded( int mode )
    {
    if( activated )
        return;
    if( effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this )
        return;
    if( useForTabBox && mode != TabBoxWindowsMode )
        {
        effects->refTabBox();
        tabBoxMode = true;
        setActive( true );
        rotateToDesktop( effects->currentTabBoxDesktop() );
        }
    }

void CubeEffect::tabBoxUpdated()
    {
    if( activated )
        rotateToDesktop( effects->currentTabBoxDesktop() );
    }

void CubeEffect::tabBoxClosed()
    {
    if( activated )
        {
        effects->unrefTabBox();
        tabBoxMode = false;
        setActive( false );
        }
    }

void CubeEffect::windowAdded( EffectWindow* )
    {
    // when there is a new window we have to paint it once without using GL List
    // to prevent that the window is black
    if( activated )
        useList = false;
    }

void CubeEffect::cubeShortcutChanged( const QKeySequence& seq )
    {
    cubeShortcut = KShortcut( seq );
    }

void CubeEffect::cylinderShortcutChanged( const QKeySequence& seq )
    {
    cylinderShortcut = KShortcut( seq );
    }

void CubeEffect::sphereShortcutChanged( const QKeySequence& seq )
    {
    sphereShortcut = KShortcut( seq );
    }

} // namespace
