1// @configure_input@ 2 3/**************************************************************************\ 4 * 5 * This file is part of the Coin 3D visualization library. 6 * Copyright (C) by Kongsberg Oil & Gas Technologies. 7 * 8 * This library is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU General Public License 10 * ("GPL") version 2 as published by the Free Software Foundation. 11 * See the file LICENSE.GPL at the root directory of this source 12 * distribution for additional information about the GNU GPL. 13 * 14 * For using Coin with software that can not be combined with the GNU 15 * GPL, and for taking advantage of the additional benefits of our 16 * support services, please contact Kongsberg Oil & Gas Technologies 17 * about acquiring a Coin Professional Edition License. 18 * 19 * See http://www.coin3d.org/ for more information. 20 * 21 * Kongsberg Oil & Gas Technologies, Bygdoy Alle 5, 0257 Oslo, NORWAY. 22 * http://www.sim.no/ sales@sim.no coin-support@coin3d.org 23 * 24\**************************************************************************/ 25 26// This file contains the generic, "templatize-able" parts of the 27// So*ExaminerViewer sourcecode. 28 29// ************************************************************************* 30 31/*! 32 \class So@Gui@ExaminerViewer So@Gui@ExaminerViewer.h Inventor/@Gui@/viewers/So@Gui@ExaminerViewer.h 33 \brief The So@Gui@ExaminerViewer class is a 3D-model examination viewer. 34 \ingroup components viewers 35 36 This class is the viewer considered to be the most "general purpose" 37 viewer, and it is often used in rapid prototyping to examine simple 38 models aswell as complete scenes (although for the latter, you might 39 be better off with one of the other viewer classes). 40 41 <center> 42 <img src="http://doc.coin3d.org/images/SoLibs/viewers/examinerviewer.png"> 43 </center> 44 45 Here is a complete, stand-alone example that shows how to set up an 46 So@Gui@ExaminerViewer as a model viewer that loads Inventor and VRML 47 files from disk and places them inside the viewer for the end-user 48 to examine: 49 50 \code 51 #include <Inventor/@Gui@/So@Gui@.h> 52 #include <Inventor/@Gui@/viewers/So@Gui@ExaminerViewer.h> 53 #include <Inventor/nodes/SoBaseColor.h> 54 #include <Inventor/nodes/SoCone.h> 55 #include <Inventor/nodes/SoSeparator.h> 56 57 int 58 main(int argc, char ** argv) 59 { 60 if (argc < 2) { 61 (void)fprintf(stderr, "\n\n\tUsage: %s <modelfilename>\n\n", 62 argc > 0 ? argv[0] : "viewerapp"); 63 exit(1); 64 } 65 66 // Initialize So@Gui@ and Inventor API libraries. This returns a main 67 // window to use. 68 @WIDGET@ mainwin = So@Gui@::init(argc, argv, argv[0]); 69 70 // Open the argument file.. 71 SoInput in; 72 SbBool ok = in.openFile(argv[1]); 73 if (!ok) { exit(1); } 74 75 // ..and import it. 76 SoSeparator * root = SoDB::readAll(&in); 77 if (root == NULL) { exit(1); } 78 root->ref(); 79 80 // Use the ExaminerViewer, for a nice interface for 3D model 81 // inspection. 82 So@Gui@ExaminerViewer * viewer = new So@Gui@ExaminerViewer(mainwin); 83 viewer->setSceneGraph(root); 84 viewer->show(); 85 86 // Pop up the main window. 87 So@Gui@::show(mainwin); 88 // Loop until exit. 89 So@Gui@::mainLoop(); 90 91 // Clean up resources. 92 delete viewer; 93 root->unref(); 94 95 return 0; 96 } 97 \endcode 98 99 So@Gui@ExaminerViewer has a convenient interface for repositioning 100 and reorientation of the camera, by panning, rotating and zooming 101 it's position. The following controls can be used: 102 103 <ul> 104 105 <li>hold down left mousebutton and move mouse pointer to rotate the 106 camera around it's current focal point (the focal point can be 107 changed by doing a seek operation)</li> 108 109 <li>hold middle mousebutton to pan (or a CTRL-key plus left 110 mousebutton, or a SHIFT-key plus left mousebutton)</li> 111 112 <li>hold down left + middle mousebutton to zoom / dolly, or CTRL + 113 middle mousebutton, or CTRL + SHIFT + the left mousebutton</li> 114 115 <li>click 's', then pick with the left mousebutton to seek</li> 116 117 <li>right mousebutton opens the popup menu</li> 118 119 <li>click 'ESC' key to switch to and from 'camera interaction' mode 120 and 'scenegraph interaction' mode (see setViewing() 121 documentation)</li> 122 123 <!-- 124 FIXME: This functionality has been disabled. See FIXME comment 125 20050202 larsa below. 126 127 <li>hold down the 'ALT' key to temporary toggle from 128 camera-interaction mode to scenegraph-interaction mode</li> 129 130 //--> 131 132 <li>'q' quits the application</li> 133 134 </ul> 135 136 The So@Gui@ExaminerViewer provides a user decoration's button for 137 toggling between orthographic or perspective camera view volumes and 138 projection methods. This is the bottom-most click button on the 139 right decoration border. 140 141 It also inherits the decoration buttons from the So@Gui@FullViewer: 142 the arrow for switching to "scenegraph interaction" mode, the hand 143 for setting back to "camera interaction" mode, the house for "reset 144 camera to home position", the blueprint house for "set new camera 145 home position", the eye for "zoom camera out to view full scene" and 146 the flashlight for setting "click to seek" mode. 147 148 Note that a common faulty assumption about all the viewer-classes is 149 that user interaction (in the "examine"-mode, not the 150 scenegraph-interaction mode) influences the model or 3D-scene in the 151 view. This is not correct, as it is always the viewer's \e camera 152 that is translated and rotated. 153 154 The initial position of the camera is placed such that all of the 155 scenegraph's geometry fits within it's view. 156 157 \sa So@Gui@FlyViewer, So@Gui@PlaneViewer 158*/ 159 160// ************************************************************************* 161 162// Documentation shared between So* toolkits follows below. 163 164/*! 165 \fn So@Gui@ExaminerViewer::So@Gui@ExaminerViewer(@WIDGET@ parent, const char * name, SbBool embed, So@Gui@FullViewer::BuildFlag flag, So@Gui@Viewer::Type type) 166 167 Constructor. See parent class for explanation of arguments. 168 Calling this constructor will make sure the examiner viewer widget 169 will be built immediately. 170*/ 171 172/*! 173 \fn So@Gui@ExaminerViewer::So@Gui@ExaminerViewer(@WIDGET@ parent, const char * name, SbBool embed, So@Gui@FullViewer::BuildFlag flag, So@Gui@Viewer::Type type, SbBool build) 174 175 Constructor. See parent class for explanation of arguments. 176*/ 177 178// ************************************************************************* 179 180#ifdef HAVE_CONFIG_H 181#include <config.h> 182#endif // HAVE_CONFIG_H 183 184#include <assert.h> 185#include <math.h> 186 187#include <Inventor/SbTime.h> 188#include <Inventor/errors/SoDebugError.h> 189#include <Inventor/nodes/SoOrthographicCamera.h> 190#include <Inventor/nodes/SoPerspectiveCamera.h> 191#include <Inventor/projectors/SbSphereSheetProjector.h> 192#include <Inventor/projectors/SbSpherePlaneProjector.h> 193#include <Inventor/events/SoKeyboardEvent.h> 194#include <Inventor/events/SoMouseButtonEvent.h> 195#include <Inventor/events/SoLocation2Event.h> 196#include <Inventor/events/SoMotion3Event.h> 197 198#include <so@gui@defs.h> 199 200#include <Inventor/@Gui@/common/gl.h> 201#include <Inventor/@Gui@/viewers/So@Gui@ExaminerViewer.h> 202#include <Inventor/@Gui@/viewers/So@Gui@ExaminerViewerP.h> 203 204#include <Inventor/@Gui@/So@Gui@Basic.h> 205#include <Inventor/@Gui@/So@Gui@Cursor.h> 206#include <Inventor/@Gui@/viewers/SoGuiFullViewerP.h> // for pan() and zoom() 207 208#define PRIVATE(obj) ((obj)->pimpl) 209#define PUBLIC(obj) ((obj)->pub) 210 211static const int MOUSEPOSLOGSIZE = 16; 212 213// Bitmap representations of an "X", a "Y" and a "Z" for the axis cross. 214static GLubyte xbmp[] = { 0x11,0x11,0x0a,0x04,0x0a,0x11,0x11 }; 215static GLubyte ybmp[] = { 0x04,0x04,0x04,0x04,0x0a,0x11,0x11 }; 216static GLubyte zbmp[] = { 0x1f,0x10,0x08,0x04,0x02,0x01,0x1f }; 217 218// ************************************************************************ 219 220/*! 221 Decide if it should be possible to start a spin animation of the 222 model in the viewer by releasing the mouse button while dragging. 223 224 If the \a enable flag is \c FALSE and we're currently animating, the 225 spin will be stopped. 226 227 \sa isAnimationEnabled 228*/ 229void 230So@Gui@ExaminerViewer::setAnimationEnabled(const SbBool enable) 231{ 232 PRIVATE(this)->spinanimatingallowed = enable; 233 if (!enable && this->isAnimating()) { this->stopAnimating(); } 234} 235 236// ************************************************************************* 237 238/*! 239 Query whether or not it is possible to start a spinning animation by 240 releasing the left mouse button while dragging the mouse. 241 242 \sa setAnimationEnabled 243*/ 244 245SbBool 246So@Gui@ExaminerViewer::isAnimationEnabled(void) const 247{ 248 return PRIVATE(this)->spinanimatingallowed; 249} 250 251// ************************************************************************* 252 253/*! 254 Stop the model from spinning. 255*/ 256 257void 258So@Gui@ExaminerViewer::stopAnimating(void) 259{ 260 if (PRIVATE(this)->currentmode != SoGuiExaminerViewerP::SPINNING) { 261#if SO@GUI@_DEBUG 262 SoDebugError::postWarning("So@Gui@ExaminerViewer::stopAnimating", 263 "not animating"); 264#endif // SO@GUI@_DEBUG 265 return; 266 } 267 PRIVATE(this)->setMode(this->isViewing() ? 268 SoGuiExaminerViewerP::IDLE : 269 SoGuiExaminerViewerP::INTERACT); 270} 271 272// ************************************************************************* 273 274/*! 275 Query if the model in the viewer is currently in spinning mode after 276 a user drag. 277*/ 278 279SbBool 280So@Gui@ExaminerViewer::isAnimating(void) const 281{ 282 return PRIVATE(this)->currentmode == SoGuiExaminerViewerP::SPINNING; 283} 284 285// ************************************************************************ 286 287/*! 288 Set the flag deciding whether or not to show the axis cross. 289 290 \sa isFeedbackVisible, getFeedbackSize, setFeedbackSize 291*/ 292 293void 294So@Gui@ExaminerViewer::setFeedbackVisibility(const SbBool enable) 295{ 296 if (enable == PRIVATE(this)->axiscrossEnabled) { 297#ifdef SO@GUI@_EXTRA_DEBUG 298 SoDebugError::postWarning("So@Gui@ExaminerViewer::setFeedbackVisibility", 299 "feedback visibility already set to %s", enable ? "TRUE" : "FALSE"); 300#endif // SO@GUI@_EXTRA_DEBUG 301 return; 302 } 303 PRIVATE(this)->axiscrossEnabled = enable; 304 305 if (this->isViewing()) { this->scheduleRedraw(); } 306} 307 308/*! 309 Check if the feedback axis cross is visible. 310 311 \sa setFeedbackVisibility, getFeedbackSize, setFeedbackSize 312*/ 313 314SbBool 315So@Gui@ExaminerViewer::isFeedbackVisible(void) const 316{ 317 return PRIVATE(this)->axiscrossEnabled; 318} 319 320// ************************************************************************ 321 322/*! 323 Set the size of the feedback axiscross. The value is interpreted as 324 an approximate percentage chunk of the dimensions of the total 325 canvas. 326 327 \sa getFeedbackSize, isFeedbackVisible, setFeedbackVisibility 328*/ 329void 330So@Gui@ExaminerViewer::setFeedbackSize(const int size) 331{ 332#if SO@GUI@_DEBUG 333 if (size < 1) { 334 SoDebugError::postWarning("So@Gui@ExaminerViewer::setFeedbackSize", 335 "the size setting should be larger than 0"); 336 return; 337 } 338#endif // SO@GUI@_DEBUG 339 340 PRIVATE(this)->axiscrossSize = size; 341 342 if (this->isFeedbackVisible() && this->isViewing()) { 343 this->scheduleRedraw(); 344 } 345} 346 347/*! 348 Return the size of the feedback axis cross. Default is 25. 349 350 \sa setFeedbackSize, isFeedbackVisible, setFeedbackVisibility 351*/ 352 353int 354So@Gui@ExaminerViewer::getFeedbackSize(void) const 355{ 356 return PRIVATE(this)->axiscrossSize; 357} 358 359// ************************************************************************* 360 361// Documented in superclass. 362SbBool 363So@Gui@ExaminerViewer::processSoEvent(const SoEvent * const ev) 364{ 365#if SO@GUI@_DEBUG && 0 // debug 366 SoDebugError::postInfo("So@Gui@ExaminerViewer::processSoEvent", 367 "[invoked], event '%s'", 368 ev->getTypeId().getName().getString()); 369#endif // debug 370 371 // We're in "interact" mode (ie *not* the camera modification mode), 372 // so don't handle the event here. It should either be forwarded to 373 // the scenegraph, or caught by So@Gui@Viewer::processSoEvent() if 374 // it's an ESC or ALT press (to switch modes). 375 if (!this->isViewing()) { return inherited::processSoEvent(ev); } 376 377 // Events when in "ready-to-seek" mode are ignored, except those 378 // which influence the seek mode itself -- these are handled further 379 // up the inheritance hierarchy. 380 if (this->isSeekMode()) { return inherited::processSoEvent(ev); } 381 382 const SoType type(ev->getTypeId()); 383 384 const SbVec2s size(this->getGLSize()); 385 const SbVec2f prevnormalized = PRIVATE(this)->lastmouseposition; 386 const SbVec2s pos(ev->getPosition()); 387 const SbVec2f posn((float) pos[0] / (float) So@Gui@Max((int)(size[0] - 1), 1), 388 (float) pos[1] / (float) So@Gui@Max((int)(size[1] - 1), 1)); 389 390 PRIVATE(this)->lastmouseposition = posn; 391 392 // Set to TRUE if any event processing happened. Note that it is not 393 // necessary to restrict ourselves to only do one "action" for an 394 // event, we only need this flag to see if any processing happened 395 // at all. 396 SbBool processed = FALSE; 397 398 const SoGuiExaminerViewerP::ViewerMode currentmode = PRIVATE(this)->currentmode; 399 SoGuiExaminerViewerP::ViewerMode newmode = currentmode; 400 401 PRIVATE(this)->ctrldown = ev->wasCtrlDown(); 402 PRIVATE(this)->shiftdown = ev->wasShiftDown(); 403 404 // Mouse Button / Spaceball Button handling 405 406 if (type.isDerivedFrom(SoMouseButtonEvent::getClassTypeId())) { 407 processed = TRUE; 408 409 const SoMouseButtonEvent * const event = (const SoMouseButtonEvent *) ev; 410 const int button = event->getButton(); 411 const SbBool press = event->getState() == SoButtonEvent::DOWN ? TRUE : FALSE; 412 413 switch (button) { 414 case SoMouseButtonEvent::BUTTON1: 415 PRIVATE(this)->button1down = press; 416 if (press && (currentmode == SoGuiExaminerViewerP::SEEK_WAIT_MODE)) { 417 newmode = SoGuiExaminerViewerP::SEEK_MODE; 418 this->seekToPoint(pos); // implicitly calls interactiveCountInc() 419 } 420 break; 421 case SoMouseButtonEvent::BUTTON2: 422 processed = FALSE; // pass on to superclass, so popup menu is shown 423 break; 424 case SoMouseButtonEvent::BUTTON3: 425 PRIVATE(this)->button3down = press; 426 break; 427#ifdef HAVE_SOMOUSEBUTTONEVENT_BUTTON5 428 case SoMouseButtonEvent::BUTTON4: 429 if (press) SoGuiFullViewerP::zoom(this->getCamera(), 0.1f); 430 break; 431 case SoMouseButtonEvent::BUTTON5: 432 if (press) SoGuiFullViewerP::zoom(this->getCamera(), -0.1f); 433 break; 434#endif // HAVE_SOMOUSEBUTTONEVENT_BUTTON5 435 default: 436 break; 437 } 438 } 439 440 // Keyboard handling 441 if (type.isDerivedFrom(SoKeyboardEvent::getClassTypeId())) { 442 const SoKeyboardEvent * const event = (const SoKeyboardEvent *) ev; 443 const SbBool press = event->getState() == SoButtonEvent::DOWN ? TRUE : FALSE; 444 switch (event->getKey()) { 445 case SoKeyboardEvent::LEFT_CONTROL: 446 case SoKeyboardEvent::RIGHT_CONTROL: 447 processed = TRUE; 448 PRIVATE(this)->ctrldown = press; 449 break; 450 case SoKeyboardEvent::LEFT_SHIFT: 451 case SoKeyboardEvent::RIGHT_SHIFT: 452 processed = TRUE; 453 PRIVATE(this)->shiftdown = press; 454 break; 455 default: 456 break; 457 } 458 } 459 460 // Mouse Movement handling 461 if (type.isDerivedFrom(SoLocation2Event::getClassTypeId())) { 462 const SoLocation2Event * const event = (const SoLocation2Event *) ev; 463 464 processed = TRUE; 465 466 if (PRIVATE(this)->currentmode == SoGuiExaminerViewerP::ZOOMING) { 467 PRIVATE(this)->zoomByCursor(posn, prevnormalized); 468 } 469 else if (PRIVATE(this)->currentmode == SoGuiExaminerViewerP::PANNING) { 470 SoGuiFullViewerP::pan(this->getCamera(), this->getGLAspectRatio(), 471 PRIVATE(this)->panningplane, posn, prevnormalized); 472 } 473 else if (PRIVATE(this)->currentmode == SoGuiExaminerViewerP::DRAGGING) { 474 PRIVATE(this)->addToLog(event->getPosition(), event->getTime()); 475 PRIVATE(this)->spin(posn); 476 } 477 else { 478 processed = FALSE; 479 } 480 } 481 482 // Spaceball & Joystick handling 483 if (type.isDerivedFrom(SoMotion3Event::getClassTypeId())) { 484 SoMotion3Event * const event = (SoMotion3Event *) ev; 485 SoCamera * const camera = this->getCamera(); 486 if (camera) { 487 if (PRIVATE(this)->motion3OnCamera) { 488 SbVec3f dir = event->getTranslation(); 489 camera->orientation.getValue().multVec(dir,dir); 490 camera->position = camera->position.getValue() + dir; 491 camera->orientation = 492 event->getRotation() * camera->orientation.getValue(); 493 processed = TRUE; 494 } 495 else { 496 // FIXME: move/rotate model 497#if SO@GUI@_DEBUG 498 SoDebugError::postInfo("So@Gui@ExaminerViewer::processSoEvent", 499 "SoMotion3Event for model movement is not implemented yet"); 500#endif // SO@GUI@_DEBUG 501 processed = TRUE; 502 } 503 } 504 } 505 506 enum { 507 BUTTON1DOWN = 1 << 0, 508 BUTTON3DOWN = 1 << 1, 509 CTRLDOWN = 1 << 2, 510 SHIFTDOWN = 1 << 3 511 }; 512 unsigned int combo = 513 (PRIVATE(this)->button1down ? BUTTON1DOWN : 0) | 514 (PRIVATE(this)->button3down ? BUTTON3DOWN : 0) | 515 (PRIVATE(this)->ctrldown ? CTRLDOWN : 0) | 516 (PRIVATE(this)->shiftdown ? SHIFTDOWN : 0); 517 518 switch (combo) { 519 case 0: 520 if (currentmode == SoGuiExaminerViewerP::SPINNING) { break; } 521 newmode = SoGuiExaminerViewerP::IDLE; 522 if ((currentmode == SoGuiExaminerViewerP::DRAGGING) && 523 this->isAnimationEnabled() && (PRIVATE(this)->log.historysize >= 3)) { 524 SbTime stoptime = (ev->getTime() - PRIVATE(this)->log.time[0]); 525 if (stoptime.getValue() < 0.100) { 526 const SbVec2s glsize(this->getGLSize()); 527 SbVec3f from = PRIVATE(this)->spinprojector->project(SbVec2f(float(PRIVATE(this)->log.position[2][0]) / float(So@Gui@Max(glsize[0]-1, 1)), 528 float(PRIVATE(this)->log.position[2][1]) / float(So@Gui@Max(glsize[1]-1, 1)))); 529 SbVec3f to = PRIVATE(this)->spinprojector->project(posn); 530 SbRotation rot = PRIVATE(this)->spinprojector->getRotation(from, to); 531 532 SbTime delta = (PRIVATE(this)->log.time[0] - PRIVATE(this)->log.time[2]); 533 double deltatime = delta.getValue(); 534 rot.invert(); 535 rot.scaleAngle(float(0.200 / deltatime)); 536 537 SbVec3f axis; 538 float radians; 539 rot.getValue(axis, radians); 540 if ((radians > 0.01f) && (deltatime < 0.300)) { 541 newmode = SoGuiExaminerViewerP::SPINNING; 542 PRIVATE(this)->spinRotation = rot; 543 } 544 } 545 } 546 break; 547 case BUTTON1DOWN: 548 newmode = SoGuiExaminerViewerP::DRAGGING; 549 break; 550 case BUTTON3DOWN: 551 case CTRLDOWN|BUTTON1DOWN: 552 case SHIFTDOWN|BUTTON1DOWN: 553 newmode = SoGuiExaminerViewerP::PANNING; 554 break; 555 case BUTTON1DOWN|BUTTON3DOWN: 556 case CTRLDOWN|BUTTON3DOWN: 557 case CTRLDOWN|SHIFTDOWN|BUTTON1DOWN: 558 newmode = SoGuiExaminerViewerP::ZOOMING; 559 break; 560 561 // There are many cases we don't handle that just falls through to 562 // the default case, like SHIFTDOWN, CTRLDOWN, CTRLDOWN|SHIFTDOWN, 563 // SHIFTDOWN|BUTTON3DOWN, SHIFTDOWN|CTRLDOWN|BUTTON3DOWN, etc. 564 // This is a feature, not a bug. :-) 565 // 566 // mortene. 567 568 default: 569 // The default will make a spin stop and otherwise not do 570 // anything. 571 if ((currentmode != SoGuiExaminerViewerP::SEEK_WAIT_MODE) && 572 (currentmode != SoGuiExaminerViewerP::SEEK_MODE)) { 573 newmode = SoGuiExaminerViewerP::IDLE; 574 } 575 break; 576 } 577 578 if (newmode != currentmode) { 579 PRIVATE(this)->setMode(newmode); 580 } 581 582 // If not handled in this class, pass on upwards in the inheritance 583 // hierarchy. 584 return processed || inherited::processSoEvent(ev); 585} 586 587// ************************************************************************* 588 589// documented in superclass 590void 591So@Gui@ExaminerViewer::setSeekMode(SbBool on) 592{ 593 // Overrides this method to make sure any animations are stopped 594 // before we go into seek mode. 595 596 // Note: this method is almost identical to the setSeekMode() in the 597 // So@Gui@FlyViewer and So@Gui@PlaneViewer, so migrate any changes. 598 599#if SO@GUI@_DEBUG 600 if (on == this->isSeekMode()) { 601 SoDebugError::postWarning("So@Gui@ExaminerViewer::setSeekMode", 602 "seek mode already %sset", on ? "" : "un"); 603 return; 604 } 605#endif // SO@GUI@_DEBUG 606 607 if (this->isAnimating()) { this->stopAnimating(); } 608 inherited::setSeekMode(on); 609 PRIVATE(this)->setMode(on ? 610 SoGuiExaminerViewerP::SEEK_WAIT_MODE : 611 (this->isViewing() ? 612 SoGuiExaminerViewerP::IDLE : SoGuiExaminerViewerP::INTERACT)); 613} 614 615// ************************************************************************* 616 617/*! 618 Decide whether or not the mouse pointer cursor should be visible in 619 the rendering canvas. 620*/ 621void 622So@Gui@ExaminerViewer::setCursorEnabled(SbBool enable) 623{ 624 inherited::setCursorEnabled(enable); 625 PRIVATE(this)->setCursorRepresentation(PRIVATE(this)->currentmode); 626} 627 628// ************************************************************************* 629 630// Documented in superclass. 631const char * 632So@Gui@ExaminerViewer::getDefaultWidgetName(void) const 633{ 634 return "So@Gui@ExaminerViewer"; 635} 636 637// ************************************************************************* 638 639// Documented in superclass. 640const char * 641So@Gui@ExaminerViewer::getDefaultTitle(void) const 642{ 643 return "Examiner Viewer"; 644} 645 646// ************************************************************************* 647 648// Documented in superclass. 649const char * 650So@Gui@ExaminerViewer::getDefaultIconTitle(void) const 651{ 652 return "Examiner Viewer"; 653} 654 655// ************************************************************************* 656 657// Documented in superclass. Overrides this method to be able to draw 658// the axis cross, if selected, and to keep a continuous animation 659// upon spin. 660void 661So@Gui@ExaminerViewer::actualRedraw(void) 662{ 663 SbTime now = SbTime::getTimeOfDay(); 664 double secs = now.getValue() - PRIVATE(this)->prevRedrawTime.getValue(); 665 666 PRIVATE(this)->prevRedrawTime = now; 667 668 if (this->isAnimating()) { 669 SbRotation deltaRotation = PRIVATE(this)->spinRotation; 670 deltaRotation.scaleAngle(float(secs * 5.0)); 671 PRIVATE(this)->reorientCamera(deltaRotation); 672 } 673 674 inherited::actualRedraw(); 675 676 if (this->isFeedbackVisible()) { PRIVATE(this)->drawAxisCross(); } 677 678 // Immediately reschedule to get continous spin animation. 679 if (this->isAnimating()) { this->scheduleRedraw(); } 680} 681 682// ************************************************************************* 683 684// doc in super 685void 686So@Gui@ExaminerViewer::afterRealizeHook(void) 687{ 688 inherited::afterRealizeHook(); 689 PRIVATE(this)->setCursorRepresentation(PRIVATE(this)->currentmode); 690} 691 692// ************************************************************************* 693 694// Documented in superclass. Overridden to provide the examiner viewer 695// functionality on the left thumbwheel (x axis rotation). 696void 697So@Gui@ExaminerViewer::leftWheelMotion(float value) 698{ 699 if (this->isAnimating()) this->stopAnimating(); 700 701 float newval = PRIVATE(this)->rotXWheelMotion(value, this->getLeftWheelValue()); 702 inherited::leftWheelMotion(newval); 703} 704 705// Documented in superclass. Overridden to provide the examiner viewer 706// functionality on the bottom thumbwheel (y axis rotation). 707void 708So@Gui@ExaminerViewer::bottomWheelMotion(float value) 709{ 710 if (this->isAnimating()) this->stopAnimating(); 711 712 float newval = PRIVATE(this)->rotYWheelMotion(value, this->getBottomWheelValue()); 713 inherited::bottomWheelMotion(newval); 714} 715 716// Documented in superclass. Overridden to provide the examiner viewer 717// functionality on the left thumbwheel (dolly/zoom). 718void 719So@Gui@ExaminerViewer::rightWheelMotion(float value) 720{ 721 SoGuiFullViewerP::zoom(this->getCamera(), this->getRightWheelValue() - value); 722 inherited::rightWheelMotion(value); 723} 724 725// ************************************************************************* 726 727// Documented in superclass. This method overridden from parent class 728// to make sure the mouse pointer cursor is updated. 729void 730So@Gui@ExaminerViewer::setViewing(SbBool enable) 731{ 732 if (!!this->isViewing() == !!enable) { 733#if SO@GUI@_DEBUG 734 SoDebugError::postWarning("So@Gui@ExaminerViewer::setViewing", 735 "current state already %s", enable ? "TRUE" : "FALSE"); 736#endif // SO@GUI@_DEBUG 737 return; 738 } 739 740 PRIVATE(this)->setMode(enable ? 741 SoGuiExaminerViewerP::IDLE : 742 SoGuiExaminerViewerP::INTERACT); 743 inherited::setViewing(enable); 744} 745 746// ************************************************************************* 747 748#ifndef DOXYGEN_SKIP_THIS 749 750// Remaining code is for the SoGuiExaminerViewerP "private 751// implementation" class. 752 753SoGuiExaminerViewerP::SoGuiExaminerViewerP(So@Gui@ExaminerViewer * publ) 754{ 755 PUBLIC(this) = publ; 756} 757 758SoGuiExaminerViewerP::~SoGuiExaminerViewerP() 759{ 760} 761 762void 763SoGuiExaminerViewerP::genericConstructor(void) 764{ 765 this->currentmode = SoGuiExaminerViewerP::IDLE; 766 767 this->prevRedrawTime = SbTime::getTimeOfDay(); 768 this->spinanimatingallowed = TRUE; 769 this->spinsamplecounter = 0; 770 this->spinincrement = SbRotation::identity(); 771 772 // FIXME: use a smaller sphere than the default one to have a larger 773 // area close to the borders that gives us "z-axis rotation"? 774 // 19990425 mortene. 775 this->spinprojector = new SbSphereSheetProjector(SbSphere(SbVec3f(0, 0, 0), 0.8f)); 776 SbViewVolume volume; 777 volume.ortho(-1, 1, -1, 1, -1, 1); 778 this->spinprojector->setViewVolume(volume); 779 780 this->axiscrossEnabled = FALSE; 781 this->axiscrossSize = 25; 782 783 this->spinRotation.setValue(SbVec3f(0, 0, -1), 0); 784 785 this->log.size = MOUSEPOSLOGSIZE; 786 this->log.position = new SbVec2s [ MOUSEPOSLOGSIZE ]; 787 this->log.time = new SbTime [ MOUSEPOSLOGSIZE ]; 788 this->log.historysize = 0; 789 this->button1down = FALSE; 790 this->button3down = FALSE; 791 this->ctrldown = FALSE; 792 this->shiftdown = FALSE; 793 this->pointer.now = SbVec2s(0, 0); 794 this->pointer.then = SbVec2s(0, 0); 795 this->motion3OnCamera = TRUE; 796} 797 798void 799SoGuiExaminerViewerP::genericDestructor(void) 800{ 801 delete this->spinprojector; 802 delete[] this->log.position; 803 delete[] this->log.time; 804} 805 806// ************************************************************************ 807 808// rotate a camera around its focalpoint, in the direction around the 809// given axis, by the given delta value (in radians) 810void 811SoGuiExaminerViewerP::rotateCamera(SoCamera * cam, 812 const SbVec3f & aroundaxis, 813 const float delta) 814{ 815 const SbVec3f DEFAULTDIRECTION(0, 0, -1); 816 const SbRotation currentorientation = cam->orientation.getValue(); 817 818 SbVec3f currentdir; 819 currentorientation.multVec(DEFAULTDIRECTION, currentdir); 820 821 const SbVec3f focalpoint = cam->position.getValue() + 822 cam->focalDistance.getValue() * currentdir; 823 824 // set new orientation 825 cam->orientation = SbRotation(aroundaxis, delta) * currentorientation; 826 827 SbVec3f newdir; 828 cam->orientation.getValue().multVec(DEFAULTDIRECTION, newdir); 829 cam->position = focalpoint - cam->focalDistance.getValue() * newdir; 830} 831 832// The "rotX" wheel is the wheel on the left decoration on the 833// examiner viewer. This function translates interaction with the 834// "rotX" wheel into camera movement. 835float 836SoGuiExaminerViewerP::rotXWheelMotion(float value, float oldvalue) 837{ 838 SoCamera * cam = PUBLIC(this)->getCamera(); 839 if (cam == NULL) return 0.0f; // can happen for empty scenegraph 840 841 SoGuiExaminerViewerP::rotateCamera(cam, SbVec3f(-1, 0, 0), value - oldvalue); 842 return value; 843} 844 845// The "rotY" wheel is the wheel on the bottom decoration on the 846// examiner viewer. This function translates interaction with the 847// "rotX" wheel into camera movement. 848float 849SoGuiExaminerViewerP::rotYWheelMotion(float value, float oldvalue) 850{ 851 SoCamera * cam = PUBLIC(this)->getCamera(); 852 if (cam == NULL) return 0.0f; // can happen for empty scenegraph 853 854 SoGuiExaminerViewerP::rotateCamera(cam, SbVec3f(0, -1, 0), value - oldvalue); 855 return value; 856} 857 858// ************************************************************************ 859 860// The viewer is a state machine, and all changes to the current state 861// are made through this call. 862void 863SoGuiExaminerViewerP::setMode(const ViewerMode newmode) 864{ 865 const ViewerMode oldmode = this->currentmode; 866 if (newmode == oldmode) { return; } 867 868 switch (newmode) { 869 case DRAGGING: 870 // Set up initial projection point for the projector object when 871 // first starting a drag operation. 872 this->spinprojector->project(this->lastmouseposition); 873 PUBLIC(this)->interactiveCountInc(); 874 this->clearLog(); 875 break; 876 877 case SPINNING: 878 PUBLIC(this)->interactiveCountInc(); 879 PUBLIC(this)->scheduleRedraw(); 880 break; 881 882 case PANNING: 883 { 884 // The plane we're projecting the mouse coordinates to get 3D 885 // coordinates should stay the same during the whole pan 886 // operation, so we should calculate this value here. 887 SoCamera * cam = PUBLIC(this)->getCamera(); 888 if (cam == NULL) { // can happen for empty scenegraph 889 this->panningplane = SbPlane(SbVec3f(0, 0, 1), 0); 890 } 891 else { 892 SbViewVolume vv = cam->getViewVolume(PUBLIC(this)->getGLAspectRatio()); 893 this->panningplane = vv.getPlane(cam->focalDistance.getValue()); 894 } 895 } 896 PUBLIC(this)->interactiveCountInc(); 897 break; 898 899 case ZOOMING: 900 PUBLIC(this)->interactiveCountInc(); 901 break; 902 903 default: // include default to avoid compiler warnings. 904 break; 905 } 906 907 switch (oldmode) { 908 case SPINNING: 909 case DRAGGING: 910 case PANNING: 911 case ZOOMING: 912 PUBLIC(this)->interactiveCountDec(); 913 break; 914 915 default: 916 break; 917 } 918 919#if SO@GUI@_DEBUG && 0 // debug 920 if (oldmode == ZOOMING) { 921 SbVec3f v = PUBLIC(this)->getCamera()->position.getValue(); 922 SoDebugError::postInfo("So@Gui@ExaminerViewerP::setMode", 923 "new camera position after zoom: <%e, %e, %e>", 924 v[0], v[1], v[2]); 925 } 926#endif // debug 927 928 this->setCursorRepresentation(newmode); 929 this->currentmode = newmode; 930} 931 932// ************************************************************************ 933 934void 935SoGuiExaminerViewerP::drawAxisCross(void) 936{ 937 // FIXME: convert this to a superimposition scenegraph instead of 938 // OpenGL calls. 20020603 mortene. 939 940 // Store GL state. 941 glPushAttrib(GL_ALL_ATTRIB_BITS); 942 GLfloat depthrange[2]; 943 glGetFloatv(GL_DEPTH_RANGE, depthrange); 944 GLdouble projectionmatrix[16]; 945 glGetDoublev(GL_PROJECTION_MATRIX, projectionmatrix); 946 947 glDepthFunc(GL_ALWAYS); 948 glDepthMask(GL_TRUE); 949 glDepthRange(0, 0); 950 glEnable(GL_DEPTH_TEST); 951 glDisable(GL_LIGHTING); 952 glEnable(GL_COLOR_MATERIAL); 953 glDisable(GL_BLEND); // Kills transparency. 954 955 // Set the viewport in the OpenGL canvas. Dimensions are calculated 956 // as a percentage of the total canvas size. 957 SbVec2s view = PUBLIC(this)->getGLSize(); 958 const int pixelarea = 959 int(float(this->axiscrossSize)/100.0f * So@Gui@Min(view[0], view[1])); 960#if 0 // middle of canvas 961 SbVec2s origin(view[0]/2 - pixelarea/2, view[1]/2 - pixelarea/2); 962#endif // middle of canvas 963#if 1 // lower right of canvas 964 SbVec2s origin(view[0] - pixelarea, 0); 965#endif // lower right of canvas 966 glViewport(origin[0], origin[1], pixelarea, pixelarea); 967 968 969 970 // Set up the projection matrix. 971 glMatrixMode(GL_PROJECTION); 972 glLoadIdentity(); 973 974 const float NEARVAL = 0.1f; 975 const float FARVAL = 10.0f; 976 const float dim = NEARVAL * float(tan(M_PI / 8.0)); // FOV is 45� (45/360 = 1/8) 977 glFrustum(-dim, dim, -dim, dim, NEARVAL, FARVAL); 978 979 980 // Set up the model matrix. 981 glMatrixMode(GL_MODELVIEW); 982 glPushMatrix(); 983 SbMatrix mx; 984 SoCamera * cam = PUBLIC(this)->getCamera(); 985 986 // If there is no camera (like for an empty scene, for instance), 987 // just use an identity rotation. 988 if (cam) { mx = cam->orientation.getValue(); } 989 else { mx = SbMatrix::identity(); } 990 991 mx = mx.inverse(); 992 mx[3][2] = -3.5; // Translate away from the projection point (along z axis). 993 glLoadMatrixf((float *)mx); 994 995 996 // Find unit vector end points. 997 SbMatrix px; 998 glGetFloatv(GL_PROJECTION_MATRIX, (float *)px); 999 SbMatrix comb = mx.multRight(px); 1000 1001 SbVec3f xpos; 1002 comb.multVecMatrix(SbVec3f(1,0,0), xpos); 1003 xpos[0] = (1 + xpos[0]) * view[0]/2; 1004 xpos[1] = (1 + xpos[1]) * view[1]/2; 1005 SbVec3f ypos; 1006 comb.multVecMatrix(SbVec3f(0,1,0), ypos); 1007 ypos[0] = (1 + ypos[0]) * view[0]/2; 1008 ypos[1] = (1 + ypos[1]) * view[1]/2; 1009 SbVec3f zpos; 1010 comb.multVecMatrix(SbVec3f(0,0,1), zpos); 1011 zpos[0] = (1 + zpos[0]) * view[0]/2; 1012 zpos[1] = (1 + zpos[1]) * view[1]/2; 1013 1014 1015 // Render the cross. 1016 { 1017 glLineWidth(2.0); 1018 1019 enum { XAXIS, YAXIS, ZAXIS }; 1020 int idx[3] = { XAXIS, YAXIS, ZAXIS }; 1021 float val[3] = { xpos[2], ypos[2], zpos[2] }; 1022 1023 // Bubble sort.. :-} 1024 if (val[0] < val[1]) { So@Gui@Swap(val[0], val[1]); So@Gui@Swap(idx[0], idx[1]); } 1025 if (val[1] < val[2]) { So@Gui@Swap(val[1], val[2]); So@Gui@Swap(idx[1], idx[2]); } 1026 if (val[0] < val[1]) { So@Gui@Swap(val[0], val[1]); So@Gui@Swap(idx[0], idx[1]); } 1027 assert((val[0] >= val[1]) && (val[1] >= val[2])); // Just checking.. 1028 1029 for (int i=0; i < 3; i++) { 1030 glPushMatrix(); 1031 if (idx[i] == XAXIS) { // X axis. 1032 glColor3f(0.500f, 0.125f, 0.125f); 1033 } else if (idx[i] == YAXIS) { // Y axis. 1034 glRotatef(90, 0, 0, 1); 1035 glColor3f(0.125f, 0.500f, 0.125f); 1036 } else { // Z axis. 1037 glRotatef(-90, 0, 1, 0); 1038 glColor3f(0.125f, 0.125f, 0.500f); 1039 } 1040 this->drawArrow(); 1041 glPopMatrix(); 1042 } 1043 } 1044 1045 // Render axis notation letters ("X", "Y", "Z"). 1046 glMatrixMode(GL_PROJECTION); 1047 glLoadIdentity(); 1048 glOrtho(0, view[0], 0, view[1], -1, 1); 1049 1050 glMatrixMode(GL_MODELVIEW); 1051 glLoadIdentity(); 1052 1053 GLint unpack; 1054 glGetIntegerv(GL_UNPACK_ALIGNMENT, &unpack); 1055 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 1056 1057 glColor3fv(SbVec3f(0.8f, 0.8f, 0.0f).getValue()); 1058 1059 glRasterPos2d(xpos[0], xpos[1]); 1060 glBitmap(8, 7, 0, 0, 0, 0, xbmp); 1061 glRasterPos2d(ypos[0], ypos[1]); 1062 glBitmap(8, 7, 0, 0, 0, 0, ybmp); 1063 glRasterPos2d(zpos[0], zpos[1]); 1064 glBitmap(8, 7, 0, 0, 0, 0, zbmp); 1065 1066 glPixelStorei(GL_UNPACK_ALIGNMENT, unpack); 1067 glPopMatrix(); 1068 1069 // Reset original state. 1070 1071 // FIXME: are these 3 lines really necessary, as we push 1072 // GL_ALL_ATTRIB_BITS at the start? 20000604 mortene. 1073 glDepthRange(depthrange[0], depthrange[1]); 1074 glMatrixMode(GL_PROJECTION); 1075 glLoadMatrixd(projectionmatrix); 1076 1077 glPopAttrib(); 1078} 1079 1080// Draw an arrow for the axis representation directly through OpenGL. 1081void 1082SoGuiExaminerViewerP::drawArrow(void) 1083{ 1084 glBegin(GL_LINES); 1085 glVertex3f(0.0f, 0.0f, 0.0f); 1086 glVertex3f(1.0f, 0.0f, 0.0f); 1087 glEnd(); 1088 glDisable(GL_CULL_FACE); 1089 glBegin(GL_TRIANGLES); 1090 glVertex3f(1.0f, 0.0f, 0.0f); 1091 glVertex3f(1.0f - 1.0f / 3.0f, +0.5f / 4.0f, 0.0f); 1092 glVertex3f(1.0f - 1.0f / 3.0f, -0.5f / 4.0f, 0.0f); 1093 glVertex3f(1.0f, 0.0f, 0.0f); 1094 glVertex3f(1.0f - 1.0f / 3.0f, 0.0f, +0.5f / 4.0f); 1095 glVertex3f(1.0f - 1.0f / 3.0f, 0.0f, -0.5f / 4.0f); 1096 glEnd(); 1097 glBegin(GL_QUADS); 1098 glVertex3f(1.0f - 1.0f / 3.0f, +0.5f / 4.0f, 0.0f); 1099 glVertex3f(1.0f - 1.0f / 3.0f, 0.0f, +0.5f / 4.0f); 1100 glVertex3f(1.0f - 1.0f / 3.0f, -0.5f / 4.0f, 0.0f); 1101 glVertex3f(1.0f - 1.0f / 3.0f, 0.0f, -0.5f / 4.0f); 1102 glEnd(); 1103} 1104 1105// ************************************************************************ 1106 1107// Rotate the camera by the given amount, then reposition it so we're 1108// still pointing at the same focal point. 1109void 1110SoGuiExaminerViewerP::reorientCamera(const SbRotation & rot) 1111{ 1112 SoCamera * cam = PUBLIC(this)->getCamera(); 1113 if (cam == NULL) return; 1114 1115 // Find global coordinates of focal point. 1116 SbVec3f direction; 1117 cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction); 1118 SbVec3f focalpoint = cam->position.getValue() + 1119 cam->focalDistance.getValue() * direction; 1120 1121 // Set new orientation value by accumulating the new rotation. 1122 cam->orientation = rot * cam->orientation.getValue(); 1123 1124 // Reposition camera so we are still pointing at the same old focal point. 1125 cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction); 1126 cam->position = focalpoint - cam->focalDistance.getValue() * direction; 1127} 1128 1129// ************************************************************************ 1130 1131// Uses the sphere sheet projector to map the mouseposition unto 1132// a 3D point and find a rotation from this and the last calculated point. 1133void 1134SoGuiExaminerViewerP::spin(const SbVec2f & pointerpos) 1135{ 1136 if (this->log.historysize < 2) return; 1137 assert(this->spinprojector != NULL); 1138 1139 SbVec2s glsize(PUBLIC(this)->getGLSize()); 1140 SbVec2f lastpos; 1141 lastpos[0] = float(this->log.position[1][0]) / float(So@Gui@Max((int)(glsize[0]-1), 1)); 1142 lastpos[1] = float(this->log.position[1][1]) / float(So@Gui@Max((int)(glsize[1]-1), 1)); 1143 1144 this->spinprojector->project(lastpos); 1145 SbRotation r; 1146 this->spinprojector->projectAndGetRotation(pointerpos, r); 1147 r.invert(); 1148 this->reorientCamera(r); 1149 1150 // Calculate an average angle magnitude value to make the transition 1151 // to a possible spin animation mode appear smooth. 1152 1153 SbVec3f dummy_axis, newaxis; 1154 float acc_angle, newangle; 1155 this->spinincrement.getValue(dummy_axis, acc_angle); 1156 acc_angle *= this->spinsamplecounter; // weight 1157 r.getValue(newaxis, newangle); 1158 acc_angle += newangle; 1159 1160 this->spinsamplecounter++; 1161 acc_angle /= this->spinsamplecounter; 1162 // FIXME: accumulate and average axis vectors aswell? 19990501 mortene. 1163 this->spinincrement.setValue(newaxis, acc_angle); 1164 1165 // Don't carry too much baggage, as that'll give unwanted results 1166 // when the user quickly trigger (as in "click-drag-release") a spin 1167 // animation. 1168 if (this->spinsamplecounter > 3) this->spinsamplecounter = 3; 1169} 1170 1171// ************************************************************************ 1172 1173// Calculate a zoom/dolly factor from the difference of the current 1174// cursor position and the last. 1175void 1176SoGuiExaminerViewerP::zoomByCursor(const SbVec2f & thispos, 1177 const SbVec2f & prevpos) 1178{ 1179 // There is no "geometrically correct" value, 20 just seems to give 1180 // about the right "feel". 1181 SoGuiFullViewerP::zoom(PUBLIC(this)->getCamera(), 1182 (thispos[1] - prevpos[1]) * 20.0f); 1183} 1184 1185// ************************************************************************* 1186// Methods used for spin animation tracking. 1187 1188// This method "clears" the mouse location log, used for spin 1189// animation calculations. 1190void 1191SoGuiExaminerViewerP::clearLog(void) 1192{ 1193 this->log.historysize = 0; 1194} 1195 1196// This method adds another point to the mouse location log, used for spin 1197// animation calculations. 1198void 1199SoGuiExaminerViewerP::addToLog(const SbVec2s pos, const SbTime time) 1200{ 1201 // In case someone changes the const size setting at the top of this 1202 // file too small. 1203 assert (this->log.size > 2 && "mouse log too small!"); 1204 1205 if (this->log.historysize > 0 && pos == this->log.position[0]) { 1206#if SO@GUI@_DEBUG && 0 // debug 1207 // This can at least happen under SoQt. 1208 SoDebugError::postInfo("SoGuiExaminerViewerP::addToLog", "got position already!"); 1209#endif // debug 1210 return; 1211 } 1212 1213 int lastidx = this->log.historysize; 1214 // If we've filled up the log, we should throw away the last item: 1215 if (lastidx == this->log.size) { lastidx--; } 1216 1217 assert(lastidx < this->log.size); 1218 for (int i = lastidx; i > 0; i--) { 1219 this->log.position[i] = this->log.position[i-1]; 1220 this->log.time[i] = this->log.time[i-1]; 1221 } 1222 1223 this->log.position[0] = pos; 1224 this->log.time[0] = time; 1225 if (this->log.historysize < this->log.size) 1226 this->log.historysize += 1; 1227} 1228 1229// ************************************************************************* 1230 1231// This method sets whether Motion3 events should affect the camera or 1232// the model. 1233void 1234SoGuiExaminerViewerP::setMotion3OnCamera(SbBool enable) 1235{ 1236 this->motion3OnCamera = enable; 1237} 1238 1239// This method returns whether Motion3 events affects the camera or 1240// the model. 1241SbBool 1242SoGuiExaminerViewerP::getMotion3OnCamera(void) const 1243{ 1244 return this->motion3OnCamera; 1245} 1246 1247// ************************************************************************ 1248 1249// Set cursor graphics according to mode. 1250void 1251SoGuiExaminerViewerP::setCursorRepresentation(int modearg) 1252{ 1253 if (!PUBLIC(this)->isCursorEnabled()) { 1254 PUBLIC(this)->setComponentCursor(So@Gui@Cursor::getBlankCursor()); 1255 return; 1256 } 1257 1258 switch (modearg) { 1259 case SoGuiExaminerViewerP::INTERACT: 1260 PUBLIC(this)->setComponentCursor(So@Gui@Cursor(So@Gui@Cursor::DEFAULT)); 1261 break; 1262 1263 case SoGuiExaminerViewerP::IDLE: 1264 case SoGuiExaminerViewerP::DRAGGING: 1265 case SoGuiExaminerViewerP::SPINNING: 1266 PUBLIC(this)->setComponentCursor(So@Gui@Cursor::getRotateCursor()); 1267 break; 1268 1269 case SoGuiExaminerViewerP::ZOOMING: 1270 PUBLIC(this)->setComponentCursor(So@Gui@Cursor::getZoomCursor()); 1271 break; 1272 1273 case SoGuiExaminerViewerP::SEEK_MODE: 1274 case SoGuiExaminerViewerP::SEEK_WAIT_MODE: 1275 PUBLIC(this)->setComponentCursor(So@Gui@Cursor(So@Gui@Cursor::CROSSHAIR)); 1276 break; 1277 1278 case SoGuiExaminerViewerP::PANNING: 1279 PUBLIC(this)->setComponentCursor(So@Gui@Cursor::getPanCursor()); 1280 break; 1281 1282 default: assert(0); break; 1283 } 1284} 1285 1286#endif // DOXYGEN_SKIP_THIS 1287 1288// ************************************************************************* 1289 1290#undef PRIVATE 1291#undef PUBLIC 1292 1293