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


octovis
Author(s): Kai M. Wurm , Armin Hornung
autogenerated on Thu Aug 27 2015 14:13:26