qglviewer.cpp
1 /****************************************************************************
2 
3  Copyright (C) 2002-2008 Gilles Debunne. All rights reserved.
4 
5  This file is part of the QGLViewer library version 2.3.10.
6 
7  http://www.libqglviewer.com - contact@libqglviewer.com
8 
9  This file may be used under the terms of the GNU General Public License
10  versions 2.0 or 3.0 as published by the Free Software Foundation and
11  appearing in the LICENSE file included in the packaging of this file.
12  In addition, as a special exception, Gilles Debunne gives you certain
13  additional rights, described in the file GPL_EXCEPTION in this package.
14 
15  libQGLViewer uses dual licensing. Commercial/proprietary software must
16  purchase a libQGLViewer Commercial License.
17 
18  This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
19  WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20 
21 *****************************************************************************/
22 
23 #include "domUtils.h"
24 #include "qglviewer.h"
25 #include "camera.h"
26 #include "keyFrameInterpolator.h"
27 
28 #if QT_VERSION >= 0x040000
29 # include <QtAlgorithms>
30 # include <QTextEdit>
31 # include <QApplication>
32 # include <QFileInfo>
33 # include <QDateTime>
34 # include <QMessageBox>
35 # include <QPushButton>
36 # include <QTabWidget>
37 # include <QTextStream>
38 # include <QMouseEvent>
39 # include <QTimer>
40 # include <QImage>
41 # include <QDir>
42 # include <QUrl>
43 #else
44 # include <qapplication.h>
45 # include <qfileinfo.h>
46 # include <qdatetime.h>
47 # include <qmessagebox.h>
48 # include <qpushbutton.h>
49 # include <qtabwidget.h>
50 # include <qtextstream.h>
51 # include <qtimer.h>
52 # include <qimage.h>
53 # include <qtextedit.h>
54 # include <qkeysequence.h>
55 # include <qdir.h>
56  // Patch for enums names change
57 # define KeyboardModifierMask KeyButtonMask
58  // Patch for QMap API change
59 # define value data
60 #endif
61 
62 using namespace std;
63 using namespace qglviewer;
64 
65 // Static private variable
66 #if QT_VERSION >= 0x040000
67 QList<QGLViewer*> QGLViewer::QGLViewerPool_;
68 #else
69 QPtrList<QGLViewer> QGLViewer::QGLViewerPool_;
70 #endif
71 
72 
96 void QGLViewer::defaultConstructor()
97 {
98  // - - - W A R N I N G - - -
99  // This method should not call initializeGL(). Otherwise, as we are in the
100  // base class constructor, the user-defined init() would never be called.
101  // The different QGLViewer::setXXX are hence protected, so that updateGL is not called.
102  // The different constructor code should then be EMPTY.
103  updateGLOK_ = false;
104 
105  // Test OpenGL context
106  // if (glGetString(GL_VERSION) == 0)
107  // qWarning("Unable to get OpenGL version, context may not be available - Check your configuration");
108 
109 #if QT_VERSION >= 0x040000
110  int poolIndex = QGLViewer::QGLViewerPool_.indexOf(NULL);
111  setFocusPolicy(Qt::StrongFocus);
112 #else
113  int poolIndex = QGLViewer::QGLViewerPool_.findRef(NULL);
114  setFocusPolicy(QWidget::StrongFocus);
115 #endif
116 
117  if (poolIndex >= 0)
118  QGLViewer::QGLViewerPool_.replace(poolIndex, this);
119  else
120  QGLViewer::QGLViewerPool_.append(this);
121 
122  camera_ = new Camera();
123  setCamera(camera());
124 
125  setDefaultShortcuts();
126  setDefaultMouseBindings();
127 
128  setSnapshotFileName(tr("snapshot", "Default snapshot file name"));
129  initializeSnapshotFormats();
130  setSnapshotCounter(0);
131  setSnapshotQuality(95);
132 
133  fpsTime_.start();
134  fpsCounter_ = 0;
135  f_p_s_ = 0.0;
136  fpsString_ = tr("%1Hz", "Frames per seconds, in Hertz").arg("?");
137  visualHint_ = 0;
138  previousPathId_ = 0;
139  // prevPos_ is not initialized since pos() is not meaningful here. It will be set by setFullScreen().
140 
141  // #CONNECTION# default values in initFromDOMElement()
142  manipulatedFrame_ = NULL;
143  manipulatedFrameIsACamera_ = false;
144  mouseGrabberIsAManipulatedFrame_ = false;
145  mouseGrabberIsAManipulatedCameraFrame_ = false;
146  displayMessage_ = false;
147  connect(&messageTimer_, SIGNAL(timeout()), SLOT(hideMessage()));
148 #if QT_VERSION >= 0x040000
149  messageTimer_.setSingleShot(true);
150 #endif
151  helpWidget_ = NULL;
152  setMouseGrabber(NULL);
153 
154  setSceneRadius(1.0);
155  showEntireScene();
156  setStateFileName(".qglviewer.xml");
157 
158  // #CONNECTION# default values in initFromDOMElement()
159  setAxisIsDrawn(false);
160  setGridIsDrawn(false);
161  setFPSIsDisplayed(false);
162  setCameraIsEdited(false);
163  setTextIsEnabled(true);
164  setStereoDisplay(false);
165  setFullScreen(false);
166 
167  animationTimerId_ = 0;
168  stopAnimation();
169  setAnimationPeriod(40); // 25Hz
170 
171  selectBuffer_ = NULL;
172  setSelectBufferSize(4*1000);
173  setSelectRegionWidth(3);
174  setSelectRegionHeight(3);
175  setSelectedName(-1);
176 
177  bufferTextureId_ = 0;
178  bufferTextureMaxU_ = 0.0;
179  bufferTextureMaxV_ = 0.0;
180  bufferTextureWidth_ = 0;
181  bufferTextureHeight_ = 0;
182  previousBufferTextureFormat_ = 0;
183  previousBufferTextureInternalFormat_ = 0;
184 
185 #if QT_VERSION >= 0x040000
186  setAttribute(Qt::WA_NoSystemBackground);
187 #endif
188 
189  tileRegion_ = NULL;
190 }
191 
192 #if QT_VERSION >= 0x040000
193 
200 QGLViewer::QGLViewer(QWidget* parent, const QGLWidget* shareWidget, Qt::WFlags flags)
201 : QGLWidget(parent, shareWidget, flags)
202 { defaultConstructor(); }
203 
209 QGLViewer::QGLViewer(QGLContext *context, QWidget* parent, const QGLWidget* shareWidget, Qt::WFlags flags)
210 : QGLWidget(context, parent, shareWidget, flags)
211 { defaultConstructor(); }
212 
217 QGLViewer::QGLViewer(const QGLFormat& format, QWidget* parent, const QGLWidget* shareWidget, Qt::WFlags flags)
218 : QGLWidget(format, parent, shareWidget, flags)
219 { defaultConstructor(); }
220 #endif
221 
227 {
228  // See closeEvent comment. Destructor is called (and not closeEvent) only when the widget is embedded.
229  // Hence we saveToFile here. It is however a bad idea if virtual domElement() has been overloaded !
230  // if (parent())
231  // saveStateToFileForAllViewers();
232 
233 #if QT_VERSION >= 0x040000
234  QGLViewer::QGLViewerPool_.replace(QGLViewer::QGLViewerPool_.indexOf(this), NULL);
235 #else
236  QGLViewer::QGLViewerPool_.replace(QGLViewer::QGLViewerPool_.findRef(this), NULL);
237 #endif
238 
239  delete camera();
240  delete[] selectBuffer_;
241  if (helpWidget())
242  {
243  // Needed for Qt 4 which has no main widget.
244  helpWidget()->close();
245  delete helpWidget_;
246  }
247 }
248 
249 
250 static QString QGLViewerVersionString()
251 {
252  return QString::number((QGLVIEWER_VERSION & 0xff0000) >> 16) + "." +
253  QString::number((QGLVIEWER_VERSION & 0x00ff00) >> 8) + "." +
254  QString::number(QGLVIEWER_VERSION & 0x0000ff);
255 }
256 
257 static int convertToKeyboardModifiers(int state)
258 {
259 #if QT_VERSION < 0x040000
260  // Qt 2 & 3 have different values for ButtonState and Modifiers.
261  // Converts CTRL,SHIFT... to ControlButton, ShiftButton...
262  if (state & Qt::MODIFIER_MASK)
263  {
264  if (state & Qt::CTRL) { state &= ~Qt::CTRL; state |= Qt::ControlButton; }
265  if (state & Qt::SHIFT) { state &= ~Qt::SHIFT; state |= Qt::ShiftButton; }
266  if (state & Qt::ALT) { state &= ~Qt::ALT; state |= Qt::AltButton; }
267 # if QT_VERSION >= 0x030100
268  if (state & Qt::META) { state &= ~Qt::META; state |= Qt::MetaButton; }
269 # endif
270  }
271 #endif
272  return state;
273 }
274 
275 static QtKeyboardModifiers convertKeyboardModifiers(QtKeyboardModifiers modifiers)
276 {
277 #if QT_VERSION < 0x040000
278  return QtKeyboardModifiers(convertToKeyboardModifiers(modifiers));
279 #else
280  return modifiers;
281 #endif
282 }
283 
284 static int convertToShortModifier(int state)
285 {
286  // Converts ControlButton, ShiftButton... to CTRL,SHIFT...
287  // convertToKeyboardModifiers does the opposite
288 #if QT_VERSION < 0x040000
289  if (state & Qt::KeyButtonMask)
290  {
291  if (state & Qt::ControlButton) { state &= ~Qt::ControlButton; state |= Qt::CTRL; }
292  if (state & Qt::ShiftButton) { state &= ~Qt::ShiftButton; state |= Qt::SHIFT; }
293  if (state & Qt::AltButton) { state &= ~Qt::AltButton; state |= Qt::ALT; }
294 # if QT_VERSION >= 0x030100
295  if (state & Qt::MetaButton) { state &= ~Qt::MetaButton; state |= Qt::META; }
296 # endif
297  }
298 #endif
299  return state;
300 }
301 
321 {
322  if (updateGLOK_)
323  qWarning("Internal debug: initializeGL() is called in QGLViewer constructor.");
324 
325  glEnable(GL_LIGHT0);
326  glEnable(GL_LIGHTING);
327  glEnable(GL_DEPTH_TEST);
328  glEnable(GL_COLOR_MATERIAL);
329 
330  // Default colors
331  setForegroundColor(QColor(180, 180, 180));
332  setBackgroundColor(QColor(51, 51, 51));
333 
334  // Clear the buffer where we're going to draw
335  if (format().stereo())
336  {
337  glDrawBuffer(GL_BACK_RIGHT);
338  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
339  glDrawBuffer(GL_BACK_LEFT);
340  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
341  }
342  else
343  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
344 
345  // Calls user defined method. Default emits a signal.
346  init();
347 
348  // Give time to glInit to finish and then call setFullScreen().
349  if (isFullScreen())
350  QTimer::singleShot( 100, this, SLOT(delayedFullScreen()) );
351 
352  updateGLOK_ = true;
353 }
354 
362 {
363  updateGLOK_ = false;
364  if (displaysInStereo())
365  {
366  for (int view=1; view>=0; --view)
367  {
368  // Clears screen, set model view matrix with shifted matrix for ith buffer
369  preDrawStereo(view);
370  // Used defined method. Default is empty
371  if (camera()->frame()->isManipulated())
372  fastDraw();
373  else
374  draw();
375  postDraw();
376  }
377  }
378  else
379  {
380  // Clears screen, set model view matrix...
381  preDraw();
382  // Used defined method. Default calls draw()
383  if (camera()->frame()->isManipulated())
384  fastDraw();
385  else
386  draw();
387  // Add visual hints: axis, camera, grid...
388  postDraw();
389  }
390  updateGLOK_ = true;
391  Q_EMIT drawFinished(true);
392 }
393 
406 {
407  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
408 
409  // GL_PROJECTION matrix
410  camera()->loadProjectionMatrix();
411  // GL_MODELVIEW matrix
412  camera()->loadModelViewMatrix();
413 
414  Q_EMIT drawNeeded();
415 }
416 
428 {
429  // Reset model view matrix to world coordinates origin
430  glMatrixMode(GL_MODELVIEW);
431  glPushMatrix();
432  camera()->loadModelViewMatrix();
433  // TODO restore model loadProjectionMatrixStereo
434 
435  // Save OpenGL state
436  glPushAttrib(GL_ALL_ATTRIB_BITS);
437 
438  // Set neutral GL state
439  glDisable(GL_TEXTURE_1D);
440  glDisable(GL_TEXTURE_2D);
441 #ifdef GL_TEXTURE_3D // OpenGL 1.2 Only...
442  glDisable(GL_TEXTURE_3D);
443 #endif
444 
445  glDisable(GL_TEXTURE_GEN_Q);
446  glDisable(GL_TEXTURE_GEN_R);
447  glDisable(GL_TEXTURE_GEN_S);
448  glDisable(GL_TEXTURE_GEN_T);
449 
450 #ifdef GL_RESCALE_NORMAL // OpenGL 1.2 Only...
451  glEnable(GL_RESCALE_NORMAL);
452 #endif
453 
454  glDisable(GL_COLOR_MATERIAL);
455  qglColor(foregroundColor());
456 
457  if (cameraIsEdited())
458  camera()->drawAllPaths();
459 
460  // Revolve Around Point, line when camera rolls, zoom region
461  drawVisualHints();
462 
463  if (gridIsDrawn()) { glLineWidth(1.0); drawGrid(camera()->sceneRadius()); }
464  if (axisIsDrawn()) { glLineWidth(2.0); drawAxis(camera()->sceneRadius()); }
465 
466  // FPS computation
467  const unsigned int maxCounter = 20;
468  if (++fpsCounter_ == maxCounter)
469  {
470  f_p_s_ = 1000.0 * maxCounter / fpsTime_.restart();
471  fpsString_ = tr("%1Hz", "Frames per seconds, in Hertz").arg(f_p_s_, 0, 'f', ((f_p_s_ < 10.0)?1:0));
472  fpsCounter_ = 0;
473  }
474 
475  // Restore foregroundColor
476  float color[4];
477  color[0] = foregroundColor().red() / 255.0;
478  color[1] = foregroundColor().green() / 255.0;
479  color[2] = foregroundColor().blue() / 255.0;
480  color[3] = 1.0;
481  glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
482  glDisable(GL_LIGHTING);
483  glDisable(GL_DEPTH_TEST);
484 
485  if (FPSIsDisplayed()) displayFPS();
486  if (displayMessage_) drawText(10, height()-10, message_);
487 
488  // Restore GL state
489  glPopAttrib();
490  glPopMatrix();
491 }
492 
498 void QGLViewer::preDrawStereo(bool leftBuffer)
499 {
500  // Set buffer to draw in
501  // Seems that SGI and Crystal Eyes are not synchronized correctly !
502  // That's why we don't draw in the appropriate buffer...
503  if (!leftBuffer)
504  glDrawBuffer(GL_BACK_LEFT);
505  else
506  glDrawBuffer(GL_BACK_RIGHT);
507 
508  // Clear the buffer where we're going to draw
509  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
510  // GL_PROJECTION matrix
511  camera()->loadProjectionMatrixStereo(leftBuffer);
512  // GL_MODELVIEW matrix
513  camera()->loadModelViewMatrixStereo(leftBuffer);
514 
515  Q_EMIT drawNeeded();
516 }
517 
526 {
527  draw();
528 }
529 
538 {
539  cameraIsEdited_ = edit;
540  if (edit)
541  {
542  previousCameraZClippingCoefficient_ = camera()->zClippingCoefficient();
543  // #CONNECTION# 5.0 also used in domElement() and in initFromDOMElement().
544  camera()->setZClippingCoefficient(5.0);
545  }
546  else
547  camera()->setZClippingCoefficient(previousCameraZClippingCoefficient_);
548 
549  Q_EMIT cameraIsEditedChanged(edit);
550 
551  if (updateGLOK_)
552  updateGL();
553 }
554 
555 // Key bindings. 0 means not defined
556 void QGLViewer::setDefaultShortcuts()
557 {
558  // D e f a u l t a c c e l e r a t o r s
559  setShortcut(DRAW_AXIS, Qt::Key_A);
560  setShortcut(DRAW_GRID, Qt::Key_G);
561  setShortcut(DISPLAY_FPS, Qt::Key_F);
562  setShortcut(ENABLE_TEXT, Qt::SHIFT+Qt::Key_Question);
563  setShortcut(EXIT_VIEWER, Qt::Key_Escape);
564  setShortcut(SAVE_SCREENSHOT, Qt::CTRL+Qt::Key_S);
565  setShortcut(CAMERA_MODE, Qt::Key_Space);
566  setShortcut(FULL_SCREEN, Qt::ALT+Qt::Key_Return);
567  setShortcut(STEREO, Qt::Key_S);
568  setShortcut(ANIMATION, Qt::Key_Return);
569  setShortcut(HELP, Qt::Key_H);
570  setShortcut(EDIT_CAMERA, Qt::Key_C);
571  setShortcut(MOVE_CAMERA_LEFT, Qt::Key_Left);
572  setShortcut(MOVE_CAMERA_RIGHT,Qt::Key_Right);
573  setShortcut(MOVE_CAMERA_UP, Qt::Key_Up);
574  setShortcut(MOVE_CAMERA_DOWN, Qt::Key_Down);
575  setShortcut(INCREASE_FLYSPEED,Qt::Key_Plus);
576  setShortcut(DECREASE_FLYSPEED,Qt::Key_Minus);
577  setShortcut(SNAPSHOT_TO_CLIPBOARD,Qt::CTRL+Qt::Key_C);
578 
579  keyboardActionDescription_[DISPLAY_FPS] = tr("Toggles the display of the FPS", "DISPLAY_FPS action description");
580  keyboardActionDescription_[SAVE_SCREENSHOT] = tr("Saves a screenshot", "SAVE_SCREENSHOT action description");
581  keyboardActionDescription_[FULL_SCREEN] = tr("Toggles full screen display", "FULL_SCREEN action description");
582  keyboardActionDescription_[DRAW_AXIS] = tr("Toggles the display of the world axis", "DRAW_AXIS action description");
583  keyboardActionDescription_[DRAW_GRID] = tr("Toggles the display of the XY grid", "DRAW_GRID action description");
584  keyboardActionDescription_[CAMERA_MODE] = tr("Changes camera mode (revolve or fly)", "CAMERA_MODE action description");
585  keyboardActionDescription_[STEREO] = tr("Toggles stereo display", "STEREO action description");
586  keyboardActionDescription_[HELP] = tr("Opens this help window", "HELP action description");
587  keyboardActionDescription_[ANIMATION] = tr("Starts/stops the animation", "ANIMATION action description");
588  keyboardActionDescription_[EDIT_CAMERA] = tr("Toggles camera paths display", "EDIT_CAMERA action description"); // TODO change
589  keyboardActionDescription_[ENABLE_TEXT] = tr("Toggles the display of the text", "ENABLE_TEXT action description");
590  keyboardActionDescription_[EXIT_VIEWER] = tr("Exits program", "EXIT_VIEWER action description");
591  keyboardActionDescription_[MOVE_CAMERA_LEFT] = tr("Moves camera left", "MOVE_CAMERA_LEFT action description");
592  keyboardActionDescription_[MOVE_CAMERA_RIGHT] = tr("Moves camera right", "MOVE_CAMERA_RIGHT action description");
593  keyboardActionDescription_[MOVE_CAMERA_UP] = tr("Moves camera up", "MOVE_CAMERA_UP action description");
594  keyboardActionDescription_[MOVE_CAMERA_DOWN] = tr("Moves camera down", "MOVE_CAMERA_DOWN action description");
595  keyboardActionDescription_[INCREASE_FLYSPEED] = tr("Increases fly speed", "INCREASE_FLYSPEED action description");
596  keyboardActionDescription_[DECREASE_FLYSPEED] = tr("Decreases fly speed", "DECREASE_FLYSPEED action description");
597  keyboardActionDescription_[SNAPSHOT_TO_CLIPBOARD] = tr("Copies a snapshot to clipboard", "SNAPSHOT_TO_CLIPBOARD action description");
598 
599  // K e y f r a m e s s h o r t c u t k e y s
600  setPathKey(Qt::Key_F1, 1);
601  setPathKey(Qt::Key_F2, 2);
602  setPathKey(Qt::Key_F3, 3);
603  setPathKey(Qt::Key_F4, 4);
604  setPathKey(Qt::Key_F5, 5);
605  setPathKey(Qt::Key_F6, 6);
606  setPathKey(Qt::Key_F7, 7);
607  setPathKey(Qt::Key_F8, 8);
608  setPathKey(Qt::Key_F9, 9);
609  setPathKey(Qt::Key_F10, 10);
610  setPathKey(Qt::Key_F11, 11);
611  setPathKey(Qt::Key_F12, 12);
612 
613 #if QT_VERSION >= 0x040000
614  setAddKeyFrameKeyboardModifiers(Qt::AltModifier);
615  setPlayPathKeyboardModifiers(Qt::NoModifier);
616 #else
617  setAddKeyFrameKeyboardModifiers(Qt::AltButton);
618  setPlayPathKeyboardModifiers(Qt::NoButton);
619 #endif
620 }
621 
622 // M o u s e b e h a v i o r
623 void QGLViewer::setDefaultMouseBindings()
624 {
625 #if QT_VERSION >= 0x040000
626  const QtKeyboardModifiers cameraKeyboardModifiers = Qt::NoModifier;
627  const QtKeyboardModifiers frameKeyboardModifiers = Qt::ControlModifier;
628 #else
629  const QtKeyboardModifiers cameraKeyboardModifiers = Qt::NoButton;
630  const QtKeyboardModifiers frameKeyboardModifiers = Qt::ControlButton;
631 #endif
632  //#CONNECTION# toggleCameraMode()
633  for (int handler=0; handler<2; ++handler)
634  {
635  MouseHandler mh = (MouseHandler)(handler);
636  QtKeyboardModifiers modifiers = (mh == FRAME) ? frameKeyboardModifiers : cameraKeyboardModifiers;
637 
638  setMouseBinding(modifiers | Qt::LeftButton, mh, ROTATE);
639  setMouseBinding(modifiers | Qt::MidButton, mh, ZOOM);
640  setMouseBinding(modifiers | Qt::RightButton, mh, TRANSLATE);
641 
642  setMouseBinding(modifiers | Qt::LeftButton | Qt::MidButton, mh, SCREEN_ROTATE);
643  // 2.2.4 setMouseBinding(modifiers | Qt::RightButton | Qt::MidButton, mh, SCREEN_TRANSLATE);
644 
645  setWheelBinding(modifiers, mh, ZOOM);
646  }
647 
648 #if QT_VERSION >= 0x040000
649  // Z o o m o n r e g i o n
650  setMouseBinding(Qt::ShiftModifier | Qt::MidButton, CAMERA, ZOOM_ON_REGION);
651  // S e l e c t
652  setMouseBinding(Qt::ShiftModifier | Qt::LeftButton, SELECT);
653 #else
654  setMouseBinding(Qt::ShiftButton | Qt::MidButton, CAMERA, ZOOM_ON_REGION);
655  setMouseBinding(Qt::ShiftButton | Qt::LeftButton, SELECT);
656 #endif
657 
658  // D o u b l e c l i c k
659  setMouseBinding(Qt::LeftButton, ALIGN_CAMERA, true);
660  setMouseBinding(Qt::MidButton, SHOW_ENTIRE_SCENE, true);
661  setMouseBinding(Qt::RightButton, CENTER_SCENE, true);
662 
663  setMouseBinding(frameKeyboardModifiers | Qt::LeftButton, ALIGN_FRAME, true);
664  setMouseBinding(frameKeyboardModifiers | Qt::RightButton, CENTER_FRAME, true);
665 
666  // S p e c i f i c d o u b l e c l i c k s
667  setMouseBinding(Qt::LeftButton, RAP_FROM_PIXEL, true, Qt::RightButton);
668  setMouseBinding(Qt::RightButton, RAP_IS_CENTER, true, Qt::LeftButton);
669  setMouseBinding(Qt::LeftButton, ZOOM_ON_PIXEL, true, Qt::MidButton);
670  setMouseBinding(Qt::RightButton, ZOOM_TO_FIT, true, Qt::MidButton);
671 
672 #ifdef Q_OS_MAC
673  // Specific Mac bindings. Double finger emulates a wheelEvent which zooms.
674  // Make Option + left emulate the right button. Other bindings should be changed accordingly.
675  // Done at the end to override previous settings.
676 # if QT_VERSION >= 0x040000
677  const QtKeyboardModifiers macKeyboardModifiers = Qt::AltModifier;
678 # else
679  const QtKeyboardModifiers macKeyboardModifiers = Qt::AltButton;
680 # endif
681 
682  setMouseBinding(macKeyboardModifiers | Qt::LeftButton, CAMERA, TRANSLATE);
683  setMouseBinding(macKeyboardModifiers | Qt::LeftButton, CENTER_SCENE, true);
684  setMouseBinding(frameKeyboardModifiers | macKeyboardModifiers | Qt::LeftButton, CENTER_FRAME, true);
685  setMouseBinding(frameKeyboardModifiers | macKeyboardModifiers | Qt::LeftButton, FRAME, TRANSLATE);
686 
687  // S p e c i f i c d o u b l e c l i c k s
688  // A single tap is actually seen as a left followed by a right button click.
689  setMouseBinding(Qt::MetaModifier | Qt::RightButton, RAP_FROM_PIXEL, true, Qt::LeftButton);
690  setMouseBinding(Qt::ShiftModifier | Qt::MetaModifier | Qt::RightButton, RAP_IS_CENTER, true, Qt::LeftButton);
691  // A tap with two fingers is actually considered a rightButton.
692  setMouseBinding(Qt::MetaModifier | Qt::RightButton, ZOOM_ON_PIXEL, false);
693  setMouseBinding(Qt::ShiftModifier | Qt::MetaModifier | Qt::RightButton, ZOOM_TO_FIT, false);
694 #endif
695 }
696 
713 void QGLViewer::setCamera(Camera* const camera)
714 {
715  if (!camera)
716  return;
717 
718  camera->setSceneRadius(sceneRadius());
719  camera->setSceneCenter(sceneCenter());
720  camera->setScreenWidthAndHeight(width(),height());
721 
722  // Disconnect current camera to this viewer.
723  disconnect(this->camera()->frame(), SIGNAL(manipulated()), this, SLOT(updateGL()));
724  disconnect(this->camera()->frame(), SIGNAL(spun()), this, SLOT(updateGL()));
725 
726  // Connect camera frame to this viewer.
727  connect(camera->frame(), SIGNAL(manipulated()), SLOT(updateGL()));
728  connect(camera->frame(), SIGNAL(spun()), SLOT(updateGL()));
729 
730  connectAllCameraKFIInterpolatedSignals(false);
731  camera_ = camera;
732  connectAllCameraKFIInterpolatedSignals();
733 
734  previousCameraZClippingCoefficient_ = this->camera()->zClippingCoefficient();
735 }
736 
737 void QGLViewer::connectAllCameraKFIInterpolatedSignals(bool connection)
738 {
739  for (QMap<int, KeyFrameInterpolator*>::ConstIterator it = camera()->kfi_.begin(), end=camera()->kfi_.end(); it != end; ++it)
740  {
741  if (connection)
742  connect(camera()->keyFrameInterpolator(it.key()), SIGNAL(interpolated()), SLOT(updateGL()));
743  else
744  disconnect(camera()->keyFrameInterpolator(it.key()), SIGNAL(interpolated()), this, SLOT(updateGL()));
745  }
746 
747  if (connection)
748  connect(camera()->interpolationKfi_, SIGNAL(interpolated()), SLOT(updateGL()));
749  else
750  disconnect(camera()->interpolationKfi_, SIGNAL(interpolated()), this, SLOT(updateGL()));
751 }
752 
769 void QGLViewer::drawLight(GLenum light, float scale) const
770 {
771  static GLUquadric* quadric = gluNewQuadric();
772 
773  const float length = sceneRadius() / 5.0 * scale;
774 
775  GLboolean lightIsOn;
776  glGetBooleanv(light, &lightIsOn);
777 
778  if (lightIsOn)
779  {
780  // All light values are given in eye coordinates
781  glPushMatrix();
782  glLoadIdentity();
783 
784  float color[4];
785  glGetLightfv(light, GL_DIFFUSE, color);
786  glColor4fv(color);
787 
788  float pos[4];
789  glGetLightfv(light, GL_POSITION, pos);
790 
791  if (pos[3] != 0.0)
792  {
793  glTranslatef(pos[0]/pos[3], pos[1]/pos[3], pos[2]/pos[3]);
794 
795  GLfloat cutOff;
796  glGetLightfv(light, GL_SPOT_CUTOFF, &cutOff);
797  if (cutOff != 180.0)
798  {
799  GLfloat dir[4];
800  glGetLightfv(light, GL_SPOT_DIRECTION, dir);
801  glMultMatrixd(Quaternion(Vec(0,0,1), Vec(dir)).matrix());
802  QGLViewer::drawArrow(length);
803  gluCylinder(quadric, 0.0, 0.7 * length * sin(cutOff * M_PI / 180.0), 0.7 * length * cos(cutOff * M_PI / 180.0), 12, 1);
804  }
805  else
806  gluSphere(quadric, 0.2*length, 10, 10);
807  }
808  else
809  {
810  // Directional light.
811  Vec dir(pos[0], pos[1], pos[2]);
812  dir.normalize();
813  Frame fr=Frame(camera()->cameraCoordinatesOf(4.0 * length * camera()->frame()->inverseTransformOf(dir)),
814  Quaternion(Vec(0,0,-1), dir));
815  glMultMatrixd(fr.matrix());
816  drawArrow(length);
817  }
818 
819  glPopMatrix();
820  }
821 }
822 
823 
866 void QGLViewer::drawText(int x, int y, const QString& text, const QFont& fnt)
867 {
868  if (!textIsEnabled())
869  return;
870 
871 #if QT_VERSION < QGLVIEWER_QT_VERSION_WITHOUT_GLUT
872  const GLfloat font_scale = 119.05f - 33.33f; // see glutStrokeCharacter man page
873 
874  startScreenCoordinatesSystem();
875 
876  // Anti-aliased characters
877  glPushAttrib(GL_ALL_ATTRIB_BITS);
878  glDisable(GL_LIGHTING);
879  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
880  glEnable(GL_BLEND);
881  glDisable(GL_DEPTH_TEST);
882  glEnable(GL_LINE_SMOOTH);
883  glLineWidth(1.0);
884 
885  glTranslatef((GLfloat)x, (GLfloat)y, 0.0);
886  const GLfloat scale = ((fnt.pixelSize()>0)?fnt.pixelSize():fnt.pointSize()) / font_scale;
887  glScalef(scale, -scale, scale);
888 
889  for (uint i=0; i<text.length(); ++i)
890  glutStrokeCharacter(GLUT_STROKE_ROMAN, text.at(i));
891 
892  glPopAttrib();
893 
894  stopScreenCoordinatesSystem();
895 #else
896 
897 # if QT_VERSION < 0x030300 && defined Q_OS_UNIX
898  // Fix bug with anti-aliased fonts on nVidia driver
899  QFont newFont(fnt);
900  newFont.setFamily("fixed");
901  newFont.setRawMode(true);
902  newFont.setPixelSize(10);
903  newFont.setFixedPitch(true);
904 # if QT_VERSION >= 0x030200
905  newFont.setStyleStrategy(QFont::OpenGLCompatible);
906 # endif
907  newFont.setStyleHint(QFont::AnyStyle, QFont::PreferBitmap);
908  renderText(x, y, text, newFont);
909 # else
910  if (tileRegion_ != NULL) {
911  renderText((x-tileRegion_->xMin) * width() / (tileRegion_->xMax - tileRegion_->xMin),
912  (y-tileRegion_->yMin) * height() / (tileRegion_->yMax - tileRegion_->yMin), text, scaledFont(fnt));
913  } else
914  renderText(x, y, text, fnt);
915 # endif
916 
917 #endif
918 }
919 
920 /* Similar to drawText(), but the text is handled as a classical 3D object of the scene.
921 
922 Although useful, this method is deprecated with recent Qt versions. Indeed, Qt renders text as
923 pixmaps that cannot be oriented. However, when GLUT is used instead of Qt (when your Qt version is
924 lower than 3.1, see drawText() documentation) orientated characters are possible and this method will work.
925 
926 \p pos and \p normal respectively represent the 3D coordinate of the text and the normal to the text
927 plane. They are expressed with respect to the \e current \c GL_MODELVIEW matrix.
928 
929 If you want your text to always face the camera (normal parallel to camera()->viewDirection), use
930 QGLWidget::renderText(x,y,z).
931 
932 See the <a href="../examples/draw3DText.html">draw3DText example</a> for an illustration. */
933 /*
934 void QGLViewer::draw3DText(const Vec& pos, const Vec& normal, const QString& text, GLfloat height)
935 {
936 #if QT_VERSION < QGLVIEWER_QT_VERSION_WITHOUT_GLUT
937 if (!textIsEnabled())
938 return;
939 
940 glMatrixMode(GL_MODELVIEW) ;
941 glPushMatrix() ;
942 
943 const GLfloat font_scale = (119.05f - 33.33f) / 8; // see glutStrokeCharacter man page
944 // const GLfloat font_scale = (119.05f - 33.33f) * 15.0f; // see glutStrokeCharacter man page
945 
946 static GLfloat lineWidth;
947 glGetFloatv(GL_LINE_WIDTH, &lineWidth);
948 
949 glTranslatef(pos.x, pos.y, pos.z);
950 glMultMatrixd(Quaternion(Vec(0.0, 0.0, 1.0), normal).matrix());
951 
952 glLineWidth(2.0);
953 
954 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
955 glEnable(GL_BLEND);
956 glEnable(GL_LINE_SMOOTH);
957 
958 const GLfloat scale = height / font_scale;
959 glScalef(scale, scale, scale);
960 
961 for (uint i=0; i<text.length(); ++i)
962 glutStrokeCharacter(GLUT_STROKE_ROMAN, text.at(i));
963 
964 glLineWidth(lineWidth);
965 
966 glMatrixMode(GL_MODELVIEW);
967 glPopMatrix() ;
968 #else
969 static bool displayed = false;
970 
971 if (!displayed)
972 {
973 qWarning("draw3DText is not supported with Qt >= 3.1.");
974 qWarning("Use QGLWidget::renderText() instead,");
975 qWarning("or use the glut glutStrokeCharacter() method.");
976 displayed = true;
977 }
978 
979 Q_UNUSED(pos)
980 Q_UNUSED(normal)
981 Q_UNUSED(text)
982 Q_UNUSED(height)
983 #endif
984 }
985 */
986 
998 void QGLViewer::displayMessage(const QString& message, int delay)
999 {
1000  message_ = message;
1001  displayMessage_ = true;
1002 #if QT_VERSION >= 0x040000
1003  // Was set to single shot in defaultConstructor.
1004  messageTimer_.start(delay);
1005 #else
1006  if (messageTimer_.isActive())
1007  messageTimer_.changeInterval(delay);
1008  else
1009  messageTimer_.start(delay, true);
1010 #endif
1011  if (textIsEnabled() && updateGLOK_)
1012  updateGL();
1013 }
1014 
1015 void QGLViewer::hideMessage()
1016 {
1017  displayMessage_ = false;
1018  if (textIsEnabled())
1019  updateGL();
1020 }
1021 
1022 
1032 void QGLViewer::displayFPS()
1033 {
1034  drawText(10, int(1.5*((QApplication::font().pixelSize()>0)?QApplication::font().pixelSize():QApplication::font().pointSize())), fpsString_);
1035 }
1036 
1069 {
1070  glMatrixMode(GL_PROJECTION);
1071  glPushMatrix();
1072  glLoadIdentity();
1073  if (tileRegion_ != NULL)
1074  if (upward)
1075  glOrtho(tileRegion_->xMin, tileRegion_->xMax, tileRegion_->yMin, tileRegion_->yMax, 0.0, -1.0);
1076  else
1077  glOrtho(tileRegion_->xMin, tileRegion_->xMax, tileRegion_->yMax, tileRegion_->yMin, 0.0, -1.0);
1078  else
1079  if (upward)
1080  glOrtho(0, width(), 0, height(), 0.0, -1.0);
1081  else
1082  glOrtho(0, width(), height(), 0, 0.0, -1.0);
1083 
1084  glMatrixMode(GL_MODELVIEW);
1085  glPushMatrix();
1086  glLoadIdentity();
1087 }
1088 
1094 {
1095  glMatrixMode(GL_PROJECTION);
1096  glPopMatrix();
1097 
1098  glMatrixMode(GL_MODELVIEW);
1099  glPopMatrix();
1100 }
1101 
1105 void QGLViewer::timerEvent(QTimerEvent *)
1106 {
1107  if (animationIsStarted())
1108  {
1109  animate();
1110  updateGL();
1111  }
1112 }
1113 
1116 {
1117  animationTimerId_ = startTimer(animationPeriod());
1118  animationStarted_ = true;
1119 }
1120 
1123 {
1124  animationStarted_ = false;
1125  if (animationTimerId_ != 0)
1126  killTimer(animationTimerId_);
1127 }
1128 
1132 void QGLViewer::closeEvent(QCloseEvent *e)
1133 {
1134  // When the user clicks on the window close (x) button:
1135  // - If the viewer is a top level window, closeEvent is called and then saves to file.
1136  // - Otherwise, nothing happen s:(
1137  // When the user press the EXIT_VIEWER keyboard shortcut:
1138  // - If the viewer is a top level window, saveStateToFile() is also called
1139  // - Otherwise, closeEvent is NOT called and keyPressEvent does the job.
1140 
1141  /* After tests:
1142  E : Embedded widget
1143  N : Widget created with new
1144  C : closeEvent called
1145  D : destructor called
1146 
1147  E N C D
1148  y y
1149  y n y
1150  n y y
1151  n n y y
1152 
1153  closeEvent is called iif the widget is NOT embedded.
1154 
1155  Destructor is called iif the widget is created on the stack
1156  or if widget (resp. parent if embedded) is created with WDestructiveClose flag.
1157 
1158  closeEvent always before destructor.
1159 
1160  Close using qApp->closeAllWindows or (x) is identical.
1161  */
1162 
1163  // #CONNECTION# Also done for EXIT_VIEWER in keyPressEvent().
1164  saveStateToFile();
1165  QGLWidget::closeEvent(e);
1166 }
1167 
1177 void QGLViewer::select(const QMouseEvent* event)
1178 {
1179  // For those who don't derive but rather rely on the signal-slot mechanism.
1180  Q_EMIT pointSelected(event);
1181  select(event->pos());
1182 }
1183 
1234 void QGLViewer::select(const QPoint& point)
1235 {
1236  beginSelection(point);
1237  drawWithNames();
1238  endSelection(point);
1239  postSelection(point);
1240 }
1241 
1253 void QGLViewer::beginSelection(const QPoint& point)
1254 {
1255  // Make OpenGL context current (may be needed with several viewers ?)
1256  makeCurrent();
1257 
1258  // Prepare the selection mode
1259  glSelectBuffer(selectBufferSize(), selectBuffer());
1260  glRenderMode(GL_SELECT);
1261  glInitNames();
1262 
1263  // Loads the matrices
1264  glMatrixMode(GL_PROJECTION);
1265  glLoadIdentity();
1266  static GLint viewport[4];
1267  camera()->getViewport(viewport);
1268  gluPickMatrix(point.x(), point.y(), selectRegionWidth(), selectRegionHeight(), viewport);
1269 
1270  // loadProjectionMatrix() first resets the GL_PROJECTION matrix with a glLoadIdentity().
1271  // The false parameter prevents this and hence multiplies the matrices.
1272  camera()->loadProjectionMatrix(false);
1273  // Reset the original (world coordinates) modelview matrix
1274  camera()->loadModelViewMatrix();
1275 }
1276 
1313 void QGLViewer::endSelection(const QPoint& point)
1314 {
1315  Q_UNUSED(point);
1316 
1317  // Flush GL buffers
1318  glFlush();
1319 
1320  // Get the number of objects that were seen through the pick matrix frustum. Reset GL_RENDER mode.
1321  GLint nbHits = glRenderMode(GL_RENDER);
1322 
1323  if (nbHits <= 0)
1324  setSelectedName(-1);
1325  else
1326  {
1327  // Interpret results: each object created 4 values in the selectBuffer().
1328  // selectBuffer[4*i+1] is the object minimum depth value, while selectBuffer[4*i+3] is the id pushed on the stack.
1329  // Of all the objects that were projected in the pick region, we select the closest one (zMin comparison).
1330  // This code needs to be modified if you use several stack levels. See glSelectBuffer() man page.
1331  GLuint zMin = (selectBuffer())[1];
1332  setSelectedName((selectBuffer())[3]);
1333  for (int i=1; i<nbHits; ++i)
1334  if ((selectBuffer())[4*i+1] < zMin)
1335  {
1336  zMin = (selectBuffer())[4*i+1];
1337  setSelectedName((selectBuffer())[4*i+3]);
1338  }
1339  }
1340 }
1341 
1346 {
1347  if (selectBuffer_)
1348  delete[] selectBuffer_;
1349  selectBufferSize_ = size;
1350  selectBuffer_ = new GLuint[selectBufferSize()];
1351 }
1352 
1353 void QGLViewer::performClickAction(ClickAction ca, const QMouseEvent* const e)
1354 {
1355  // Note: action that need it should updateGL().
1356  switch (ca)
1357  {
1358  // # CONNECTION setMouseBinding prevents adding NO_CLICK_ACTION in clickBinding_
1359  // This case should hence not be possible. Prevents unused case warning.
1360  case NO_CLICK_ACTION :
1361  break;
1362  case ZOOM_ON_PIXEL :
1363  camera()->interpolateToZoomOnPixel(e->pos());
1364  break;
1365  case ZOOM_TO_FIT :
1366  camera()->interpolateToFitScene();
1367  break;
1368  case SELECT :
1369  select(e);
1370  updateGL();
1371  break;
1372  case RAP_FROM_PIXEL :
1373  if (camera()->setRevolveAroundPointFromPixel(e->pos()))
1374  {
1375  setVisualHintsMask(1);
1376  updateGL();
1377  }
1378  break;
1379  case RAP_IS_CENTER :
1380  camera()->setRevolveAroundPoint(sceneCenter());
1381  setVisualHintsMask(1);
1382  updateGL();
1383  break;
1384  case CENTER_FRAME :
1385  if (manipulatedFrame())
1386  manipulatedFrame()->projectOnLine(camera()->position(), camera()->viewDirection());
1387  break;
1388  case CENTER_SCENE :
1389  camera()->centerScene();
1390  break;
1391  case SHOW_ENTIRE_SCENE :
1392  camera()->showEntireScene();
1393  break;
1394  case ALIGN_FRAME :
1395  if (manipulatedFrame())
1396  manipulatedFrame()->alignWithFrame(camera()->frame());
1397  break;
1398  case ALIGN_CAMERA :
1399  camera()->frame()->alignWithFrame(NULL, true);
1400  break;
1401  }
1402 }
1403 
1420 void QGLViewer::mousePressEvent(QMouseEvent* e)
1421 {
1422  //#CONNECTION# mouseDoubleClickEvent has the same structure
1423  //#CONNECTION# mouseString() concatenates bindings description in inverse order.
1424  ClickActionPrivate cap;
1425  cap.doubleClick = false;
1426 #if QT_VERSION >= 0x040000
1427  cap.modifiers = e->modifiers();
1428  cap.button = e->button();
1429  cap.buttonsBefore = (QtMouseButtons)(e->buttons() & ~(e->button()));
1430 #else
1431  cap.modifiers = (QtKeyboardModifiers)(e->state() & Qt::KeyboardModifierMask);
1432  cap.button = (QtMouseButtons)((e->stateAfter() & Qt::MouseButtonMask) & (~(e->state() & Qt::MouseButtonMask)));
1433  cap.buttonsBefore = (QtMouseButtons)(e->state() & Qt::MouseButtonMask);
1434 #endif
1435 
1436  if (clickBinding_.contains(cap))
1437  performClickAction(clickBinding_[cap], e);
1438  else
1439  if (mouseGrabber())
1440  {
1441  if (mouseGrabberIsAManipulatedFrame_)
1442  {
1443  for (QMap<int, MouseActionPrivate>::ConstIterator it=mouseBinding_.begin(), end=mouseBinding_.end(); it!=end; ++it)
1444 #if QT_VERSION >= 0x040000
1445  if ((it.value().handler == FRAME) && ((it.key() & Qt::MouseButtonMask) == e->buttons()))
1446 #else
1447  if ((it.data().handler == FRAME) && ((it.key() & Qt::MouseButtonMask) == (e->stateAfter() & Qt::MouseButtonMask)))
1448 #endif
1449  {
1450  ManipulatedFrame* mf = dynamic_cast<ManipulatedFrame*>(mouseGrabber());
1451  if (mouseGrabberIsAManipulatedCameraFrame_)
1452  {
1453  mf->ManipulatedFrame::startAction(it.value().action, it.value().withConstraint);
1454  mf->ManipulatedFrame::mousePressEvent(e, camera());
1455  }
1456  else
1457  {
1458  mf->startAction(it.value().action, it.value().withConstraint);
1459  mf->mousePressEvent(e, camera());
1460  }
1461  break;
1462  }
1463  }
1464  else
1465  mouseGrabber()->mousePressEvent(e, camera());
1466  updateGL();
1467  }
1468  else
1469  {
1470  //#CONNECTION# wheelEvent has the same structure
1471 #if QT_VERSION >= 0x040000
1472  const int state = e->modifiers() | e->buttons();
1473 #else
1474  const int state = e->stateAfter();
1475 #endif
1476 
1477  if (mouseBinding_.contains(state))
1478  {
1479  MouseActionPrivate map = mouseBinding_[state];
1480  switch (map.handler)
1481  {
1482  case CAMERA :
1483  camera()->frame()->startAction(map.action, map.withConstraint);
1484  camera()->frame()->mousePressEvent(e, camera());
1485  break;
1486  case FRAME :
1487  if (manipulatedFrame())
1488  {
1489  if (manipulatedFrameIsACamera_)
1490  {
1491  manipulatedFrame()->ManipulatedFrame::startAction(map.action, map.withConstraint);
1492  manipulatedFrame()->ManipulatedFrame::mousePressEvent(e, camera());
1493  }
1494  else
1495  {
1496  manipulatedFrame()->startAction(map.action, map.withConstraint);
1497  manipulatedFrame()->mousePressEvent(e, camera());
1498  }
1499  }
1500  break;
1501  }
1502  if (map.action == SCREEN_ROTATE)
1503  // Display visual hint line
1504  updateGL();
1505  }
1506 #if QT_VERSION >= 0x030000
1507  else
1508  e->ignore();
1509 #endif
1510  }
1511 }
1512 
1552 void QGLViewer::mouseMoveEvent(QMouseEvent* e)
1553 {
1554  if (mouseGrabber())
1555  {
1556  mouseGrabber()->checkIfGrabsMouse(e->x(), e->y(), camera());
1557  if (mouseGrabber()->grabsMouse())
1558  if (mouseGrabberIsAManipulatedCameraFrame_)
1559  (dynamic_cast<ManipulatedFrame*>(mouseGrabber()))->ManipulatedFrame::mouseMoveEvent(e, camera());
1560  else
1561  mouseGrabber()->mouseMoveEvent(e, camera());
1562  else
1563  setMouseGrabber(NULL);
1564  updateGL();
1565  }
1566 
1567  if (!mouseGrabber())
1568  {
1569  //#CONNECTION# mouseReleaseEvent has the same structure
1570  if (camera()->frame()->isManipulated())
1571  {
1572  camera()->frame()->mouseMoveEvent(e, camera());
1573  // #CONNECTION# manipulatedCameraFrame::mouseMoveEvent specific if at the beginning
1574  if (camera()->frame()->action_ == ZOOM_ON_REGION)
1575  updateGL();
1576  }
1577  else // !
1578  if ((manipulatedFrame()) && (manipulatedFrame()->isManipulated()))
1579  if (manipulatedFrameIsACamera_)
1580  manipulatedFrame()->ManipulatedFrame::mouseMoveEvent(e, camera());
1581  else
1582  manipulatedFrame()->mouseMoveEvent(e, camera());
1583  else
1584  if (hasMouseTracking())
1585  {
1586 #if QT_VERSION >= 0x040000
1587  Q_FOREACH (MouseGrabber* mg, MouseGrabber::MouseGrabberPool())
1588  {
1589 #else
1590  QPtrListIterator<MouseGrabber> it(MouseGrabber::MouseGrabberPool());
1591  for (MouseGrabber* mg; (mg = it.current()); ++it)
1592  {
1593 #endif
1594  mg->checkIfGrabsMouse(e->x(), e->y(), camera());
1595  if (mg->grabsMouse())
1596  {
1597  setMouseGrabber(mg);
1598  // Check that MouseGrabber is not disabled
1599  if (mouseGrabber() == mg)
1600  {
1601  updateGL();
1602  break;
1603  }
1604  }
1605  }
1606  }
1607  }
1608 }
1609 
1615 void QGLViewer::mouseReleaseEvent(QMouseEvent* e)
1616 {
1617  if (mouseGrabber())
1618  {
1619  if (mouseGrabberIsAManipulatedCameraFrame_)
1620  (dynamic_cast<ManipulatedFrame*>(mouseGrabber()))->ManipulatedFrame::mouseReleaseEvent(e, camera());
1621  else
1622  mouseGrabber()->mouseReleaseEvent(e, camera());
1623  mouseGrabber()->checkIfGrabsMouse(e->x(), e->y(), camera());
1624  if (!(mouseGrabber()->grabsMouse()))
1625  setMouseGrabber(NULL);
1626  // updateGL();
1627  }
1628  else
1629  //#CONNECTION# mouseMoveEvent has the same structure
1630  if (camera()->frame()->isManipulated())
1631  {
1632  // bool updateGLNeeded = ((camera()->frame()->action_ == ZOOM_ON_REGION) ||
1633  // (camera()->frame()->action_ == SCREEN_ROTATE));
1634  camera()->frame()->mouseReleaseEvent(e, camera());
1635  // if (updateGLNeeded)
1636  // Needed in all cases because of fastDraw().
1637  // updateGL();
1638  }
1639  else
1640  if ((manipulatedFrame()) && (manipulatedFrame()->isManipulated()))
1641  {
1642  // bool updateGLNeeded = (manipulatedFrame()->action_ == SCREEN_ROTATE);
1643  if (manipulatedFrameIsACamera_)
1644  manipulatedFrame()->ManipulatedFrame::mouseReleaseEvent(e, camera());
1645  else
1646  manipulatedFrame()->mouseReleaseEvent(e, camera());
1647  // if (updateGLNeeded)
1648  // updateGL();
1649  }
1650 #if QT_VERSION >= 0x030000
1651  else
1652  e->ignore();
1653 #endif
1654 
1655  // Not absolutely needed (see above commented code for the optimal version), but may reveal
1656  // useful for specific applications.
1657  updateGL();
1658 }
1659 
1664 void QGLViewer::wheelEvent(QWheelEvent* e)
1665 {
1666  if (mouseGrabber())
1667  {
1668  if (mouseGrabberIsAManipulatedFrame_)
1669  {
1670  for (QMap<QtKeyboardModifiers, MouseActionPrivate>::ConstIterator it=wheelBinding_.begin(), end=wheelBinding_.end(); it!=end; ++it)
1671  if (it.value().handler == FRAME)
1672  {
1673  ManipulatedFrame* mf = dynamic_cast<ManipulatedFrame*>(mouseGrabber());
1674  if (mouseGrabberIsAManipulatedCameraFrame_)
1675  {
1676  mf->ManipulatedFrame::startAction(it.value().action, it.value().withConstraint);
1677  mf->ManipulatedFrame::wheelEvent(e, camera());
1678  }
1679  else
1680  {
1681  mf->startAction(it.value().action, it.value().withConstraint);
1682  mf->wheelEvent(e, camera());
1683  }
1684  break;
1685  }
1686  }
1687  else
1688  mouseGrabber()->wheelEvent(e, camera());
1689  updateGL();
1690  }
1691  else
1692  {
1693  //#CONNECTION# mousePressEvent has the same structure
1694 #if QT_VERSION >= 0x040000
1695  const QtKeyboardModifiers modifiers = e->modifiers();
1696 #else
1697  const QtKeyboardModifiers modifiers = e->state();
1698 #endif
1699  if (wheelBinding_.contains(modifiers))
1700  {
1701  MouseActionPrivate map = wheelBinding_[modifiers];
1702  switch (map.handler)
1703  {
1704  case CAMERA :
1705  camera()->frame()->startAction(map.action, map.withConstraint);
1706  camera()->frame()->wheelEvent(e, camera());
1707  break;
1708  case FRAME :
1709  if (manipulatedFrame()) {
1710  if (manipulatedFrameIsACamera_)
1711  {
1712  manipulatedFrame()->ManipulatedFrame::startAction(map.action, map.withConstraint);
1713  manipulatedFrame()->ManipulatedFrame::wheelEvent(e, camera());
1714  }
1715  else
1716  {
1717  manipulatedFrame()->startAction(map.action, map.withConstraint);
1718  manipulatedFrame()->wheelEvent(e, camera());
1719  }
1720  }
1721  break;
1722  }
1723  }
1724 #if QT_VERSION >= 0x030000
1725  else
1726  e->ignore();
1727 #endif
1728  }
1729 }
1730 
1736 {
1737  //#CONNECTION# mousePressEvent has the same structure
1738  ClickActionPrivate cap;
1739  cap.doubleClick = true;
1740 #if QT_VERSION >= 0x040000
1741  cap.modifiers = e->modifiers();
1742  cap.button = e->button();
1743  cap.buttonsBefore = (QtMouseButtons)(e->buttons() & ~(e->button()));
1744 #else
1745  cap.modifiers = (QtKeyboardModifiers)(e->state() & Qt::KeyboardModifierMask);
1746  cap.button = (QtMouseButtons)((e->stateAfter() & Qt::MouseButtonMask) & (~(e->state() & Qt::MouseButtonMask)));
1747  cap.buttonsBefore = (QtMouseButtons)(e->state() & Qt::MouseButtonMask);
1748 #endif
1749  if (clickBinding_.contains(cap))
1750  performClickAction(clickBinding_[cap], e);
1751  else
1752  if (mouseGrabber())
1753  mouseGrabber()->mouseDoubleClickEvent(e, camera());
1754 #if QT_VERSION >= 0x030000
1755  else
1756  e->ignore();
1757 #endif
1758 }
1759 
1765 {
1766  if (format().stereo())
1767  {
1768  stereo_ = stereo;
1769  if (!displaysInStereo())
1770  {
1771  glDrawBuffer(GL_BACK_LEFT);
1772  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1773  glDrawBuffer(GL_BACK_RIGHT);
1774  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1775  }
1776 
1777  Q_EMIT stereoChanged(stereo_);
1778 
1779  if (updateGLOK_)
1780  updateGL();
1781  }
1782  else
1783  if (stereo)
1784  QMessageBox::warning(this, tr("Stereo not supported", "Message box window title"), tr("Stereo is not supported on this display."));
1785  else
1786  stereo_ = false;
1787 }
1788 
1793 void QGLViewer::setFullScreen(bool fullScreen)
1794 {
1795  fullScreen_ = fullScreen;
1796 
1797  // Tricky. A timer does it later if !updateGLOK_.
1798  if (!updateGLOK_)
1799  return;
1800 
1801  QWidget* tlw = topLevelWidget();
1802 
1803  if (isFullScreen())
1804  {
1805  prevPos_ = topLevelWidget()->pos();
1806  tlw->showFullScreen();
1807  tlw->move(0,0);
1808  }
1809  else
1810  {
1811  tlw->showNormal();
1812  tlw->move(prevPos_);
1813  }
1814 }
1815 
1823 {
1824  if (!mouseGrabberIsEnabled(mouseGrabber))
1825  return;
1826 
1827  mouseGrabber_ = mouseGrabber;
1828 
1829  mouseGrabberIsAManipulatedFrame_ = (dynamic_cast<ManipulatedFrame*>(mouseGrabber) != NULL);
1830  mouseGrabberIsAManipulatedCameraFrame_ = ((dynamic_cast<ManipulatedCameraFrame*>(mouseGrabber) != NULL) &&
1831  (mouseGrabber != camera()->frame()));
1832  Q_EMIT mouseGrabberChanged(mouseGrabber);
1833 }
1834 
1836 void QGLViewer::setMouseGrabberIsEnabled(const qglviewer::MouseGrabber* const mouseGrabber, bool enabled)
1837 {
1838  if (enabled)
1839  disabledMouseGrabbers_.remove(reinterpret_cast<size_t>(mouseGrabber));
1840  else
1841  disabledMouseGrabbers_[reinterpret_cast<size_t>(mouseGrabber)];
1842 }
1843 
1844 static QString keyboardModifiersString(QtKeyboardModifiers m, bool noButton=false)
1845 {
1846 #if QT_VERSION >= 0x040000
1847  if (noButton && (m==Qt::NoModifier))
1848 #else
1849  if (noButton && (m==Qt::NoButton))
1850 #endif
1851  return QGLViewer::tr("(no button)");
1852 
1853  QString keySequence = "";
1854 
1855 #if QT_VERSION >= 0x040000
1856  if (m & Qt::ControlModifier) keySequence += "Ctrl+";
1857  if (m & Qt::AltModifier) keySequence += "Alt+";
1858  if (m & Qt::ShiftModifier) keySequence += "Shift+";
1859  if (m & Qt::MetaModifier) keySequence += "Meta+";
1860 #else
1861  if (m & Qt::ControlButton) keySequence += "Ctrl+";
1862  if (m & Qt::AltButton) keySequence += "Alt+";
1863  if (m & Qt::ShiftButton) keySequence += "Shift+";
1864 # if QT_VERSION >= 0x030000
1865  if (m & Qt::MetaButton) keySequence += "Meta+";
1866 # endif
1867 #endif
1868 
1869  if (keySequence.length() > 0)
1870 #if QT_VERSION >= 0x040000
1871  return QKeySequence(keySequence+"X").toString(QKeySequence::NativeText).replace("X", "");
1872 #else
1873  return QString(QKeySequence(keySequence+"X")).replace("X", "");
1874 #endif
1875  else
1876  return QString();
1877 }
1878 
1879 static QString mouseButtonsString(QtMouseButtons b)
1880 {
1881  QString result("");
1882  bool addAmpersand = false;
1883  if (b & Qt::LeftButton) { result += QGLViewer::tr("Left", "left mouse button"); addAmpersand=true; }
1884  if (b & Qt::MidButton) { if (addAmpersand) result += " & "; result += QGLViewer::tr("Middle", "middle mouse button"); addAmpersand=true; }
1885  if (b & Qt::RightButton) { if (addAmpersand) result += " & "; result += QGLViewer::tr("Right", "right mouse button"); }
1886  return result;
1887 }
1888 
1889 QString QGLViewer::mouseActionString(QGLViewer::MouseAction ma)
1890 {
1891  switch (ma)
1892  {
1893  case QGLViewer::NO_MOUSE_ACTION : return QString::null;
1894  case QGLViewer::ROTATE : return QGLViewer::tr("Rotates", "ROTATE mouse action");
1895  case QGLViewer::ZOOM : return QGLViewer::tr("Zooms", "ZOOM mouse action");
1896  case QGLViewer::TRANSLATE : return QGLViewer::tr("Translates", "TRANSLATE mouse action");
1897  case QGLViewer::MOVE_FORWARD : return QGLViewer::tr("Moves forward", "MOVE_FORWARD mouse action");
1898  case QGLViewer::LOOK_AROUND : return QGLViewer::tr("Looks around", "LOOK_AROUND mouse action");
1899  case QGLViewer::MOVE_BACKWARD : return QGLViewer::tr("Moves backward", "MOVE_BACKWARD mouse action");
1900  case QGLViewer::SCREEN_ROTATE : return QGLViewer::tr("Rotates in screen plane", "SCREEN_ROTATE mouse action");
1901  case QGLViewer::ROLL : return QGLViewer::tr("Rolls", "ROLL mouse action");
1902  case QGLViewer::DRIVE : return QGLViewer::tr("Drives", "DRIVE mouse action");
1903  case QGLViewer::SCREEN_TRANSLATE : return QGLViewer::tr("Horizontally/Vertically translates", "SCREEN_TRANSLATE mouse action");
1904  case QGLViewer::ZOOM_ON_REGION : return QGLViewer::tr("Zooms on region for", "ZOOM_ON_REGION mouse action");
1905  }
1906  return QString::null;
1907 }
1908 
1909 QString QGLViewer::clickActionString(QGLViewer::ClickAction ca)
1910 {
1911  switch (ca)
1912  {
1913  case QGLViewer::NO_CLICK_ACTION : return QString::null;
1914  case QGLViewer::ZOOM_ON_PIXEL : return QGLViewer::tr("Zooms on pixel", "ZOOM_ON_PIXEL click action");
1915  case QGLViewer::ZOOM_TO_FIT : return QGLViewer::tr("Zooms to fit scene", "ZOOM_TO_FIT click action");
1916  case QGLViewer::SELECT : return QGLViewer::tr("Selects", "SELECT click action");
1917  case QGLViewer::RAP_FROM_PIXEL : return QGLViewer::tr("Sets revolve around point", "RAP_FROM_PIXEL click action");
1918  case QGLViewer::RAP_IS_CENTER : return QGLViewer::tr("Resets revolve around point", "RAP_IS_CENTER click action");
1919  case QGLViewer::CENTER_FRAME : return QGLViewer::tr("Centers frame", "CENTER_FRAME click action");
1920  case QGLViewer::CENTER_SCENE : return QGLViewer::tr("Centers scene", "CENTER_SCENE click action");
1921  case QGLViewer::SHOW_ENTIRE_SCENE : return QGLViewer::tr("Shows entire scene", "SHOW_ENTIRE_SCENE click action");
1922  case QGLViewer::ALIGN_FRAME : return QGLViewer::tr("Aligns frame", "ALIGN_FRAME click action");
1923  case QGLViewer::ALIGN_CAMERA : return QGLViewer::tr("Aligns camera", "ALIGN_CAMERA click action");
1924  }
1925  return QString::null;
1926 }
1927 
1928 QString QGLViewer::formatClickActionPrivate(ClickActionPrivate cap)
1929 {
1930  bool buttonsBefore = cap.buttonsBefore != Qt::NoButton;
1931  return tr("%1%2%3%4%5%6", "Modifier / button or wheel / double click / with / button / pressed")
1932  .arg(keyboardModifiersString(cap.modifiers))
1933  .arg(mouseButtonsString(cap.button)+(cap.button == Qt::NoButton ? tr("Wheel", "Mouse wheel") : ""))
1934  .arg(cap.doubleClick ? tr(" double click", "Suffix after mouse button") : "")
1935  .arg(buttonsBefore ? tr(" with ", "As in : Left button with Ctrl pressed") : "")
1936  .arg(buttonsBefore ? mouseButtonsString(cap.buttonsBefore) : "")
1937  .arg(buttonsBefore ? tr(" pressed", "As in : Left button with Ctrl pressed") : "");
1938 }
1939 
1974 void QGLViewer::setMouseBindingDescription(int state, QString description, bool doubleClick, QtMouseButtons buttonsBefore)
1975 {
1976  ClickActionPrivate cap;
1977  cap.modifiers = QtKeyboardModifiers(convertToKeyboardModifiers(state) & Qt::KeyboardModifierMask);
1978  cap.button = QtMouseButtons(state & Qt::MouseButtonMask);
1979  cap.doubleClick = doubleClick;
1980  cap.buttonsBefore = buttonsBefore;
1981 
1982  if (description.isEmpty())
1983  mouseDescription_.remove(cap);
1984  else
1985  mouseDescription_[cap] = description;
1986 }
1987 
1988 static QString tableLine(const QString& left, const QString& right)
1989 {
1990  static bool even = false;
1991  const QString tdtd("</b></td><td>");
1992  const QString tdtr("</td></tr>\n");
1993 
1994  QString res("<tr bgcolor=\"");
1995 
1996  if (even)
1997  res += "#eeeeff\">";
1998  else
1999  res += "#ffffff\">";
2000  res += "<td><b>" + left + tdtd + right + tdtr;
2001  even = !even;
2002 
2003  return res;
2004 }
2005 
2015 QString QGLViewer::mouseString() const
2016 {
2017  QString text("<center><table border=\"1\" cellspacing=\"0\" cellpadding=\"4\">\n");
2018  const QString trtd("<tr><td>");
2019  const QString tdtr("</td></tr>\n");
2020  const QString tdtd("</td><td>");
2021 
2022  text += QString("<tr bgcolor=\"#aaaacc\"><th align=\"center\">%1</th><th align=\"center\">%2</th></tr>\n").
2023  arg(tr("Button(s)", "Buttons column header in help window mouse tab")).arg(tr("Description", "Description column header in help window mouse tab"));
2024 
2025  QMap<ClickActionPrivate, QString> mouseBinding;
2026 
2027  // User-defined mouse bindings come first.
2028  for (QMap<ClickActionPrivate, QString>::ConstIterator itm=mouseDescription_.begin(), endm=mouseDescription_.end(); itm!=endm; ++itm)
2029  mouseBinding[itm.key()] = itm.value();
2030 
2031  for (QMap<ClickActionPrivate, QString>::ConstIterator it=mouseBinding.begin(), end=mouseBinding.end(); it != end; ++it)
2032  {
2033  // Should not be needed (see setMouseBindingDescription())
2034  if (it.value().isNull())
2035  continue;
2036 
2037  text += tableLine(formatClickActionPrivate(it.key()), it.value());
2038  }
2039 
2040  // Optionnal separator line
2041  if (!mouseBinding.isEmpty())
2042  {
2043  mouseBinding.clear();
2044  text += QString("<tr bgcolor=\"#aaaacc\"><td colspan=2>%1</td></tr>\n").arg(tr("Standard mouse bindings", "In help window mouse tab"));
2045  }
2046 
2047  // Then concatenates the descriptions of wheelBinding_, mouseBinding_ and clickBinding_.
2048  // The order is significant and corresponds to the priorities set in mousePressEvent() (reverse priority order, last one overwrites previous)
2049  // #CONNECTION# mousePressEvent() order
2050  for (QMap<QtKeyboardModifiers, MouseActionPrivate>::ConstIterator itw=wheelBinding_.begin(), endw=wheelBinding_.end(); itw != endw; ++itw)
2051  {
2052  ClickActionPrivate cap;
2053  cap.doubleClick = false;
2054  cap.modifiers = itw.key();
2055  cap.button = Qt::NoButton;
2056  cap.buttonsBefore = Qt::NoButton;
2057 
2058  QString text = mouseActionString(itw.value().action);
2059 
2060  if (!text.isNull())
2061  {
2062  switch (itw.value().handler)
2063  {
2064  case CAMERA: text += " " + tr("camera", "Suffix after action"); break;
2065  case FRAME: text += " " + tr("manipulated frame", "Suffix after action"); break;
2066  }
2067  if (!(itw.value().withConstraint))
2068  text += "*";
2069  }
2070 
2071  mouseBinding[cap] = text;
2072  }
2073 
2074  for (QMap<int, MouseActionPrivate>::ConstIterator itmb=mouseBinding_.begin(), endmb=mouseBinding_.end();
2075  itmb != endmb; ++itmb)
2076  {
2077  ClickActionPrivate cap;
2078  cap.doubleClick = false;
2079  cap.modifiers = QtKeyboardModifiers(itmb.key() & Qt::KeyboardModifierMask);
2080  cap.button = QtMouseButtons(itmb.key() & Qt::MouseButtonMask);
2081  cap.buttonsBefore = Qt::NoButton;
2082 
2083  QString text = mouseActionString(itmb.value().action);
2084 
2085  if (!text.isNull())
2086  {
2087  switch (itmb.value().handler)
2088  {
2089  case CAMERA: text += " " + tr("camera", "Suffix after action"); break;
2090  case FRAME: text += " " + tr("manipulated frame", "Suffix after action"); break;
2091  }
2092  if (!(itmb.value().withConstraint))
2093  text += "*";
2094  }
2095  mouseBinding[cap] = text;
2096  }
2097 
2098  for (QMap<ClickActionPrivate, ClickAction>::ConstIterator itcb=clickBinding_.begin(), endcb=clickBinding_.end(); itcb!=endcb; ++itcb)
2099  mouseBinding[itcb.key()] = clickActionString(itcb.value());
2100 
2101  for (QMap<ClickActionPrivate, QString>::ConstIterator it2=mouseBinding.begin(), end2=mouseBinding.end(); it2 != end2; ++it2)
2102  {
2103  if (it2.value().isNull())
2104  continue;
2105 
2106  text += tableLine(formatClickActionPrivate(it2.key()), it2.value());
2107  }
2108 
2109  text += "</table></center>";
2110 
2111  return text;
2112 }
2113 
2128 void QGLViewer::setKeyDescription(int key, QString description)
2129 {
2130 #if QT_VERSION >= 0x030000
2131  // #CONNECTION# keyString. In Qt 2.3, longs modifier overlap with key codes.
2132  key = convertToKeyboardModifiers(key);
2133 #endif
2134  if (description.isEmpty())
2135  keyDescription_.remove(key);
2136  else
2137  keyDescription_[key] = description;
2138 }
2139 
2140 static QString keyString(int key)
2141 {
2142 #if QT_VERSION >= 0x030000
2143  return QString(QKeySequence(convertToShortModifier(key)));
2144 #else
2145  // #CONNECTION# setKeyDescription. In Qt 2.3, longs modifier overlap with key codes.
2146  return QString(QKeySequence(key));
2147 #endif
2148 }
2149 
2150 QString QGLViewer::cameraPathKeysString() const
2151 {
2152  if (pathIndex_.isEmpty())
2153  return QString::null;
2154 
2155 #if QT_VERSION >= 0x040000 || QT_VERSION < 0x030000
2156  QVector<int> keys;
2157 #else
2158  QValueVector<int> keys;
2159 #endif
2160  keys.reserve(pathIndex_.count());
2161  for (QMap<Qt::Key, int>::ConstIterator i = pathIndex_.begin(), endi=pathIndex_.end(); i != endi; ++i)
2162  keys.push_back(i.key());
2163 #if QT_VERSION >= 0x040000
2164  qSort(keys);
2165 #else
2166 # if QT_VERSION >= 0x030000
2167  qHeapSort(keys);
2168 # else
2169  sort(keys.begin(), keys.end());
2170 # endif
2171 #endif
2172 
2173 #if QT_VERSION >= 0x040000 || QT_VERSION < 0x030000
2174  QVector<int>::const_iterator it = keys.begin(), end = keys.end();
2175 #else
2176  QValueVector<int>::const_iterator it = keys.begin(), end = keys.end();
2177 #endif
2178  QString res = keyString(*it);
2179 
2180  const int maxDisplayedKeys = 6;
2181  int nbDisplayedKeys = 0;
2182  int previousKey = (*it);
2183  int state = 0;
2184  ++it;
2185  while ((it != end) && (nbDisplayedKeys < maxDisplayedKeys-1))
2186  {
2187  switch (state)
2188  {
2189  case 0 :
2190  if ((*it) == previousKey + 1)
2191  state++;
2192  else
2193  {
2194  res += ", " + keyString(*it);
2195  nbDisplayedKeys++;
2196  }
2197  break;
2198  case 1 :
2199  if ((*it) == previousKey + 1)
2200  state++;
2201  else
2202  {
2203  res += ", " + keyString(previousKey);
2204  res += ", " + keyString(*it);
2205  nbDisplayedKeys += 2;
2206  state = 0;
2207  }
2208  break;
2209  default :
2210  if ((*it) != previousKey + 1)
2211  {
2212  res += ".." + keyString(previousKey);
2213  res += ", " + keyString(*it);
2214  nbDisplayedKeys += 2;
2215  state = 0;
2216  }
2217  break;
2218  }
2219  previousKey = *it;
2220  ++it;
2221  }
2222 
2223  if (state == 1)
2224  res += ", " + keyString(previousKey);
2225  if (state == 2)
2226  res += ".." + keyString(previousKey);
2227  if (it != end)
2228  res += "...";
2229 
2230  return res;
2231 }
2232 
2242 {
2243  QString text("<center><table border=\"1\" cellspacing=\"0\" cellpadding=\"4\">\n");
2244  text += QString("<tr bgcolor=\"#aaaacc\"><th align=\"center\">%1</th><th align=\"center\">%2</th></tr>\n").
2245  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"));
2246 
2247  QMap<int, QString> keyDescription;
2248 
2249  // 1 - User defined key descriptions
2250  for (QMap<int, QString>::ConstIterator kd=keyDescription_.begin(), kdend=keyDescription_.end(); kd!=kdend; ++kd)
2251  keyDescription[kd.key()] = kd.value();
2252 
2253  // Add to text in sorted order
2254  for (QMap<int, QString>::ConstIterator kb=keyDescription.begin(), endb=keyDescription.end(); kb!=endb; ++kb)
2255  text += tableLine(keyString(kb.key()), kb.value());
2256 
2257 
2258  // 2 - Optional separator line
2259  if (!keyDescription.isEmpty())
2260  {
2261  keyDescription.clear();
2262  text += QString("<tr bgcolor=\"#aaaacc\"><td colspan=2>%1</td></tr>\n").arg(QGLViewer::tr("Standard viewer keys", "In help window keys tab"));
2263  }
2264 
2265 
2266  // 3 - KeyboardAction bindings description
2267  for (QMap<KeyboardAction, int>::ConstIterator it=keyboardBinding_.begin(), end=keyboardBinding_.end(); it != end; ++it)
2268  if ((it.value() != 0) && ((!cameraIsInRevolveMode()) || ((it.key() != INCREASE_FLYSPEED) && (it.key() != DECREASE_FLYSPEED))))
2269  keyDescription[it.value()] = keyboardActionDescription_[it.key()];
2270 
2271  // Add to text in sorted order
2272  for (QMap<int, QString>::ConstIterator kb2=keyDescription.begin(), endb2=keyDescription.end(); kb2!=endb2; ++kb2)
2273  text += tableLine(keyString(kb2.key()), kb2.value());
2274 
2275 
2276  // 4 - Camera paths keys description
2277  const QString cpks = cameraPathKeysString();
2278  if (!cpks.isNull())
2279  {
2280  text += "<tr bgcolor=\"#ccccff\"><td colspan=2>\n";
2281  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";
2282  text += tableLine(keyboardModifiersString(playPathKeyboardModifiers()) + "<i>" + QGLViewer::tr("Fx", "Generic function key (F1..F12)") + "</i>",
2283  QGLViewer::tr("Plays path (or resets saved position)"));
2284  text += tableLine(keyboardModifiersString(addKeyFrameKeyboardModifiers()) + "<i>" + QGLViewer::tr("Fx", "Generic function key (F1..F12)") + "</i>",
2285  QGLViewer::tr("Adds a key frame to path (or defines a position)"));
2286  text += tableLine(keyboardModifiersString(addKeyFrameKeyboardModifiers()) + "<i>" + QGLViewer::tr("Fx", "Generic function key (F1..F12)") + "</i>+<i>" + QGLViewer::tr("Fx", "Generic function key (F1..F12)") + "</i>",
2287  QGLViewer::tr("Deletes path (or saved position)"));
2288  }
2289  text += "</table></center>";
2290 
2291  return text;
2292 }
2293 
2296  help();
2297 #if QT_VERSION >= 0x040000
2298  helpWidget()->setCurrentIndex(3);
2299 #else
2300  helpWidget()->setCurrentPage(3);
2301 #endif
2302 }
2303 
2304 
2315 {
2316  Q_EMIT helpRequired();
2317 
2318  bool resize = false;
2319  int width=600;
2320  int height=400;
2321 
2322  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")};
2323 
2324  if (!helpWidget())
2325  {
2326  // Qt4 requires a NULL parent...
2327  helpWidget_ = new QTabWidget(NULL);
2328 #if QT_VERSION >= 0x040000
2329  helpWidget()->setWindowTitle(tr("Help", "Help window title"));
2330 #else
2331  helpWidget()->setCaption(tr("Help", "Help window title"));
2332 #endif
2333 
2334  resize = true;
2335  for (int i=0; i<4; ++i)
2336  {
2337  QTextEdit* tab = new QTextEdit(NULL);
2338 #if QT_VERSION >= 0x030000
2339  tab->setReadOnly(true);
2340 #endif
2341 
2342 #if QT_VERSION >= 0x040000
2343  helpWidget()->insertTab(i, tab, label[i]);
2344  if (i==3) {
2345 # include "qglviewer-icon.xpm"
2346  QPixmap pixmap(qglviewer_icon);
2347  tab->document()->addResource(QTextDocument::ImageResource,
2348  QUrl("mydata://qglviewer-icon.xpm"), QVariant(pixmap));
2349  }
2350 #else
2351  tab->setTextFormat(Qt::RichText);
2352  helpWidget()->insertTab(tab, label[i]);
2353 #endif
2354  }
2355  }
2356 
2357 
2358 #if QT_VERSION < 0x030000
2359  const int currentPageIndex = helpWidget()->currentPageIndex();
2360 #endif
2361 
2362  for (int i=0; i<4; ++i)
2363  {
2364  QString text;
2365  switch (i)
2366  {
2367  case 0 : text = helpString(); break;
2368  case 1 : text = keyboardString(); break;
2369  case 2 : text = mouseString(); break;
2370  case 3 : text = QString("<center><br><img src=\"mydata://qglviewer-icon.xpm\">") + tr(
2371  "<h1>libQGLViewer</h1>"
2372  "<h3>Version %1</h3><br>"
2373  "A versatile 3D viewer based on OpenGL and Qt<br>"
2374  "Copyright 2002-%2 Gilles Debunne<br>"
2375  "<code>%3</code>").arg(QGLViewerVersionString()).arg("2010").arg("http://www.libqglviewer.com") +
2376  QString("</center>");
2377  break;
2378  default : break;
2379  }
2380 
2381 #if QT_VERSION >= 0x040000
2382  QTextEdit* textEdit = (QTextEdit*)(helpWidget()->widget(i));
2383  textEdit->setHtml(text);
2384 #else
2385 # if QT_VERSION < 0x030000
2386  helpWidget()->setCurrentPage(i);
2387  QTextEdit* textEdit = (QTextEdit*)(helpWidget()->currentPage());
2388 # else
2389  QTextEdit* textEdit = (QTextEdit*)(helpWidget()->page(i));
2390 # endif
2391  textEdit->setText(text);
2392 #endif
2393 
2394 #if QT_VERSION < 0x040000
2395  if (resize && (textEdit->heightForWidth(width) > height))
2396  height = textEdit->heightForWidth(width);
2397 #else
2398  if (resize && (textEdit->height() > height))
2399  height = textEdit->height();
2400 #endif
2401  }
2402 
2403 #if QT_VERSION < 0x030000
2404  helpWidget()->setCurrentPage(currentPageIndex);
2405 #endif
2406 
2407  if (resize)
2408  helpWidget()->resize(width, height+40); // 40 pixels is ~ tabs' height
2409  helpWidget()->show();
2410  helpWidget()->raise();
2411 }
2412 
2439 void QGLViewer::keyPressEvent(QKeyEvent *e)
2440 {
2441  if (e->key() == 0)
2442  {
2443  e->ignore();
2444  return;
2445  }
2446 
2447  const Qt::Key key = Qt::Key(e->key());
2448 #if QT_VERSION >= 0x040000
2449  const QtKeyboardModifiers modifiers = e->modifiers();
2450 #else
2451  const QtKeyboardModifiers modifiers = (QtKeyboardModifiers)(e->state() & Qt::KeyboardModifierMask);
2452 #endif
2453 
2454  QMap<KeyboardAction, int>::ConstIterator it=keyboardBinding_.begin(), end=keyboardBinding_.end();
2455  while ((it != end) && (it.value() != (key | modifiers)))
2456  ++it;
2457 
2458  if (it != end)
2459  handleKeyboardAction(it.key());
2460  else
2461  if (pathIndex_.contains(Qt::Key(key)))
2462  {
2463  // Camera paths
2464  int index = pathIndex_[Qt::Key(key)];
2465 
2466  static QTime doublePress; // try to double press on two viewers at the same time !
2467 
2468  if (modifiers == playPathKeyboardModifiers())
2469  {
2470  int elapsed = doublePress.restart();
2471  if ((elapsed < 250) && (index==previousPathId_))
2472  camera()->resetPath(index);
2473  else
2474  {
2475  // Stop previous interpolation before starting a new one.
2476  if (index != previousPathId_)
2477  {
2478  KeyFrameInterpolator* previous = camera()->keyFrameInterpolator(previousPathId_);
2479  if ((previous) && (previous->interpolationIsStarted()))
2480  previous->resetInterpolation();
2481  }
2482  camera()->playPath(index);
2483  }
2484  previousPathId_ = index;
2485  }
2486  else if (modifiers == addKeyFrameKeyboardModifiers())
2487  {
2488  int elapsed = doublePress.restart();
2489  if ((elapsed < 250) && (index==previousPathId_))
2490  {
2491  if (camera()->keyFrameInterpolator(index))
2492  {
2493  disconnect(camera()->keyFrameInterpolator(index), SIGNAL(interpolated()), this, SLOT(updateGL()));
2494  if (camera()->keyFrameInterpolator(index)->numberOfKeyFrames() > 1)
2495  displayMessage(tr("Path %1 deleted", "Feedback message").arg(index));
2496  else
2497  displayMessage(tr("Position %1 deleted", "Feedback message").arg(index));
2498  camera()->deletePath(index);
2499  }
2500  }
2501  else
2502  {
2503  bool nullBefore = (camera()->keyFrameInterpolator(index) == NULL);
2504  camera()->addKeyFrameToPath(index);
2505  if (nullBefore)
2506  connect(camera()->keyFrameInterpolator(index), SIGNAL(interpolated()), SLOT(updateGL()));
2507  int nbKF = camera()->keyFrameInterpolator(index)->numberOfKeyFrames();
2508  if (nbKF > 1)
2509  displayMessage(tr("Path %1, position %2 added", "Feedback message").arg(index).arg(nbKF));
2510  else
2511  displayMessage(tr("Position %1 saved", "Feedback message").arg(index));
2512  }
2513  previousPathId_ = index;
2514  }
2515  updateGL();
2516  }
2517  else
2518  e->ignore();
2519 }
2520 
2521 void QGLViewer::handleKeyboardAction(KeyboardAction id)
2522 {
2523  switch (id)
2524  {
2525  case DRAW_AXIS : toggleAxisIsDrawn(); break;
2526  case DRAW_GRID : toggleGridIsDrawn(); break;
2527  case DISPLAY_FPS : toggleFPSIsDisplayed(); break;
2528  case ENABLE_TEXT : toggleTextIsEnabled(); break;
2529  case EXIT_VIEWER : saveStateToFileForAllViewers(); qApp->closeAllWindows(); break;
2530  case SAVE_SCREENSHOT : saveSnapshot(false, false); break;
2531  case FULL_SCREEN : toggleFullScreen(); break;
2532  case STEREO : toggleStereoDisplay(); break;
2533  case ANIMATION : toggleAnimation(); break;
2534  case HELP : help(); break;
2535  case EDIT_CAMERA : toggleCameraIsEdited(); break;
2536  case SNAPSHOT_TO_CLIPBOARD : snapshotToClipboard(); break;
2537  case CAMERA_MODE :
2538  toggleCameraMode();
2539  displayMessage(cameraIsInRevolveMode()?tr("Camera in revolve around mode", "Feedback message"):tr("Camera in fly mode", "Feedback message"));
2540  break;
2541 
2542  case MOVE_CAMERA_LEFT :
2543  camera()->frame()->translate(camera()->frame()->inverseTransformOf(Vec(-10.0*camera()->flySpeed(), 0.0, 0.0)));
2544  updateGL();
2545  break;
2546  case MOVE_CAMERA_RIGHT :
2547  camera()->frame()->translate(camera()->frame()->inverseTransformOf(Vec( 10.0*camera()->flySpeed(), 0.0, 0.0)));
2548  updateGL();
2549  break;
2550  case MOVE_CAMERA_UP :
2551  camera()->frame()->translate(camera()->frame()->inverseTransformOf(Vec(0.0, 10.0*camera()->flySpeed(), 0.0)));
2552  updateGL();
2553  break;
2554  case MOVE_CAMERA_DOWN :
2555  camera()->frame()->translate(camera()->frame()->inverseTransformOf(Vec(0.0, -10.0*camera()->flySpeed(), 0.0)));
2556  updateGL();
2557  break;
2558 
2559  case INCREASE_FLYSPEED : camera()->setFlySpeed(camera()->flySpeed() * 1.5); break;
2560  case DECREASE_FLYSPEED : camera()->setFlySpeed(camera()->flySpeed() / 1.5); break;
2561  }
2562 }
2563 
2568 void QGLViewer::resizeGL(int width, int height)
2569 {
2570  QGLWidget::resizeGL(width, height);
2571  glViewport( 0, 0, GLint(width), GLint(height) );
2572  camera()->setScreenWidthAndHeight(this->width(), this->height());
2573 }
2574 
2576 // K e y b o a r d s h o r t c u t s //
2578 
2597 {
2598  keyboardBinding_[action] = convertToKeyboardModifiers(key);
2599 }
2600 
2617 {
2618  if (keyboardBinding_.contains(action))
2619  return convertToShortModifier(keyboardBinding_[action]);
2620  else
2621  return 0;
2622 }
2623 
2624 #ifndef DOXYGEN
2625 void QGLViewer::setKeyboardAccelerator(KeyboardAction action, int key)
2626 {
2627  qWarning("setKeyboardAccelerator is deprecated. Use setShortcut instead.");
2628  setShortcut(action, key);
2629 }
2630 
2631 int QGLViewer::keyboardAccelerator(KeyboardAction action) const
2632 {
2633  qWarning("keyboardAccelerator is deprecated. Use shortcut instead.");
2634  return shortcut(action);
2635 }
2636 #endif
2637 
2639 
2655 Qt::Key QGLViewer::pathKey(int index) const
2656 {
2657  for (QMap<Qt::Key, int>::ConstIterator it = pathIndex_.begin(), end=pathIndex_.end(); it != end; ++it)
2658  if (it.value() == index)
2659  return it.key();
2660  return Qt::Key(0);
2661 }
2662 
2674 void QGLViewer::setPathKey(int key, int index)
2675 {
2676  if (key < 0)
2677  pathIndex_.remove(Qt::Key(-key));
2678  else
2679  pathIndex_[Qt::Key(key)] = index;
2680 }
2681 
2683 void QGLViewer::setPlayPathKeyboardModifiers(QtKeyboardModifiers modifiers)
2684 {
2685  playPathKeyboardModifiers_ = convertKeyboardModifiers(modifiers);
2686 }
2687 
2689 void QGLViewer::setAddKeyFrameKeyboardModifiers(QtKeyboardModifiers modifiers)
2690 {
2691  addKeyFrameKeyboardModifiers_ = convertKeyboardModifiers(modifiers);
2692 }
2693 
2707 QtKeyboardModifiers QGLViewer::addKeyFrameKeyboardModifiers() const
2708 {
2709  return addKeyFrameKeyboardModifiers_;
2710 }
2711 
2724 QtKeyboardModifiers QGLViewer::playPathKeyboardModifiers() const
2725 {
2726  return playPathKeyboardModifiers_;
2727 }
2728 
2729 #ifndef DOXYGEN
2730 // Deprecated methods
2731 QtKeyboardModifiers QGLViewer::addKeyFrameStateKey() const
2732 {
2733  qWarning("addKeyFrameStateKey has been renamed addKeyFrameKeyboardModifiers");
2734  return addKeyFrameKeyboardModifiers(); }
2735 
2736 QtKeyboardModifiers QGLViewer::playPathStateKey() const
2737 {
2738  qWarning("playPathStateKey has been renamed playPathKeyboardModifiers");
2739  return playPathKeyboardModifiers();
2740 }
2741 
2742 void QGLViewer::setAddKeyFrameStateKey(int buttonState)
2743 {
2744  qWarning("setAddKeyFrameStateKey has been renamed setAddKeyFrameKeyboardModifiers");
2745  setAddKeyFrameKeyboardModifiers(QtKeyboardModifiers(buttonState & Qt::KeyboardModifierMask));
2746 }
2747 
2748 void QGLViewer::setPlayPathStateKey(int buttonState)
2749 {
2750  qWarning("setPlayPathStateKey has been renamed setPlayPathKeyboardModifiers");
2751  setPlayPathKeyboardModifiers(QtKeyboardModifiers(buttonState & Qt::KeyboardModifierMask));
2752 }
2753 
2754 Qt::Key QGLViewer::keyFrameKey(int index) const
2755 {
2756  qWarning("keyFrameKey has been renamed pathKey.");
2757  return pathKey(index);
2758 }
2759 
2760 QtKeyboardModifiers QGLViewer::playKeyFramePathStateKey() const
2761 {
2762  qWarning("playKeyFramePathStateKey has been renamed playPathKeyboardModifiers.");
2763  return playPathKeyboardModifiers();
2764 }
2765 
2766 void QGLViewer::setKeyFrameKey(int index, int key)
2767 {
2768  qWarning("setKeyFrameKey is deprecated, use setPathKey instead, with swapped parameters.");
2769  setPathKey(key, index);
2770 }
2771 
2772 void QGLViewer::setPlayKeyFramePathStateKey(int buttonState)
2773 {
2774  qWarning("setPlayKeyFramePathStateKey has been renamed setPlayPathKeyboardModifiers.");
2775  setPlayPathKeyboardModifiers(QtKeyboardModifiers(buttonState & Qt::KeyboardModifierMask));
2776 }
2777 #endif
2778 
2780 // M o u s e b e h a v i o r s t a t e k e y s //
2782 
2823 void QGLViewer::setHandlerKeyboardModifiers(MouseHandler handler, QtKeyboardModifiers modifiers)
2824 {
2825  QMap<int, MouseActionPrivate> newMouseBinding;
2826  QMap<QtKeyboardModifiers, MouseActionPrivate> newWheelBinding;
2827  QMap<ClickActionPrivate, ClickAction> newClickBinding_;
2828 
2829  QMap<int, MouseActionPrivate>::Iterator mit;
2830  QMap<QtKeyboardModifiers, MouseActionPrivate>::Iterator wit;
2831 
2832  // First copy unchanged bindings.
2833  for (mit = mouseBinding_.begin(); mit != mouseBinding_.end(); ++mit)
2834  if ((mit.value().handler != handler) || (mit.value().action == ZOOM_ON_REGION))
2835  newMouseBinding[mit.key()] = mit.value();
2836 
2837  for (wit = wheelBinding_.begin(); wit != wheelBinding_.end(); ++wit)
2838  if (wit.value().handler != handler)
2839  newWheelBinding[wit.key()] = wit.value();
2840 
2841  // Then, add modified bindings, that can overwrite the previous ones.
2842  modifiers = convertKeyboardModifiers(modifiers);
2843  for (mit = mouseBinding_.begin(); mit != mouseBinding_.end(); ++mit)
2844  if ((mit.value().handler == handler) && (mit.value().action != ZOOM_ON_REGION))
2845  {
2846  int newState = modifiers | (mit.key() & Qt::MouseButtonMask);
2847  newMouseBinding[newState] = mit.value();
2848  }
2849 
2850  for (wit = wheelBinding_.begin(); wit != wheelBinding_.end(); ++wit)
2851  if (wit.value().handler == handler)
2852  {
2853  QtKeyboardModifiers newState = modifiers;
2854  newWheelBinding[newState] = wit.value();
2855  }
2856 
2857  // Same for button bindings
2858  for (QMap<ClickActionPrivate, ClickAction>::ConstIterator cb=clickBinding_.begin(), end=clickBinding_.end(); cb != end; ++cb)
2859  if (((handler==CAMERA) && ((cb.value() == CENTER_SCENE) || (cb.value() == ALIGN_CAMERA))) ||
2860  ((handler==FRAME) && ((cb.value() == CENTER_FRAME) || (cb.value() == ALIGN_FRAME))))
2861  {
2862  ClickActionPrivate cap;
2863  cap.modifiers = modifiers;
2864  cap.button = cb.key().button;
2865  cap.doubleClick = cb.key().doubleClick;
2866  cap.buttonsBefore = cb.key().buttonsBefore;
2867  newClickBinding_[cap] = cb.value();
2868  }
2869  else
2870  newClickBinding_[cb.key()] = cb.value();
2871 
2872  mouseBinding_ = newMouseBinding;
2873  wheelBinding_ = newWheelBinding;
2874  clickBinding_ = newClickBinding_;
2875 }
2876 
2877 
2878 #ifndef DOXYGEN
2879 void QGLViewer::setHandlerStateKey(MouseHandler handler, int buttonState)
2880 {
2881  qWarning("setHandlerStateKey has been renamed setHandlerKeyboardModifiers");
2882  setHandlerKeyboardModifiers(handler, QtKeyboardModifiers(buttonState & Qt::KeyboardModifierMask));
2883 }
2884 
2885 void QGLViewer::setMouseStateKey(MouseHandler handler, int buttonState)
2886 {
2887  qWarning("setMouseStateKey has been renamed setHandlerKeyboardModifiers.");
2888  setHandlerKeyboardModifiers(handler, QtKeyboardModifiers(buttonState & Qt::KeyboardModifierMask));
2889 }
2890 #endif
2891 
2923 void QGLViewer::setMouseBinding(int state, MouseHandler handler, MouseAction action, bool withConstraint)
2924 {
2925  if ((handler == FRAME) && ((action == MOVE_FORWARD) || (action == MOVE_BACKWARD) ||
2926  (action == ROLL) || (action == LOOK_AROUND) ||
2927  (action == ZOOM_ON_REGION)))
2928  {
2929 #if QT_VERSION >= 0x040000
2930  qWarning("Cannot bind %s to FRAME", mouseActionString(action).toLatin1().constData());
2931 #else
2932  qWarning("Cannot bind %s to FRAME", mouseActionString(action).latin1());
2933 #endif
2934  }
2935  else
2936  if ((state & Qt::MouseButtonMask) == 0)
2937  qWarning("No mouse button specified in setMouseBinding");
2938  else
2939  {
2940  MouseActionPrivate map;
2941  map.handler = handler;
2942  map.action = action;
2943  map.withConstraint = withConstraint;
2944  state = convertToKeyboardModifiers(state);
2945 
2946  mouseBinding_.remove(state);
2947 
2948  if (action != NO_MOUSE_ACTION)
2949  mouseBinding_.insert(state, map);
2950 
2951  ClickActionPrivate cap;
2952  cap.modifiers = QtKeyboardModifiers(state & Qt::KeyboardModifierMask);
2953  cap.button = QtMouseButtons(state & Qt::MouseButtonMask);
2954  cap.doubleClick = false;
2955  cap.buttonsBefore = Qt::NoButton;
2956  clickBinding_.remove(cap);
2957  }
2958 }
2959 
2978 void QGLViewer::setMouseBinding(int state, ClickAction action, bool doubleClick, QtMouseButtons buttonsBefore)
2979 {
2980  if ((buttonsBefore != Qt::NoButton) && !doubleClick)
2981  qWarning("Buttons before is only meaningful when doubleClick is true in setMouseBinding().");
2982  else
2983  if ((state & Qt::MouseButtonMask) == 0)
2984  qWarning("No mouse button specified in setMouseBinding");
2985  else
2986  {
2987  ClickActionPrivate cap;
2988  state = convertToKeyboardModifiers(state);
2989  cap.modifiers = QtKeyboardModifiers(state & Qt::KeyboardModifierMask);
2990  cap.button = QtMouseButtons(state & Qt::MouseButtonMask);
2991  cap.doubleClick = doubleClick;
2992  cap.buttonsBefore = buttonsBefore;
2993  clickBinding_.remove(cap);
2994 
2995  // #CONNECTION performClickAction comment on NO_CLICK_ACTION
2996  if (action != NO_CLICK_ACTION)
2997  clickBinding_.insert(cap, action);
2998 
2999  if ((!doubleClick) && (buttonsBefore == Qt::NoButton))
3000  mouseBinding_.remove(state);
3001  }
3002 }
3003 
3014 void QGLViewer::setWheelBinding(QtKeyboardModifiers modifiers, MouseHandler handler, MouseAction action, bool withConstraint)
3015 {
3016  //#CONNECTION# ManipulatedFrame::wheelEvent and ManipulatedCameraFrame::wheelEvent switches
3017  if ((action != ZOOM) && (action != MOVE_FORWARD) && (action != MOVE_BACKWARD) && (action != NO_MOUSE_ACTION))
3018 #if QT_VERSION >= 0x040000
3019  qWarning("Cannot bind %s to wheel", mouseActionString(action).toLatin1().constData());
3020 #else
3021  qWarning("Cannot bind %s to wheel", + mouseActionString(action).latin1());
3022 #endif
3023  else
3024  if ((handler == FRAME) && (action != ZOOM) && (action != NO_MOUSE_ACTION))
3025 #if QT_VERSION >= 0x040000
3026  qWarning("Cannot bind %s to FRAME wheel", mouseActionString(action).toLatin1().constData());
3027 #else
3028  qWarning("Cannot bind %s to FRAME wheel", mouseActionString(action).latin1());
3029 #endif
3030  else
3031  {
3032  MouseActionPrivate map;
3033  map.handler = handler;
3034  map.action = action;
3035  map.withConstraint = withConstraint;
3036  modifiers = convertKeyboardModifiers(modifiers);
3037  wheelBinding_.remove(modifiers);
3038 
3039  if (action != NO_MOUSE_ACTION)
3040  wheelBinding_.insert(modifiers, map);
3041  }
3042 }
3043 
3056 {
3057  state = convertToKeyboardModifiers(state);
3058  if (mouseBinding_.contains(state))
3059  return mouseBinding_[state].action;
3060  else
3061  return NO_MOUSE_ACTION;
3062 }
3063 
3074 int QGLViewer::mouseHandler(int state) const
3075 {
3076  state = convertToKeyboardModifiers(state);
3077  if (mouseBinding_.contains(state))
3078  return mouseBinding_[state].handler;
3079  else
3080  return -1;
3081 }
3082 
3100 int QGLViewer::mouseButtonState(MouseHandler handler, MouseAction action, bool withConstraint) const
3101 {
3102  for (QMap<int, MouseActionPrivate>::ConstIterator it=mouseBinding_.begin(), end=mouseBinding_.end(); it != end; ++it)
3103  if ( (it.value().handler == handler) && (it.value().action == action) && (it.value().withConstraint == withConstraint) )
3104  return it.key();
3105 
3106  return Qt::NoButton;
3107 }
3108 
3114 QGLViewer::MouseAction QGLViewer::wheelAction(QtKeyboardModifiers modifiers) const
3115 {
3116  modifiers = convertKeyboardModifiers(modifiers);
3117  if (wheelBinding_.contains(modifiers))
3118  return wheelBinding_[modifiers].action;
3119  else
3120  return NO_MOUSE_ACTION;
3121 }
3122 
3124 int QGLViewer::wheelHandler(QtKeyboardModifiers modifiers) const
3125 {
3126  modifiers = convertKeyboardModifiers(modifiers);
3127  if (wheelBinding_.contains(modifiers))
3128  return wheelBinding_[modifiers].handler;
3129  else
3130  return -1;
3131 }
3132 
3137 int QGLViewer::wheelButtonState(MouseHandler handler, MouseAction action, bool withConstraint) const
3138 {
3139  for (QMap<QtKeyboardModifiers, MouseActionPrivate>::ConstIterator it=wheelBinding_.begin(), end=wheelBinding_.end(); it!=end; ++it)
3140  if ( (it.value().handler == handler) && (it.value().action == action) && (it.value().withConstraint == withConstraint) )
3141  return it.key();
3142 
3143  return -1;
3144 }
3145 
3147 QGLViewer::ClickAction QGLViewer::clickAction(int state, bool doubleClick, QtMouseButtons buttonsBefore) const
3148 {
3149  ClickActionPrivate cap;
3150  cap.modifiers = QtKeyboardModifiers(convertToKeyboardModifiers(state) & Qt::KeyboardModifierMask);
3151  cap.button = QtMouseButtons(state & Qt::MouseButtonMask);
3152  cap.doubleClick = doubleClick;
3153  cap.buttonsBefore = buttonsBefore;
3154  if (clickBinding_.contains(cap))
3155  return clickBinding_[cap];
3156  else
3157  return NO_CLICK_ACTION;
3158 }
3159 
3165 void QGLViewer::getClickButtonState(ClickAction ca, int& state, bool& doubleClick, QtMouseButtons& buttonsBefore) const
3166 {
3167  for (QMap<ClickActionPrivate, ClickAction>::ConstIterator it=clickBinding_.begin(), end=clickBinding_.end(); it != end; ++it)
3168  if (it.value() == ca)
3169  {
3170  state = it.key().modifiers | it.key().button;
3171  doubleClick = it.key().doubleClick;
3172  buttonsBefore = it.key().buttonsBefore;
3173  return;
3174  }
3175 
3176  state = Qt::NoButton;
3177 }
3178 
3182 bool QGLViewer::cameraIsInRevolveMode() const
3183 {
3184  //#CONNECTION# used in toggleCameraMode() and keyboardString()
3185  return mouseButtonState(CAMERA, ROTATE) != Qt::NoButton;
3186 }
3187 
3202 {
3203  bool revolveMode = cameraIsInRevolveMode();
3204  int bs;
3205  if (revolveMode)
3206  bs = mouseButtonState(CAMERA, ROTATE);
3207  else
3208  bs = mouseButtonState(CAMERA, MOVE_FORWARD);
3209  QtKeyboardModifiers modifiers = QtKeyboardModifiers(bs & Qt::KeyboardModifierMask);
3210 
3211  //#CONNECTION# setDefaultMouseBindings()
3212  if (revolveMode)
3213  {
3214  camera()->frame()->updateFlyUpVector();
3215  camera()->frame()->stopSpinning();
3216 
3217  setMouseBinding(modifiers | Qt::LeftButton, CAMERA, MOVE_FORWARD);
3218  setMouseBinding(modifiers | Qt::MidButton, CAMERA, LOOK_AROUND);
3219  setMouseBinding(modifiers | Qt::RightButton, CAMERA, MOVE_BACKWARD);
3220 
3221  setMouseBinding(modifiers | Qt::LeftButton | Qt::MidButton, CAMERA, ROLL);
3222  // 2.2.4 setMouseBinding(modifiers | Qt::RightButton | Qt::MidButton, CAMERA, SCREEN_TRANSLATE);
3223 
3224  setMouseBinding(Qt::LeftButton, NO_CLICK_ACTION, true);
3225  setMouseBinding(Qt::MidButton, NO_CLICK_ACTION, true);
3226  setMouseBinding(Qt::RightButton, NO_CLICK_ACTION, true);
3227 
3228  setWheelBinding(modifiers, CAMERA, MOVE_FORWARD);
3229  }
3230  else
3231  {
3232  // Should stop flyTimer. But unlikely and not easy.
3233  setMouseBinding(modifiers | Qt::LeftButton, CAMERA, ROTATE);
3234  setMouseBinding(modifiers | Qt::MidButton, CAMERA, ZOOM);
3235  setMouseBinding(modifiers | Qt::RightButton, CAMERA, TRANSLATE);
3236 
3237  setMouseBinding(modifiers | Qt::LeftButton | Qt::MidButton, CAMERA, SCREEN_ROTATE);
3238  // 2.2.4 setMouseBinding(modifiers | Qt::RightButton | Qt::MidButton, CAMERA, SCREEN_TRANSLATE);
3239 
3240  setMouseBinding(Qt::LeftButton, ALIGN_CAMERA, true);
3241  setMouseBinding(Qt::MidButton, SHOW_ENTIRE_SCENE, true);
3242  setMouseBinding(Qt::RightButton, CENTER_SCENE, true);
3243 
3244  setWheelBinding(modifiers, CAMERA, ZOOM);
3245  }
3246 }
3247 
3249 // M a n i p u l a t e d f r a m e s //
3251 
3264 {
3265  if (manipulatedFrame())
3266  {
3267  manipulatedFrame()->stopSpinning();
3268 
3269  if (manipulatedFrame() != camera()->frame())
3270  {
3271  disconnect(manipulatedFrame(), SIGNAL(manipulated()), this, SLOT(updateGL()));
3272  disconnect(manipulatedFrame(), SIGNAL(spun()), this, SLOT(updateGL()));
3273  }
3274  }
3275 
3276  manipulatedFrame_ = frame;
3277 
3278  manipulatedFrameIsACamera_ = ((manipulatedFrame() != camera()->frame()) &&
3279  (dynamic_cast<ManipulatedCameraFrame*>(manipulatedFrame()) != NULL));
3280 
3281  if (manipulatedFrame())
3282  {
3283  // Prevent multiple connections, that would result in useless display updates
3284  if (manipulatedFrame() != camera()->frame())
3285  {
3286  connect(manipulatedFrame(), SIGNAL(manipulated()), SLOT(updateGL()));
3287  connect(manipulatedFrame(), SIGNAL(spun()), SLOT(updateGL()));
3288  }
3289  }
3290 }
3291 
3292 #ifndef DOXYGEN
3293 
3294 // V i s u a l H i n t s //
3296 
3312 {
3313  // Revolve Around point cross
3314  if (visualHint_ & 1)
3315  {
3316  const float size = 15.0;
3317  Vec proj = camera()->projectedCoordinatesOf(camera()->revolveAroundPoint());
3318  startScreenCoordinatesSystem();
3319  glDisable(GL_LIGHTING);
3320  glDisable(GL_DEPTH_TEST);
3321  glLineWidth(3.0);
3322  glBegin(GL_LINES);
3323  glVertex2f(proj.x - size, proj.y);
3324  glVertex2f(proj.x + size, proj.y);
3325  glVertex2f(proj.x, proj.y - size);
3326  glVertex2f(proj.x, proj.y + size);
3327  glEnd();
3328  glEnable(GL_DEPTH_TEST);
3329  stopScreenCoordinatesSystem();
3330  }
3331 
3332  // if (visualHint_ & 2)
3333  // drawText(80, 10, "Play");
3334 
3335  // Screen rotate line
3336  ManipulatedFrame* mf = NULL;
3337  Vec pnt;
3338  if (camera()->frame()->action_ == SCREEN_ROTATE)
3339  {
3340  mf = camera()->frame();
3341  pnt = camera()->revolveAroundPoint();
3342  }
3343  if (manipulatedFrame() && (manipulatedFrame()->action_ == SCREEN_ROTATE))
3344  {
3345  mf = manipulatedFrame();
3346  // Maybe useful if the mf is a manipCameraFrame...
3347  // pnt = manipulatedFrame()->revolveAroundPoint();
3348  pnt = manipulatedFrame()->position();
3349  }
3350 
3351  if (mf)
3352  {
3353  pnt = camera()->projectedCoordinatesOf(pnt);
3354  startScreenCoordinatesSystem();
3355  glDisable(GL_LIGHTING);
3356  glDisable(GL_DEPTH_TEST);
3357  glLineWidth(3.0);
3358  glBegin(GL_LINES);
3359  glVertex2f(pnt.x, pnt.y);
3360  glVertex2f(mf->prevPos_.x(), mf->prevPos_.y());
3361  glEnd();
3362  glEnable(GL_DEPTH_TEST);
3363  stopScreenCoordinatesSystem();
3364  }
3365 
3366  // Zoom on region: draw a rectangle
3367  if (camera()->frame()->action_ == ZOOM_ON_REGION)
3368  {
3369  startScreenCoordinatesSystem();
3370  glDisable(GL_LIGHTING);
3371  glDisable(GL_DEPTH_TEST);
3372  glLineWidth(2.0);
3373  glBegin(GL_LINE_LOOP);
3374  glVertex2i(camera()->frame()->pressPos_.x(), camera()->frame()->pressPos_.y());
3375  glVertex2i(camera()->frame()->prevPos_.x(), camera()->frame()->pressPos_.y());
3376  glVertex2i(camera()->frame()->prevPos_.x(), camera()->frame()->prevPos_.y());
3377  glVertex2i(camera()->frame()->pressPos_.x(), camera()->frame()->prevPos_.y());
3378  glEnd();
3379  glEnable(GL_DEPTH_TEST);
3380  stopScreenCoordinatesSystem();
3381  }
3382 }
3383 
3387 void QGLViewer::setVisualHintsMask(int mask, int delay)
3388 {
3389  visualHint_ = visualHint_ | mask;
3390  QTimer::singleShot(delay, this, SLOT(resetVisualHints()));
3391 }
3392 
3395 {
3396  visualHint_ = 0;
3397 }
3398 #endif
3399 
3401 // A x i s a n d G r i d d i s p l a y l i s t s //
3403 
3413 void QGLViewer::drawArrow(float length, float radius, int nbSubdivisions)
3414 {
3415  static GLUquadric* quadric = gluNewQuadric();
3416 
3417  if (radius < 0.0)
3418  radius = 0.05 * length;
3419 
3420  const float head = 2.5*(radius / length) + 0.1;
3421  const float coneRadiusCoef = 4.0 - 5.0 * head;
3422 
3423  gluCylinder(quadric, radius, radius, length * (1.0 - head/coneRadiusCoef), nbSubdivisions, 1);
3424  glTranslatef(0.0, 0.0, length * (1.0 - head));
3425  gluCylinder(quadric, coneRadiusCoef * radius, 0.0, head * length, nbSubdivisions, 1);
3426  glTranslatef(0.0, 0.0, -length * (1.0 - head));
3427 }
3428 
3433 void QGLViewer::drawArrow(const Vec& from, const Vec& to, float radius, int nbSubdivisions)
3434 {
3435  glPushMatrix();
3436  glTranslatef(from[0],from[1],from[2]);
3437  const Vec dir = to-from;
3438  glMultMatrixd(Quaternion(Vec(0,0,1), dir).matrix());
3439  QGLViewer::drawArrow(dir.norm(), radius, nbSubdivisions);
3440  glPopMatrix();
3441 }
3442 
3461 void QGLViewer::drawAxis(float length)
3462 {
3463  const float charWidth = length / 40.0;
3464  const float charHeight = length / 30.0;
3465  const float charShift = 1.04 * length;
3466 
3467  GLboolean lighting, colorMaterial;
3468  glGetBooleanv(GL_LIGHTING, &lighting);
3469  glGetBooleanv(GL_COLOR_MATERIAL, &colorMaterial);
3470 
3471  glDisable(GL_LIGHTING);
3472 
3473  glBegin(GL_LINES);
3474  // The X
3475  glVertex3f(charShift, charWidth, -charHeight);
3476  glVertex3f(charShift, -charWidth, charHeight);
3477  glVertex3f(charShift, -charWidth, -charHeight);
3478  glVertex3f(charShift, charWidth, charHeight);
3479  // The Y
3480  glVertex3f( charWidth, charShift, charHeight);
3481  glVertex3f(0.0, charShift, 0.0);
3482  glVertex3f(-charWidth, charShift, charHeight);
3483  glVertex3f(0.0, charShift, 0.0);
3484  glVertex3f(0.0, charShift, 0.0);
3485  glVertex3f(0.0, charShift, -charHeight);
3486  // The Z
3487  glVertex3f(-charWidth, charHeight, charShift);
3488  glVertex3f( charWidth, charHeight, charShift);
3489  glVertex3f( charWidth, charHeight, charShift);
3490  glVertex3f(-charWidth, -charHeight, charShift);
3491  glVertex3f(-charWidth, -charHeight, charShift);
3492  glVertex3f( charWidth, -charHeight, charShift);
3493  glEnd();
3494 
3495  glEnable(GL_LIGHTING);
3496  glDisable(GL_COLOR_MATERIAL);
3497 
3498  float color[4];
3499  color[0] = 0.7f; color[1] = 0.7f; color[2] = 1.0f; color[3] = 1.0f;
3500  glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
3501  QGLViewer::drawArrow(length, 0.01*length);
3502 
3503  color[0] = 1.0f; color[1] = 0.7f; color[2] = 0.7f; color[3] = 1.0f;
3504  glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
3505  glPushMatrix();
3506  glRotatef(90.0, 0.0, 1.0, 0.0);
3507  QGLViewer::drawArrow(length, 0.01*length);
3508  glPopMatrix();
3509 
3510  color[0] = 0.7f; color[1] = 1.0f; color[2] = 0.7f; color[3] = 1.0f;
3511  glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
3512  glPushMatrix();
3513  glRotatef(-90.0, 1.0, 0.0, 0.0);
3514  QGLViewer::drawArrow(length, 0.01*length);
3515  glPopMatrix();
3516 
3517  if (colorMaterial)
3518  glEnable(GL_COLOR_MATERIAL);
3519  if (!lighting)
3520  glDisable(GL_LIGHTING);
3521 }
3522 
3529 void QGLViewer::drawGrid(float size, int nbSubdivisions)
3530 {
3531  GLboolean lighting;
3532  glGetBooleanv(GL_LIGHTING, &lighting);
3533 
3534  glDisable(GL_LIGHTING);
3535 
3536  glBegin(GL_LINES);
3537  for (int i=0; i<=nbSubdivisions; ++i)
3538  {
3539  const float pos = size*(2.0*i/nbSubdivisions-1.0);
3540  glVertex2f(pos, -size);
3541  glVertex2f(pos, +size);
3542  glVertex2f(-size, pos);
3543  glVertex2f( size, pos);
3544  }
3545  glEnd();
3546 
3547  if (lighting)
3548  glEnable(GL_LIGHTING);
3549 }
3550 
3552 // 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 //
3554 
3556 void QGLViewer::saveStateToFileForAllViewers()
3557 {
3558 #if QT_VERSION >= 0x040000
3559  Q_FOREACH (QGLViewer* viewer, QGLViewer::QGLViewerPool())
3560  {
3561 #else
3562  QPtrListIterator<QGLViewer> it(QGLViewer::QGLViewerPool());
3563  for (QGLViewer* viewer; (viewer = it.current()) != 0; ++it)
3564  {
3565 #endif
3566  if (viewer)
3567  viewer->saveStateToFile();
3568  }
3569 }
3570 
3572 // S a v e s t a t e b e t w e e n s e s s i o n s //
3574 
3589 {
3590  QString name = stateFileName_;
3591 
3592  if (!name.isEmpty() && QGLViewer::QGLViewerIndex(this) > 0)
3593  {
3594  QFileInfo fi(name);
3595 #if QT_VERSION >= 0x040000
3596  if (fi.suffix().isEmpty())
3597 #else
3598  if (fi.extension(false).isEmpty())
3599 #endif
3600  name += QString::number(QGLViewer::QGLViewerIndex(this));
3601  else
3602 #if QT_VERSION >= 0x040000
3603  name = fi.absolutePath() + '/' + fi.completeBaseName() + QString::number(QGLViewer::QGLViewerIndex(this)) + "." + fi.suffix();
3604 #else
3605 # if QT_VERSION >= 0x030000
3606  name = fi.dirPath() + '/' + fi.baseName(true) + QString::number(QGLViewer::QGLViewerIndex(this)) + "." + fi.extension(false);
3607 # else
3608  name = fi.dirPath() + '/' + fi.baseName() + QString::number(QGLViewer::QGLViewerIndex(this)) + "." + fi.extension();
3609 # endif
3610 #endif
3611  }
3612 
3613  return name;
3614 }
3615 
3624 {
3625  QString name = stateFileName();
3626 
3627  if (name.isEmpty())
3628  return;
3629 
3630  QFileInfo fileInfo(name);
3631 
3632  if (fileInfo.isDir())
3633  {
3634  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));
3635  return;
3636  }
3637 
3638 #if QT_VERSION >= 0x040000
3639  const QString dirName = fileInfo.absolutePath();
3640 #else
3641  const QString dirName = fileInfo.dirPath();
3642 #endif
3643  if (!QFileInfo(dirName).exists())
3644  {
3645  QDir dir;
3646 #if QT_VERSION >= 0x040000
3647  if (!(dir.mkdir(dirName)))
3648 #else
3649  if (!(dir.mkdir(dirName, true)))
3650 #endif
3651  {
3652  QMessageBox::warning(this, tr("Save to file error", "Message box window title"), tr("Unable to create directory %1").arg(dirName));
3653  return;
3654  }
3655  }
3656 
3657  // Write the DOM tree to file
3658  QFile f(name);
3659 #if QT_VERSION >= 0x040000
3660  if (f.open(QIODevice::WriteOnly))
3661 #else
3662  if (f.open(IO_WriteOnly))
3663 #endif
3664  {
3665  QTextStream out(&f);
3666  QDomDocument doc("QGLVIEWER");
3667  doc.appendChild(domElement("QGLViewer", doc));
3668  doc.save(out, 2);
3669  f.flush();
3670  f.close();
3671  }
3672  else
3673 #if QT_VERSION < 0x030200
3674  QMessageBox::warning(this, tr("Save to file error", "Message box window title"), tr("Unable to save to file %1").arg(name));
3675 #else
3676  QMessageBox::warning(this, tr("Save to file error", "Message box window title"), tr("Unable to save to file %1").arg(name) + ":\n" + f.errorString());
3677 #endif
3678 }
3679 
3701 {
3702  QString name = stateFileName();
3703 
3704  if (name.isEmpty())
3705  return false;
3706 
3707  QFileInfo fileInfo(name);
3708 
3709  if (!fileInfo.isFile())
3710  // No warning since it would be displayed at first start.
3711  return false;
3712 
3713  if (!fileInfo.isReadable())
3714  {
3715  QMessageBox::warning(this, tr("Problem in state restoration", "Message box window title"), tr("File %1 is not readable.").arg(name));
3716  return false;
3717  }
3718 
3719  // Read the DOM tree form file
3720  QFile f(name);
3721 #if QT_VERSION >= 0x040000
3722  if (f.open(QIODevice::ReadOnly) == true)
3723 #else
3724  if (f.open(IO_ReadOnly) == true)
3725 #endif
3726  {
3727  QDomDocument doc;
3728  doc.setContent(&f);
3729  f.close();
3730  QDomElement main = doc.documentElement();
3731  initFromDOMElement(main);
3732  }
3733  else
3734  {
3735 #if QT_VERSION < 0x030200
3736  QMessageBox::warning(this, tr("Open file error", "Message box window title"), tr("Unable to open file %1").arg(name));
3737 #else
3738  QMessageBox::warning(this, tr("Open file error", "Message box window title"), tr("Unable to open file %1").arg(name) + ":\n" + f.errorString());
3739 #endif
3740  return false;
3741  }
3742 
3743  return true;
3744 }
3745 
3778 QDomElement QGLViewer::domElement(const QString& name, QDomDocument& document) const
3779 {
3780  QDomElement de = document.createElement(name);
3781  de.setAttribute("version", QGLViewerVersionString());
3782 
3783  QDomElement stateNode = document.createElement("State");
3784  // stateNode.setAttribute("mouseTracking", (hasMouseTracking()?"true":"false"));
3785  stateNode.appendChild(DomUtils::QColorDomElement(foregroundColor(), "foregroundColor", document));
3786  stateNode.appendChild(DomUtils::QColorDomElement(backgroundColor(), "backgroundColor", document));
3787  stateNode.setAttribute("stereo", (displaysInStereo()?"true":"false"));
3788  stateNode.setAttribute("cameraMode", (cameraIsInRevolveMode()?"revolve":"fly"));
3789  de.appendChild(stateNode);
3790 
3791  QDomElement displayNode = document.createElement("Display");
3792  displayNode.setAttribute("axisIsDrawn", (axisIsDrawn()?"true":"false"));
3793  displayNode.setAttribute("gridIsDrawn", (gridIsDrawn()?"true":"false"));
3794  displayNode.setAttribute("FPSIsDisplayed", (FPSIsDisplayed()?"true":"false"));
3795  displayNode.setAttribute("cameraIsEdited", (cameraIsEdited()?"true":"false"));
3796  // displayNode.setAttribute("textIsEnabled", (textIsEnabled()?"true":"false"));
3797  de.appendChild(displayNode);
3798 
3799  QDomElement geometryNode = document.createElement("Geometry");
3800  geometryNode.setAttribute("fullScreen", (isFullScreen()?"true":"false"));
3801  if (isFullScreen())
3802  {
3803  geometryNode.setAttribute("prevPosX", QString::number(prevPos_.x()));
3804  geometryNode.setAttribute("prevPosY", QString::number(prevPos_.y()));
3805  }
3806  else
3807  {
3808  QWidget* tlw = topLevelWidget();
3809  geometryNode.setAttribute("width", QString::number(tlw->width()));
3810  geometryNode.setAttribute("height", QString::number(tlw->height()));
3811  geometryNode.setAttribute("posX", QString::number(tlw->pos().x()));
3812  geometryNode.setAttribute("posY", QString::number(tlw->pos().y()));
3813  }
3814  de.appendChild(geometryNode);
3815 
3816  // Restore original Camera zClippingCoefficient before saving.
3817  if (cameraIsEdited())
3818  camera()->setZClippingCoefficient(previousCameraZClippingCoefficient_);
3819  de.appendChild(camera()->domElement("Camera", document));
3820  if (cameraIsEdited())
3821  // #CONNECTION# 5.0 from setCameraIsEdited()
3822  camera()->setZClippingCoefficient(5.0);
3823 
3824  if (manipulatedFrame())
3825  de.appendChild(manipulatedFrame()->domElement("ManipulatedFrame", document));
3826 
3827  return de;
3828 }
3829 
3864 void QGLViewer::initFromDOMElement(const QDomElement& element)
3865 {
3866  const QString version = element.attribute("version");
3867  // if (version != QGLViewerVersionString())
3868  if (version[0] != '2')
3869  // Patches for previous versions should go here when the state file syntax is modified.
3870 #if QT_VERSION >= 0x040000
3871  qWarning("State file created using QGLViewer version %s may not be correctly read.", version.toLatin1().constData());
3872 #else
3873  qWarning("State file created using QGLViewer version %s may not be correctly read.", version.latin1());
3874 #endif
3875 
3876  QDomElement child=element.firstChild().toElement();
3877  bool tmpCameraIsEdited = cameraIsEdited();
3878  while (!child.isNull())
3879  {
3880  if (child.tagName() == "State")
3881  {
3882  // #CONNECTION# default values from defaultConstructor()
3883  // setMouseTracking(DomUtils::boolFromDom(child, "mouseTracking", false));
3884  setStereoDisplay(DomUtils::boolFromDom(child, "stereo", false));
3885  if ((child.attribute("cameraMode", "revolve") == "fly") && (cameraIsInRevolveMode()))
3886  toggleCameraMode();
3887 
3888  QDomElement ch=child.firstChild().toElement();
3889  while (!ch.isNull())
3890  {
3891  if (ch.tagName() == "foregroundColor")
3892  setForegroundColor(DomUtils::QColorFromDom(ch));
3893  if (ch.tagName() == "backgroundColor")
3894  setBackgroundColor(DomUtils::QColorFromDom(ch));
3895  ch = ch.nextSibling().toElement();
3896  }
3897  }
3898 
3899  if (child.tagName() == "Display")
3900  {
3901  // #CONNECTION# default values from defaultConstructor()
3902  setAxisIsDrawn(DomUtils::boolFromDom(child, "axisIsDrawn", false));
3903  setGridIsDrawn(DomUtils::boolFromDom(child, "gridIsDrawn", false));
3904  setFPSIsDisplayed(DomUtils::boolFromDom(child, "FPSIsDisplayed", false));
3905  // See comment below.
3906  tmpCameraIsEdited = DomUtils::boolFromDom(child, "cameraIsEdited", false);
3907  // setTextIsEnabled(DomUtils::boolFromDom(child, "textIsEnabled", true));
3908  }
3909 
3910  if (child.tagName() == "Geometry")
3911  {
3912  setFullScreen(DomUtils::boolFromDom(child, "fullScreen", false));
3913 
3914  if (isFullScreen())
3915  {
3916  prevPos_.setX(DomUtils::intFromDom(child, "prevPosX", 0));
3917  prevPos_.setY(DomUtils::intFromDom(child, "prevPosY", 0));
3918  }
3919  else
3920  {
3921  int width = DomUtils::intFromDom(child, "width", 600);
3922  int height = DomUtils::intFromDom(child, "height", 400);
3923  topLevelWidget()->resize(width, height);
3924  camera()->setScreenWidthAndHeight(this->width(), this->height());
3925 
3926  QPoint pos;
3927  pos.setX(DomUtils::intFromDom(child, "posX", 0));
3928  pos.setY(DomUtils::intFromDom(child, "posY", 0));
3929  topLevelWidget()->move(pos);
3930  }
3931  }
3932 
3933  if (child.tagName() == "Camera")
3934  {
3935  connectAllCameraKFIInterpolatedSignals(false);
3936  camera()->initFromDOMElement(child);
3937  connectAllCameraKFIInterpolatedSignals();
3938  }
3939 
3940  if ((child.tagName() == "ManipulatedFrame") && (manipulatedFrame()))
3941  manipulatedFrame()->initFromDOMElement(child);
3942 
3943  child = child.nextSibling().toElement();
3944  }
3945 
3946  // The Camera always stores its "real" zClippingCoef in domElement(). If it is edited,
3947  // its "real" coef must be saved and the coef set to 5.0, as is done in setCameraIsEdited().
3948  // BUT : Camera and Display are read in an arbitrary order. We must initialize Camera's
3949  // "real" coef BEFORE calling setCameraIsEdited. Hence this temp cameraIsEdited and delayed call
3950  cameraIsEdited_ = tmpCameraIsEdited;
3951  if (cameraIsEdited_)
3952  {
3953  previousCameraZClippingCoefficient_ = camera()->zClippingCoefficient();
3954  // #CONNECTION# 5.0 from setCameraIsEdited.
3955  camera()->setZClippingCoefficient(5.0);
3956  }
3957 }
3958 
3959 #ifndef DOXYGEN
3960 
3962 void QGLViewer::saveToFile(const QString& fileName)
3963 {
3964  if (!fileName.isEmpty())
3965  setStateFileName(fileName);
3966 
3967  qWarning("saveToFile() is deprecated, use saveStateToFile() instead.");
3968  saveStateToFile();
3969 }
3970 
3973 bool QGLViewer::restoreFromFile(const QString& fileName)
3974 {
3975  if (!fileName.isEmpty())
3976  setStateFileName(fileName);
3977 
3978  qWarning("restoreFromFile() is deprecated, use restoreStateFromFile() instead.");
3979  return restoreStateFromFile();
3980 }
3981 #endif
3982 
4036 void QGLViewer::copyBufferToTexture(GLint internalFormat, GLenum format)
4037 {
4038  int h = 16;
4039  int w = 16;
4040  // Todo compare performance with qt code.
4041  while (w < width())
4042  w <<= 1;
4043  while (h < height())
4044  h <<= 1;
4045 
4046  bool init = false;
4047 
4048  if ((w != bufferTextureWidth_) || (h != bufferTextureHeight_))
4049  {
4050  bufferTextureWidth_ = w;
4051  bufferTextureHeight_ = h;
4052  bufferTextureMaxU_ = width() / float(bufferTextureWidth_);
4053  bufferTextureMaxV_ = height() / float(bufferTextureHeight_);
4054  init = true;
4055  }
4056 
4057  if (bufferTextureId() == 0)
4058  {
4059  glGenTextures(1, &bufferTextureId_);
4060  glBindTexture(GL_TEXTURE_2D, bufferTextureId_);
4061  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
4062  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
4063  init = true;
4064  }
4065  else
4066  glBindTexture(GL_TEXTURE_2D, bufferTextureId_);
4067 
4068  if ((format != previousBufferTextureFormat_) ||
4069  (internalFormat != previousBufferTextureInternalFormat_))
4070  {
4071  previousBufferTextureFormat_ = format;
4072  previousBufferTextureInternalFormat_ = internalFormat;
4073  init = true;
4074  }
4075 
4076  if (init)
4077  {
4078  if (format == GL_NONE)
4079  format = internalFormat;
4080 
4081  glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, bufferTextureWidth_, bufferTextureHeight_, 0, format, GL_UNSIGNED_BYTE, NULL);
4082  }
4083 
4084  glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, width(), height());
4085 }
4086 
4094 {
4095  if (glIsTexture(bufferTextureId_))
4096  return bufferTextureId_;
4097  else
4098  return 0;
4099 }