/*
**************************************************************************
                                 description
                             --------------------
    copyright            : (C) 2000-2003 by Andreas Zehender
    email                : zehender@kde.org
**************************************************************************

**************************************************************************
*                                                                        *
*  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.                                   *
*                                                                        *
**************************************************************************/

#include "pmglview.h"
#include "pmpart.h"
#include "pmrendermanager.h"
#include "pmcamera.h"
#include "pmscene.h"
#include "pmdatachangecommand.h"
#include "pmdebug.h"
#include "pmdefaults.h"

#include <math.h>
#include <tqpopupmenu.h>
#include <tqpainter.h>
#include <tqapplication.h>
#include <tqcursor.h>
#include <tqcolor.h>
#include <tqglobal.h>
#include <tqlayout.h>
#include <tqlabel.h>
#include <tqcombobox.h>
#include <tqdom.h>

#include <kxmlguifactory.h>
#include <kaction.h>
#include <kconfig.h>
#include <kstaticdeleter.h>
#include <klocale.h>
#include <kiconloader.h>
#include <kdialog.h>

#include <GL/glx.h>

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
#include <X11/Xatom.h>
#include <X11/Xmu/StdCmap.h>

const double c_sizeFactor = log( 2.0 ) / 100.0;
const int c_mouseChangeDelayPixel = 3;
const int c_mouseChangeDelayMs = 300;
const int c_multipleSelectDelayPixel = 3;
const double c_defaultAutoScrollSpeed = 200; // pixels per second
const int c_minAutoScrollUpdateTime = 30; //ms

const double keyMoveSpeed = 40.0;
const double keyScaleFactor = 1.4;

static int glxAttributeList[] = { GLX_LEVEL, 0,
                                  GLX_DOUBLEBUFFER,
                                  GLX_RGBA,
                                  GLX_RED_SIZE, 1,
                                  GLX_GREEN_SIZE, 1,
                                  GLX_BLUE_SIZE, 1,
                                  //GLX_ALPHA_SIZE, 1,
                                  GLX_DEPTH_SIZE, 1,
                                  None };

class PMGLViewStatic
{
public:
   PMGLViewStatic( )
   {
      m_colormap = 0;
      m_context = NULL;
      m_colormapAllocated = false;
      m_display = 0;
      m_visualInfo = 0;
   }
   ~PMGLViewStatic( )
   {
      if( m_colormapAllocated )
         XFreeColormap( m_display, m_colormap );
      if( m_context != NULL )
         glXDestroyContext( m_display, m_context );
      if( m_visualInfo )
         XFree( m_visualInfo );
   }

   Colormap m_colormap;
   GLXContext m_context;
   bool m_colormapAllocated;
   Display* m_display;
   XVisualInfo* m_visualInfo;
};

static PMGLViewStatic* s_pSharedData = 0;
static KStaticDeleter<PMGLViewStatic> s_staticDeleter;
bool PMGLView::s_bDirect = true;


PMGLView::PMGLView( PMPart* part, PMViewType t,
                    TQWidget* parent, const char* name, WFlags f )
      : PMViewBase( parent, name, f | TQt::WWinOwnDC | TQt::WRepaintNoErase )
{
   m_pPart = part;
   m_type = t;
   m_bScaleMode = false;
   m_scaleIntX = 0.0;
   m_scaleIntY = 0.0;
   m_bTranslateMode = false;
   m_bGraphicalChangeMode = false;
   m_bMousePressed = false;
   m_bMidMousePressed = false;
   m_dTransX = 0.0;
   m_dTransY = 0.0;
   m_dScale = 30.0;
   m_bInverseValid = false;
   m_pActiveObject = part->activeObject( );
   m_bMementoCreated = false;
   m_bSelectUnderMouse = false;
   m_bDeselectUnderMouse = false;
   m_bMultipleSelectionMode = false;
   m_bSelectionStarted = false;
   m_bAutoScroll = false;
   m_autoScrollSpeed = c_defaultAutoScrollSpeed;
   m_bAboutToUpdate = false;
   m_projectionUpToDate = false;
   m_contextClickPosition = PMVector( 0.0, 0.0 );
   m_objectActions.setAutoDelete( true );

   m_controlPointsPosition.setAutoDelete( true );
   m_pUnderMouse = 0;

   setCamera( m_pPart->firstCamera( ) );

   initializeGL( );

   setMouseTracking( true );
   setFocusPolicy( TQ_WheelFocus );

   PMRenderManager* rm = PMRenderManager::theManager( );
   rm->viewCreated( );

   setMinimumSize( 50, 50 );

   connect( part, TQT_SIGNAL( refresh( ) ), TQT_SLOT( slotRefresh( ) ) );
   connect( part, TQT_SIGNAL( clear( ) ), TQT_SLOT( slotClear( ) ) );

   connect( this, TQT_SIGNAL( objectChanged( PMObject*, const int, TQObject* ) ),
            part, TQT_SLOT( slotObjectChanged( PMObject*, const int, TQObject* ) ) );
   connect( part, TQT_SIGNAL( objectChanged( PMObject*, const int, TQObject* ) ),
            TQT_SLOT( slotObjectChanged( PMObject*, const int, TQObject* ) ) );

   connect( part, TQT_SIGNAL( activeRenderModeChanged( ) ),
            TQT_SLOT( slotActiveRenderModeChanged( ) ) );

   connect( &m_startTimer, TQT_SIGNAL( timeout( ) ),
            TQT_SLOT( slotMouseChangeTimer( ) ) );
   connect( &m_autoScrollTimer, TQT_SIGNAL( timeout( ) ),
            TQT_SLOT( slotAutoScroll( ) ) );

   connect( rm, TQT_SIGNAL( renderingStarted( PMGLView* ) ),
            TQT_SLOT( slotRenderingStarted( PMGLView* ) ) );
   connect( rm, TQT_SIGNAL( aboutToUpdate( PMGLView* ) ),
            TQT_SLOT( slotAboutToUpdate( PMGLView* ) ) );
   connect( rm, TQT_SIGNAL( renderingFinished( PMGLView* ) ),
            TQT_SLOT( slotRenderingFinished( PMGLView* ) ) );
   connect( rm, TQT_SIGNAL( renderingSettingsChanged( ) ),
            TQT_SLOT( slotRefresh( ) ) );

   connect( this, TQT_SIGNAL( controlPointMessage( const TQString& ) ),
            m_pPart, TQT_SIGNAL( controlPointMessage( const TQString& ) ) );

   updateControlPoints( );
}

void PMGLView::initializeGL( )
{
   Display* display = x11Display( );
   int screen = x11Screen( );
   int i;

   if( !s_pSharedData )
   {
      s_staticDeleter.setObject( s_pSharedData, new PMGLViewStatic );
      s_pSharedData->m_display = display;

      if( PMRenderManager::hasOpenGL( ) )
      {
         // get an appropriate visual
         XVisualInfo* vi = glXChooseVisual( display, screen, glxAttributeList );
         s_pSharedData->m_visualInfo = vi;

         if( vi )
         {
            kdDebug( PMArea ) << "PMGLView: XVisual found\n";

            // create a color map (from qgl_x11.cpp)
            // check if we can use the global colormap
            // should be the default ?
            if( vi->visualid ==
                XVisualIDFromVisual( ( Visual* ) TQPaintDevice::x11AppVisual( ) ) )
            {
               kdDebug( PMArea ) << "PMGLView: Default colormap used\n";
               s_pSharedData->m_colormap = TQPaintDevice::x11AppColormap();
               s_pSharedData->m_colormapAllocated = false;
            }

            if( !s_pSharedData->m_colormap )
            {
               const char* v = glXQueryServerString( display, vi->screen,
                                                     GLX_VERSION );
               bool mesa_gl = false;
               if( v )
                  mesa_gl = strstr( v, "Mesa" ) != 0;
               if( mesa_gl )
               {
                  XStandardColormap* c;
                  int n;
                  Atom hp_cmaps = XInternAtom( display, "_HP_RGB_SMOOTH_MAP_LIST",
                                               TRUE );

                  if( hp_cmaps && ( vi->visual->c_class == TrueColor )
                      && ( vi->depth == 8 ) )
                  {
                     if( XGetRGBColormaps( display, RootWindow( display,vi->screen ),
                                           &c, &n, hp_cmaps ) )
                     {
                        i = 0;
                        while( ( i < n ) && ( s_pSharedData->m_colormap == 0 ) )
                        {
                           if( c[i].visualid == vi->visual->visualid )
                           {
                              s_pSharedData->m_colormap = c[i].colormap;
                              kdDebug( PMArea ) << "PMGLView: HP_RGB scmap used\n";
                           }
                           i++;
                        }
                        XFree( ( char* ) c );
                     }
                  }
               }
            }

#if !defined(Q_OS_SOLARIS)
            if( !s_pSharedData->m_colormap )
            {
               XStandardColormap* c;
               int n;

               if( XmuLookupStandardColormap( display, vi->screen, vi->visualid,
                                              vi->depth, XA_RGB_DEFAULT_MAP,
                                              FALSE, TRUE ) )
               {
                  if( XGetRGBColormaps( display, RootWindow( display, vi->screen ),
                                        &c, &n, XA_RGB_DEFAULT_MAP ) )
                  {
                     i = 0;
                     while( ( i < n ) && ( s_pSharedData->m_colormap == 0 ) )
                     {
                        if( c[i].visualid == vi->visualid )
                        {
                           s_pSharedData->m_colormap = c[i].colormap;
                           kdDebug( PMArea ) << "PMGLView: RGB_DEFAULT scmap used\n";
                        }
                        i++;
                     }
                     XFree( ( char* ) c );
                  }
               }
            }
#endif

            if( !s_pSharedData->m_colormap )
            {
               // create a new colormap otherwise
               kdDebug( PMArea ) << "PMGLView: New colormap created\n";
               s_pSharedData->m_colormap =
                  XCreateColormap( display,
                                   RootWindow( display, vi->screen ),
                                   vi->visual, AllocNone );
               s_pSharedData->m_colormapAllocated = true;
            }

            // create the context
            // this context is shared between all gl views!
            s_pSharedData->m_context = glXCreateContext( display, vi, 0, s_bDirect );

            if( s_pSharedData->m_context != NULL )
               kdDebug( PMArea ) << "PMGLView: glx context created\n";

         }
      }
   }

   if( s_pSharedData->m_context != NULL )
   {
      XVisualInfo* vi = s_pSharedData->m_visualInfo;

      // create the window
      XSetWindowAttributes swa;
      swa.colormap = s_pSharedData->m_colormap;
      swa.border_pixel = 0;
      swa.background_pixel = 0;

      Window p;
      p = RootWindow( display, vi->screen );
      TQWidget* pw = parentWidget( );
      if( pw )
         p = pw->winId( );

      Window w = XCreateWindow( display, p, x( ), y( ), width( ),
                                height( ), 0, vi->depth, InputOutput,
                                vi->visual,
                                CWColormap | CWBorderPixel | CWBackPixel,
                                &swa );

      // tell the windowmanager to use the colormap
      Window* colorMapWindows = 0;
      Window* newWindows = 0;
      int num;
      if( XGetWMColormapWindows( display, tqtopLevelWidget( )->winId( ),
                                 &colorMapWindows, &num ) )
      {
         // create a new list and append the new window
         bool replaced = false;
         newWindows = new Window[num+1];

         for( i = 0; i < num; i++ )
         {
            newWindows[i] = colorMapWindows[i];
            if( newWindows[i] == winId( ) ) // old window
            {
               newWindows[i] = w;
               replaced = true;
            }
         }
         if( !replaced )
         {
            newWindows[num] = w;
            num++;
         }
      }
      else
      {
         num = 1;
         newWindows = new Window[1];
         newWindows[0] = w;
      }
      // tell TQt to use this window
      create( w );

      XSetWMColormapWindows( display, tqtopLevelWidget( )->winId( ),
                             newWindows, num );
      delete[] newWindows;

      XFlush( x11Display( ) );
   }
   else
   {
      TQVBoxLayout* topLayout = new TQVBoxLayout( this );
      TQLabel* label = new TQLabel( i18n( "No OpenGL support" ), this );
      label->tqsetAlignment( TQt::AlignCenter );
      topLayout->addWidget( label );
   }

   //setProjection( );
}

PMGLView::~PMGLView( )
{
   PMRenderManager* rm = PMRenderManager::theManager( );
   rm->removeView( this );
   rm->viewDeleted( );
   emit destroyed( this );
}

bool PMGLView::isValid( ) const
{
   if( s_pSharedData && ( s_pSharedData->m_context != NULL ) )
      return true;
   return false;
}

void PMGLView::makeCurrent( )
{
   if( isValid( ) )
      glXMakeCurrent( x11Display( ), winId( ), s_pSharedData->m_context );
}

void PMGLView::swapBuffers( )
{
   if( isValid( ) )
      glXSwapBuffers( x11Display( ), winId( ) );
}

void PMGLView::setScale( double scale )
{
   if( m_dScale > 0 )
   {
      m_dScale = scale;
      invalidateProjection( );
   }
   else
      kdError( PMArea ) << "Scale <= 0 in PMGLView::setScale\n";
}

void PMGLView::setTranslationX( double d )
{
   m_dTransX = d;
   invalidateProjection( );
}

void PMGLView::setTranslationY( double d )
{
   m_dTransY = d;
   invalidateProjection( );
}

void PMGLView::resizeEvent( TQResizeEvent* )
{
   invalidateProjection( );
}

void PMGLView::paintEvent( TQPaintEvent* )
{
   tqrepaint( );
}

void PMGLView::invalidateProjection( bool graphicalChange /*= true*/ )
{
   m_viewTransformation = PMMatrix::identity( );


   if( m_type != PMViewCamera )
   {
      m_viewTransformation = m_viewTransformation * PMMatrix::scale( m_dScale, m_dScale, m_dScale );
      m_viewTransformation = m_viewTransformation * PMMatrix::translation( m_dTransX, m_dTransY, 0 );

      switch( m_type )
      {
         case PMViewPosZ:
            m_normal = PMVector( 0.0, 0.0, 1.0 );
            break;
         case PMViewNegZ:
            m_viewTransformation = m_viewTransformation * PMMatrix::rotation( 0.0, M_PI, 0.0 );
            m_normal = PMVector( 0.0, 0.0, -1.0 );
            break;
         case PMViewNegY:
            m_viewTransformation = m_viewTransformation * PMMatrix::rotation( M_PI_2, 0.0, 0.0 );
            m_normal = PMVector( 0.0, -1.0, 0.0 );
            break;
         case PMViewPosY:
            m_normal = PMVector( 0.0, 1.0, 0.0 );
            m_viewTransformation = m_viewTransformation * PMMatrix::rotation( -M_PI_2, 0.0, 0.0 );
            break;
         case PMViewPosX:
            m_viewTransformation = m_viewTransformation * PMMatrix::rotation( 0.0, M_PI_2, 0.0 );
            m_normal = PMVector( 1.0, 0.0, 0.0 );
            break;
         case PMViewNegX:
            m_viewTransformation = m_viewTransformation * PMMatrix::rotation( 0.0, -M_PI_2, 0.0 );
            m_normal = PMVector( -1.0, 0.0, 0.0 );
            break;
         default:
            break;
      }

      m_viewTransformation = m_viewTransformation * PMMatrix::scale( 1.0, 1.0, -1.0 );

      if( m_controlPoints.count( ) > 0 )
         recalculateTransformations( );
      recalculateControlPointPosition( );
   }
   m_projectionUpToDate = false;
   tqrepaint( graphicalChange );
}

void PMGLView::enableTranslateMode( bool yes )
{
   if( m_type != PMViewCamera )
   {
      m_bScaleMode = false;
      m_bTranslateMode = yes;
      setCursor( yes ? crossCursor : arrowCursor );
   }
}

void PMGLView::enableScaleMode( bool yes )
{
   if( m_type != PMViewCamera )
   {
      m_bScaleMode = yes;
      m_bTranslateMode = false;
      setCursor( yes ? crossCursor : arrowCursor );
   }
}

void PMGLView::mousePressEvent( TQMouseEvent* e )
{
   if( m_bScaleMode || m_bTranslateMode )
   {
      if( ( e->button( ) & Qt::LeftButton ) && ( e->state( ) == 0 ) )
      {
         m_bMousePressed = true;
         m_mousePos = e->pos( );
         m_scaleIntX = screenToInternalX( e->x( ) );
         m_scaleIntY = screenToInternalY( e->y( ) );
      }
   }
   else if( m_type != PMViewCamera )
   {
      if( ( e->button( ) & Qt::LeftButton ) && m_bInverseValid
          && m_pActiveObject )
      {
         if( m_pUnderMouse )
         {
            // check the control point selection
            if( m_pActiveObject->multipleSelectControlPoints( ) )
            {
               if( m_pUnderMouse->selected( ) )
               {
                  if( e->state( ) & ControlButton )
                     m_bDeselectUnderMouse = true;
                  else
                     m_bSelectUnderMouse = true;
               }
               else
               {
                  if( e->state( ) & ControlButton )
                     selectControlPoint( m_pUnderMouse,
                                         !m_pUnderMouse->selected( ), false );
                  else
                     selectControlPoint( m_pUnderMouse, true );
               }
            }
            else
               selectControlPoint( m_pUnderMouse, true );

            // start the graphical change
            if( !m_bGraphicalChangeMode )
            {
               m_bGraphicalChangeMode = true;
               m_bMementoCreated = false;
               m_changeStartPos = e->pos( );
               m_changeStartTime = TQTime::currentTime( );
               m_currentMousePos = m_changeStartPos;
               m_startTimer.start( c_mouseChangeDelayMs, true );
            }
         }
         else
         {
            if( m_pActiveObject->multipleSelectControlPoints( ) )
            {
               // multiple selection mode
               // start only when the view is rendered
               if( !PMRenderManager::theManager( )->containsTask( this ) )
               {
                  m_bMultipleSelectionMode = true;
                  m_bSelectionStarted = false;
                  m_selectionStart = e->pos( );
                  m_selectionEnd = m_selectionStart;
               }
            }
            else
               selectControlPoint( 0, false );
         }
      }
   }


   if( !( m_bGraphicalChangeMode || m_bMousePressed ) )
   {
      if( ( e->button( ) == Qt::RightButton ) && ( e->state( ) == 0 ) )
      {
         m_contextClickPosition = PMVector( screenToInternalX( e->x( ) ),
                                            screenToInternalY( e->y( ) ) );

         if( m_pUnderMouse )
         {
            // check the control point selection
            if( m_pActiveObject->multipleSelectControlPoints( ) )
            {
               if( !m_pUnderMouse->selected( ) )
                  selectControlPoint( m_pUnderMouse, true );
            }
            else
               selectControlPoint( m_pUnderMouse, true );
         }

         contextMenu( );
      }
   }

   if( e->button( ) == Qt::MidButton )
   {
      m_bMidMousePressed = true;
      m_mousePos = e->pos( );
   }
}

void PMGLView::mouseReleaseEvent( TQMouseEvent* e )
{
   m_bMousePressed = false;
   if( m_bGraphicalChangeMode )
   {
      m_startTimer.stop( );

      if( m_bMementoCreated )
      {
         PMDataChangeCommand* cmd;
         cmd = new PMDataChangeCommand( m_pActiveObject->takeMemento( ) );
         m_pPart->executeCommand( cmd );

         checkUnderMouse( ( int ) screenToInternalX( e->x( ) ),
                          ( int ) screenToInternalY( e->y( ) ) );
      }
      else
      {
         if( m_pUnderMouse )
         {
            if( m_bSelectUnderMouse )
               selectControlPoint( m_pUnderMouse, true );
            else if( m_bDeselectUnderMouse )
               selectControlPoint( m_pUnderMouse, false, false );
         }
      }
      m_bGraphicalChangeMode = false;
   }
   else if( m_bMultipleSelectionMode )
   {
      if( m_bSelectionStarted )
      {
         int sx, sy, ex, ey, w, h;
         double isx, isy, iex, iey;
         TQPtrListIterator<PMVector> pit( m_controlPointsPosition );
         PMControlPointListIterator cit( m_controlPoints );
         PMVector p;

         calculateSelectionBox( sx, sy, ex, ey, w, h );
         isx = screenToInternalX( sx );
         iex = screenToInternalX( ex );
         isy = screenToInternalY( ey );
         iey = screenToInternalY( sy );
         restoreSelectionBox( );

         while( pit.current( ) && cit.current( ) )
         {
            p = *( pit.current( ) );

            if( ( isx <= p[0] ) && ( iex >= p[0] )
                && ( isy <= p[1] ) && ( iey >= p[1] ) )
               selectControlPoint( cit.current( ), true, false );
            else if( !( e->state( ) & ControlButton ) )
               selectControlPoint( cit.current( ), false, false );

            ++cit;
            ++pit;
         }
      }
      else
         selectControlPoint( 0, false );

      m_bMultipleSelectionMode = false;
   }

   if( m_bAutoScroll )
   {
      m_bAutoScroll = false;
      m_autoScrollTimer.stop( );
   }

   if( e->button( ) & Qt::MidButton )
      m_bMidMousePressed = false;

   m_bSelectUnderMouse = false;
   m_bDeselectUnderMouse = false;
}

void PMGLView::mouseMoveEvent( TQMouseEvent* e )
{
   if( m_bMousePressed )
   {
      if( m_bScaleMode )
      {
         int d = e->x( ) - m_mousePos.x( );
         if( d != 0 )
         {
            double s = exp( d * c_sizeFactor );
            double c = 1.0 / ( m_dScale * s ) - 1.0 / m_dScale;
            m_dTransX += m_scaleIntX * c;
            m_dTransY += m_scaleIntY * c;
            m_dScale *= s;
            invalidateProjection( );
         }
      }
      else if( m_bTranslateMode )
      {
         m_dTransX += ( e->x( ) - m_mousePos.x( ) ) / m_dScale;
         m_dTransY -= ( e->y( ) - m_mousePos.y( ) ) / m_dScale;
         invalidateProjection( );
      }
      m_mousePos = e->pos( );
   }
   else if( m_bMidMousePressed )
   {
      m_dTransX += ( e->x( ) - m_mousePos.x( ) ) / m_dScale;
      m_dTransY -= ( e->y( ) - m_mousePos.y( ) ) / m_dScale;
      invalidateProjection( );

      m_mousePos = e->pos( );
   }
   else if( m_bGraphicalChangeMode )
   {
      m_currentMousePos = e->pos( );
      if( !m_bMementoCreated )
      {
         TQPoint movement = e->pos( ) - m_changeStartPos;
         if( ( m_changeStartTime.msecsTo( TQTime::currentTime( ) ) > c_mouseChangeDelayMs )
            || ( movement.manhattanLength( ) > c_mouseChangeDelayPixel ) )
         {
            m_startTimer.stop( );
            startChange( m_changeStartPos );
         }
      }

      if( m_bMementoCreated )
         graphicalChange( e->pos( ) );
   }
   else if( m_bMultipleSelectionMode )
   {
      if( !m_bSelectionStarted )
      {
         m_selectionEnd = e->pos( );
         startSelection( );
      }
      else
      {
         restoreSelectionBox( );
         m_selectionEnd = e->pos( );
         saveSelectionBox( );
         paintSelectionBox( );
      }
   }
   else if( !( m_bScaleMode || m_bTranslateMode ) )
   {
      checkUnderMouse( ( int ) screenToInternalX( e->x( ) ),
                       ( int ) screenToInternalY( e->y( ) ) );
   }

   if( m_bMultipleSelectionMode || m_bGraphicalChangeMode )
   {
      bool as = m_bAutoScroll;

      if( e->x( ) < 0 )
         m_autoScrollDirectionX = 1;
      else if( e->x( ) >= width( ) )
         m_autoScrollDirectionX = -1;
      else
         m_autoScrollDirectionX = 0;

      if( e->y( ) < 0 )
         m_autoScrollDirectionY = 1;
      else if( e->y( ) >= height( ) )
         m_autoScrollDirectionY = -1;
      else
         m_autoScrollDirectionY = 0;

      if( ( m_autoScrollDirectionX != 0 ) || ( m_autoScrollDirectionY != 0 ) )
         m_bAutoScroll = true;
      else
         m_bAutoScroll = false;

      if( m_bAutoScroll && !as )
      {
         m_lastAutoScrollUpdate = TQTime::currentTime( );
         m_autoScrollTimer.start( c_minAutoScrollUpdateTime, true );
      }
      if( !m_bAutoScroll && as )
         m_autoScrollTimer.stop( );
   }
}

void PMGLView::wheelEvent( TQWheelEvent* e )
{
   if( m_type != PMViewCamera )
   {
      double s = exp( e->delta( ) / 4 * c_sizeFactor );
      double deltaX = screenToInternalX( e->x( ) );
      double deltaY = screenToInternalY( e->y( ) );
      double c = 1.0 / ( m_dScale * s ) - 1.0 / m_dScale;
      m_dTransX += deltaX * c;
      m_dTransY += deltaY * c;
      m_dScale *= s;
      invalidateProjection( );
   }
}

void PMGLView::keyPressEvent( TQKeyEvent* e )
{
   bool accept = true;

   if( e->state( ) == 0 )
   {
      if( m_type != PMViewCamera )
      {
         if( m_dScale > 0 )
         {
            switch( e->key( ) )
            {
               case Key_Left:
                  m_dTransX -= keyMoveSpeed / m_dScale;
                  break;
               case Key_Right:
                  m_dTransX += keyMoveSpeed / m_dScale;
                  break;
               case Key_Up:
                  m_dTransY += keyMoveSpeed / m_dScale;
                  break;
               case Key_Down:
                  m_dTransY -= keyMoveSpeed / m_dScale;
                  break;
               default:
                  accept = false;
            }
         }
         else
            kdError( PMArea ) << "scale <= 0 in PMGLView::keyPressEvent\n";
      }
   }
   else if( e->state( ) == ControlButton )
   {
      if( m_type != PMViewCamera )
      {
         switch( e->key( ) )
         {
            case Key_Left:
            case Key_Down:
               m_dScale /= keyScaleFactor;
               break;
            case Key_Right:
            case Key_Up:
               m_dScale *= keyScaleFactor;
               break;
            default:
               accept = false;
         }
      }
   }
   else
      accept = false;

   if( accept )
      invalidateProjection( );
   else
      e->ignore( );
}

void PMGLView::slotAutoScroll( )
{
   if( m_bAutoScroll )
   {
      TQTime now = TQTime::currentTime( );
      int msecs = m_lastAutoScrollUpdate.msecsTo( now );
      int pixels = ( int ) ( m_autoScrollSpeed * msecs / 1000.0 );

      if( pixels < 1 )
         pixels = 1;
      if( pixels > ( width( ) * 3 / 4 ) )
         pixels = width( ) * 3 / 4;
      if( pixels > ( height( ) * 3 / 4 ) )
         pixels = height( ) * 3 / 4;

      if( m_bGraphicalChangeMode && !m_bMementoCreated )
         startChange( m_changeStartPos );
      if( m_bMultipleSelectionMode )
         restoreSelectionBox( );

      m_dTransX += pixels * m_autoScrollDirectionX / m_dScale;
      m_dTransY -= pixels * m_autoScrollDirectionY / m_dScale;
      invalidateProjection( );

   if( m_bGraphicalChangeMode )
      if( m_bMultipleSelectionMode )
      {
         m_selectionStart += TQPoint( pixels * m_autoScrollDirectionX,
                                     pixels * m_autoScrollDirectionY );

         saveSelectionBox( );
         paintSelectionBox( );
      }

      if( m_bGraphicalChangeMode )
         graphicalChange( mapFromGlobal( TQCursor::pos( ) ) );
      else
         tqrepaint( );

      m_lastAutoScrollUpdate = now;
   }
}

void PMGLView::slotMouseChangeTimer( )
{
   if( !m_bMementoCreated )
   {
      if( m_currentMousePos != m_changeStartPos )
      {
         startChange( m_changeStartPos );
         graphicalChange( m_currentMousePos );
      }
   }
}

void PMGLView::startChange( const TQPoint& mousePos )
{
   m_pActiveObject->createMemento( );
   m_bMementoCreated = true;

   PMVector p = mousePosition( m_pUnderMouse, mousePos.x( ), mousePos.y( ) );
   p.transform( m_inversePointsTransformation );

   if( m_pActiveObject->multipleSelectControlPoints( ) )
   {
      PMControlPointListIterator it( m_controlPoints );
      for( ; it.current( ); ++it )
         if( it.current( )->selected( ) )
            it.current( )->startChange( p, m_normal );
   }
   else
      m_pUnderMouse->startChange( p, m_normal );
}

void PMGLView::graphicalChange( const TQPoint& mousePos )
{
   PMVector p = mousePosition( m_pUnderMouse, mousePos.x( ), mousePos.y( ) );
   p.transform( m_inversePointsTransformation );
   if( m_pActiveObject->multipleSelectControlPoints( ) )
   {
      PMControlPointListIterator it( m_controlPoints );
      for( ; it.current( ); ++it )
         if( it.current( )->selected( ) )
            it.current( )->change( p );
   }
   else
      m_pUnderMouse->change( p );

   PMObjectList changedObjects;
   m_pActiveObject->controlPointsChangedList( m_controlPoints, changedObjects );

   if( changedObjects.isEmpty( ) )
      emit objectChanged( m_pActiveObject, PMCGraphicalChange, TQT_TQOBJECT(this) );
   else
   {
      PMObjectListIterator it( changedObjects );
      for( ; it.current( ); ++it )
         emit objectChanged( it.current( ), PMCGraphicalChange, TQT_TQOBJECT(this) );
   }
}

void PMGLView::checkUnderMouse( int x, int y )
{
   // is cursor over a control point?
   // double z;
   PMVector* v;
   PMControlPoint* p;
   PMControlPoint* old = m_pUnderMouse;

   if( m_bInverseValid && ( m_type != PMViewCamera ) )
   {
      m_pUnderMouse = 0;
//      z = -1e10;

      v = m_controlPointsPosition.first( );
      p = m_controlPoints.first( );

      while( p )
      {
         if( p->displayType( ) == PMControlPoint::CPCross )
         {
            if( !m_pUnderMouse )
               m_pUnderMouse = p;
         }
         else
         {
            if( ( fabs( x - (*v)[0] ) < ( controlPointSize / 2.0 + 0.1 ) )
                && ( fabs( y - (*v)[1] ) < ( controlPointSize / 2.0 + 0.1 ) ) )
            {
               if( m_pUnderMouse )
               {
                  if( p->selected( ) && !m_pUnderMouse->selected( ) )
                     m_pUnderMouse = p;
               }
               else
                  m_pUnderMouse = p;
            }
         }

         p = m_controlPoints.next( );
         v = m_controlPointsPosition.next( );
      }
   }
   else
      m_pUnderMouse = 0;

   setCursor( m_pUnderMouse ? crossCursor : arrowCursor );
   if( m_pUnderMouse != old )
   {
      if( m_pUnderMouse )
         emit controlPointMessage( m_pUnderMouse->description( ) );
      else
         emit controlPointMessage( "" );
   }
}

void PMGLView::updateControlPoints( )
{
   m_controlPoints.clear( );
   m_controlPoints = m_pPart->activeControlPoints( );

   if( ( m_controlPoints.count( ) > 0 ) && m_pActiveObject )
   {
      m_objectsTransformation = m_pActiveObject->transformedWith( );
      recalculateTransformations( );
   }

   if( m_bGraphicalChangeMode )
      m_bGraphicalChangeMode = false;

   recalculateControlPointPosition( );
}

void PMGLView::recalculateControlPointPosition( )
{
   PMControlPointListIterator it( m_controlPoints );
   m_controlPointsPosition.clear( );
   PMVector* v;

   for( ; it.current( ); ++it )
   {
      v = new PMVector( m_controlPointsTransformation * it.current( )->position( ) );
      m_controlPointsPosition.append( v );
   }
   if( !m_bGraphicalChangeMode )
   {
      m_pUnderMouse = 0;
      emit controlPointMessage( "" );
   }
}

PMVector PMGLView::mousePosition( PMControlPoint* cp, int x, int y )
{
   PMVector result;
   int index;
   PMVector* p;

   result[0] = screenToInternalX( x );
   result[1] = screenToInternalY( y );
   if( cp )
   {
      index = m_controlPoints.findRef( cp );
      if( index >= 0 )
      {
         p = m_controlPointsPosition.at( ( uint ) index );
         if( p )
            result[2] = p->z( );
      }
   }
   return result;
}

void PMGLView::recalculateTransformations( )
{
   int r, c;

   m_controlPointsTransformation = m_viewTransformation * m_objectsTransformation;

   if( m_controlPointsTransformation.canBuildInverse( ) )
   {
      m_inversePointsTransformation = m_controlPointsTransformation.inverse( );

      for( c = 0; c < 4; c++ )
         for( r = 0; r < 4; r++ )
            if( fabs( m_inversePointsTransformation[c][r] ) < 1e-8 )
               m_inversePointsTransformation[c][r] = 0.0;

      m_bInverseValid = true;
   }
   else
      m_bInverseValid = false;
}

void PMGLView::setType( PMViewType t )
{
   if( m_type != t )
      m_viewTransformation = PMMatrix::identity( );
   m_type = t;
   invalidateProjection( );

   emit viewTypeChanged( viewTypeAsString( t ) );
}

void PMGLView::setCamera( PMCamera* c )
{
   m_pCamera = c;
   invalidateProjection( );
}

void PMGLView::slotRefresh( )
{
   if( m_type == PMViewCamera )
      if( !m_pCamera )
         setCamera( m_pPart->firstCamera( ) );

   tqrepaint( );
}

void PMGLView::slotClear( )
{
   m_controlPointsPosition.clear( );
   m_controlPoints.clear( );
   m_pUnderMouse = 0;
   m_pCamera = 0;
   m_pActiveObject = 0;

   slotStopRendering( );
}

void PMGLView::slotActiveRenderModeChanged( )
{
   if( ( m_type == PMViewCamera ) && m_pCamera )
      invalidateProjection( );
}

void PMGLView::slotStopRendering( )
{
   PMRenderManager* rm = PMRenderManager::theManager( );
   rm->removeView( this );
}

void PMGLView::slotObjectChanged( PMObject* obj, const int mode,
                                  TQObject* sender )
{
   bool redraw = false;

   if( mode & PMCNewSelection )
   {
      if( obj )
      {
         if( obj != m_pActiveObject )
         {
            m_pActiveObject = obj;
            redraw = true;
         }
      }
      else
      {
         m_pActiveObject = 0;
         redraw = true;
      }
   }
   if( mode & ( PMCSelected | PMCDeselected ) )
   {
      m_pActiveObject = 0;
      redraw = true;
   }
   if( mode & ( PMCViewStructure | PMCGraphicalChange ) )
   {
      if( m_type == PMGLView::PMViewCamera )
      {
         if( obj->type( ) == "Camera" )
            if( m_pCamera == ( PMCamera* ) obj )
               invalidateProjection( );

         if( obj->parent( ) )
            if( obj->parent( )->type( ) == "Camera" )
               if( m_pCamera == ( PMCamera* ) obj->parent( ) )
                  if( obj->hasTransformationMatrix( ) )
                     invalidateProjection( );
      }

      redraw = true;
   }
   if( mode & PMCNewControlPoints )
   {
      updateControlPoints( );
      m_pActiveObject = m_pPart->activeObject( );
      redraw = true;
   }
   if( mode & PMCControlPointSelection )
   {
      redraw = true;
   }
   if( mode & PMCDescription )
   {
      if( m_type == PMGLView::PMViewCamera && obj && obj == m_pCamera )
         redraw = true;
   }
   if( mode & PMCAdd )
   {
      if( m_type == PMGLView::PMViewCamera )
      {
         if( obj->type( ) == "Camera" )
            if( !m_pCamera )
               setCamera( ( PMCamera* ) obj );
         if( obj->parent( ) )
            if( obj->parent( )->type( ) == "Camera" )
               if( m_pCamera == ( PMCamera* ) obj->parent( ) )
                  if( obj->hasTransformationMatrix( ) )
                     invalidateProjection( );
      }
      redraw = true;
   }

   if( mode & PMCRemove )
   {
      if( obj->type( ) == "Camera" )
         if( m_pCamera == ( PMCamera* ) obj )
            setCamera( 0 );

      if( m_type == PMGLView::PMViewCamera )
         if( obj->parent( ) )
            if( obj->parent( )->type( ) == "Camera" )
               if( m_pCamera == ( PMCamera* ) obj->parent( ) )
                  if( obj->hasTransformationMatrix( ) )
                     invalidateProjection( );

      redraw = true;
   }

   if( mode & PMCChildren )
      redraw = true;

   if( redraw )
      tqrepaint( TQT_BASE_OBJECT(sender) == TQT_BASE_OBJECT(this) );
}

void PMGLView::tqrepaint( bool graphicalChange )
{
   if( isValid( ) )
   {
      PMObject* obj = m_pActiveObject;

      if( obj )
         obj = topLevelRenderingObject( obj );
      else
      {
         const PMObjectList& selected = m_pPart->selectedObjects( );
         PMObjectListIterator it( selected );
         if( it.current( ) )
            obj = topLevelRenderingObject( it.current( ) );

         if( obj && ( obj->type( ) != "Scene" ) )
            for( ++it; it.current( ) && obj; ++it )
               if( topLevelRenderingObject( it.current( ) ) != obj )
                  obj = 0;
      }

      if( !obj )
         obj = m_pPart->scene( );

      if( obj )
      {
         double aspectRatio = 1.0;
         PMRenderMode* mode = m_pPart->scene( )->renderModes( )->current( );
         if( mode )
            if( mode->height( ) != 0 )
               aspectRatio = ( double ) mode->width( )
                             / ( double ) mode->height( );

         PMRenderManager* rm = PMRenderManager::theManager( );
         rm->addView( this, m_pActiveObject, obj,
                      &m_controlPoints, aspectRatio,
                      m_pPart->scene( )->visibilityLevel( ), graphicalChange );
      }
   }
}

PMObject* PMGLView::topLevelRenderingObject( PMObject* o ) const
{
   PMObject* obj = o;
   bool stop = false;

   if( obj )
   {
      do
      {
         if( !obj )
            stop = true;
         else if( obj->isA( "Scene" ) || obj->isA( "Declare" ) )
            stop = true;
         else
            obj = obj->parent( );
      }
      while( !stop );
   }
   else
      obj = m_pPart->scene( );

   return obj;
}

void PMGLView::selectControlPoint( PMControlPoint* cp, bool select, bool deselectOthers )
{
   bool selectionChanged = false;

   if( cp )
   {
      if( deselectOthers )
      {
         PMControlPointListIterator it( m_controlPoints );
         for( ; it.current( ); ++it )
         {
            bool s;
            if( it.current( ) == cp )
               s = select;
            else
               s = false;

            if( s != it.current( )->selected( ) )
            {
               selectionChanged = true;
               it.current( )->setSelected( s );
            }
         }
      }
      else
      {
         if( select != cp->selected( ) )
         {
            selectionChanged = true;
            cp->setSelected( select );
         }
      }
   }
   else
   {
      PMControlPointListIterator it( m_controlPoints );
      for( ; it.current( ); ++it )
      {
         if( select != it.current( )->selected( ) )
         {
            selectionChanged = true;
            it.current( )->setSelected( select );
         }
      }
   }

   if( selectionChanged )
      emit objectChanged( m_pActiveObject, PMCControlPointSelection, TQT_TQOBJECT(this) );
}

void PMGLView::startSelection( )
{
   if( !m_bSelectionStarted )
   {
      saveSelectionBox( );
      paintSelectionBox( );

      m_bSelectionStarted = true;
   }
}

void PMGLView::restoreSelectionBox( )
{
   if( !m_bAboutToUpdate )
   {
      int sx, sy, ex, ey, w, h;
      calculateSelectionBox( sx, sy, ex, ey, w, h );

      if( !m_selectionPixmap[0].isNull( ) )
         bitBlt( this, sx, sy, &( m_selectionPixmap[0] ), 0, 0, w, 1, CopyROP );
      if( !m_selectionPixmap[1].isNull( ) )
         bitBlt( this, sx, ey, &( m_selectionPixmap[1] ), 0, 0, w, 1, CopyROP );
      if( !m_selectionPixmap[2].isNull( ) )
         bitBlt( this, sx, sy+1, &( m_selectionPixmap[2] ), 0, 0, 1, h-2, CopyROP );
      if( !m_selectionPixmap[3].isNull( ) )
         bitBlt( this, ex, sy+1, &( m_selectionPixmap[3] ), 0, 0, 1, h-2, CopyROP );
   }
}

void PMGLView::saveSelectionBox( )
{
   if( !m_bAboutToUpdate )
   {
      int sx, sy, ex, ey, w, h;
      calculateSelectionBox( sx, sy, ex, ey, w, h );

      m_selectionPixmap[0].resize( w, 1 );
      if( !m_selectionPixmap[0].isNull( ) )
         bitBlt( &( m_selectionPixmap[0] ), 0, 0, this, sx, sy, w, 1, CopyROP );
      m_selectionPixmap[1].resize( w, 1 );
      if( !m_selectionPixmap[1].isNull( ) )
         bitBlt( &( m_selectionPixmap[1] ), 0, 0, this, sx, ey, w, 1, CopyROP );

      m_selectionPixmap[2].resize( 1, h-2 );
      if( !m_selectionPixmap[2].isNull( ) )
         bitBlt( &( m_selectionPixmap[2] ), 0, 0, this, sx, sy+1, 1, h-2, CopyROP );
      m_selectionPixmap[3].resize( 1, h-2 );
      if( !m_selectionPixmap[3].isNull( ) )
         bitBlt( &( m_selectionPixmap[3] ), 0, 0, this, ex, sy+1, 1, h-2, CopyROP );
   }
}

void PMGLView::paintSelectionBox( )
{
   if( !m_bAboutToUpdate )
   {
      int sx, sy, ex, ey, w, h;
      calculateSelectionBox( sx, sy, ex, ey, w, h );
      TQPainter p;
      p.begin( this );
      p.setPen( PMRenderManager::theManager( )->controlPointColor( 1 ) );
      p.drawRect( sx, sy, w, h );
      p.end( );
   }
}

void PMGLView::calculateSelectionBox( int& sx, int& sy, int& ex, int& ey,
                                      int& w, int& h )
{
   if( m_selectionStart.x( ) < m_selectionEnd.x( ) )
   {
      sx = m_selectionStart.x( );
      ex = m_selectionEnd.x( );
   }
   else
   {
      ex = m_selectionStart.x( );
      sx = m_selectionEnd.x( );
   }

   if( m_selectionStart.y( ) < m_selectionEnd.y( ) )
   {
      sy = m_selectionStart.y( );
      ey = m_selectionEnd.y( );
   }
   else
   {
      ey = m_selectionStart.y( );
      sy = m_selectionEnd.y( );
   }

   w = ex - sx + 1;
   h = ey - sy + 1;
}

double PMGLView::screenToInternalX( int x ) const
{
   return rint( x - width( ) / 2.0 + 0.1 );
}

double PMGLView::screenToInternalY( int y ) const
{
   return rint( height( ) / 2.0 - y - 0.1 );
}

void PMGLView::slotRenderingStarted( PMGLView* )
{
}

void PMGLView::slotAboutToUpdate( PMGLView* view )
{
   if( view == this )
      m_bAboutToUpdate = true;
}

void PMGLView::slotRenderingFinished( PMGLView* view )
{
   if( view == this )
   {
      m_bAboutToUpdate = false;
      if( m_bMultipleSelectionMode )
      {
         saveSelectionBox( );
         paintSelectionBox( );
      }

      if( m_bAutoScroll )
      {
         TQTime now = TQTime::currentTime( );
         int msecs = m_lastAutoScrollUpdate.msecsTo( now );

         if( msecs < c_minAutoScrollUpdateTime )
            m_autoScrollTimer.start( c_minAutoScrollUpdateTime - msecs, true );
         else
            m_autoScrollTimer.start( 0, true );
      }
   }
}

TQString PMGLView::viewTypeAsString( PMViewType t )
{
   TQString str;

   switch( t )
   {
      case PMViewPosX:
         str = i18n( "Left" );
         break;
      case PMViewNegX:
         str = i18n( "Right" );
         break;
      case PMViewPosY:
         str = i18n( "Bottom" );
         break;
      case PMViewNegY:
         str = i18n( "Top" );
         break;
      case PMViewPosZ:
         str = i18n( "Front" );
         break;
      case PMViewNegZ:
         str = i18n( "Back" );
         break;
      case PMViewCamera:
         str = i18n( "Camera" );
         break;
   }
   return str;
}

void PMGLView::saveConfig( KConfig* /*cfg*/ )
{
}

void PMGLView::restoreConfig( KConfig* /*cfg*/ )
{
}

void PMGLView::contextMenu( )
{
   TQPopupMenu* m = new TQPopupMenu( );
   m->insertItem( i18n( "Left View" ), this, TQT_SLOT( slotSetTypePosX( ) ) );
   m->insertItem( i18n( "Right View" ), this, TQT_SLOT( slotSetTypeNegX( ) ) );
   m->insertItem( i18n( "Top View" ), this, TQT_SLOT( slotSetTypeNegY( ) ) );
   m->insertItem( i18n( "Bottom View" ), this, TQT_SLOT( slotSetTypePosY( ) ) );
   m->insertItem( i18n( "Front View" ), this, TQT_SLOT( slotSetTypePosZ( ) ) );
   m->insertItem( i18n( "Back View" ), this, TQT_SLOT( slotSetTypeNegZ( ) ) );

   TQPopupMenu* cm = new TQPopupMenu( m );
   TQPtrListIterator<PMCamera> it = m_pPart->cameras( );
   TQString name;
   if( !it.current( ) )
      cm->insertItem( i18n( "No Cameras" ) );
   else
   {
      int cnr = 0;
      for( ; it.current( ); ++it, ++cnr )
      {
         name = it.current( )->name( );
         if( name.isEmpty( ) )
            name = i18n( "(unnamed)" );
         cm->insertItem( name, cnr );
      }
   }
   connect( cm, TQT_SIGNAL( activated( int ) ), TQT_SLOT( slotCameraView( int ) ) );

   m->insertItem( SmallIconSet( "pmcamera" ), i18n( "Camera" ), cm );

   m->insertSeparator( );

   m->insertItem( i18n( "Snap to Grid" ), this, TQT_SLOT( slotSnapToGrid( ) ) );
   m_objectActions.clear( );
   if( m_pActiveObject )
   {
      m_pActiveObject->addObjectActions( m_controlPoints, m_objectActions );
      if( !m_objectActions.isEmpty( ) )
      {
         PMObjectAction* oa = 0;
         TQPtrListIterator<PMObjectAction> ait( m_objectActions );

         for( ; ait.current( ); ++ait )
         {
            oa = ait.current( );
            oa->setMenuID( m->insertItem( oa->description( ) ) );
         }
      }
   }
   connect( m, TQT_SIGNAL( activated( int ) ), TQT_SLOT( slotObjectAction( int ) ) );

   m->insertSeparator( );

   TQPopupMenu* menu = new TQPopupMenu( m );
   PMControlPointListIterator pit( m_controlPoints );

   if( !pit.current( ) )
      menu->insertItem( i18n( "No Control Points" ) );
   else
   {
      int cnr = 0;
      for( ; pit.current( ); ++pit, ++cnr )
         menu->insertItem( pit.current( )->description( ), cnr );
   }
   connect( menu, TQT_SIGNAL( activated( int ) ), TQT_SLOT( slotControlPoint( int ) ) );

   m->insertItem( i18n( "Control Points" ), menu );

   m->exec( TQCursor::pos( ) );
   delete m;
}

void PMGLView::slotCameraView( int id )
{
   int i;
   TQPtrListIterator<PMCamera> it = m_pPart->cameras( );

   for( i = 0; i < id; i++ )
      ++it;
   if( it.current( ) )
   {
      setCamera( it.current( ) );
      setType( PMGLView::PMViewCamera );
   }
}

void PMGLView::slotObjectAction( int id )
{
   TQPtrListIterator<PMObjectAction> it( m_objectActions );
   PMObjectAction* oa = 0;

   for( ; it.current( ) && !oa; ++it )
      if( it.current( )->menuID( ) == id )
         oa = it.current( );

   if( oa && m_pActiveObject )
   {
      // otherwise no object action was selected in the context menu

      m_pActiveObject->createMemento( );
      m_pActiveObject->objectActionCalled( oa, m_controlPoints,
                                           m_controlPointsPosition,
                                           m_contextClickPosition );
      PMDataChangeCommand* cmd;
      cmd = new PMDataChangeCommand( m_pActiveObject->takeMemento( ) );
      cmd->setText( oa->description( ) );
      m_pPart->executeCommand( cmd );
   }
}

void PMGLView::slotControlPoint( int id )
{
   PMControlPoint* p = m_controlPoints.at( id );
   if( p )
   {
      PMControlPointListIterator cit( m_controlPoints );
      for( ; cit.current( ); ++cit )
         cit.current( )->setSelected( p == cit.current( ) );
      emit objectChanged( m_pActiveObject, PMCControlPointSelection, TQT_TQOBJECT(this) );
   }
}

void PMGLView::slotSnapToGrid( )
{
   if( m_pActiveObject )
   {
      if( !m_pActiveObject->mementoCreated( ) )
         m_pActiveObject->createMemento( );

      PMControlPointListIterator it( m_controlPoints );
      for( ; it.current( ); ++it )
         if( it.current( )->selected( ) )
            it.current( )->snapToGrid( );

      m_pActiveObject->controlPointsChanged( m_controlPoints );

      PMDataChangeCommand* cmd;
      cmd = new PMDataChangeCommand( m_pActiveObject->takeMemento( ) );
      cmd->setText( i18n( "Snap to Grid" ) );
      m_pPart->executeCommand( cmd );
   }
}

TQString PMGLView::description( ) const
{
   return viewTypeAsString( m_type );
}

void PMGLView::restoreViewConfig( PMViewOptions* vo )
{
   if( vo && vo->viewType( ) == "glview" )
   {
      PMGLViewOptions* o = ( PMGLViewOptions* ) vo;
      m_type = o->glViewType( );
   }
}

void PMGLView::saveViewConfig( PMViewOptions* vo ) const
{
   if( vo && vo->viewType( ) == "glview" )
   {
      PMGLViewOptions* o = ( PMGLViewOptions* ) vo;
      o->setGLViewType( m_type );
   }
}

void PMGLViewOptions::loadData( TQDomElement& e )
{
   TQString s = e.attribute( "type", "Camera" );
   if( s == "Camera" ) m_glViewType = PMGLView::PMViewCamera;
   else if( s == "X" ) m_glViewType = PMGLView::PMViewPosX;
   else if( s == "Y" ) m_glViewType = PMGLView::PMViewPosY;
   else if( s == "Z" ) m_glViewType = PMGLView::PMViewPosZ;
   else if( s == "NegX" ) m_glViewType = PMGLView::PMViewNegX;
   else if( s == "NegY" ) m_glViewType = PMGLView::PMViewNegY;
   else if( s == "NegZ" ) m_glViewType = PMGLView::PMViewNegZ;
}

void PMGLViewOptions::saveData( TQDomElement& e )
{
   switch( m_glViewType )
   {
      case PMGLView::PMViewCamera:
         e.setAttribute( "type", "Camera" );
         break;
      case PMGLView::PMViewPosX:
         e.setAttribute( "type", "X" );
         break;
      case PMGLView::PMViewPosY:
         e.setAttribute( "type", "Y" );
         break;
      case PMGLView::PMViewPosZ:
         e.setAttribute( "type", "Z" );
         break;
      case PMGLView::PMViewNegX:
         e.setAttribute( "type", "NegX" );
         break;
      case PMGLView::PMViewNegY:
         e.setAttribute( "type", "NegY" );
         break;
      case PMGLView::PMViewNegZ:
         e.setAttribute( "type", "NegZ" );
         break;
      default:
         kdError( PMArea ) << i18n( "Unknown GL view type." )
                           << endl;
         break;
   }
}

TQString PMGLViewFactory::description( ) const
{
   return i18n( "3D View" );
}

TQString PMGLViewFactory::description( PMViewOptions* vo ) const
{
   if( vo && vo->viewType( ) == "glview" )
   {
      PMGLViewOptions* o = ( PMGLViewOptions* ) vo;
      return i18n( "3D View (%1)" ).tqarg(
         PMGLView::viewTypeAsString( o->glViewType( ) ) );
   }
   return description( );
}

PMViewOptionsWidget* PMGLViewFactory::newOptionsWidget( TQWidget* parent,
                                                        PMViewOptions* o )
{
   return new PMGLViewOptionsWidget( parent, o );
}

PMViewOptions* PMGLViewFactory::newOptionsInstance( ) const
{
   PMGLViewOptions* o = new PMGLViewOptions( );
   return o;
}

PMGLViewOptionsWidget::PMGLViewOptionsWidget( TQWidget* parent,
                                              PMViewOptions* o )
      : PMViewOptionsWidget( parent )
{
   m_pOptions = ( PMGLViewOptions* ) o;

   TQHBoxLayout* hl = new TQHBoxLayout( this, 0, KDialog::spacingHint( ) );
   TQLabel* l = new TQLabel( i18n( "3D view type:" ), this );
   hl->addWidget( l );

   m_pGLViewType = new TQComboBox( false, this );
   m_pGLViewType->insertItem( i18n( "Top" ) );
   m_pGLViewType->insertItem( i18n( "Bottom" ) );
   m_pGLViewType->insertItem( i18n( "Left" ) );
   m_pGLViewType->insertItem( i18n( "Right" ) );
   m_pGLViewType->insertItem( i18n( "Front" ) );
   m_pGLViewType->insertItem( i18n( "Back" ) );
   m_pGLViewType->insertItem( i18n( "Camera" ) );

   switch( m_pOptions->glViewType( ) )
   {
      case PMGLView::PMViewNegY:
         m_pGLViewType->setCurrentItem( 0 );
         break;
      case PMGLView::PMViewPosY:
         m_pGLViewType->setCurrentItem( 1 );
         break;
      case PMGLView::PMViewPosX:
         m_pGLViewType->setCurrentItem( 2 );
         break;
      case PMGLView::PMViewNegX:
         m_pGLViewType->setCurrentItem( 3 );
         break;
      case PMGLView::PMViewPosZ:
         m_pGLViewType->setCurrentItem( 4 );
         break;
      case PMGLView::PMViewNegZ:
         m_pGLViewType->setCurrentItem( 5 );
         break;
      case PMGLView::PMViewCamera:
         m_pGLViewType->setCurrentItem( 6 );
         break;
   }

   connect( m_pGLViewType, TQT_SIGNAL( activated( int ) ),
            TQT_SLOT( slotGLViewTypeChanged( int ) ) );
   hl->addWidget( m_pGLViewType );
}

void PMGLViewOptionsWidget::slotGLViewTypeChanged( int index )
{
   switch( index )
   {
      case 0:
         m_pOptions->setGLViewType( PMGLView::PMViewNegY );
         break;
      case 1:
         m_pOptions->setGLViewType( PMGLView::PMViewPosY );
         break;
      case 2:
         m_pOptions->setGLViewType( PMGLView::PMViewPosX );
         break;
      case 3:
         m_pOptions->setGLViewType( PMGLView::PMViewNegX );
         break;
      case 4:
         m_pOptions->setGLViewType( PMGLView::PMViewPosZ );
         break;
      case 5:
         m_pOptions->setGLViewType( PMGLView::PMViewNegZ );
         break;
      case 6:
         m_pOptions->setGLViewType( PMGLView::PMViewCamera );
         break;
   }
   emit viewTypeDescriptionChanged( );
}

#include "pmglview.moc"