worldsim/3rdParts/qglviewer/qglviewer.cpp

00001 /****************************************************************************
00002 
00003  Copyright (C) 2002-2008 Gilles Debunne. All rights reserved.
00004 
00005  This file is part of the QGLViewer library version 2.3.10.
00006 
00007  http://www.libqglviewer.com - contact@libqglviewer.com
00008 
00009  This file may be used under the terms of the GNU General Public License 
00010  versions 2.0 or 3.0 as published by the Free Software Foundation and
00011  appearing in the LICENSE file included in the packaging of this file.
00012  In addition, as a special exception, Gilles Debunne gives you certain 
00013  additional rights, described in the file GPL_EXCEPTION in this package.
00014 
00015  libQGLViewer uses dual licensing. Commercial/proprietary software must
00016  purchase a libQGLViewer Commercial License.
00017 
00018  This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00019  WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00020 
00021 *****************************************************************************/
00022 
00023 #include "domUtils.h"
00024 #include "qglviewer.h"
00025 #include "camera.h"
00026 #include "keyFrameInterpolator.h"
00027 
00028 #if QT_VERSION >= 0x040000
00029 # include <QtAlgorithms>
00030 # include <QTextEdit>
00031 # include <QApplication>
00032 # include <QFileInfo>
00033 # include <QDateTime>
00034 # include <QMessageBox>
00035 # include <QPushButton>
00036 # include <QTabWidget>
00037 # include <QTextStream>
00038 # include <QMouseEvent>
00039 # include <QTimer>
00040 # include <QImage>
00041 # include <QDir>
00042 # include <QUrl>
00043 #else
00044 # include <qapplication.h>
00045 # include <qfileinfo.h>
00046 # include <qdatetime.h>
00047 # include <qmessagebox.h>
00048 # include <qpushbutton.h>
00049 # include <qtabwidget.h>
00050 # include <qtextstream.h>
00051 # include <qtimer.h>
00052 # include <qimage.h>
00053 # include <qtextedit.h>
00054 # include <qkeysequence.h>
00055 # include <qdir.h>
00056   // Patch for enums names change
00057 # define KeyboardModifierMask KeyButtonMask
00058   // Patch for QMap API change
00059 # define value data
00060 #endif
00061 
00062 using namespace std;
00063 using namespace qglviewer;
00064 
00065 // Static private variable
00066 #if QT_VERSION >= 0x040000
00067 QList<QGLViewer*> QGLViewer::QGLViewerPool_;
00068 #else
00069 QPtrList<QGLViewer> QGLViewer::QGLViewerPool_;
00070 #endif
00071 
00072 
00096 void QGLViewer::defaultConstructor()
00097 {
00098     //      - - -  W A R N I N G  - - -
00099     // This method should not call initializeGL(). Otherwise, as we are in the
00100     // base class constructor, the user-defined init() would never be called.
00101     // The different QGLViewer::setXXX are hence protected, so that updateGL is not called.
00102     // The different constructor code should then be EMPTY.
00103     updateGLOK_ = false;
00104 
00105     // Test OpenGL context
00106     // if (glGetString(GL_VERSION) == 0)
00107     // qWarning("Unable to get OpenGL version, context may not be available - Check your configuration");
00108 
00109 #if QT_VERSION >= 0x040000
00110     int poolIndex = QGLViewer::QGLViewerPool_.indexOf(NULL);
00111     setFocusPolicy(Qt::StrongFocus);
00112 #else
00113     int poolIndex = QGLViewer::QGLViewerPool_.findRef(NULL);
00114     setFocusPolicy(QWidget::StrongFocus);
00115 #endif
00116 
00117         if (poolIndex >= 0)
00118           QGLViewer::QGLViewerPool_.replace(poolIndex, this);
00119         else
00120       QGLViewer::QGLViewerPool_.append(this);
00121 
00122     camera_ = new Camera();
00123     setCamera(camera());
00124 
00125     setDefaultShortcuts();
00126     setDefaultMouseBindings();
00127 
00128     setSnapshotFileName(tr("snapshot", "Default snapshot file name"));
00129     initializeSnapshotFormats();
00130     setSnapshotCounter(0);
00131     setSnapshotQuality(95);
00132 
00133     fpsTime_.start();
00134     fpsCounter_     = 0;
00135     f_p_s_          = 0.0;
00136     fpsString_      = tr("%1Hz", "Frames per seconds, in Hertz").arg("?");
00137     visualHint_     = 0;
00138     previousPathId_ = 0;
00139     // prevPos_ is not initialized since pos() is not meaningful here. It will be set by setFullScreen().
00140 
00141     // #CONNECTION# default values in initFromDOMElement()
00142     manipulatedFrame_ = NULL;
00143     manipulatedFrameIsACamera_ = false;
00144     mouseGrabberIsAManipulatedFrame_ = false;
00145     mouseGrabberIsAManipulatedCameraFrame_ = false;
00146     displayMessage_ = false;
00147     connect(&messageTimer_, SIGNAL(timeout()), SLOT(hideMessage()));
00148 #if QT_VERSION >= 0x040000
00149     messageTimer_.setSingleShot(true);
00150 #endif
00151     helpWidget_ = NULL;
00152     setMouseGrabber(NULL);
00153 
00154     setSceneRadius(1.0);
00155     showEntireScene();
00156     setStateFileName(".qglviewer.xml");
00157 
00158     // #CONNECTION# default values in initFromDOMElement()
00159     setAxisIsDrawn(false);
00160     setGridIsDrawn(false);
00161     setFPSIsDisplayed(false);
00162     setCameraIsEdited(false);
00163     setTextIsEnabled(true);
00164     setStereoDisplay(false);
00165     setFullScreen(false);
00166 
00167     animationTimerId_ = 0;
00168     stopAnimation();
00169     setAnimationPeriod(40); // 25Hz
00170 
00171     selectBuffer_ = NULL;
00172     setSelectBufferSize(4*1000);
00173     setSelectRegionWidth(3);
00174     setSelectRegionHeight(3);
00175     setSelectedName(-1);
00176 
00177     bufferTextureId_ = 0;
00178     bufferTextureMaxU_ = 0.0;
00179     bufferTextureMaxV_ = 0.0;
00180     bufferTextureWidth_ = 0;
00181     bufferTextureHeight_ = 0;
00182     previousBufferTextureFormat_ = 0;
00183     previousBufferTextureInternalFormat_ = 0;
00184 
00185 #if QT_VERSION >= 0x040000
00186     setAttribute(Qt::WA_NoSystemBackground);
00187 #endif
00188 
00189     tileRegion_ = NULL;
00190 }
00191 
00192 #if QT_VERSION >= 0x040000
00193 
00200 QGLViewer::QGLViewer(QWidget* parent, const QGLWidget* shareWidget, Qt::WFlags flags)
00201 : QGLWidget(parent, shareWidget, flags)
00202 { defaultConstructor(); }
00203 
00209 QGLViewer::QGLViewer(QGLContext *context, QWidget* parent, const QGLWidget* shareWidget, Qt::WFlags flags)
00210 : QGLWidget(context, parent, shareWidget, flags)
00211 { defaultConstructor(); }
00212 
00217 QGLViewer::QGLViewer(const QGLFormat& format, QWidget* parent, const QGLWidget* shareWidget, Qt::WFlags flags)
00218 : QGLWidget(format, parent, shareWidget, flags)
00219 { defaultConstructor(); }
00220 #endif
00221 
00226 QGLViewer::~QGLViewer()
00227 {
00228     // See closeEvent comment. Destructor is called (and not closeEvent) only when the widget is embedded.
00229     // Hence we saveToFile here. It is however a bad idea if virtual domElement() has been overloaded !
00230     // if (parent())
00231     // saveStateToFileForAllViewers();
00232 
00233 #if QT_VERSION >= 0x040000
00234     QGLViewer::QGLViewerPool_.replace(QGLViewer::QGLViewerPool_.indexOf(this), NULL);
00235 #else
00236     QGLViewer::QGLViewerPool_.replace(QGLViewer::QGLViewerPool_.findRef(this), NULL);
00237 #endif
00238 
00239     delete camera();
00240     delete[] selectBuffer_;
00241     if (helpWidget())
00242     {
00243         // Needed for Qt 4 which has no main widget.
00244         helpWidget()->close();
00245         delete helpWidget_;
00246     }
00247 }
00248 
00249 
00250 static QString QGLViewerVersionString()
00251 {
00252     return QString::number((QGLVIEWER_VERSION & 0xff0000) >> 16) + "." +
00253         QString::number((QGLVIEWER_VERSION & 0x00ff00) >> 8) + "." +
00254         QString::number(QGLVIEWER_VERSION & 0x0000ff);
00255 }
00256 
00257 static int convertToKeyboardModifiers(int state)
00258 {
00259 #if QT_VERSION < 0x040000
00260     // Qt 2 & 3 have different values for ButtonState and Modifiers.
00261     // Converts CTRL,SHIFT... to ControlButton, ShiftButton...
00262     if (state & Qt::MODIFIER_MASK)
00263     {
00264         if (state & Qt::CTRL)  { state &= ~Qt::CTRL;    state |= Qt::ControlButton; }
00265         if (state & Qt::SHIFT) { state &= ~Qt::SHIFT;   state |= Qt::ShiftButton; }
00266         if (state & Qt::ALT)   { state &= ~Qt::ALT;     state |= Qt::AltButton; }
00267 # if QT_VERSION >= 0x030100
00268         if (state & Qt::META)  { state &= ~Qt::META;    state |= Qt::MetaButton; }
00269 # endif
00270     }
00271 #endif
00272     return state;
00273 }
00274 
00275 static QtKeyboardModifiers convertKeyboardModifiers(QtKeyboardModifiers modifiers)
00276 {
00277 #if QT_VERSION < 0x040000
00278     return QtKeyboardModifiers(convertToKeyboardModifiers(modifiers));
00279 #else
00280     return modifiers;
00281 #endif
00282 }
00283 
00284 static int convertToShortModifier(int state)
00285 {
00286     // Converts ControlButton, ShiftButton... to CTRL,SHIFT...
00287     // convertToKeyboardModifiers does the opposite
00288 #if QT_VERSION < 0x040000
00289     if (state & Qt::KeyButtonMask)
00290     {
00291         if (state & Qt::ControlButton) { state &= ~Qt::ControlButton;   state |= Qt::CTRL; }
00292         if (state & Qt::ShiftButton)   { state &= ~Qt::ShiftButton; state |= Qt::SHIFT; }
00293         if (state & Qt::AltButton)     { state &= ~Qt::AltButton;       state |= Qt::ALT; }
00294 # if QT_VERSION >= 0x030100
00295         if (state & Qt::MetaButton)    { state &= ~Qt::MetaButton;  state |= Qt::META; }
00296 # endif
00297     }
00298 #endif
00299     return state;
00300 }
00301 
00320 void QGLViewer::initializeGL()
00321 {
00322     if (updateGLOK_)
00323         qWarning("Internal debug: initializeGL() is called in QGLViewer constructor.");
00324 
00325     glEnable(GL_LIGHT0);
00326     glEnable(GL_LIGHTING);
00327     glEnable(GL_DEPTH_TEST);
00328     glEnable(GL_COLOR_MATERIAL);
00329 
00330     // Default colors
00331     setForegroundColor(QColor(180, 180, 180));
00332     setBackgroundColor(QColor(51, 51, 51));
00333 
00334     // Clear the buffer where we're going to draw
00335     if (format().stereo())
00336     {
00337         glDrawBuffer(GL_BACK_RIGHT);
00338         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
00339         glDrawBuffer(GL_BACK_LEFT);
00340         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
00341     }
00342     else
00343         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
00344 
00345     // Calls user defined method. Default emits a signal.
00346     init();
00347 
00348     // Give time to glInit to finish and then call setFullScreen().
00349     if (isFullScreen())
00350         QTimer::singleShot( 100, this, SLOT(delayedFullScreen()) );
00351 
00352     updateGLOK_ = true;
00353 }
00354 
00361 void QGLViewer::paintGL()
00362 {
00363     updateGLOK_ = false;
00364     if (displaysInStereo())
00365     {
00366         for (int view=1; view>=0; --view)
00367         {
00368             // Clears screen, set model view matrix with shifted matrix for ith buffer
00369             preDrawStereo(view);
00370             // Used defined method. Default is empty
00371             if (camera()->frame()->isManipulated())
00372                 fastDraw();
00373             else
00374                 draw();
00375             postDraw();
00376         }
00377     }
00378     else
00379     {
00380         // Clears screen, set model view matrix...
00381         preDraw();
00382         // Used defined method. Default calls draw()
00383         if (camera()->frame()->isManipulated())
00384             fastDraw();
00385         else
00386             draw();
00387         // Add visual hints: axis, camera, grid...
00388         postDraw();
00389     }
00390     updateGLOK_ = true;
00391     Q_EMIT drawFinished(true);
00392 }
00393 
00405 void QGLViewer::preDraw()
00406 {
00407     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
00408 
00409     // GL_PROJECTION matrix
00410     camera()->loadProjectionMatrix();
00411     // GL_MODELVIEW matrix
00412     camera()->loadModelViewMatrix();
00413 
00414     Q_EMIT drawNeeded();
00415 }
00416 
00427 void QGLViewer::postDraw()
00428 {
00429     // Reset model view matrix to world coordinates origin
00430     glMatrixMode(GL_MODELVIEW);
00431     glPushMatrix();
00432     camera()->loadModelViewMatrix();
00433     // TODO restore model loadProjectionMatrixStereo
00434 
00435     // Save OpenGL state
00436     glPushAttrib(GL_ALL_ATTRIB_BITS);
00437 
00438     // Set neutral GL state
00439     glDisable(GL_TEXTURE_1D);
00440     glDisable(GL_TEXTURE_2D);
00441 #ifdef GL_TEXTURE_3D  // OpenGL 1.2 Only...
00442     glDisable(GL_TEXTURE_3D);
00443 #endif
00444 
00445     glDisable(GL_TEXTURE_GEN_Q);
00446     glDisable(GL_TEXTURE_GEN_R);
00447     glDisable(GL_TEXTURE_GEN_S);
00448     glDisable(GL_TEXTURE_GEN_T);
00449 
00450 #ifdef GL_RESCALE_NORMAL  // OpenGL 1.2 Only...
00451     glEnable(GL_RESCALE_NORMAL);
00452 #endif
00453 
00454     glDisable(GL_COLOR_MATERIAL);
00455     qglColor(foregroundColor());
00456 
00457     if (cameraIsEdited())
00458         camera()->drawAllPaths();
00459 
00460     // Revolve Around Point, line when camera rolls, zoom region
00461     drawVisualHints();
00462 
00463     if (gridIsDrawn()) { glLineWidth(1.0); drawGrid(camera()->sceneRadius()); }
00464     if (axisIsDrawn()) { glLineWidth(2.0); drawAxis(camera()->sceneRadius()); }
00465 
00466     // FPS computation
00467     const unsigned int maxCounter = 20;
00468     if (++fpsCounter_ == maxCounter)
00469     {
00470         f_p_s_ = 1000.0 * maxCounter / fpsTime_.restart();
00471         fpsString_ = tr("%1Hz", "Frames per seconds, in Hertz").arg(f_p_s_, 0, 'f', ((f_p_s_ < 10.0)?1:0));
00472         fpsCounter_ = 0;
00473     }
00474 
00475     // Restore foregroundColor
00476     float color[4];
00477     color[0] = foregroundColor().red()   / 255.0;
00478     color[1] = foregroundColor().green() / 255.0;
00479     color[2] = foregroundColor().blue()  / 255.0;
00480     color[3] = 1.0;
00481     glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
00482     glDisable(GL_LIGHTING);
00483     glDisable(GL_DEPTH_TEST);
00484 
00485     if (FPSIsDisplayed()) displayFPS();
00486     if (displayMessage_) drawText(10, height()-10,  message_);
00487 
00488     // Restore GL state
00489     glPopAttrib();
00490     glPopMatrix();
00491 }
00492 
00498 void QGLViewer::preDrawStereo(bool leftBuffer)
00499 {
00500     // Set buffer to draw in
00501     // Seems that SGI and Crystal Eyes are not synchronized correctly !
00502     // That's why we don't draw in the appropriate buffer...
00503     if (!leftBuffer)
00504         glDrawBuffer(GL_BACK_LEFT);
00505     else
00506         glDrawBuffer(GL_BACK_RIGHT);
00507 
00508     // Clear the buffer where we're going to draw
00509     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
00510     // GL_PROJECTION matrix
00511     camera()->loadProjectionMatrixStereo(leftBuffer);
00512     // GL_MODELVIEW matrix
00513     camera()->loadModelViewMatrixStereo(leftBuffer);
00514 
00515     Q_EMIT drawNeeded();
00516 }
00517 
00525 void QGLViewer::fastDraw()
00526 {
00527     draw();
00528 }
00529 
00537 void QGLViewer::setCameraIsEdited(bool edit)
00538 {
00539     cameraIsEdited_ = edit;
00540     if (edit)
00541     {
00542         previousCameraZClippingCoefficient_ = camera()->zClippingCoefficient();
00543         // #CONNECTION# 5.0 also used in domElement() and in initFromDOMElement().
00544         camera()->setZClippingCoefficient(5.0);
00545     }
00546     else
00547         camera()->setZClippingCoefficient(previousCameraZClippingCoefficient_);
00548 
00549     Q_EMIT cameraIsEditedChanged(edit);
00550 
00551     if (updateGLOK_)
00552         updateGL();
00553 }
00554 
00555 // Key bindings. 0 means not defined
00556 void QGLViewer::setDefaultShortcuts()
00557 {
00558     // D e f a u l t   a c c e l e r a t o r s
00559     setShortcut(DRAW_AXIS,  Qt::Key_A);
00560     setShortcut(DRAW_GRID,  Qt::Key_G);
00561     setShortcut(DISPLAY_FPS,    Qt::Key_F);
00562     setShortcut(ENABLE_TEXT,    Qt::SHIFT+Qt::Key_Question);
00563     setShortcut(EXIT_VIEWER,    Qt::Key_Escape);
00564     setShortcut(SAVE_SCREENSHOT,    Qt::CTRL+Qt::Key_S);
00565     setShortcut(CAMERA_MODE,    Qt::Key_Space);
00566     setShortcut(FULL_SCREEN,    Qt::ALT+Qt::Key_Return);
00567     setShortcut(STEREO,     Qt::Key_S);
00568     setShortcut(ANIMATION,  Qt::Key_Return);
00569     setShortcut(HELP,       Qt::Key_H);
00570     setShortcut(EDIT_CAMERA,    Qt::Key_C);
00571     setShortcut(MOVE_CAMERA_LEFT,   Qt::Key_Left);
00572     setShortcut(MOVE_CAMERA_RIGHT,Qt::Key_Right);
00573     setShortcut(MOVE_CAMERA_UP, Qt::Key_Up);
00574     setShortcut(MOVE_CAMERA_DOWN,   Qt::Key_Down);
00575     setShortcut(INCREASE_FLYSPEED,Qt::Key_Plus);
00576     setShortcut(DECREASE_FLYSPEED,Qt::Key_Minus);
00577     setShortcut(SNAPSHOT_TO_CLIPBOARD,Qt::CTRL+Qt::Key_C);
00578 
00579     keyboardActionDescription_[DISPLAY_FPS] = tr("Toggles the display of the FPS", "DISPLAY_FPS action description");
00580     keyboardActionDescription_[SAVE_SCREENSHOT] = tr("Saves a screenshot", "SAVE_SCREENSHOT action description");
00581     keyboardActionDescription_[FULL_SCREEN] = tr("Toggles full screen display", "FULL_SCREEN action description");
00582     keyboardActionDescription_[DRAW_AXIS] = tr("Toggles the display of the world axis", "DRAW_AXIS action description");
00583     keyboardActionDescription_[DRAW_GRID] = tr("Toggles the display of the XY grid", "DRAW_GRID action description");
00584     keyboardActionDescription_[CAMERA_MODE] = tr("Changes camera mode (revolve or fly)", "CAMERA_MODE action description");
00585     keyboardActionDescription_[STEREO] = tr("Toggles stereo display", "STEREO action description");
00586     keyboardActionDescription_[HELP] = tr("Opens this help window", "HELP action description");
00587     keyboardActionDescription_[ANIMATION] = tr("Starts/stops the animation", "ANIMATION action description");
00588     keyboardActionDescription_[EDIT_CAMERA] = tr("Toggles camera paths display", "EDIT_CAMERA action description"); // TODO change
00589     keyboardActionDescription_[ENABLE_TEXT] = tr("Toggles the display of the text", "ENABLE_TEXT action description");
00590     keyboardActionDescription_[EXIT_VIEWER] = tr("Exits program", "EXIT_VIEWER action description");
00591     keyboardActionDescription_[MOVE_CAMERA_LEFT] = tr("Moves camera left", "MOVE_CAMERA_LEFT action description");
00592     keyboardActionDescription_[MOVE_CAMERA_RIGHT] = tr("Moves camera right", "MOVE_CAMERA_RIGHT action description");
00593     keyboardActionDescription_[MOVE_CAMERA_UP] = tr("Moves camera up", "MOVE_CAMERA_UP action description");
00594     keyboardActionDescription_[MOVE_CAMERA_DOWN] = tr("Moves camera down", "MOVE_CAMERA_DOWN action description");
00595     keyboardActionDescription_[INCREASE_FLYSPEED] = tr("Increases fly speed", "INCREASE_FLYSPEED action description");
00596     keyboardActionDescription_[DECREASE_FLYSPEED] = tr("Decreases fly speed", "DECREASE_FLYSPEED action description");
00597     keyboardActionDescription_[SNAPSHOT_TO_CLIPBOARD] = tr("Copies a snapshot to clipboard", "SNAPSHOT_TO_CLIPBOARD action description");
00598 
00599     // K e y f r a m e s   s h o r t c u t   k e y s
00600     setPathKey(Qt::Key_F1,   1);
00601     setPathKey(Qt::Key_F2,   2);
00602     setPathKey(Qt::Key_F3,   3);
00603     setPathKey(Qt::Key_F4,   4);
00604     setPathKey(Qt::Key_F5,   5);
00605     setPathKey(Qt::Key_F6,   6);
00606     setPathKey(Qt::Key_F7,   7);
00607     setPathKey(Qt::Key_F8,   8);
00608     setPathKey(Qt::Key_F9,   9);
00609     setPathKey(Qt::Key_F10, 10);
00610     setPathKey(Qt::Key_F11, 11);
00611     setPathKey(Qt::Key_F12, 12);
00612 
00613 #if QT_VERSION >= 0x040000
00614     setAddKeyFrameKeyboardModifiers(Qt::AltModifier);
00615     setPlayPathKeyboardModifiers(Qt::NoModifier);
00616 #else
00617     setAddKeyFrameKeyboardModifiers(Qt::AltButton);
00618     setPlayPathKeyboardModifiers(Qt::NoButton);
00619 #endif
00620 }
00621 
00622 // M o u s e   b e h a v i o r
00623 void QGLViewer::setDefaultMouseBindings()
00624 {
00625 #if QT_VERSION >= 0x040000
00626     const QtKeyboardModifiers cameraKeyboardModifiers = Qt::NoModifier;
00627     const QtKeyboardModifiers frameKeyboardModifiers = Qt::ControlModifier;
00628 #else
00629     const QtKeyboardModifiers cameraKeyboardModifiers = Qt::NoButton;
00630     const QtKeyboardModifiers frameKeyboardModifiers = Qt::ControlButton;
00631 #endif
00632     //#CONNECTION# toggleCameraMode()
00633     for (int handler=0; handler<2; ++handler)
00634     {
00635         MouseHandler mh = (MouseHandler)(handler);
00636         QtKeyboardModifiers modifiers = (mh == FRAME) ? frameKeyboardModifiers : cameraKeyboardModifiers;
00637         
00638         setMouseBinding(modifiers | Qt::LeftButton,  mh, ROTATE);
00639         setMouseBinding(modifiers | Qt::MidButton,   mh, ZOOM);
00640         setMouseBinding(modifiers | Qt::RightButton, mh, TRANSLATE);
00641 
00642         setMouseBinding(modifiers | Qt::LeftButton  | Qt::MidButton,  mh, SCREEN_ROTATE);
00643         // 2.2.4 setMouseBinding(modifiers | Qt::RightButton | Qt::MidButton,  mh, SCREEN_TRANSLATE);
00644 
00645         setWheelBinding(modifiers, mh, ZOOM);
00646     }
00647 
00648 #if QT_VERSION >= 0x040000
00649     // Z o o m   o n   r e g i o n
00650     setMouseBinding(Qt::ShiftModifier | Qt::MidButton, CAMERA, ZOOM_ON_REGION);
00651     // S e l e c t
00652     setMouseBinding(Qt::ShiftModifier | Qt::LeftButton, SELECT);
00653 #else
00654     setMouseBinding(Qt::ShiftButton | Qt::MidButton, CAMERA, ZOOM_ON_REGION);
00655     setMouseBinding(Qt::ShiftButton | Qt::LeftButton, SELECT);
00656 #endif
00657 
00658     // D o u b l e   c l i c k
00659     setMouseBinding(Qt::LeftButton,  ALIGN_CAMERA,      true);
00660     setMouseBinding(Qt::MidButton,   SHOW_ENTIRE_SCENE, true);
00661     setMouseBinding(Qt::RightButton, CENTER_SCENE,      true);
00662 
00663     setMouseBinding(frameKeyboardModifiers | Qt::LeftButton,  ALIGN_FRAME,  true);
00664     setMouseBinding(frameKeyboardModifiers | Qt::RightButton, CENTER_FRAME, true);
00665 
00666     // S p e c i f i c   d o u b l e   c l i c k s
00667     setMouseBinding(Qt::LeftButton,  RAP_FROM_PIXEL, true, Qt::RightButton);
00668     setMouseBinding(Qt::RightButton, RAP_IS_CENTER,  true, Qt::LeftButton);
00669     setMouseBinding(Qt::LeftButton,  ZOOM_ON_PIXEL,  true, Qt::MidButton);
00670     setMouseBinding(Qt::RightButton, ZOOM_TO_FIT,    true, Qt::MidButton);
00671 
00672 #ifdef Q_OS_MAC
00673     // Specific Mac bindings. Double finger emulates a wheelEvent which zooms.
00674     // Make Option + left emulate the right button. Other bindings should be changed accordingly.
00675     // Done at the end to override previous settings.
00676 # if QT_VERSION >= 0x040000
00677     const QtKeyboardModifiers macKeyboardModifiers = Qt::AltModifier;
00678 # else
00679     const QtKeyboardModifiers macKeyboardModifiers = Qt::AltButton;
00680 # endif
00681 
00682     setMouseBinding(macKeyboardModifiers | Qt::LeftButton, CAMERA, TRANSLATE);
00683     setMouseBinding(macKeyboardModifiers | Qt::LeftButton, CENTER_SCENE, true);
00684     setMouseBinding(frameKeyboardModifiers | macKeyboardModifiers | Qt::LeftButton, CENTER_FRAME, true);
00685     setMouseBinding(frameKeyboardModifiers | macKeyboardModifiers | Qt::LeftButton, FRAME, TRANSLATE);
00686     
00687     // S p e c i f i c   d o u b l e   c l i c k s
00688     // A single tap is actually seen as a left followed by a right button click.
00689     setMouseBinding(Qt::MetaModifier | Qt::RightButton,  RAP_FROM_PIXEL, true, Qt::LeftButton);
00690     setMouseBinding(Qt::ShiftModifier | Qt::MetaModifier | Qt::RightButton, RAP_IS_CENTER,  true, Qt::LeftButton);
00691     // A tap with two fingers is actually considered a rightButton.
00692     setMouseBinding(Qt::MetaModifier | Qt::RightButton,  ZOOM_ON_PIXEL, false);
00693     setMouseBinding(Qt::ShiftModifier | Qt::MetaModifier | Qt::RightButton, ZOOM_TO_FIT, false);
00694 #endif
00695 }
00696 
00713 void QGLViewer::setCamera(Camera* const camera)
00714 {
00715     if (!camera)
00716         return;
00717 
00718     camera->setSceneRadius(sceneRadius());
00719     camera->setSceneCenter(sceneCenter());
00720     camera->setScreenWidthAndHeight(width(),height());
00721 
00722     // Disconnect current camera to this viewer.
00723     disconnect(this->camera()->frame(), SIGNAL(manipulated()), this, SLOT(updateGL()));
00724     disconnect(this->camera()->frame(), SIGNAL(spun()), this, SLOT(updateGL()));
00725 
00726     // Connect camera frame to this viewer.
00727     connect(camera->frame(), SIGNAL(manipulated()), SLOT(updateGL()));
00728     connect(camera->frame(), SIGNAL(spun()), SLOT(updateGL()));
00729 
00730     connectAllCameraKFIInterpolatedSignals(false);
00731     camera_ = camera;
00732     connectAllCameraKFIInterpolatedSignals();
00733 
00734     previousCameraZClippingCoefficient_ = this->camera()->zClippingCoefficient();
00735 }
00736 
00737 void QGLViewer::connectAllCameraKFIInterpolatedSignals(bool connection)
00738 {
00739     for (QMap<int, KeyFrameInterpolator*>::ConstIterator it = camera()->kfi_.begin(), end=camera()->kfi_.end(); it != end; ++it)
00740     {
00741         if (connection)
00742             connect(camera()->keyFrameInterpolator(it.key()), SIGNAL(interpolated()), SLOT(updateGL()));
00743         else
00744             disconnect(camera()->keyFrameInterpolator(it.key()), SIGNAL(interpolated()), this, SLOT(updateGL()));
00745     }
00746 
00747     if (connection)
00748         connect(camera()->interpolationKfi_, SIGNAL(interpolated()), SLOT(updateGL()));
00749     else
00750         disconnect(camera()->interpolationKfi_, SIGNAL(interpolated()), this, SLOT(updateGL()));
00751 }
00752 
00769 void QGLViewer::drawLight(GLenum light, float scale) const
00770 {
00771     static GLUquadric* quadric = gluNewQuadric();
00772 
00773     const float length = sceneRadius() / 5.0 * scale;
00774 
00775     GLboolean lightIsOn;
00776     glGetBooleanv(light, &lightIsOn);
00777 
00778     if (lightIsOn)
00779     {
00780         // All light values are given in eye coordinates
00781         glPushMatrix();
00782         glLoadIdentity();
00783 
00784         float color[4];
00785         glGetLightfv(light, GL_DIFFUSE, color);
00786         glColor4fv(color);
00787 
00788         float pos[4];
00789         glGetLightfv(light, GL_POSITION, pos);
00790 
00791         if (pos[3] != 0.0)
00792         {
00793             glTranslatef(pos[0]/pos[3], pos[1]/pos[3], pos[2]/pos[3]);
00794 
00795             GLfloat cutOff;
00796             glGetLightfv(light, GL_SPOT_CUTOFF, &cutOff);
00797             if (cutOff != 180.0)
00798             {
00799                 GLfloat dir[4];
00800                 glGetLightfv(light, GL_SPOT_DIRECTION, dir);
00801                 glMultMatrixd(Quaternion(Vec(0,0,1), Vec(dir)).matrix());
00802                 QGLViewer::drawArrow(length);
00803                 gluCylinder(quadric, 0.0, 0.7 * length * sin(cutOff * M_PI / 180.0), 0.7 * length * cos(cutOff * M_PI / 180.0), 12, 1);
00804             }
00805             else
00806                 gluSphere(quadric, 0.2*length, 10, 10);
00807         }
00808         else
00809         {
00810             // Directional light.
00811             Vec dir(pos[0], pos[1], pos[2]);
00812             dir.normalize();
00813             Frame fr=Frame(camera()->cameraCoordinatesOf(4.0 * length * camera()->frame()->inverseTransformOf(dir)),
00814                 Quaternion(Vec(0,0,-1), dir));
00815             glMultMatrixd(fr.matrix());
00816             drawArrow(length);
00817         }
00818 
00819         glPopMatrix();
00820     }
00821 }
00822 
00823 
00866 void QGLViewer::drawText(int x, int y, const QString& text, const QFont& fnt)
00867 {
00868     if (!textIsEnabled())
00869         return;
00870 
00871 #if QT_VERSION < QGLVIEWER_QT_VERSION_WITHOUT_GLUT
00872     const GLfloat font_scale = 119.05f - 33.33f; // see glutStrokeCharacter man page
00873 
00874     startScreenCoordinatesSystem();
00875 
00876     // Anti-aliased characters
00877     glPushAttrib(GL_ALL_ATTRIB_BITS);
00878     glDisable(GL_LIGHTING);
00879     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
00880     glEnable(GL_BLEND);
00881     glDisable(GL_DEPTH_TEST);
00882     glEnable(GL_LINE_SMOOTH);
00883     glLineWidth(1.0);
00884 
00885     glTranslatef((GLfloat)x, (GLfloat)y, 0.0);
00886     const GLfloat scale = ((fnt.pixelSize()>0)?fnt.pixelSize():fnt.pointSize()) / font_scale;
00887     glScalef(scale, -scale, scale);
00888 
00889     for (uint i=0; i<text.length(); ++i)
00890         glutStrokeCharacter(GLUT_STROKE_ROMAN, text.at(i));
00891 
00892     glPopAttrib();
00893 
00894     stopScreenCoordinatesSystem();
00895 #else
00896 
00897 # if QT_VERSION < 0x030300 && defined Q_OS_UNIX
00898     // Fix bug with anti-aliased fonts on nVidia driver
00899     QFont newFont(fnt);
00900     newFont.setFamily("fixed");
00901     newFont.setRawMode(true);
00902     newFont.setPixelSize(10);
00903     newFont.setFixedPitch(true);
00904 #   if QT_VERSION >= 0x030200
00905     newFont.setStyleStrategy(QFont::OpenGLCompatible);
00906 #   endif
00907     newFont.setStyleHint(QFont::AnyStyle, QFont::PreferBitmap);
00908     renderText(x, y, text, newFont);
00909 # else
00910     if (tileRegion_ != NULL) {
00911       renderText((x-tileRegion_->xMin) * width() / (tileRegion_->xMax - tileRegion_->xMin), 
00912                      (y-tileRegion_->yMin) * height() / (tileRegion_->yMax - tileRegion_->yMin), text, scaledFont(fnt));
00913     } else
00914       renderText(x, y, text, fnt);
00915 # endif
00916 
00917 #endif
00918 }
00919 
00920 /* Similar to drawText(), but the text is handled as a classical 3D object of the scene.
00921 
00922 Although useful, this method is deprecated with recent Qt versions. Indeed, Qt renders text as
00923 pixmaps that cannot be oriented. However, when GLUT is used instead of Qt (when your Qt version is
00924 lower than 3.1, see drawText() documentation) orientated characters are possible and this method will work.
00925 
00926 \p pos and \p normal respectively represent the 3D coordinate of the text and the normal to the text
00927 plane. They are expressed with respect to the \e current \c GL_MODELVIEW matrix.
00928 
00929 If you want your text to always face the camera (normal parallel to camera()->viewDirection), use
00930 QGLWidget::renderText(x,y,z).
00931 
00932 See the <a href="../examples/draw3DText.html">draw3DText example</a> for an illustration. */
00933 /*
00934 void QGLViewer::draw3DText(const Vec& pos, const Vec& normal, const QString& text, GLfloat height)
00935 {
00936 #if QT_VERSION < QGLVIEWER_QT_VERSION_WITHOUT_GLUT
00937 if (!textIsEnabled())
00938 return;
00939 
00940 glMatrixMode(GL_MODELVIEW) ;
00941 glPushMatrix() ;
00942 
00943 const GLfloat font_scale = (119.05f - 33.33f) / 8; // see glutStrokeCharacter man page
00944 // const GLfloat font_scale = (119.05f - 33.33f) * 15.0f; // see glutStrokeCharacter man page
00945 
00946 static GLfloat lineWidth;
00947 glGetFloatv(GL_LINE_WIDTH, &lineWidth);
00948 
00949 glTranslatef(pos.x, pos.y, pos.z);
00950 glMultMatrixd(Quaternion(Vec(0.0, 0.0, 1.0), normal).matrix());
00951 
00952 glLineWidth(2.0);
00953 
00954 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
00955 glEnable(GL_BLEND);
00956 glEnable(GL_LINE_SMOOTH);
00957 
00958 const GLfloat scale = height / font_scale;
00959 glScalef(scale, scale, scale);
00960 
00961 for (uint i=0; i<text.length(); ++i)
00962 glutStrokeCharacter(GLUT_STROKE_ROMAN, text.at(i));
00963 
00964 glLineWidth(lineWidth);
00965 
00966 glMatrixMode(GL_MODELVIEW);
00967 glPopMatrix() ;
00968 #else
00969 static bool displayed = false;
00970 
00971 if (!displayed)
00972 {
00973 qWarning("draw3DText is not supported with Qt >= 3.1.");
00974 qWarning("Use QGLWidget::renderText() instead,");
00975 qWarning("or use the glut glutStrokeCharacter() method.");
00976 displayed = true;
00977 }
00978 
00979 Q_UNUSED(pos)
00980 Q_UNUSED(normal)
00981 Q_UNUSED(text)
00982 Q_UNUSED(height)
00983 #endif
00984 }
00985 */
00986 
00998 void QGLViewer::displayMessage(const QString& message, int delay)
00999 {
01000     message_ = message;
01001     displayMessage_ = true;
01002 #if QT_VERSION >= 0x040000
01003     // Was set to single shot in defaultConstructor.
01004     messageTimer_.start(delay);
01005 #else
01006     if (messageTimer_.isActive())
01007         messageTimer_.changeInterval(delay);
01008     else
01009         messageTimer_.start(delay, true);
01010 #endif
01011     if (textIsEnabled() && updateGLOK_)
01012         updateGL();
01013 }
01014 
01015 void QGLViewer::hideMessage()
01016 {
01017     displayMessage_ = false;
01018     if (textIsEnabled())
01019         updateGL();
01020 }
01021 
01022 
01032 void QGLViewer::displayFPS()
01033 {
01034     drawText(10, int(1.5*((QApplication::font().pixelSize()>0)?QApplication::font().pixelSize():QApplication::font().pointSize())), fpsString_);
01035 }
01036 
01068 void QGLViewer::startScreenCoordinatesSystem(bool upward) const
01069 {
01070     glMatrixMode(GL_PROJECTION);
01071     glPushMatrix();
01072     glLoadIdentity();
01073     if (tileRegion_ != NULL)
01074       if (upward)
01075         glOrtho(tileRegion_->xMin, tileRegion_->xMax, tileRegion_->yMin, tileRegion_->yMax, 0.0, -1.0);
01076       else
01077         glOrtho(tileRegion_->xMin, tileRegion_->xMax, tileRegion_->yMax, tileRegion_->yMin, 0.0, -1.0);
01078     else
01079       if (upward)
01080         glOrtho(0, width(), 0, height(), 0.0, -1.0);
01081       else
01082         glOrtho(0, width(), height(), 0, 0.0, -1.0);
01083 
01084     glMatrixMode(GL_MODELVIEW);
01085     glPushMatrix();
01086     glLoadIdentity();
01087 }
01088 
01093 void QGLViewer::stopScreenCoordinatesSystem() const
01094 {
01095     glMatrixMode(GL_PROJECTION);
01096     glPopMatrix();
01097 
01098     glMatrixMode(GL_MODELVIEW);
01099     glPopMatrix();
01100 }
01101 
01105 void QGLViewer::timerEvent(QTimerEvent *)
01106 {
01107     if (animationIsStarted())
01108     {
01109         animate();
01110         updateGL();
01111     }
01112 }
01113 
01115 void QGLViewer::startAnimation()
01116 {
01117     animationTimerId_ = startTimer(animationPeriod());
01118     animationStarted_ = true;
01119 }
01120 
01122 void QGLViewer::stopAnimation()
01123 {
01124     animationStarted_ = false;
01125     if (animationTimerId_ != 0)
01126         killTimer(animationTimerId_);
01127 }
01128 
01132 void QGLViewer::closeEvent(QCloseEvent *e)
01133 {
01134     // When the user clicks on the window close (x) button:
01135     // - If the viewer is a top level window, closeEvent is called and then saves to file.
01136     // - Otherwise, nothing happen s:(
01137     // When the user press the EXIT_VIEWER keyboard shortcut:
01138     // - If the viewer is a top level window, saveStateToFile() is also called
01139     // - Otherwise, closeEvent is NOT called and keyPressEvent does the job.
01140 
01141     /* After tests:
01142     E : Embedded widget
01143     N : Widget created with new
01144     C : closeEvent called
01145     D : destructor called
01146 
01147     E   N   C   D
01148     y   y
01149     y   n       y
01150     n   y   y
01151     n   n   y   y
01152 
01153     closeEvent is called iif the widget is NOT embedded.
01154 
01155     Destructor is called iif the widget is created on the stack
01156     or if widget (resp. parent if embedded) is created with WDestructiveClose flag.
01157 
01158     closeEvent always before destructor.
01159 
01160     Close using qApp->closeAllWindows or (x) is identical.
01161     */
01162 
01163     // #CONNECTION# Also done for EXIT_VIEWER in keyPressEvent().
01164     saveStateToFile();
01165     QGLWidget::closeEvent(e);
01166 }
01167 
01177 void QGLViewer::select(const QMouseEvent* event)
01178 {
01179     // For those who don't derive but rather rely on the signal-slot mechanism.
01180     Q_EMIT pointSelected(event);
01181     select(event->pos());
01182 }
01183 
01234 void QGLViewer::select(const QPoint& point)
01235 {
01236     beginSelection(point);
01237     drawWithNames();
01238     endSelection(point);
01239     postSelection(point);
01240 }
01241 
01253 void QGLViewer::beginSelection(const QPoint& point)
01254 {
01255     // Make OpenGL context current (may be needed with several viewers ?)
01256     makeCurrent();
01257 
01258     // Prepare the selection mode
01259     glSelectBuffer(selectBufferSize(), selectBuffer());
01260     glRenderMode(GL_SELECT);
01261     glInitNames();
01262 
01263     // Loads the matrices
01264     glMatrixMode(GL_PROJECTION);
01265     glLoadIdentity();
01266     static GLint viewport[4];
01267     camera()->getViewport(viewport);
01268     gluPickMatrix(point.x(), point.y(), selectRegionWidth(), selectRegionHeight(), viewport);
01269 
01270     // loadProjectionMatrix() first resets the GL_PROJECTION matrix with a glLoadIdentity().
01271     // The false parameter prevents this and hence multiplies the matrices.
01272     camera()->loadProjectionMatrix(false);
01273     // Reset the original (world coordinates) modelview matrix
01274     camera()->loadModelViewMatrix();
01275 }
01276 
01313 void QGLViewer::endSelection(const QPoint& point)
01314 {
01315     Q_UNUSED(point);
01316 
01317     // Flush GL buffers
01318     glFlush();
01319 
01320     // Get the number of objects that were seen through the pick matrix frustum. Reset GL_RENDER mode.
01321     GLint nbHits = glRenderMode(GL_RENDER);
01322 
01323     if (nbHits <= 0)
01324         setSelectedName(-1);
01325     else
01326     {
01327         // Interpret results: each object created 4 values in the selectBuffer().
01328         // selectBuffer[4*i+1] is the object minimum depth value, while selectBuffer[4*i+3] is the id pushed on the stack.
01329         // Of all the objects that were projected in the pick region, we select the closest one (zMin comparison).
01330         // This code needs to be modified if you use several stack levels. See glSelectBuffer() man page.
01331         GLuint zMin = (selectBuffer())[1];
01332         setSelectedName((selectBuffer())[3]);
01333         for (int i=1; i<nbHits; ++i)
01334             if ((selectBuffer())[4*i+1] < zMin)
01335             {
01336                 zMin = (selectBuffer())[4*i+1];
01337                 setSelectedName((selectBuffer())[4*i+3]);
01338             }
01339     }
01340 }
01341 
01345 void QGLViewer::setSelectBufferSize(int size)
01346 {
01347     if (selectBuffer_)
01348         delete[] selectBuffer_;
01349     selectBufferSize_ = size;
01350     selectBuffer_ = new GLuint[selectBufferSize()];
01351 }
01352 
01353 void QGLViewer::performClickAction(ClickAction ca, const QMouseEvent* const e)
01354 {
01355     // Note: action that need it should updateGL().
01356     switch (ca)
01357     {
01358         // # CONNECTION setMouseBinding prevents adding NO_CLICK_ACTION in clickBinding_
01359         // This case should hence not be possible. Prevents unused case warning.
01360     case NO_CLICK_ACTION :
01361         break;
01362     case ZOOM_ON_PIXEL :
01363         camera()->interpolateToZoomOnPixel(e->pos());
01364         break;
01365     case ZOOM_TO_FIT :
01366         camera()->interpolateToFitScene();
01367         break;
01368     case SELECT :
01369         select(e);
01370         updateGL();
01371         break;
01372     case RAP_FROM_PIXEL :
01373         if (camera()->setRevolveAroundPointFromPixel(e->pos()))
01374         {
01375             setVisualHintsMask(1);
01376             updateGL();
01377         }
01378         break;
01379     case RAP_IS_CENTER :
01380         camera()->setRevolveAroundPoint(sceneCenter());
01381         setVisualHintsMask(1);
01382         updateGL();
01383         break;
01384     case CENTER_FRAME :
01385         if (manipulatedFrame())
01386             manipulatedFrame()->projectOnLine(camera()->position(), camera()->viewDirection());
01387         break;
01388     case CENTER_SCENE :
01389         camera()->centerScene();
01390         break;
01391     case SHOW_ENTIRE_SCENE :
01392         camera()->showEntireScene();
01393         break;
01394     case ALIGN_FRAME :
01395         if (manipulatedFrame())
01396             manipulatedFrame()->alignWithFrame(camera()->frame());
01397         break;
01398     case ALIGN_CAMERA :
01399         camera()->frame()->alignWithFrame(NULL, true);
01400         break;
01401     }
01402 }
01403 
01420 void QGLViewer::mousePressEvent(QMouseEvent* e)
01421 {
01422     //#CONNECTION# mouseDoubleClickEvent has the same structure
01423     //#CONNECTION# mouseString() concatenates bindings description in inverse order.
01424     ClickActionPrivate cap;
01425     cap.doubleClick = false;
01426 #if QT_VERSION >= 0x040000
01427     cap.modifiers = e->modifiers();
01428     cap.button = e->button();
01429     cap.buttonsBefore = (QtMouseButtons)(e->buttons() & ~(e->button()));
01430 #else
01431     cap.modifiers = (QtKeyboardModifiers)(e->state() & Qt::KeyboardModifierMask);
01432     cap.button = (QtMouseButtons)((e->stateAfter() & Qt::MouseButtonMask) & (~(e->state() & Qt::MouseButtonMask)));
01433     cap.buttonsBefore = (QtMouseButtons)(e->state() & Qt::MouseButtonMask);
01434 #endif
01435 
01436     if (clickBinding_.contains(cap))
01437         performClickAction(clickBinding_[cap], e);
01438     else
01439         if (mouseGrabber())
01440         {
01441             if (mouseGrabberIsAManipulatedFrame_)
01442             {
01443                 for (QMap<int, MouseActionPrivate>::ConstIterator it=mouseBinding_.begin(), end=mouseBinding_.end(); it!=end; ++it)
01444 #if QT_VERSION >= 0x040000
01445                     if ((it.value().handler == FRAME) && ((it.key() & Qt::MouseButtonMask) == e->buttons()))
01446 #else
01447                     if ((it.data().handler == FRAME) && ((it.key() & Qt::MouseButtonMask) == (e->stateAfter() & Qt::MouseButtonMask)))
01448 #endif
01449                     {
01450                         ManipulatedFrame* mf = dynamic_cast<ManipulatedFrame*>(mouseGrabber());
01451                         if (mouseGrabberIsAManipulatedCameraFrame_)
01452                         {
01453                             mf->ManipulatedFrame::startAction(it.value().action, it.value().withConstraint);
01454                             mf->ManipulatedFrame::mousePressEvent(e, camera());
01455                         }
01456                         else
01457                         {
01458                             mf->startAction(it.value().action, it.value().withConstraint);
01459                             mf->mousePressEvent(e, camera());
01460                         }
01461                         break;
01462                     }
01463             }
01464             else
01465                 mouseGrabber()->mousePressEvent(e, camera());
01466             updateGL();
01467         }
01468         else
01469         {
01470             //#CONNECTION# wheelEvent has the same structure
01471 #if QT_VERSION >= 0x040000
01472             const int state = e->modifiers() | e->buttons();
01473 #else
01474             const int state = e->stateAfter();
01475 #endif
01476 
01477             if (mouseBinding_.contains(state))
01478             {
01479                 MouseActionPrivate map = mouseBinding_[state];
01480                 switch (map.handler)
01481                 {
01482                 case CAMERA :
01483                     camera()->frame()->startAction(map.action, map.withConstraint);
01484                     camera()->frame()->mousePressEvent(e, camera());
01485                     break;
01486                 case FRAME :
01487                     if (manipulatedFrame())
01488                     {
01489                         if (manipulatedFrameIsACamera_)
01490                         {
01491                             manipulatedFrame()->ManipulatedFrame::startAction(map.action, map.withConstraint);
01492                             manipulatedFrame()->ManipulatedFrame::mousePressEvent(e, camera());
01493                         }
01494                         else
01495                         {
01496                             manipulatedFrame()->startAction(map.action, map.withConstraint);
01497                             manipulatedFrame()->mousePressEvent(e, camera());
01498                         }
01499                     }
01500                     break;
01501                 }
01502                 if (map.action == SCREEN_ROTATE)
01503                     // Display visual hint line
01504                     updateGL();
01505             }
01506 #if QT_VERSION >= 0x030000
01507             else
01508                 e->ignore();
01509 #endif
01510         }
01511 }
01512 
01552 void QGLViewer::mouseMoveEvent(QMouseEvent* e)
01553 {
01554     if (mouseGrabber())
01555     {
01556         mouseGrabber()->checkIfGrabsMouse(e->x(), e->y(), camera());
01557         if (mouseGrabber()->grabsMouse())
01558             if (mouseGrabberIsAManipulatedCameraFrame_)
01559                 (dynamic_cast<ManipulatedFrame*>(mouseGrabber()))->ManipulatedFrame::mouseMoveEvent(e, camera());
01560             else
01561                 mouseGrabber()->mouseMoveEvent(e, camera());
01562         else
01563             setMouseGrabber(NULL);
01564         updateGL();
01565     }
01566 
01567     if (!mouseGrabber())
01568     {
01569         //#CONNECTION# mouseReleaseEvent has the same structure
01570         if (camera()->frame()->isManipulated())
01571         {
01572             camera()->frame()->mouseMoveEvent(e, camera());
01573             // #CONNECTION# manipulatedCameraFrame::mouseMoveEvent specific if at the beginning
01574             if (camera()->frame()->action_ == ZOOM_ON_REGION)
01575                 updateGL();
01576         }
01577         else // !
01578             if ((manipulatedFrame()) && (manipulatedFrame()->isManipulated()))
01579                 if (manipulatedFrameIsACamera_)
01580                     manipulatedFrame()->ManipulatedFrame::mouseMoveEvent(e, camera());
01581                 else
01582                     manipulatedFrame()->mouseMoveEvent(e, camera());
01583             else
01584                 if (hasMouseTracking())
01585                 {
01586 #if QT_VERSION >= 0x040000
01587                     Q_FOREACH (MouseGrabber* mg, MouseGrabber::MouseGrabberPool())
01588                     {
01589 #else
01590                     QPtrListIterator<MouseGrabber> it(MouseGrabber::MouseGrabberPool());
01591                     for (MouseGrabber* mg; (mg = it.current()); ++it)
01592                     {
01593 #endif
01594                         mg->checkIfGrabsMouse(e->x(), e->y(), camera());
01595                         if (mg->grabsMouse())
01596                         {
01597                             setMouseGrabber(mg);
01598                             // Check that MouseGrabber is not disabled
01599                             if (mouseGrabber() == mg)
01600                             {
01601                                 updateGL();
01602                                 break;
01603                             }
01604                         }
01605                     }
01606                 }
01607     }
01608 }
01609 
01615 void QGLViewer::mouseReleaseEvent(QMouseEvent* e)
01616 {
01617     if (mouseGrabber())
01618     {
01619         if (mouseGrabberIsAManipulatedCameraFrame_)
01620             (dynamic_cast<ManipulatedFrame*>(mouseGrabber()))->ManipulatedFrame::mouseReleaseEvent(e, camera());
01621         else
01622             mouseGrabber()->mouseReleaseEvent(e, camera());
01623         mouseGrabber()->checkIfGrabsMouse(e->x(), e->y(), camera());
01624         if (!(mouseGrabber()->grabsMouse()))
01625             setMouseGrabber(NULL);
01626         // updateGL();
01627     }
01628     else
01629         //#CONNECTION# mouseMoveEvent has the same structure
01630         if (camera()->frame()->isManipulated())
01631         {
01632             // bool updateGLNeeded = ((camera()->frame()->action_ == ZOOM_ON_REGION) ||
01633             // (camera()->frame()->action_ == SCREEN_ROTATE));
01634             camera()->frame()->mouseReleaseEvent(e, camera());
01635             // if (updateGLNeeded)
01636             // Needed in all cases because of fastDraw().
01637             // updateGL();
01638         }
01639         else
01640             if ((manipulatedFrame()) && (manipulatedFrame()->isManipulated()))
01641             {
01642                 // bool updateGLNeeded = (manipulatedFrame()->action_ == SCREEN_ROTATE);
01643                 if (manipulatedFrameIsACamera_)
01644                     manipulatedFrame()->ManipulatedFrame::mouseReleaseEvent(e, camera());
01645                 else
01646                     manipulatedFrame()->mouseReleaseEvent(e, camera());
01647                 // if (updateGLNeeded)
01648                 // updateGL();
01649             }
01650 #if QT_VERSION >= 0x030000
01651             else
01652                 e->ignore();
01653 #endif
01654 
01655     // Not absolutely needed (see above commented code for the optimal version), but may reveal
01656     // useful for specific applications.
01657     updateGL();
01658 }
01659 
01664 void QGLViewer::wheelEvent(QWheelEvent* e)
01665 {
01666     if (mouseGrabber())
01667     {
01668         if (mouseGrabberIsAManipulatedFrame_)
01669         {
01670             for (QMap<QtKeyboardModifiers, MouseActionPrivate>::ConstIterator it=wheelBinding_.begin(), end=wheelBinding_.end(); it!=end; ++it)
01671                 if (it.value().handler == FRAME)
01672                 {
01673                     ManipulatedFrame* mf = dynamic_cast<ManipulatedFrame*>(mouseGrabber());
01674                     if (mouseGrabberIsAManipulatedCameraFrame_)
01675                     {
01676                         mf->ManipulatedFrame::startAction(it.value().action, it.value().withConstraint);
01677                         mf->ManipulatedFrame::wheelEvent(e, camera());
01678                     }
01679                     else
01680                     {
01681                         mf->startAction(it.value().action, it.value().withConstraint);
01682                         mf->wheelEvent(e, camera());
01683                     }
01684                     break;
01685                 }
01686         }
01687         else
01688             mouseGrabber()->wheelEvent(e, camera());
01689         updateGL();
01690     }
01691     else
01692     {
01693         //#CONNECTION# mousePressEvent has the same structure
01694 #if QT_VERSION >= 0x040000
01695         const QtKeyboardModifiers modifiers = e->modifiers();
01696 #else
01697         const QtKeyboardModifiers modifiers = e->state();
01698 #endif
01699         if (wheelBinding_.contains(modifiers))
01700         {
01701             MouseActionPrivate map = wheelBinding_[modifiers];
01702             switch (map.handler)
01703             {
01704             case CAMERA :
01705                 camera()->frame()->startAction(map.action, map.withConstraint);
01706                 camera()->frame()->wheelEvent(e, camera());
01707                 break;
01708             case FRAME :
01709                 if (manipulatedFrame()) {
01710                     if (manipulatedFrameIsACamera_)
01711                     {
01712                         manipulatedFrame()->ManipulatedFrame::startAction(map.action, map.withConstraint);
01713                         manipulatedFrame()->ManipulatedFrame::wheelEvent(e, camera());
01714                     }
01715                     else
01716                     {
01717                         manipulatedFrame()->startAction(map.action, map.withConstraint);
01718                         manipulatedFrame()->wheelEvent(e, camera());
01719                     }
01720                 }
01721                 break;
01722             }
01723         }
01724 #if QT_VERSION >= 0x030000
01725         else
01726             e->ignore();
01727 #endif
01728     }
01729 }
01730 
01735 void QGLViewer::mouseDoubleClickEvent(QMouseEvent* e)
01736 {
01737     //#CONNECTION# mousePressEvent has the same structure
01738     ClickActionPrivate cap;
01739     cap.doubleClick = true;
01740 #if QT_VERSION >= 0x040000
01741     cap.modifiers = e->modifiers();
01742     cap.button = e->button();
01743     cap.buttonsBefore = (QtMouseButtons)(e->buttons() & ~(e->button()));
01744 #else
01745     cap.modifiers = (QtKeyboardModifiers)(e->state() & Qt::KeyboardModifierMask);
01746     cap.button = (QtMouseButtons)((e->stateAfter() & Qt::MouseButtonMask) & (~(e->state() & Qt::MouseButtonMask)));
01747     cap.buttonsBefore = (QtMouseButtons)(e->state() & Qt::MouseButtonMask);
01748 #endif
01749     if (clickBinding_.contains(cap))
01750         performClickAction(clickBinding_[cap], e);
01751     else
01752         if (mouseGrabber())
01753             mouseGrabber()->mouseDoubleClickEvent(e, camera());
01754 #if QT_VERSION >= 0x030000
01755         else
01756             e->ignore();
01757 #endif
01758 }
01759 
01764 void QGLViewer::setStereoDisplay(bool stereo)
01765 {
01766     if (format().stereo())
01767     {
01768         stereo_ = stereo;
01769         if (!displaysInStereo())
01770         {
01771             glDrawBuffer(GL_BACK_LEFT);
01772             glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
01773             glDrawBuffer(GL_BACK_RIGHT);
01774             glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
01775         }
01776 
01777         Q_EMIT stereoChanged(stereo_);
01778 
01779         if (updateGLOK_)
01780             updateGL();
01781     }
01782     else
01783         if (stereo)
01784             QMessageBox::warning(this, tr("Stereo not supported", "Message box window title"), tr("Stereo is not supported on this display."));
01785         else
01786             stereo_ = false;
01787 }
01788 
01793 void QGLViewer::setFullScreen(bool fullScreen)
01794 {
01795     fullScreen_ = fullScreen;
01796 
01797     // Tricky. A timer does it later if !updateGLOK_.
01798     if (!updateGLOK_)
01799         return;
01800 
01801     QWidget* tlw = topLevelWidget();
01802 
01803     if (isFullScreen())
01804     {
01805         prevPos_ = topLevelWidget()->pos();
01806         tlw->showFullScreen();
01807         tlw->move(0,0);
01808     }
01809     else
01810     {
01811         tlw->showNormal();
01812         tlw->move(prevPos_);
01813     }
01814 }
01815 
01822 void QGLViewer::setMouseGrabber(MouseGrabber* mouseGrabber)
01823 {
01824     if (!mouseGrabberIsEnabled(mouseGrabber))
01825         return;
01826 
01827     mouseGrabber_ = mouseGrabber;
01828 
01829     mouseGrabberIsAManipulatedFrame_       = (dynamic_cast<ManipulatedFrame*>(mouseGrabber) != NULL);
01830     mouseGrabberIsAManipulatedCameraFrame_ = ((dynamic_cast<ManipulatedCameraFrame*>(mouseGrabber) != NULL) &&
01831         (mouseGrabber != camera()->frame()));
01832     Q_EMIT mouseGrabberChanged(mouseGrabber);
01833 }
01834 
01836 void QGLViewer::setMouseGrabberIsEnabled(const qglviewer::MouseGrabber* const mouseGrabber, bool enabled)
01837 {
01838     if (enabled)
01839         disabledMouseGrabbers_.remove(reinterpret_cast<size_t>(mouseGrabber));
01840     else
01841         disabledMouseGrabbers_[reinterpret_cast<size_t>(mouseGrabber)];
01842 }
01843 
01844 static QString keyboardModifiersString(QtKeyboardModifiers m, bool noButton=false)
01845 {
01846 #if QT_VERSION >= 0x040000
01847     if (noButton && (m==Qt::NoModifier)) 
01848 #else
01849     if (noButton && (m==Qt::NoButton))
01850 #endif
01851         return QGLViewer::tr("(no button)");
01852     
01853     QString keySequence = "";
01854 
01855 #if QT_VERSION >= 0x040000
01856     if (m & Qt::ControlModifier) keySequence += "Ctrl+";
01857     if (m & Qt::AltModifier) keySequence += "Alt+";
01858     if (m & Qt::ShiftModifier) keySequence += "Shift+";
01859     if (m & Qt::MetaModifier) keySequence += "Meta+";
01860 #else
01861     if (m & Qt::ControlButton) keySequence += "Ctrl+";
01862     if (m & Qt::AltButton) keySequence += "Alt+";
01863     if (m & Qt::ShiftButton) keySequence += "Shift+";
01864 # if QT_VERSION >= 0x030000
01865     if (m & Qt::MetaButton) keySequence += "Meta+";
01866 # endif
01867 #endif
01868 
01869     if (keySequence.length() > 0)
01870 #if QT_VERSION >= 0x040000
01871         return QKeySequence(keySequence+"X").toString(QKeySequence::NativeText).replace("X", "");
01872 #else
01873         return QString(QKeySequence(keySequence+"X")).replace("X", "");
01874 #endif
01875     else
01876         return QString();
01877 }
01878 
01879 static QString mouseButtonsString(QtMouseButtons b)
01880 {
01881     QString result("");
01882     bool addAmpersand = false;
01883     if (b & Qt::LeftButton)    { result += QGLViewer::tr("Left", "left mouse button"); addAmpersand=true; }
01884     if (b & Qt::MidButton)     { if (addAmpersand) result += " & "; result += QGLViewer::tr("Middle", "middle mouse button"); addAmpersand=true; }
01885     if (b & Qt::RightButton)   { if (addAmpersand) result += " & "; result += QGLViewer::tr("Right", "right mouse button"); }
01886     return result;
01887 }
01888 
01889 QString QGLViewer::mouseActionString(QGLViewer::MouseAction ma)
01890 {
01891     switch (ma)
01892     {
01893     case QGLViewer::NO_MOUSE_ACTION : return QString::null;
01894     case QGLViewer::ROTATE : return QGLViewer::tr("Rotates", "ROTATE mouse action");
01895     case QGLViewer::ZOOM : return QGLViewer::tr("Zooms", "ZOOM mouse action");
01896     case QGLViewer::TRANSLATE : return QGLViewer::tr("Translates", "TRANSLATE mouse action");
01897     case QGLViewer::MOVE_FORWARD : return QGLViewer::tr("Moves forward", "MOVE_FORWARD mouse action");
01898     case QGLViewer::LOOK_AROUND : return QGLViewer::tr("Looks around", "LOOK_AROUND mouse action");
01899     case QGLViewer::MOVE_BACKWARD : return QGLViewer::tr("Moves backward", "MOVE_BACKWARD mouse action");
01900     case QGLViewer::SCREEN_ROTATE : return QGLViewer::tr("Rotates in screen plane", "SCREEN_ROTATE mouse action");
01901     case QGLViewer::ROLL : return QGLViewer::tr("Rolls", "ROLL mouse action");
01902     case QGLViewer::DRIVE : return QGLViewer::tr("Drives", "DRIVE mouse action");
01903     case QGLViewer::SCREEN_TRANSLATE : return QGLViewer::tr("Horizontally/Vertically translates", "SCREEN_TRANSLATE mouse action");
01904     case QGLViewer::ZOOM_ON_REGION : return QGLViewer::tr("Zooms on region for", "ZOOM_ON_REGION mouse action");
01905     }
01906     return QString::null;
01907 }
01908 
01909 QString QGLViewer::clickActionString(QGLViewer::ClickAction ca)
01910 {
01911     switch (ca)
01912     {
01913     case QGLViewer::NO_CLICK_ACTION : return QString::null;
01914     case QGLViewer::ZOOM_ON_PIXEL : return QGLViewer::tr("Zooms on pixel", "ZOOM_ON_PIXEL click action");
01915     case QGLViewer::ZOOM_TO_FIT : return QGLViewer::tr("Zooms to fit scene", "ZOOM_TO_FIT click action");
01916     case QGLViewer::SELECT : return QGLViewer::tr("Selects", "SELECT click action");
01917     case QGLViewer::RAP_FROM_PIXEL : return QGLViewer::tr("Sets revolve around point", "RAP_FROM_PIXEL click action");
01918     case QGLViewer::RAP_IS_CENTER : return QGLViewer::tr("Resets revolve around point", "RAP_IS_CENTER click action");
01919     case QGLViewer::CENTER_FRAME : return QGLViewer::tr("Centers frame", "CENTER_FRAME click action");
01920     case QGLViewer::CENTER_SCENE : return QGLViewer::tr("Centers scene", "CENTER_SCENE click action");
01921     case QGLViewer::SHOW_ENTIRE_SCENE : return QGLViewer::tr("Shows entire scene", "SHOW_ENTIRE_SCENE click action");
01922     case QGLViewer::ALIGN_FRAME : return QGLViewer::tr("Aligns frame", "ALIGN_FRAME click action");
01923     case QGLViewer::ALIGN_CAMERA : return QGLViewer::tr("Aligns camera", "ALIGN_CAMERA click action");
01924     }
01925     return QString::null;
01926 }
01927 
01928 QString QGLViewer::formatClickActionPrivate(ClickActionPrivate cap)
01929 {
01930     bool buttonsBefore = cap.buttonsBefore != Qt::NoButton;
01931     return tr("%1%2%3%4%5%6", "Modifier / button or wheel / double click / with / button / pressed")
01932         .arg(keyboardModifiersString(cap.modifiers))
01933         .arg(mouseButtonsString(cap.button)+(cap.button == Qt::NoButton ? tr("Wheel", "Mouse wheel") : ""))
01934         .arg(cap.doubleClick ? tr(" double click", "Suffix after mouse button") : "")
01935         .arg(buttonsBefore ? tr(" with ", "As in : Left button with Ctrl pressed") : "")
01936         .arg(buttonsBefore ? mouseButtonsString(cap.buttonsBefore) : "")
01937         .arg(buttonsBefore ? tr(" pressed", "As in : Left button with Ctrl pressed") : "");
01938 }
01939 
01974 void QGLViewer::setMouseBindingDescription(int state, QString description, bool doubleClick, QtMouseButtons buttonsBefore)
01975 {
01976     ClickActionPrivate cap;
01977     cap.modifiers = QtKeyboardModifiers(convertToKeyboardModifiers(state) & Qt::KeyboardModifierMask);
01978     cap.button = QtMouseButtons(state & Qt::MouseButtonMask);
01979     cap.doubleClick = doubleClick;
01980     cap.buttonsBefore = buttonsBefore;
01981 
01982     if (description.isEmpty())
01983         mouseDescription_.remove(cap);
01984     else
01985         mouseDescription_[cap] = description;
01986 }
01987 
01988 static QString tableLine(const QString& left, const QString& right)
01989 {
01990     static bool even = false;
01991     const QString tdtd("</b></td><td>");
01992     const QString tdtr("</td></tr>\n");
01993 
01994     QString res("<tr bgcolor=\"");
01995 
01996     if (even)
01997         res += "#eeeeff\">";
01998     else
01999         res += "#ffffff\">";
02000     res += "<td><b>" + left + tdtd + right + tdtr;
02001     even = !even;
02002 
02003     return res;
02004 }
02005 
02015 QString QGLViewer::mouseString() const
02016 {
02017     QString text("<center><table border=\"1\" cellspacing=\"0\" cellpadding=\"4\">\n");
02018     const QString trtd("<tr><td>");
02019     const QString tdtr("</td></tr>\n");
02020     const QString tdtd("</td><td>");
02021 
02022     text += QString("<tr bgcolor=\"#aaaacc\"><th align=\"center\">%1</th><th align=\"center\">%2</th></tr>\n").
02023         arg(tr("Button(s)", "Buttons column header in help window mouse tab")).arg(tr("Description", "Description column header in help window mouse tab"));
02024 
02025     QMap<ClickActionPrivate, QString> mouseBinding;
02026 
02027     // User-defined mouse bindings come first.
02028     for (QMap<ClickActionPrivate, QString>::ConstIterator itm=mouseDescription_.begin(), endm=mouseDescription_.end(); itm!=endm; ++itm)
02029         mouseBinding[itm.key()] = itm.value();
02030 
02031     for (QMap<ClickActionPrivate, QString>::ConstIterator it=mouseBinding.begin(), end=mouseBinding.end(); it != end; ++it)
02032     {
02033         // Should not be needed (see setMouseBindingDescription())
02034         if (it.value().isNull())
02035             continue;
02036 
02037         text += tableLine(formatClickActionPrivate(it.key()), it.value());
02038     }
02039 
02040     // Optionnal separator line
02041     if (!mouseBinding.isEmpty())
02042     {
02043         mouseBinding.clear();
02044         text += QString("<tr bgcolor=\"#aaaacc\"><td colspan=2>%1</td></tr>\n").arg(tr("Standard mouse bindings", "In help window mouse tab"));
02045     }
02046 
02047     // Then concatenates the descriptions of wheelBinding_, mouseBinding_ and clickBinding_.
02048     // The order is significant and corresponds to the priorities set in mousePressEvent() (reverse priority order, last one overwrites previous)
02049     // #CONNECTION# mousePressEvent() order
02050     for (QMap<QtKeyboardModifiers, MouseActionPrivate>::ConstIterator itw=wheelBinding_.begin(), endw=wheelBinding_.end(); itw != endw; ++itw)
02051     {
02052         ClickActionPrivate cap;
02053         cap.doubleClick = false;
02054         cap.modifiers = itw.key();
02055         cap.button = Qt::NoButton;
02056         cap.buttonsBefore = Qt::NoButton;
02057 
02058         QString text = mouseActionString(itw.value().action);
02059 
02060         if (!text.isNull())
02061         {
02062             switch (itw.value().handler)
02063             {
02064             case CAMERA: text += " " + tr("camera", "Suffix after action"); break;
02065             case FRAME:  text += " " + tr("manipulated frame", "Suffix after action"); break;
02066             }
02067             if (!(itw.value().withConstraint))
02068                 text += "*";
02069         }
02070 
02071         mouseBinding[cap] = text;
02072     }
02073 
02074     for (QMap<int, MouseActionPrivate>::ConstIterator itmb=mouseBinding_.begin(), endmb=mouseBinding_.end();
02075         itmb != endmb; ++itmb)
02076     {
02077         ClickActionPrivate cap;
02078         cap.doubleClick = false;
02079         cap.modifiers = QtKeyboardModifiers(itmb.key() & Qt::KeyboardModifierMask);
02080         cap.button = QtMouseButtons(itmb.key() & Qt::MouseButtonMask);
02081         cap.buttonsBefore = Qt::NoButton;
02082 
02083         QString text = mouseActionString(itmb.value().action);
02084 
02085         if (!text.isNull())
02086         {
02087             switch (itmb.value().handler)
02088             {
02089             case CAMERA: text += " " + tr("camera", "Suffix after action"); break;
02090             case FRAME:  text += " " + tr("manipulated frame", "Suffix after action"); break;
02091             }
02092             if (!(itmb.value().withConstraint))
02093                 text += "*";
02094         }
02095         mouseBinding[cap] = text;
02096     }
02097 
02098     for (QMap<ClickActionPrivate, ClickAction>::ConstIterator itcb=clickBinding_.begin(), endcb=clickBinding_.end(); itcb!=endcb; ++itcb)
02099         mouseBinding[itcb.key()] = clickActionString(itcb.value());
02100 
02101     for (QMap<ClickActionPrivate, QString>::ConstIterator it2=mouseBinding.begin(), end2=mouseBinding.end(); it2 != end2; ++it2)
02102     {
02103         if (it2.value().isNull())
02104             continue;
02105 
02106         text += tableLine(formatClickActionPrivate(it2.key()), it2.value());
02107     }
02108 
02109     text += "</table></center>";
02110 
02111     return text;
02112 }
02113 
02128 void QGLViewer::setKeyDescription(int key, QString description)
02129 {
02130 #if QT_VERSION >= 0x030000
02131     // #CONNECTION# keyString. In Qt 2.3, longs modifier overlap with key codes.
02132     key = convertToKeyboardModifiers(key);
02133 #endif
02134     if (description.isEmpty())
02135         keyDescription_.remove(key);
02136     else
02137         keyDescription_[key] = description;
02138 }
02139 
02140 static QString keyString(int key)
02141 {
02142 #if QT_VERSION >= 0x030000
02143     return QString(QKeySequence(convertToShortModifier(key)));
02144 #else
02145     // #CONNECTION# setKeyDescription. In Qt 2.3, longs modifier overlap with key codes.
02146     return QString(QKeySequence(key));
02147 #endif
02148 }
02149 
02150 QString QGLViewer::cameraPathKeysString() const
02151 {
02152     if (pathIndex_.isEmpty())
02153         return QString::null;
02154 
02155 #if QT_VERSION >= 0x040000 || QT_VERSION < 0x030000
02156     QVector<int> keys;
02157 #else
02158     QValueVector<int> keys;
02159 #endif
02160     keys.reserve(pathIndex_.count());
02161     for (QMap<Qt::Key, int>::ConstIterator i = pathIndex_.begin(), endi=pathIndex_.end(); i != endi; ++i)
02162         keys.push_back(i.key());
02163 #if QT_VERSION >= 0x040000
02164     qSort(keys);
02165 #else
02166 # if QT_VERSION >= 0x030000
02167     qHeapSort(keys);
02168 # else
02169     sort(keys.begin(), keys.end());
02170 # endif
02171 #endif
02172 
02173 #if QT_VERSION >= 0x040000 || QT_VERSION < 0x030000
02174     QVector<int>::const_iterator it = keys.begin(), end = keys.end();
02175 #else
02176     QValueVector<int>::const_iterator it = keys.begin(), end = keys.end();
02177 #endif
02178     QString res = keyString(*it);
02179 
02180     const int maxDisplayedKeys = 6;
02181     int nbDisplayedKeys = 0;
02182     int previousKey = (*it);
02183     int state = 0;
02184     ++it;
02185     while ((it != end) && (nbDisplayedKeys < maxDisplayedKeys-1))
02186     {
02187         switch (state)
02188         {
02189         case 0 :
02190             if ((*it) == previousKey + 1)
02191                 state++;
02192             else
02193             {
02194                 res += ", " + keyString(*it);
02195                 nbDisplayedKeys++;
02196             }
02197             break;
02198         case 1 :
02199             if ((*it) == previousKey + 1)
02200                 state++;
02201             else
02202             {
02203                 res += ", " + keyString(previousKey);
02204                 res += ", " + keyString(*it);
02205                 nbDisplayedKeys += 2;
02206                 state = 0;
02207             }
02208             break;
02209         default :
02210             if ((*it) != previousKey + 1)
02211             {
02212                 res += ".." + keyString(previousKey);
02213                 res += ", " + keyString(*it);
02214                 nbDisplayedKeys += 2;
02215                 state = 0;
02216             }
02217             break;
02218         }
02219         previousKey = *it;
02220         ++it;
02221     }
02222 
02223     if (state == 1)
02224         res += ", " + keyString(previousKey);
02225     if (state == 2)
02226         res += ".." + keyString(previousKey);
02227     if (it != end)
02228         res += "...";
02229 
02230     return res;
02231 }
02232 
02241 QString QGLViewer::keyboardString() const
02242 {
02243     QString text("<center><table border=\"1\" cellspacing=\"0\" cellpadding=\"4\">\n");
02244     text += QString("<tr bgcolor=\"#aaaacc\"><th align=\"center\">%1</th><th align=\"center\">%2</th></tr>\n").
02245         arg(QGLViewer::tr("Key(s)", "Keys column header in help window mouse tab")).arg(QGLViewer::tr("Description", "Description column header in help window mouse tab"));
02246 
02247     QMap<int, QString> keyDescription;
02248 
02249     // 1 - User defined key descriptions
02250     for (QMap<int, QString>::ConstIterator kd=keyDescription_.begin(), kdend=keyDescription_.end(); kd!=kdend; ++kd)
02251         keyDescription[kd.key()] = kd.value();
02252 
02253     // Add to text in sorted order
02254     for (QMap<int, QString>::ConstIterator kb=keyDescription.begin(), endb=keyDescription.end(); kb!=endb; ++kb)
02255         text += tableLine(keyString(kb.key()), kb.value());
02256 
02257 
02258     // 2 - Optional separator line
02259     if (!keyDescription.isEmpty())
02260     {
02261         keyDescription.clear();
02262         text += QString("<tr bgcolor=\"#aaaacc\"><td colspan=2>%1</td></tr>\n").arg(QGLViewer::tr("Standard viewer keys", "In help window keys tab"));
02263     }
02264 
02265 
02266     // 3 - KeyboardAction bindings description
02267     for (QMap<KeyboardAction, int>::ConstIterator it=keyboardBinding_.begin(), end=keyboardBinding_.end(); it != end; ++it)
02268         if ((it.value() != 0) && ((!cameraIsInRevolveMode()) || ((it.key() != INCREASE_FLYSPEED) && (it.key() != DECREASE_FLYSPEED))))
02269             keyDescription[it.value()] = keyboardActionDescription_[it.key()];
02270 
02271     // Add to text in sorted order
02272     for (QMap<int, QString>::ConstIterator kb2=keyDescription.begin(), endb2=keyDescription.end(); kb2!=endb2; ++kb2)
02273         text += tableLine(keyString(kb2.key()), kb2.value());
02274 
02275 
02276     // 4 - Camera paths keys description
02277     const QString cpks = cameraPathKeysString();
02278     if (!cpks.isNull())
02279     {
02280         text += "<tr bgcolor=\"#ccccff\"><td colspan=2>\n";
02281         text += QGLViewer::tr("Camera paths are controlled using %1 (noted <i>Fx</i> below):", "Help window key tab camera keys").arg(cpks) + "</td></tr>\n";
02282         text += tableLine(keyboardModifiersString(playPathKeyboardModifiers()) + "<i>" + QGLViewer::tr("Fx", "Generic function key (F1..F12)") + "</i>",
02283             QGLViewer::tr("Plays path (or resets saved position)"));
02284         text += tableLine(keyboardModifiersString(addKeyFrameKeyboardModifiers()) + "<i>" + QGLViewer::tr("Fx", "Generic function key (F1..F12)") + "</i>",
02285             QGLViewer::tr("Adds a key frame to path (or defines a position)"));
02286         text += tableLine(keyboardModifiersString(addKeyFrameKeyboardModifiers()) + "<i>" + QGLViewer::tr("Fx", "Generic function key (F1..F12)") + "</i>+<i>" + QGLViewer::tr("Fx", "Generic function key (F1..F12)") + "</i>",
02287             QGLViewer::tr("Deletes path (or saved position)"));
02288     }
02289     text += "</table></center>";
02290 
02291     return text;
02292 }
02293 
02295 void QGLViewer::aboutQGLViewer() {
02296     help();
02297 #if QT_VERSION >= 0x040000
02298     helpWidget()->setCurrentIndex(3);
02299 #else
02300     helpWidget()->setCurrentPage(3);
02301 #endif
02302 }
02303 
02304     
02314 void QGLViewer::help()
02315 {
02316     Q_EMIT helpRequired();
02317 
02318     bool resize = false;
02319     int width=600;
02320     int height=400;
02321 
02322     static QString label[] = {tr("&Help", "Help window tab title"), tr("&Keyboard", "Help window tab title"), tr("&Mouse", "Help window tab title"), tr("&About", "Help window about title")};
02323 
02324     if (!helpWidget())
02325     {
02326         // Qt4 requires a NULL parent...
02327         helpWidget_ = new QTabWidget(NULL);
02328 #if QT_VERSION >= 0x040000
02329         helpWidget()->setWindowTitle(tr("Help", "Help window title"));
02330 #else
02331         helpWidget()->setCaption(tr("Help", "Help window title"));
02332 #endif
02333 
02334         resize = true;
02335         for (int i=0; i<4; ++i)
02336         {
02337             QTextEdit* tab = new QTextEdit(NULL);
02338 #if QT_VERSION >= 0x030000
02339             tab->setReadOnly(true);
02340 #endif
02341 
02342 #if QT_VERSION >= 0x040000
02343             helpWidget()->insertTab(i, tab, label[i]);
02344             if (i==3) {
02345 #               include "qglviewer-icon.xpm"
02346                 QPixmap pixmap(qglviewer_icon);
02347                 tab->document()->addResource(QTextDocument::ImageResource,
02348                                              QUrl("mydata://qglviewer-icon.xpm"), QVariant(pixmap));
02349             }
02350 #else
02351             tab->setTextFormat(Qt::RichText);
02352             helpWidget()->insertTab(tab, label[i]);
02353 #endif
02354         }
02355     }
02356     
02357     
02358 #if QT_VERSION < 0x030000
02359     const int currentPageIndex = helpWidget()->currentPageIndex();
02360 #endif
02361 
02362     for (int i=0; i<4; ++i)
02363     {
02364         QString text;
02365         switch (i)
02366         {
02367         case 0 : text = helpString();     break;
02368         case 1 : text = keyboardString(); break;
02369         case 2 : text = mouseString();    break;
02370         case 3 : text = QString("<center><br><img src=\"mydata://qglviewer-icon.xpm\">") + tr(
02371                            "<h1>libQGLViewer</h1>"
02372                            "<h3>Version %1</h3><br>"                                                                      
02373                            "A versatile 3D viewer based on OpenGL and Qt<br>"
02374                            "Copyright 2002-%2 Gilles Debunne<br>"
02375                            "<code>%3</code>").arg(QGLViewerVersionString()).arg("2010").arg("http://www.libqglviewer.com") +
02376                            QString("</center>");
02377                 break;
02378         default : break;
02379         }
02380 
02381 #if QT_VERSION >= 0x040000
02382         QTextEdit* textEdit = (QTextEdit*)(helpWidget()->widget(i));
02383         textEdit->setHtml(text);
02384 #else
02385 # if QT_VERSION < 0x030000
02386         helpWidget()->setCurrentPage(i);
02387         QTextEdit* textEdit = (QTextEdit*)(helpWidget()->currentPage());
02388 # else
02389         QTextEdit* textEdit = (QTextEdit*)(helpWidget()->page(i));
02390 # endif
02391         textEdit->setText(text);
02392 #endif
02393 
02394 #if QT_VERSION < 0x040000
02395         if (resize && (textEdit->heightForWidth(width) > height))
02396             height = textEdit->heightForWidth(width);
02397 #else
02398         if (resize && (textEdit->height() > height))
02399             height = textEdit->height();
02400 #endif
02401     }
02402 
02403 #if QT_VERSION < 0x030000
02404     helpWidget()->setCurrentPage(currentPageIndex);
02405 #endif
02406 
02407     if (resize)
02408         helpWidget()->resize(width, height+40); // 40 pixels is ~ tabs' height
02409     helpWidget()->show();
02410     helpWidget()->raise();
02411 }
02412 
02439 void QGLViewer::keyPressEvent(QKeyEvent *e)
02440 {
02441     if (e->key() == 0)
02442     {
02443         e->ignore();
02444         return;
02445     }
02446 
02447     const Qt::Key key = Qt::Key(e->key());
02448 #if QT_VERSION >= 0x040000
02449     const QtKeyboardModifiers modifiers = e->modifiers();
02450 #else
02451     const QtKeyboardModifiers modifiers = (QtKeyboardModifiers)(e->state() & Qt::KeyboardModifierMask);
02452 #endif
02453 
02454     QMap<KeyboardAction, int>::ConstIterator it=keyboardBinding_.begin(), end=keyboardBinding_.end();
02455     while ((it != end) && (it.value() != (key | modifiers)))
02456         ++it;
02457 
02458     if (it != end)
02459         handleKeyboardAction(it.key());
02460     else
02461         if (pathIndex_.contains(Qt::Key(key)))
02462         {
02463             // Camera paths
02464             int index = pathIndex_[Qt::Key(key)];
02465 
02466             static QTime doublePress; // try to double press on two viewers at the same time !
02467 
02468             if (modifiers == playPathKeyboardModifiers())
02469             {
02470                 int elapsed = doublePress.restart();
02471                 if ((elapsed < 250) && (index==previousPathId_))
02472                     camera()->resetPath(index);
02473                 else
02474                 {
02475                     // Stop previous interpolation before starting a new one.
02476                     if (index != previousPathId_)
02477                     {
02478                         KeyFrameInterpolator* previous = camera()->keyFrameInterpolator(previousPathId_);
02479                         if ((previous) && (previous->interpolationIsStarted()))
02480                             previous->resetInterpolation();
02481                     }
02482                     camera()->playPath(index);
02483                 }
02484                 previousPathId_ = index;
02485             }
02486             else if (modifiers == addKeyFrameKeyboardModifiers())
02487             {
02488                 int elapsed = doublePress.restart();
02489                 if ((elapsed < 250) && (index==previousPathId_))
02490                 {
02491                     if (camera()->keyFrameInterpolator(index))
02492                     {
02493                         disconnect(camera()->keyFrameInterpolator(index), SIGNAL(interpolated()), this, SLOT(updateGL()));
02494                         if (camera()->keyFrameInterpolator(index)->numberOfKeyFrames() > 1)
02495                             displayMessage(tr("Path %1 deleted", "Feedback message").arg(index));
02496                         else
02497                             displayMessage(tr("Position %1 deleted", "Feedback message").arg(index));
02498                         camera()->deletePath(index);
02499                     }
02500                 }
02501                 else
02502                 {
02503                     bool nullBefore = (camera()->keyFrameInterpolator(index) == NULL);
02504                     camera()->addKeyFrameToPath(index);
02505                     if (nullBefore)
02506                         connect(camera()->keyFrameInterpolator(index), SIGNAL(interpolated()), SLOT(updateGL()));
02507                     int nbKF = camera()->keyFrameInterpolator(index)->numberOfKeyFrames();
02508                     if (nbKF > 1)
02509                         displayMessage(tr("Path %1, position %2 added", "Feedback message").arg(index).arg(nbKF));
02510                     else
02511                         displayMessage(tr("Position %1 saved", "Feedback message").arg(index));
02512                 }
02513                 previousPathId_ = index;
02514             }
02515             updateGL();
02516         }
02517         else
02518             e->ignore();
02519 }
02520 
02521 void QGLViewer::handleKeyboardAction(KeyboardAction id)
02522 {
02523     switch (id)
02524     {
02525     case DRAW_AXIS :        toggleAxisIsDrawn(); break;
02526     case DRAW_GRID :        toggleGridIsDrawn(); break;
02527     case DISPLAY_FPS :      toggleFPSIsDisplayed(); break;
02528     case ENABLE_TEXT :      toggleTextIsEnabled(); break;
02529     case EXIT_VIEWER :      saveStateToFileForAllViewers(); qApp->closeAllWindows(); break;
02530     case SAVE_SCREENSHOT :      saveSnapshot(false, false); break;
02531     case FULL_SCREEN :      toggleFullScreen(); break;
02532     case STEREO :           toggleStereoDisplay(); break;
02533     case ANIMATION :        toggleAnimation(); break;
02534     case HELP :         help(); break;
02535     case EDIT_CAMERA :      toggleCameraIsEdited(); break;
02536     case SNAPSHOT_TO_CLIPBOARD :    snapshotToClipboard(); break;
02537     case CAMERA_MODE :
02538         toggleCameraMode();
02539         displayMessage(cameraIsInRevolveMode()?tr("Camera in revolve around mode", "Feedback message"):tr("Camera in fly mode", "Feedback message"));
02540         break;
02541 
02542     case MOVE_CAMERA_LEFT :
02543         camera()->frame()->translate(camera()->frame()->inverseTransformOf(Vec(-10.0*camera()->flySpeed(), 0.0, 0.0)));
02544         updateGL();
02545         break;
02546     case MOVE_CAMERA_RIGHT :
02547         camera()->frame()->translate(camera()->frame()->inverseTransformOf(Vec( 10.0*camera()->flySpeed(), 0.0, 0.0)));
02548         updateGL();
02549         break;
02550     case MOVE_CAMERA_UP :
02551         camera()->frame()->translate(camera()->frame()->inverseTransformOf(Vec(0.0,  10.0*camera()->flySpeed(), 0.0)));
02552         updateGL();
02553         break;
02554     case MOVE_CAMERA_DOWN :
02555         camera()->frame()->translate(camera()->frame()->inverseTransformOf(Vec(0.0, -10.0*camera()->flySpeed(), 0.0)));
02556         updateGL();
02557         break;
02558 
02559     case INCREASE_FLYSPEED : camera()->setFlySpeed(camera()->flySpeed() * 1.5); break;
02560     case DECREASE_FLYSPEED : camera()->setFlySpeed(camera()->flySpeed() / 1.5); break;
02561     }
02562 }
02563 
02568 void QGLViewer::resizeGL(int width, int height)
02569 {
02570     QGLWidget::resizeGL(width, height);
02571     glViewport( 0, 0, GLint(width), GLint(height) );
02572     camera()->setScreenWidthAndHeight(this->width(), this->height());
02573 }
02574 
02576 //              K e y b o a r d   s h o r t c u t s                     //
02578 
02596 void QGLViewer::setShortcut(KeyboardAction action, int key)
02597 {
02598     keyboardBinding_[action] = convertToKeyboardModifiers(key);
02599 }
02600 
02616 int QGLViewer::shortcut(KeyboardAction action) const
02617 {
02618     if (keyboardBinding_.contains(action))
02619         return convertToShortModifier(keyboardBinding_[action]);
02620     else
02621         return 0;
02622 }
02623 
02624 #ifndef DOXYGEN
02625 void QGLViewer::setKeyboardAccelerator(KeyboardAction action, int key)
02626 {
02627     qWarning("setKeyboardAccelerator is deprecated. Use setShortcut instead.");
02628     setShortcut(action, key);
02629 }
02630 
02631 int QGLViewer::keyboardAccelerator(KeyboardAction action) const
02632 {
02633     qWarning("keyboardAccelerator is deprecated. Use shortcut instead.");
02634     return shortcut(action);
02635 }
02636 #endif
02637 
02639 
02655 Qt::Key QGLViewer::pathKey(int index) const
02656 {
02657     for (QMap<Qt::Key, int>::ConstIterator it = pathIndex_.begin(), end=pathIndex_.end(); it != end; ++it)
02658         if (it.value() == index)
02659             return it.key();
02660     return Qt::Key(0);
02661 }
02662 
02674 void QGLViewer::setPathKey(int key, int index)
02675 {
02676     if (key < 0)
02677         pathIndex_.remove(Qt::Key(-key));
02678     else
02679         pathIndex_[Qt::Key(key)] = index;
02680 }
02681 
02683 void QGLViewer::setPlayPathKeyboardModifiers(QtKeyboardModifiers modifiers)
02684 {
02685     playPathKeyboardModifiers_ = convertKeyboardModifiers(modifiers);
02686 }
02687 
02689 void QGLViewer::setAddKeyFrameKeyboardModifiers(QtKeyboardModifiers modifiers)
02690 {
02691     addKeyFrameKeyboardModifiers_ = convertKeyboardModifiers(modifiers);
02692 }
02693 
02707 QtKeyboardModifiers QGLViewer::addKeyFrameKeyboardModifiers() const
02708 {
02709     return addKeyFrameKeyboardModifiers_;
02710 }
02711 
02724 QtKeyboardModifiers QGLViewer::playPathKeyboardModifiers() const
02725 {
02726     return playPathKeyboardModifiers_;
02727 }
02728 
02729 #ifndef DOXYGEN
02730 // Deprecated methods
02731 QtKeyboardModifiers QGLViewer::addKeyFrameStateKey() const
02732 {
02733     qWarning("addKeyFrameStateKey has been renamed addKeyFrameKeyboardModifiers");
02734     return addKeyFrameKeyboardModifiers(); }
02735 
02736 QtKeyboardModifiers QGLViewer::playPathStateKey() const
02737 {
02738     qWarning("playPathStateKey has been renamed playPathKeyboardModifiers");
02739     return playPathKeyboardModifiers();
02740 }
02741 
02742 void QGLViewer::setAddKeyFrameStateKey(int buttonState)
02743 {
02744     qWarning("setAddKeyFrameStateKey has been renamed setAddKeyFrameKeyboardModifiers");
02745     setAddKeyFrameKeyboardModifiers(QtKeyboardModifiers(buttonState & Qt::KeyboardModifierMask));
02746 }
02747 
02748 void QGLViewer::setPlayPathStateKey(int buttonState)
02749 {
02750     qWarning("setPlayPathStateKey has been renamed setPlayPathKeyboardModifiers");
02751     setPlayPathKeyboardModifiers(QtKeyboardModifiers(buttonState & Qt::KeyboardModifierMask));
02752 }
02753 
02754 Qt::Key QGLViewer::keyFrameKey(int index) const
02755 {
02756     qWarning("keyFrameKey has been renamed pathKey.");
02757     return pathKey(index);
02758 }
02759 
02760 QtKeyboardModifiers QGLViewer::playKeyFramePathStateKey() const
02761 {
02762     qWarning("playKeyFramePathStateKey has been renamed playPathKeyboardModifiers.");
02763     return playPathKeyboardModifiers();
02764 }
02765 
02766 void QGLViewer::setKeyFrameKey(int index, int key)
02767 {
02768     qWarning("setKeyFrameKey is deprecated, use setPathKey instead, with swapped parameters.");
02769     setPathKey(key, index);
02770 }
02771 
02772 void QGLViewer::setPlayKeyFramePathStateKey(int buttonState)
02773 {
02774     qWarning("setPlayKeyFramePathStateKey has been renamed setPlayPathKeyboardModifiers.");
02775     setPlayPathKeyboardModifiers(QtKeyboardModifiers(buttonState & Qt::KeyboardModifierMask));
02776 }
02777 #endif
02778 
02780 //              M o u s e   b e h a v i o r   s t a t e   k e y s             //
02782 
02823 void QGLViewer::setHandlerKeyboardModifiers(MouseHandler handler, QtKeyboardModifiers modifiers)
02824 {
02825     QMap<int, MouseActionPrivate> newMouseBinding;
02826     QMap<QtKeyboardModifiers, MouseActionPrivate> newWheelBinding;
02827     QMap<ClickActionPrivate, ClickAction> newClickBinding_;
02828 
02829     QMap<int, MouseActionPrivate>::Iterator mit;
02830     QMap<QtKeyboardModifiers, MouseActionPrivate>::Iterator wit;
02831 
02832     // First copy unchanged bindings.
02833     for (mit = mouseBinding_.begin(); mit != mouseBinding_.end(); ++mit)
02834         if ((mit.value().handler != handler) || (mit.value().action == ZOOM_ON_REGION))
02835             newMouseBinding[mit.key()] = mit.value();
02836 
02837     for (wit = wheelBinding_.begin(); wit != wheelBinding_.end(); ++wit)
02838         if (wit.value().handler != handler)
02839             newWheelBinding[wit.key()] = wit.value();
02840 
02841     // Then, add modified bindings, that can overwrite the previous ones.
02842     modifiers = convertKeyboardModifiers(modifiers);
02843     for (mit = mouseBinding_.begin(); mit != mouseBinding_.end(); ++mit)
02844         if ((mit.value().handler == handler) && (mit.value().action != ZOOM_ON_REGION))
02845         {
02846             int newState = modifiers | (mit.key() & Qt::MouseButtonMask);
02847             newMouseBinding[newState] = mit.value();
02848         }
02849 
02850         for (wit = wheelBinding_.begin(); wit != wheelBinding_.end(); ++wit)
02851             if (wit.value().handler == handler)
02852             {
02853                 QtKeyboardModifiers newState = modifiers;
02854                 newWheelBinding[newState] = wit.value();
02855             }
02856 
02857             // Same for button bindings
02858             for (QMap<ClickActionPrivate, ClickAction>::ConstIterator cb=clickBinding_.begin(), end=clickBinding_.end(); cb != end; ++cb)
02859                 if (((handler==CAMERA) && ((cb.value() == CENTER_SCENE) || (cb.value() == ALIGN_CAMERA))) ||
02860                     ((handler==FRAME)  && ((cb.value() == CENTER_FRAME) || (cb.value() == ALIGN_FRAME))))
02861                 {
02862                     ClickActionPrivate cap;
02863                     cap.modifiers = modifiers;
02864                     cap.button = cb.key().button;
02865                     cap.doubleClick = cb.key().doubleClick;
02866                     cap.buttonsBefore = cb.key().buttonsBefore;
02867                     newClickBinding_[cap] = cb.value();
02868                 }
02869                 else
02870                     newClickBinding_[cb.key()] = cb.value();
02871 
02872             mouseBinding_ = newMouseBinding;
02873             wheelBinding_ = newWheelBinding;
02874             clickBinding_ = newClickBinding_;
02875 }
02876 
02877 
02878 #ifndef DOXYGEN
02879 void QGLViewer::setHandlerStateKey(MouseHandler handler, int buttonState)
02880 {
02881     qWarning("setHandlerStateKey has been renamed setHandlerKeyboardModifiers");
02882     setHandlerKeyboardModifiers(handler, QtKeyboardModifiers(buttonState & Qt::KeyboardModifierMask));
02883 }
02884 
02885 void QGLViewer::setMouseStateKey(MouseHandler handler, int buttonState)
02886 {
02887     qWarning("setMouseStateKey has been renamed setHandlerKeyboardModifiers.");
02888     setHandlerKeyboardModifiers(handler, QtKeyboardModifiers(buttonState & Qt::KeyboardModifierMask));
02889 }
02890 #endif
02891 
02923 void QGLViewer::setMouseBinding(int state, MouseHandler handler, MouseAction action, bool withConstraint)
02924 {
02925     if ((handler == FRAME) && ((action == MOVE_FORWARD) || (action == MOVE_BACKWARD) ||
02926         (action == ROLL) || (action == LOOK_AROUND) ||
02927         (action == ZOOM_ON_REGION)))
02928     {
02929 #if QT_VERSION >= 0x040000
02930         qWarning("Cannot bind %s to FRAME", mouseActionString(action).toLatin1().constData());
02931 #else
02932         qWarning("Cannot bind %s to FRAME", mouseActionString(action).latin1());
02933 #endif
02934     }
02935     else
02936         if ((state & Qt::MouseButtonMask) == 0)
02937             qWarning("No mouse button specified in setMouseBinding");
02938         else
02939         {
02940             MouseActionPrivate map;
02941             map.handler = handler;
02942             map.action = action;
02943             map.withConstraint = withConstraint;
02944             state = convertToKeyboardModifiers(state);
02945 
02946             mouseBinding_.remove(state);
02947 
02948             if (action != NO_MOUSE_ACTION)
02949                 mouseBinding_.insert(state, map);
02950 
02951             ClickActionPrivate cap;
02952             cap.modifiers = QtKeyboardModifiers(state & Qt::KeyboardModifierMask);
02953             cap.button = QtMouseButtons(state & Qt::MouseButtonMask);
02954             cap.doubleClick = false;
02955             cap.buttonsBefore = Qt::NoButton;
02956             clickBinding_.remove(cap);
02957         }
02958 }
02959 
02978 void QGLViewer::setMouseBinding(int state, ClickAction action, bool doubleClick, QtMouseButtons buttonsBefore)
02979 {
02980     if ((buttonsBefore != Qt::NoButton) && !doubleClick)
02981         qWarning("Buttons before is only meaningful when doubleClick is true in setMouseBinding().");
02982     else
02983         if ((state & Qt::MouseButtonMask) == 0)
02984             qWarning("No mouse button specified in setMouseBinding");
02985         else
02986         {
02987             ClickActionPrivate cap;
02988             state = convertToKeyboardModifiers(state);
02989             cap.modifiers = QtKeyboardModifiers(state & Qt::KeyboardModifierMask);
02990             cap.button = QtMouseButtons(state & Qt::MouseButtonMask);
02991             cap.doubleClick = doubleClick;
02992             cap.buttonsBefore = buttonsBefore;
02993             clickBinding_.remove(cap);
02994 
02995             // #CONNECTION performClickAction comment on NO_CLICK_ACTION
02996             if (action != NO_CLICK_ACTION)
02997                 clickBinding_.insert(cap, action);
02998 
02999             if ((!doubleClick) && (buttonsBefore == Qt::NoButton))
03000                 mouseBinding_.remove(state);
03001         }
03002 }
03003 
03014 void QGLViewer::setWheelBinding(QtKeyboardModifiers modifiers, MouseHandler handler, MouseAction action, bool withConstraint)
03015 {
03016     //#CONNECTION# ManipulatedFrame::wheelEvent and ManipulatedCameraFrame::wheelEvent switches
03017     if ((action != ZOOM) && (action != MOVE_FORWARD) && (action != MOVE_BACKWARD) && (action != NO_MOUSE_ACTION))
03018 #if QT_VERSION >= 0x040000
03019         qWarning("Cannot bind %s to wheel", mouseActionString(action).toLatin1().constData());
03020 #else
03021         qWarning("Cannot bind %s to wheel",  + mouseActionString(action).latin1());
03022 #endif
03023     else
03024         if ((handler == FRAME) && (action != ZOOM) && (action != NO_MOUSE_ACTION))
03025 #if QT_VERSION >= 0x040000
03026             qWarning("Cannot bind %s to FRAME wheel", mouseActionString(action).toLatin1().constData());
03027 #else
03028             qWarning("Cannot bind %s to FRAME wheel", mouseActionString(action).latin1());
03029 #endif
03030         else
03031         {
03032             MouseActionPrivate map;
03033             map.handler = handler;
03034             map.action  = action;
03035             map.withConstraint = withConstraint;
03036             modifiers = convertKeyboardModifiers(modifiers);
03037             wheelBinding_.remove(modifiers);
03038 
03039             if (action != NO_MOUSE_ACTION)
03040                 wheelBinding_.insert(modifiers, map);
03041         }
03042 }
03043 
03055 QGLViewer::MouseAction QGLViewer::mouseAction(int state) const
03056 {
03057     state = convertToKeyboardModifiers(state);
03058     if (mouseBinding_.contains(state))
03059         return mouseBinding_[state].action;
03060     else
03061         return NO_MOUSE_ACTION;
03062 }
03063 
03074 int QGLViewer::mouseHandler(int state) const
03075 {
03076     state = convertToKeyboardModifiers(state);
03077     if (mouseBinding_.contains(state))
03078         return mouseBinding_[state].handler;
03079     else
03080         return -1;
03081 }
03082 
03100 int QGLViewer::mouseButtonState(MouseHandler handler, MouseAction action, bool withConstraint) const
03101 {
03102     for (QMap<int, MouseActionPrivate>::ConstIterator it=mouseBinding_.begin(), end=mouseBinding_.end(); it != end; ++it)
03103         if ( (it.value().handler == handler) && (it.value().action == action) && (it.value().withConstraint == withConstraint) )
03104             return it.key();
03105 
03106     return Qt::NoButton;
03107 }
03108 
03114 QGLViewer::MouseAction QGLViewer::wheelAction(QtKeyboardModifiers modifiers) const
03115 {
03116     modifiers = convertKeyboardModifiers(modifiers);
03117     if (wheelBinding_.contains(modifiers))
03118         return wheelBinding_[modifiers].action;
03119     else
03120         return NO_MOUSE_ACTION;
03121 }
03122 
03124 int QGLViewer::wheelHandler(QtKeyboardModifiers modifiers) const
03125 {
03126     modifiers = convertKeyboardModifiers(modifiers);
03127     if (wheelBinding_.contains(modifiers))
03128         return wheelBinding_[modifiers].handler;
03129     else
03130         return -1;
03131 }
03132 
03137 int QGLViewer::wheelButtonState(MouseHandler handler, MouseAction action, bool withConstraint) const
03138 {
03139     for (QMap<QtKeyboardModifiers, MouseActionPrivate>::ConstIterator it=wheelBinding_.begin(), end=wheelBinding_.end(); it!=end; ++it)
03140         if ( (it.value().handler == handler) && (it.value().action == action) && (it.value().withConstraint == withConstraint) )
03141             return it.key();
03142 
03143     return -1;
03144 }
03145 
03147 QGLViewer::ClickAction QGLViewer::clickAction(int state, bool doubleClick, QtMouseButtons buttonsBefore) const
03148 {
03149     ClickActionPrivate cap;
03150     cap.modifiers = QtKeyboardModifiers(convertToKeyboardModifiers(state) & Qt::KeyboardModifierMask);
03151     cap.button = QtMouseButtons(state & Qt::MouseButtonMask);
03152     cap.doubleClick = doubleClick;
03153     cap.buttonsBefore = buttonsBefore;
03154     if (clickBinding_.contains(cap))
03155         return clickBinding_[cap];
03156     else
03157         return NO_CLICK_ACTION;
03158 }
03159 
03165 void QGLViewer::getClickButtonState(ClickAction ca, int& state, bool& doubleClick, QtMouseButtons& buttonsBefore) const
03166 {
03167     for (QMap<ClickActionPrivate, ClickAction>::ConstIterator it=clickBinding_.begin(), end=clickBinding_.end(); it != end; ++it)
03168         if (it.value() == ca)
03169         {
03170             state = it.key().modifiers | it.key().button;
03171             doubleClick = it.key().doubleClick;
03172             buttonsBefore = it.key().buttonsBefore;
03173             return;
03174         }
03175 
03176         state = Qt::NoButton;
03177 }
03178 
03182 bool QGLViewer::cameraIsInRevolveMode() const
03183 {
03184     //#CONNECTION# used in toggleCameraMode() and keyboardString()
03185     return mouseButtonState(CAMERA, ROTATE) != Qt::NoButton;
03186 }
03187 
03201 void QGLViewer::toggleCameraMode()
03202 {
03203     bool revolveMode = cameraIsInRevolveMode();
03204     int bs;
03205     if (revolveMode)
03206         bs = mouseButtonState(CAMERA, ROTATE);
03207     else
03208         bs = mouseButtonState(CAMERA, MOVE_FORWARD);
03209     QtKeyboardModifiers modifiers = QtKeyboardModifiers(bs & Qt::KeyboardModifierMask);
03210 
03211     //#CONNECTION# setDefaultMouseBindings()
03212     if (revolveMode)
03213     {
03214         camera()->frame()->updateFlyUpVector();
03215         camera()->frame()->stopSpinning();
03216 
03217         setMouseBinding(modifiers | Qt::LeftButton,  CAMERA, MOVE_FORWARD);
03218         setMouseBinding(modifiers | Qt::MidButton,   CAMERA, LOOK_AROUND);
03219         setMouseBinding(modifiers | Qt::RightButton, CAMERA, MOVE_BACKWARD);
03220 
03221         setMouseBinding(modifiers | Qt::LeftButton  | Qt::MidButton,  CAMERA, ROLL);
03222         // 2.2.4 setMouseBinding(modifiers | Qt::RightButton | Qt::MidButton,  CAMERA, SCREEN_TRANSLATE);
03223 
03224         setMouseBinding(Qt::LeftButton,  NO_CLICK_ACTION, true);
03225         setMouseBinding(Qt::MidButton,   NO_CLICK_ACTION, true);
03226         setMouseBinding(Qt::RightButton, NO_CLICK_ACTION, true);
03227 
03228         setWheelBinding(modifiers, CAMERA, MOVE_FORWARD);
03229     }
03230     else
03231     {
03232         // Should stop flyTimer. But unlikely and not easy.
03233         setMouseBinding(modifiers | Qt::LeftButton,  CAMERA, ROTATE);
03234         setMouseBinding(modifiers | Qt::MidButton,   CAMERA, ZOOM);
03235         setMouseBinding(modifiers | Qt::RightButton, CAMERA, TRANSLATE);
03236 
03237         setMouseBinding(modifiers | Qt::LeftButton  | Qt::MidButton,  CAMERA, SCREEN_ROTATE);
03238         // 2.2.4 setMouseBinding(modifiers | Qt::RightButton | Qt::MidButton,  CAMERA, SCREEN_TRANSLATE);
03239 
03240         setMouseBinding(Qt::LeftButton,  ALIGN_CAMERA,      true);
03241         setMouseBinding(Qt::MidButton,   SHOW_ENTIRE_SCENE, true);
03242         setMouseBinding(Qt::RightButton, CENTER_SCENE,      true);
03243 
03244         setWheelBinding(modifiers, CAMERA, ZOOM);
03245     }
03246 }
03247 
03249 //              M a n i p u l a t e d   f r a m e s                           //
03251 
03263 void QGLViewer::setManipulatedFrame(ManipulatedFrame* frame)
03264 {
03265     if (manipulatedFrame())
03266     {
03267         manipulatedFrame()->stopSpinning();
03268 
03269         if (manipulatedFrame() != camera()->frame())
03270         {
03271             disconnect(manipulatedFrame(), SIGNAL(manipulated()), this, SLOT(updateGL()));
03272             disconnect(manipulatedFrame(), SIGNAL(spun()), this, SLOT(updateGL()));
03273         }
03274     }
03275 
03276     manipulatedFrame_ = frame;
03277 
03278     manipulatedFrameIsACamera_ = ((manipulatedFrame() != camera()->frame()) &&
03279         (dynamic_cast<ManipulatedCameraFrame*>(manipulatedFrame()) != NULL));
03280 
03281     if (manipulatedFrame())
03282     {
03283         // Prevent multiple connections, that would result in useless display updates
03284         if (manipulatedFrame() != camera()->frame())
03285         {
03286             connect(manipulatedFrame(), SIGNAL(manipulated()), SLOT(updateGL()));
03287             connect(manipulatedFrame(), SIGNAL(spun()), SLOT(updateGL()));
03288         }
03289     }
03290 }
03291 
03292 #ifndef DOXYGEN
03293 
03294 //                          V i s u a l   H i n t s                           //
03296 
03311 void QGLViewer::drawVisualHints()
03312 {
03313     // Revolve Around point cross
03314     if (visualHint_ & 1)
03315     {
03316         const float size = 15.0;
03317         Vec proj = camera()->projectedCoordinatesOf(camera()->revolveAroundPoint());
03318         startScreenCoordinatesSystem();
03319         glDisable(GL_LIGHTING);
03320         glDisable(GL_DEPTH_TEST);
03321         glLineWidth(3.0);
03322         glBegin(GL_LINES);
03323         glVertex2f(proj.x - size, proj.y);
03324         glVertex2f(proj.x + size, proj.y);
03325         glVertex2f(proj.x, proj.y - size);
03326         glVertex2f(proj.x, proj.y + size);
03327         glEnd();
03328         glEnable(GL_DEPTH_TEST);
03329         stopScreenCoordinatesSystem();
03330     }
03331 
03332     // if (visualHint_ & 2)
03333     // drawText(80, 10, "Play");
03334 
03335     // Screen rotate line
03336     ManipulatedFrame* mf = NULL;
03337     Vec pnt;
03338     if (camera()->frame()->action_ == SCREEN_ROTATE)
03339     {
03340         mf = camera()->frame();
03341         pnt = camera()->revolveAroundPoint();
03342     }
03343     if (manipulatedFrame() && (manipulatedFrame()->action_ == SCREEN_ROTATE))
03344     {
03345         mf = manipulatedFrame();
03346         // Maybe useful if the mf is a manipCameraFrame...
03347         // pnt = manipulatedFrame()->revolveAroundPoint();
03348         pnt = manipulatedFrame()->position();
03349     }
03350 
03351     if (mf)
03352     {
03353         pnt = camera()->projectedCoordinatesOf(pnt);
03354         startScreenCoordinatesSystem();
03355         glDisable(GL_LIGHTING);
03356         glDisable(GL_DEPTH_TEST);
03357         glLineWidth(3.0);
03358         glBegin(GL_LINES);
03359         glVertex2f(pnt.x, pnt.y);
03360         glVertex2f(mf->prevPos_.x(), mf->prevPos_.y());
03361         glEnd();
03362         glEnable(GL_DEPTH_TEST);
03363         stopScreenCoordinatesSystem();
03364     }
03365 
03366     // Zoom on region: draw a rectangle
03367     if (camera()->frame()->action_ == ZOOM_ON_REGION)
03368     {
03369         startScreenCoordinatesSystem();
03370         glDisable(GL_LIGHTING);
03371         glDisable(GL_DEPTH_TEST);
03372         glLineWidth(2.0);
03373         glBegin(GL_LINE_LOOP);
03374         glVertex2i(camera()->frame()->pressPos_.x(), camera()->frame()->pressPos_.y());
03375         glVertex2i(camera()->frame()->prevPos_.x(),  camera()->frame()->pressPos_.y());
03376         glVertex2i(camera()->frame()->prevPos_.x(),  camera()->frame()->prevPos_.y());
03377         glVertex2i(camera()->frame()->pressPos_.x(), camera()->frame()->prevPos_.y());
03378         glEnd();
03379         glEnable(GL_DEPTH_TEST);
03380         stopScreenCoordinatesSystem();
03381     }
03382 }
03383 
03387 void QGLViewer::setVisualHintsMask(int mask, int delay)
03388 {
03389     visualHint_ = visualHint_ | mask;
03390     QTimer::singleShot(delay, this, SLOT(resetVisualHints()));
03391 }
03392 
03394 void QGLViewer::resetVisualHints()
03395 {
03396     visualHint_ = 0;
03397 }
03398 #endif
03399 
03401 //       A x i s   a n d   G r i d   d i s p l a y   l i s t s                //
03403 
03413 void QGLViewer::drawArrow(float length, float radius, int nbSubdivisions)
03414 {
03415     static GLUquadric* quadric = gluNewQuadric();
03416 
03417     if (radius < 0.0)
03418         radius = 0.05 * length;
03419 
03420     const float head = 2.5*(radius / length) + 0.1;
03421     const float coneRadiusCoef = 4.0 - 5.0 * head;
03422 
03423     gluCylinder(quadric, radius, radius, length * (1.0 - head/coneRadiusCoef), nbSubdivisions, 1);
03424     glTranslatef(0.0, 0.0, length * (1.0 - head));
03425     gluCylinder(quadric, coneRadiusCoef * radius, 0.0, head * length, nbSubdivisions, 1);
03426     glTranslatef(0.0, 0.0, -length * (1.0 - head));
03427 }
03428 
03433 void QGLViewer::drawArrow(const Vec& from, const Vec& to, float radius, int nbSubdivisions)
03434 {
03435     glPushMatrix();
03436     glTranslatef(from[0],from[1],from[2]);
03437     const Vec dir = to-from;
03438     glMultMatrixd(Quaternion(Vec(0,0,1), dir).matrix());
03439     QGLViewer::drawArrow(dir.norm(), radius, nbSubdivisions);
03440     glPopMatrix();
03441 }
03442 
03461 void QGLViewer::drawAxis(float length)
03462 {
03463     const float charWidth = length / 40.0;
03464     const float charHeight = length / 30.0;
03465     const float charShift = 1.04 * length;
03466 
03467     GLboolean lighting, colorMaterial;
03468     glGetBooleanv(GL_LIGHTING, &lighting);
03469     glGetBooleanv(GL_COLOR_MATERIAL, &colorMaterial);
03470 
03471     glDisable(GL_LIGHTING);
03472 
03473     glBegin(GL_LINES);
03474     // The X
03475     glVertex3f(charShift,  charWidth, -charHeight);
03476     glVertex3f(charShift, -charWidth,  charHeight);
03477     glVertex3f(charShift, -charWidth, -charHeight);
03478     glVertex3f(charShift,  charWidth,  charHeight);
03479     // The Y
03480     glVertex3f( charWidth, charShift, charHeight);
03481     glVertex3f(0.0,        charShift, 0.0);
03482     glVertex3f(-charWidth, charShift, charHeight);
03483     glVertex3f(0.0,        charShift, 0.0);
03484     glVertex3f(0.0,        charShift, 0.0);
03485     glVertex3f(0.0,        charShift, -charHeight);
03486     // The Z
03487     glVertex3f(-charWidth,  charHeight, charShift);
03488     glVertex3f( charWidth,  charHeight, charShift);
03489     glVertex3f( charWidth,  charHeight, charShift);
03490     glVertex3f(-charWidth, -charHeight, charShift);
03491     glVertex3f(-charWidth, -charHeight, charShift);
03492     glVertex3f( charWidth, -charHeight, charShift);
03493     glEnd();
03494 
03495     glEnable(GL_LIGHTING);
03496     glDisable(GL_COLOR_MATERIAL);
03497 
03498     float color[4];
03499     color[0] = 0.7f;  color[1] = 0.7f;  color[2] = 1.0f;  color[3] = 1.0f;
03500     glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
03501     QGLViewer::drawArrow(length, 0.01*length);
03502 
03503     color[0] = 1.0f;  color[1] = 0.7f;  color[2] = 0.7f;  color[3] = 1.0f;
03504     glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
03505     glPushMatrix();
03506     glRotatef(90.0, 0.0, 1.0, 0.0);
03507     QGLViewer::drawArrow(length, 0.01*length);
03508     glPopMatrix();
03509 
03510     color[0] = 0.7f;  color[1] = 1.0f;  color[2] = 0.7f;  color[3] = 1.0f;
03511     glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
03512     glPushMatrix();
03513     glRotatef(-90.0, 1.0, 0.0, 0.0);
03514     QGLViewer::drawArrow(length, 0.01*length);
03515     glPopMatrix();
03516 
03517     if (colorMaterial)
03518         glEnable(GL_COLOR_MATERIAL);
03519     if (!lighting)
03520         glDisable(GL_LIGHTING);
03521 }
03522 
03529 void QGLViewer::drawGrid(float size, int nbSubdivisions)
03530 {
03531     GLboolean lighting;
03532     glGetBooleanv(GL_LIGHTING, &lighting);
03533 
03534     glDisable(GL_LIGHTING);
03535 
03536     glBegin(GL_LINES);
03537     for (int i=0; i<=nbSubdivisions; ++i)
03538     {
03539         const float pos = size*(2.0*i/nbSubdivisions-1.0);
03540         glVertex2f(pos, -size);
03541         glVertex2f(pos, +size);
03542         glVertex2f(-size, pos);
03543         glVertex2f( size, pos);
03544     }
03545     glEnd();
03546 
03547     if (lighting)
03548         glEnable(GL_LIGHTING);
03549 }
03550 
03552 //       S t a t i c    m e t h o d s   :  Q G L V i e w e r   P o o l        //
03554 
03556 void QGLViewer::saveStateToFileForAllViewers()
03557 {
03558 #if QT_VERSION >= 0x040000
03559     Q_FOREACH (QGLViewer* viewer, QGLViewer::QGLViewerPool())
03560     {
03561 #else
03562     QPtrListIterator<QGLViewer> it(QGLViewer::QGLViewerPool());
03563     for (QGLViewer* viewer; (viewer = it.current()) != 0; ++it)
03564     {
03565 #endif
03566         if (viewer)
03567             viewer->saveStateToFile();
03568     }
03569 }
03570 
03572 //       S a v e   s t a t e   b e t w e e n    s e s s i o n s         //
03574 
03588 QString QGLViewer::stateFileName() const
03589 {
03590     QString name = stateFileName_;
03591 
03592     if (!name.isEmpty() && QGLViewer::QGLViewerIndex(this) > 0)
03593     {
03594         QFileInfo fi(name);
03595 #if QT_VERSION >= 0x040000
03596         if (fi.suffix().isEmpty())
03597 #else
03598         if (fi.extension(false).isEmpty())
03599 #endif
03600             name += QString::number(QGLViewer::QGLViewerIndex(this));
03601         else
03602 #if QT_VERSION >= 0x040000
03603             name = fi.absolutePath() + '/' + fi.completeBaseName() + QString::number(QGLViewer::QGLViewerIndex(this)) + "." + fi.suffix();
03604 #else
03605 # if QT_VERSION >= 0x030000
03606             name = fi.dirPath() + '/' + fi.baseName(true) + QString::number(QGLViewer::QGLViewerIndex(this)) + "." + fi.extension(false);
03607 # else
03608             name = fi.dirPath() + '/' + fi.baseName() + QString::number(QGLViewer::QGLViewerIndex(this)) + "." + fi.extension();
03609 # endif
03610 #endif
03611     }
03612 
03613     return name;
03614 }
03615 
03623 void QGLViewer::saveStateToFile()
03624 {
03625     QString name = stateFileName();
03626 
03627     if (name.isEmpty())
03628         return;
03629 
03630     QFileInfo fileInfo(name);
03631 
03632     if (fileInfo.isDir())
03633     {
03634         QMessageBox::warning(this, tr("Save to file error", "Message box window title"), tr("State file name (%1) references a directory instead of a file.").arg(name));
03635         return;
03636     }
03637 
03638 #if QT_VERSION >= 0x040000
03639     const QString dirName = fileInfo.absolutePath();
03640 #else
03641     const QString dirName = fileInfo.dirPath();
03642 #endif
03643     if (!QFileInfo(dirName).exists())
03644     {
03645         QDir dir;
03646 #if QT_VERSION >= 0x040000
03647         if (!(dir.mkdir(dirName)))
03648 #else
03649         if (!(dir.mkdir(dirName, true)))
03650 #endif
03651         {
03652             QMessageBox::warning(this, tr("Save to file error", "Message box window title"), tr("Unable to create directory %1").arg(dirName));
03653             return;
03654         }
03655     }
03656 
03657     // Write the DOM tree to file
03658     QFile f(name);
03659 #if QT_VERSION >= 0x040000
03660     if (f.open(QIODevice::WriteOnly))
03661 #else
03662     if (f.open(IO_WriteOnly))
03663 #endif
03664     {
03665         QTextStream out(&f);
03666         QDomDocument doc("QGLVIEWER");
03667         doc.appendChild(domElement("QGLViewer", doc));
03668         doc.save(out, 2);
03669         f.flush();
03670         f.close();
03671     }
03672     else
03673 #if QT_VERSION < 0x030200
03674         QMessageBox::warning(this, tr("Save to file error", "Message box window title"), tr("Unable to save to file %1").arg(name));
03675 #else
03676         QMessageBox::warning(this, tr("Save to file error", "Message box window title"), tr("Unable to save to file %1").arg(name) + ":\n" + f.errorString());
03677 #endif
03678 }
03679 
03700 bool QGLViewer::restoreStateFromFile()
03701 {
03702     QString name = stateFileName();
03703 
03704     if (name.isEmpty())
03705         return false;
03706 
03707     QFileInfo fileInfo(name);
03708 
03709     if (!fileInfo.isFile())
03710         // No warning since it would be displayed at first start.
03711         return false;
03712 
03713     if (!fileInfo.isReadable())
03714     {
03715         QMessageBox::warning(this, tr("Problem in state restoration", "Message box window title"), tr("File %1 is not readable.").arg(name));
03716         return false;
03717     }
03718 
03719     // Read the DOM tree form file
03720     QFile f(name);
03721 #if QT_VERSION >= 0x040000
03722     if (f.open(QIODevice::ReadOnly) == true)
03723 #else
03724     if (f.open(IO_ReadOnly) == true)
03725 #endif
03726     {
03727         QDomDocument doc;
03728         doc.setContent(&f);
03729         f.close();
03730         QDomElement main = doc.documentElement();
03731         initFromDOMElement(main);
03732     }
03733     else
03734     {
03735 #if QT_VERSION < 0x030200
03736         QMessageBox::warning(this, tr("Open file error", "Message box window title"), tr("Unable to open file %1").arg(name));
03737 #else
03738         QMessageBox::warning(this, tr("Open file error", "Message box window title"), tr("Unable to open file %1").arg(name) + ":\n" + f.errorString());
03739 #endif
03740         return false;
03741     }
03742 
03743     return true;
03744 }
03745 
03778 QDomElement QGLViewer::domElement(const QString& name, QDomDocument& document) const
03779 {
03780     QDomElement de = document.createElement(name);
03781     de.setAttribute("version", QGLViewerVersionString());
03782 
03783     QDomElement stateNode = document.createElement("State");
03784     // stateNode.setAttribute("mouseTracking", (hasMouseTracking()?"true":"false"));
03785     stateNode.appendChild(DomUtils::QColorDomElement(foregroundColor(), "foregroundColor", document));
03786     stateNode.appendChild(DomUtils::QColorDomElement(backgroundColor(), "backgroundColor", document));
03787     stateNode.setAttribute("stereo", (displaysInStereo()?"true":"false"));
03788     stateNode.setAttribute("cameraMode", (cameraIsInRevolveMode()?"revolve":"fly"));
03789     de.appendChild(stateNode);
03790 
03791     QDomElement displayNode = document.createElement("Display");
03792     displayNode.setAttribute("axisIsDrawn",       (axisIsDrawn()?"true":"false"));
03793     displayNode.setAttribute("gridIsDrawn",       (gridIsDrawn()?"true":"false"));
03794     displayNode.setAttribute("FPSIsDisplayed",    (FPSIsDisplayed()?"true":"false"));
03795     displayNode.setAttribute("cameraIsEdited",    (cameraIsEdited()?"true":"false"));
03796     // displayNode.setAttribute("textIsEnabled",  (textIsEnabled()?"true":"false"));
03797     de.appendChild(displayNode);
03798 
03799     QDomElement geometryNode = document.createElement("Geometry");
03800     geometryNode.setAttribute("fullScreen", (isFullScreen()?"true":"false"));
03801     if (isFullScreen())
03802     {
03803         geometryNode.setAttribute("prevPosX", QString::number(prevPos_.x()));
03804         geometryNode.setAttribute("prevPosY", QString::number(prevPos_.y()));
03805     }
03806     else
03807     {
03808         QWidget* tlw = topLevelWidget();
03809         geometryNode.setAttribute("width",  QString::number(tlw->width()));
03810         geometryNode.setAttribute("height", QString::number(tlw->height()));
03811         geometryNode.setAttribute("posX",   QString::number(tlw->pos().x()));
03812         geometryNode.setAttribute("posY",   QString::number(tlw->pos().y()));
03813     }
03814     de.appendChild(geometryNode);
03815 
03816     // Restore original Camera zClippingCoefficient before saving.
03817     if (cameraIsEdited())
03818         camera()->setZClippingCoefficient(previousCameraZClippingCoefficient_);
03819     de.appendChild(camera()->domElement("Camera", document));
03820     if (cameraIsEdited())
03821         // #CONNECTION# 5.0 from setCameraIsEdited()
03822         camera()->setZClippingCoefficient(5.0);
03823 
03824     if (manipulatedFrame())
03825         de.appendChild(manipulatedFrame()->domElement("ManipulatedFrame", document));
03826 
03827     return de;
03828 }
03829 
03864 void QGLViewer::initFromDOMElement(const QDomElement& element)
03865 {
03866     const QString version = element.attribute("version");
03867     // if (version != QGLViewerVersionString())
03868     if (version[0] != '2')
03869         // Patches for previous versions should go here when the state file syntax is modified.
03870 #if QT_VERSION >= 0x040000
03871         qWarning("State file created using QGLViewer version %s may not be correctly read.", version.toLatin1().constData());
03872 #else
03873         qWarning("State file created using QGLViewer version %s may not be correctly read.", version.latin1());
03874 #endif
03875 
03876     QDomElement child=element.firstChild().toElement();
03877     bool tmpCameraIsEdited = cameraIsEdited();
03878     while (!child.isNull())
03879     {
03880         if (child.tagName() == "State")
03881         {
03882             // #CONNECTION# default values from defaultConstructor()
03883             // setMouseTracking(DomUtils::boolFromDom(child, "mouseTracking", false));
03884             setStereoDisplay(DomUtils::boolFromDom(child, "stereo", false));
03885             if ((child.attribute("cameraMode", "revolve") == "fly") && (cameraIsInRevolveMode()))
03886                 toggleCameraMode();
03887 
03888             QDomElement ch=child.firstChild().toElement();
03889             while (!ch.isNull())
03890             {
03891                 if (ch.tagName() == "foregroundColor")
03892                     setForegroundColor(DomUtils::QColorFromDom(ch));
03893                 if (ch.tagName() == "backgroundColor")
03894                     setBackgroundColor(DomUtils::QColorFromDom(ch));
03895                 ch = ch.nextSibling().toElement();
03896             }
03897         }
03898 
03899         if (child.tagName() == "Display")
03900         {
03901             // #CONNECTION# default values from defaultConstructor()
03902             setAxisIsDrawn(DomUtils::boolFromDom(child, "axisIsDrawn", false));
03903             setGridIsDrawn(DomUtils::boolFromDom(child, "gridIsDrawn", false));
03904             setFPSIsDisplayed(DomUtils::boolFromDom(child, "FPSIsDisplayed", false));
03905             // See comment below.
03906             tmpCameraIsEdited = DomUtils::boolFromDom(child, "cameraIsEdited", false);
03907             // setTextIsEnabled(DomUtils::boolFromDom(child, "textIsEnabled", true));
03908         }
03909 
03910         if (child.tagName() == "Geometry")
03911         {
03912             setFullScreen(DomUtils::boolFromDom(child, "fullScreen", false));
03913 
03914             if (isFullScreen())
03915             {
03916                 prevPos_.setX(DomUtils::intFromDom(child, "prevPosX", 0));
03917                 prevPos_.setY(DomUtils::intFromDom(child, "prevPosY", 0));
03918             }
03919             else
03920             {
03921                 int width  = DomUtils::intFromDom(child, "width",  600);
03922                 int height = DomUtils::intFromDom(child, "height", 400);
03923                 topLevelWidget()->resize(width, height);
03924                 camera()->setScreenWidthAndHeight(this->width(), this->height());
03925 
03926                 QPoint pos;
03927                 pos.setX(DomUtils::intFromDom(child, "posX", 0));
03928                 pos.setY(DomUtils::intFromDom(child, "posY", 0));
03929                 topLevelWidget()->move(pos);
03930             }
03931         }
03932 
03933         if (child.tagName() == "Camera")
03934         {
03935             connectAllCameraKFIInterpolatedSignals(false);
03936             camera()->initFromDOMElement(child);
03937             connectAllCameraKFIInterpolatedSignals();
03938         }
03939 
03940         if ((child.tagName() == "ManipulatedFrame") && (manipulatedFrame()))
03941             manipulatedFrame()->initFromDOMElement(child);
03942 
03943         child = child.nextSibling().toElement();
03944     }
03945 
03946     // The Camera always stores its "real" zClippingCoef in domElement(). If it is edited,
03947     // its "real" coef must be saved and the coef set to 5.0, as is done in setCameraIsEdited().
03948     // BUT : Camera and Display are read in an arbitrary order. We must initialize Camera's
03949     // "real" coef BEFORE calling setCameraIsEdited. Hence this temp cameraIsEdited and delayed call
03950     cameraIsEdited_ = tmpCameraIsEdited;
03951     if (cameraIsEdited_)
03952     {
03953         previousCameraZClippingCoefficient_ = camera()->zClippingCoefficient();
03954         // #CONNECTION# 5.0 from setCameraIsEdited.
03955         camera()->setZClippingCoefficient(5.0);
03956     }
03957 }
03958 
03959 #ifndef DOXYGEN
03960 
03962 void QGLViewer::saveToFile(const QString& fileName)
03963 {
03964     if (!fileName.isEmpty())
03965         setStateFileName(fileName);
03966 
03967     qWarning("saveToFile() is deprecated, use saveStateToFile() instead.");
03968     saveStateToFile();
03969 }
03970 
03973 bool QGLViewer::restoreFromFile(const QString& fileName)
03974 {
03975     if (!fileName.isEmpty())
03976         setStateFileName(fileName);
03977 
03978     qWarning("restoreFromFile() is deprecated, use restoreStateFromFile() instead.");
03979     return restoreStateFromFile();
03980 }
03981 #endif
03982 
04036 void QGLViewer::copyBufferToTexture(GLint internalFormat, GLenum format)
04037 {
04038     int h = 16;
04039     int w = 16;
04040     // Todo compare performance with qt code.
04041     while (w < width())
04042         w <<= 1;
04043     while (h < height())
04044         h <<= 1;
04045 
04046     bool init = false;
04047 
04048     if ((w != bufferTextureWidth_) || (h != bufferTextureHeight_))
04049     {
04050         bufferTextureWidth_ = w;
04051         bufferTextureHeight_ = h;
04052         bufferTextureMaxU_ = width()  / float(bufferTextureWidth_);
04053         bufferTextureMaxV_ = height() / float(bufferTextureHeight_);
04054         init = true;
04055     }
04056 
04057     if (bufferTextureId() == 0)
04058     {
04059         glGenTextures(1, &bufferTextureId_);
04060         glBindTexture(GL_TEXTURE_2D, bufferTextureId_);
04061         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
04062         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
04063         init = true;
04064     }
04065     else
04066         glBindTexture(GL_TEXTURE_2D, bufferTextureId_);
04067 
04068     if ((format != previousBufferTextureFormat_) ||
04069         (internalFormat != previousBufferTextureInternalFormat_))
04070     {
04071         previousBufferTextureFormat_ = format;
04072         previousBufferTextureInternalFormat_ = internalFormat;
04073         init = true;
04074     }
04075 
04076     if (init)
04077     {
04078         if (format == GL_NONE)
04079             format = internalFormat;
04080 
04081         glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, bufferTextureWidth_, bufferTextureHeight_, 0, format, GL_UNSIGNED_BYTE, NULL);
04082     }
04083 
04084     glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, width(), height());
04085 }
04086 
04093 GLuint QGLViewer::bufferTextureId() const
04094 {
04095     if (glIsTexture(bufferTextureId_))
04096         return bufferTextureId_;
04097     else
04098         return 0;
04099 }