00001 #include "Iguana/GLBrowsers/interface/Ig3DBaseBrowser.h"
00002 #include "Iguana/GLBrowsers/interface/xtypeinfo.h"
00003 #include "Iguana/GLModels/interface/Ig3DBaseModel.h"
00004 #include "Iguana/GLModels/interface/Ig3DBaseModelEvent.h"
00005 #include "Iguana/GLModels/interface/Ig3DBaseRep.h"
00006 #include "Iguana/GLBrowsers/interface/Ig3DSystem.h"
00007 #include "Iguana/Studio/interface/IgQtAppStatusBarService.h"
00008 #include "Iguana/Studio/interface/IgQtSite.h"
00009 #include "Iguana/Studio/interface/IgQtObjectMenuService.h"
00010 #include "Iguana/Studio/interface/IgQtObjectMenuMessage.h"
00011 #include "Iguana/Studio/interface/IgQtObjectMenu.h"
00012 #include "Iguana/Inventor/interface/IgSoGL2PSAction.h"
00013 #include "Iguana/Inventor/interface/IgSoGridPlane.h"
00014 #include "Iguana/Framework/interface/IgRepSet.h"
00015 #include "Iguana/Framework/interface/IgRepContext.h"
00016 #include "Iguana/Framework/interface/IgSelectionService.h"
00017 #include "Iguana/Framework/interface/IgSelectionMessage.h"
00018 #include "Iguana/Framework/interface/xtypeinfo.h"
00019 #include "Iguana/Framework/interface/IgExtensionDB.h"
00020 #include <climits>
00021 #include <qhbox.h>
00022 #include <qwidget.h>
00023 #include <qmenubar.h>
00024 #include <qpopupmenu.h>
00025 #include <qfiledialog.h>
00026 #include <qcolordialog.h>
00027 #include <qinputdialog.h>
00028 #include <qbuttongroup.h>
00029 #include <qlayout.h>
00030 #include <qtooltip.h>
00031 #include <qwhatsthis.h>
00032 #include <qvbox.h>
00033 #include <qapplication.h>
00034 #include <qmessagebox.h>
00035 #include <qdir.h>
00036 #include <qfileinfo.h>
00037 #include <qcursor.h>
00038 #include <Inventor/Qt/SoQtCursor.h>
00039 #include <Inventor/SoPath.h>
00040 #include <Inventor/SbLinear.h>
00041 #include <Inventor/SoInput.h>
00042 #include <Inventor/SoPickedPoint.h>
00043 #include <Inventor/SoOffscreenRenderer.h>
00044 #include <Inventor/nodes/SoGroup.h>
00045 #include <Inventor/nodes/SoSelection.h>
00046 #include <Inventor/nodes/SoPerspectiveCamera.h>
00047 #include <Inventor/nodes/SoOrthographicCamera.h>
00048 #include <Inventor/nodes/SoClipPlane.h>
00049 #include <Inventor/actions/SoLineHighlightRenderAction.h>
00050 #include <Inventor/actions/SoBoxHighlightRenderAction.h>
00051 #include <Inventor/actions/SoSearchAction.h>
00052 #include <Inventor/actions/SoWriteAction.h>
00053 #include <Inventor/actions/SoSearchAction.h>
00054 #include <Inventor/actions/SoGetBoundingBoxAction.h>
00055 #include <Inventor/sensors/SoFieldSensor.h>
00056 #include <Inventor/Qt/SoQt.h>
00057 #include "Iguana/GL2PS/interface/gl2ps.h"
00058
00059 #undef emit
00060 #include <classlib/utils/Callback.h>
00061 #include <classlib/utils/DebugAids.h>
00062 #include <classlib/iobase/Filename.h>
00063 #include <classlib/utils/Log.h>
00064 #include <typeinfo>
00065
00066
00067 enum {
00068 INTERACT_BUTTON = 0,
00069 EXAMINE_BUTTON,
00070 HOME_BUTTON,
00071 SET_HOME_BUTTON,
00072 VIEW_ALL_BUTTON,
00073 SEEK_BUTTON,
00074 CAMERA_BUTTON
00075 };
00076
00077 lat::logflag LF3dbrowser = { 0, "3dbrowser", true, -1 };
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087 QWidget *
00088 Ig3DBaseBrowser::initialise (IgState *state, IgSite *site)
00089 {
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100 Ig3DSystem::init (state, IgQtSite::selfFrom (site)->topLevelWidget ());
00101
00102
00103 QWidget *w = new QWidget (IgQtSite::hostFrom (site));
00104 (new QVBoxLayout (w))->setAutoAdd (true);
00105 QSize size = IgQtSite::hostFrom (site)->size();
00106 if (size.width () > 1)
00107 w->resize (size);
00108 return w;
00109 }
00110
00111
00112
00113 Ig3DBaseBrowser::Ig3DBaseBrowser (IgState *state,
00114 IgSite *site,
00115 Ig3DBaseModel *model)
00116 : SoQtExaminerViewer (initialise (state, site), "3D Browser",
00117 true, BUILD_ALL, BROWSER, false),
00118 m_state (new IgState (state)),
00119 m_model (model),
00120 m_selection (0),
00121 m_menuRep (0),
00122 m_first_time (true),
00123 m_grid (false),
00124 m_gl2psOptions (GL2PS_SIMPLE_LINE_OFFSET
00125 | GL2PS_BEST_ROOT
00126 | GL2PS_OCCLUSION_CULL),
00127 m_currentPick (0),
00128 m_whatsThisPicking (false),
00129 m_oldView (true),
00130 m_oldSeek (false),
00131 m_gl2psFBBufferSize (1024*1024),
00132 m_farDistanceSensor (0),
00133 m_nearDistanceSensor (0)
00134 {
00135 initWidget (site);
00136
00137
00138
00139 SoCamera * const camera = SoQtExaminerViewer::getCamera ();
00140
00141 ASSERT (camera);
00142 camera->farDistance = SHRT_MAX;
00143 camera->nearDistance = 0.1;
00144 m_farDistanceSensor = new SoFieldSensor (&farDistanceSensorCB, this);
00145 m_farDistanceSensor->attach (&camera->farDistance);
00146 m_nearDistanceSensor = new SoFieldSensor (&nearDistanceSensorCB, this);
00147 m_nearDistanceSensor->attach (&camera->nearDistance);
00148
00149 SoNode *node = model->attachPoint ()->findMagic (
00150 Ig3DBaseModel::encode ("Default Grid Group"));
00151 if (node && dynamic_cast<SoGroup*>(node)->getNumChildren ())
00152 m_grid = true;
00153 }
00154
00155 SbBool
00156 Ig3DBaseBrowser::eventCallback (void *closure, QEvent *event)
00157 {
00158 Ig3DBaseBrowser *self = static_cast<Ig3DBaseBrowser *> (closure);
00159 if (!self->getParentWidget()->hasMouse () ||
00160 !self->isViewing())
00161 return false;
00162
00163 if (QWheelEvent *wheelEvent = dynamic_cast<QWheelEvent *> (event))
00164 {
00165 self->zoom (0.1 * (wheelEvent->delta () > 0 ? 1:-1));
00166 return true;
00167 }
00168 else if (QMouseEvent *mEvent = dynamic_cast<QMouseEvent *> (event))
00169 {
00170 if (mEvent->button () == Qt::LeftButton
00171 && mEvent->type () == QEvent::MouseButtonRelease
00172 && self->isSeekMode())
00173 {
00174 self->setComponentCursor (SoQtCursor::getRotateCursor ());
00175 return true;
00176 }
00177 return false;
00178 }
00179 return false;
00180 }
00181
00182 void
00183 Ig3DBaseBrowser::initWidget (IgSite *site)
00184 {
00185 ASSERT (m_model);
00186
00187 IgQtSite::host (site, getParentWidget ());
00188 setBaseWidget (buildWidget (getParentWidget ()));
00189
00190
00191
00192
00193
00194
00195 ASSERT (IgSelectionService::get (m_state));
00196 IgSelectionService::get (m_state)
00197 ->add (lat::CreateCallback (this, &Ig3DBaseBrowser::selectMessage));
00198 m_model->listen (Ig3DBaseModel::ModelChanged,
00199 lat::CreateCallback (this, &Ig3DBaseBrowser::modelChanged));
00200
00201
00202
00203 SoGroup *graph = m_model->sceneGraph ();
00204 ASSERT (graph);
00205 setSceneGraph (graph);
00206
00207
00208
00209 ASSERT (graph->isOfType (SoSelection::getClassTypeId ()));
00210 SoSelection *top = static_cast<SoSelection *> (graph);
00211 top->policy.setValue (SoSelection::SINGLE);
00212
00213 redrawOnSelectionChange (top);
00214 setGLRenderAction (new SoLineHighlightRenderAction);
00215 setEventCallback (eventCallback, this);
00216
00217
00218 show ();
00219 }
00220
00221 void
00222 Ig3DBaseBrowser::focusIn (void)
00223 {
00224
00225
00226 SoSelection *top = static_cast<SoSelection *> (m_model->sceneGraph ());
00227 top->setPickFilterCallback (onPick, this);
00228 top->addSelectionCallback (onSelect, this);
00229 top->addDeselectionCallback (onDeselect, this);
00230 }
00231
00232 void
00233 Ig3DBaseBrowser::focusOut (void)
00234 {
00235 SoSelection *top = static_cast<SoSelection *> (m_model->sceneGraph ());
00236 top->removeSelectionCallback (onSelect, this);
00237 top->removeDeselectionCallback (onDeselect, this);
00238 }
00239
00243 void
00244 Ig3DBaseBrowser::createViewerButtons (QWidget* parent, SbPList* buttonlist)
00245 {
00246 SoQtExaminerViewer::createViewerButtons (parent, buttonlist);
00247
00248
00249 int nrbuttons = buttonlist->getLength();
00250 if (nrbuttons == 0) return;
00251
00252 QToolTip::add ((QWidget*) ((*buttonlist ) [INTERACT_BUTTON]), "Select/Pick");
00253 QWhatsThis::add ((QWidget*) ((*buttonlist) [INTERACT_BUTTON]),
00254 tr ("<b>Select/Pick Button:</b>\n"
00255 "Selects object manipulation or pick mode \n"
00256 "(and deselects camera or viewer mode).\n"
00257 "The cursor shape will change to an arrow.\n"
00258 "In this mode, the user is manipulating objects\n"
00259 "in the scene graph."));
00260
00261 QToolTip::add ((QWidget*) ((*buttonlist ) [EXAMINE_BUTTON]), "View");
00262 QWhatsThis::add ((QWidget*) ((*buttonlist) [EXAMINE_BUTTON]),
00263 tr ("<b>View Button:</b>\n"
00264 "Selects camera or viewer mode (and deselects object \n"
00265 "manipulation or pick mode). The cursor shape will\n"
00266 "change to a circular arrows icon. In this mode,\n"
00267 "the user is moving the camera in 3D space."));
00268
00269 QToolTip::add ((QWidget*) ((*buttonlist ) [HOME_BUTTON]), "Home");
00270 QWhatsThis::add ((QWidget*) ((*buttonlist) [HOME_BUTTON]),
00271 tr ("<b>Home Button:</b>\n"
00272 "Returns the camera to its home position\n"
00273 "(initial position if not reset)."));
00274
00275 QToolTip::add ((QWidget*) ((*buttonlist ) [SET_HOME_BUTTON]), "Set Home");
00276 QWhatsThis::add ((QWidget*) ((*buttonlist) [SET_HOME_BUTTON]),
00277 tr ("<b>Set Home Button:</b>\n"
00278 "Resets the home position to the current\n"
00279 "camera position."));
00280
00281 QToolTip::add ((QWidget*) ((*buttonlist ) [VIEW_ALL_BUTTON]), "View All");
00282 QWhatsThis::add ((QWidget*) ((*buttonlist) [VIEW_ALL_BUTTON]),
00283 tr ("<b>View All Button:</b>\n"
00284 "Brings the entire scene graph into view."));
00285
00286 QToolTip::add ((QWidget*) ((*buttonlist ) [SEEK_BUTTON]), "Seek");
00287 QWhatsThis::add ((QWidget*) ((*buttonlist) [SEEK_BUTTON]),
00288 tr ("<b>Seek Button:</b>\n"
00289 "Allows the user to select a new center\n"
00290 "of rotation for the camera. When clicked\n"
00291 "on (and in viewer mode) the cursor changes\n"
00292 "to a crosshair. The next left mouse\n"
00293 "buttonpress causes whatever is underneath\n"
00294 "the cursor to be selected as the new center\n"
00295 "of rotation. Once the button is released,\n"
00296 "the camera either jumps or animates to its\n"
00297 "new position depending on the current\n"
00298 "setting of the seek time in the preferences\n"
00299 "dialog box."));
00300
00301 QToolTip::add ((QWidget*) ((*buttonlist ) [CAMERA_BUTTON]), "Projection");
00302 QWhatsThis::add ((QWidget*) ((*buttonlist) [CAMERA_BUTTON]),
00303 tr ("<b>Projection Button:</b>\n"
00304 "Selects the type of camera used\n"
00305 "by the viewer. It toggles between\n"
00306 "the two available camera types -\n"
00307 "perspective and orthographic."));
00308 }
00309
00310 QWidget *
00311 Ig3DBaseBrowser::buildLeftTrim (QWidget *parent)
00312 {
00313 QWidget *result = new QWidget (parent);
00314 result->setFixedWidth (0);
00315
00316 return result;
00317 }
00318
00319 QWidget *
00320 Ig3DBaseBrowser::buildBottomTrim (QWidget *parent)
00321 {
00322 QWidget *result = new QWidget (parent);
00323 result->setFixedHeight (0);
00324
00325 return result;
00326 }
00327
00328 QWidget *
00329 Ig3DBaseBrowser::buildRightTrim (QWidget *parent)
00330 {
00331 QWidget *result = new QWidget (parent);
00332 result->setFixedWidth (0);
00333
00334 return result;
00335 }
00336
00337
00338 Ig3DBaseBrowser::~Ig3DBaseBrowser (void)
00339 {
00340
00341 IgSelectionService::get (m_state)
00342 ->remove (lat::CreateCallback (this, &Ig3DBaseBrowser::selectMessage));
00343 m_model->unlisten (Ig3DBaseModel::ModelChanged,
00344 lat::CreateCallback (this, &Ig3DBaseBrowser::modelChanged));
00345 }
00346
00347 IgState *
00348 Ig3DBaseBrowser::state (void) const
00349 { return m_state; }
00350
00351 Ig3DBaseModel *
00352 Ig3DBaseBrowser::model (void) const
00353 { return m_model; }
00354
00355 void
00356 Ig3DBaseBrowser::setAutoClipping (SbBool enable)
00357 {
00358 SoQtViewer::setAutoClipping (enable);
00359 if (enable)
00360 {
00361 SoCamera *camera = this->getCamera ();
00362 camera->farDistance.touch ();
00363 camera->nearDistance.touch ();
00364 }
00365 }
00366
00367 void
00368 Ig3DBaseBrowser::setFeedbackVisibility (bool enable)
00369 {
00370 bool old = isFeedbackVisible ();
00371 if (enable != old)
00372 SoQtExaminerViewer::setFeedbackVisibility (enable);
00373 }
00374
00375 void
00376 Ig3DBaseBrowser::setGridVisibility (bool enable)
00377 {
00378 Ig3DBaseBrowser::drawGrid (enable);
00379 }
00380
00381 bool
00382 Ig3DBaseBrowser::isGridVisible (void)
00383 {
00384 return m_grid;
00385 }
00386
00387 void
00388 Ig3DBaseBrowser::drawGrid (const bool enable)
00389 {
00390
00391 SoNode* grid = findGroup (model ()->attachPoint (), Ig3DBaseModel::encode ("Grid Planes").getString ());
00392 SoGroup* all = 0;
00393
00394 if (grid)
00395 {
00396
00397 all = dynamic_cast<SoGroup *> (findGroup (grid, Ig3DBaseModel::encode ("All").getString ()));
00398 }
00399
00400 if (all)
00401 {
00402 unsigned nbrChildren = all->getNumChildren ();
00403 IgSoGridPlane* plane = 0;
00404
00405
00406 for (unsigned i = 0; i < nbrChildren; i++)
00407 {
00408 plane = dynamic_cast<IgSoGridPlane* > (all->getChild (i));
00409 if (plane)
00410 {
00411 plane->on = enable;
00412 }
00413 }
00414 }
00415 m_grid = enable;
00416 }
00417
00418 void
00419 Ig3DBaseBrowser::zoomIn (void)
00420 {
00421
00422
00423 zoom (-0.5f);
00424 }
00425
00426 void
00427 Ig3DBaseBrowser::zoomOut (void)
00428 {
00429
00430
00431 zoom (0.5f);
00432 }
00433
00434 void
00435 Ig3DBaseBrowser::zoom (const float diffvalue)
00436 {
00437 SoCamera *cam = this->getCamera ();
00438 if (cam == NULL) return;
00439 SoType t = cam->getTypeId ();
00440
00441
00442 float multiplicator = exp (diffvalue);
00443
00444 if (t.isDerivedFrom (SoOrthographicCamera::getClassTypeId ()))
00445 {
00446
00447
00448
00449
00450 SoOrthographicCamera *oc = (SoOrthographicCamera *) cam;
00451 oc->height = oc->height.getValue () * multiplicator;
00452 }
00453 else if (t.isDerivedFrom (SoPerspectiveCamera::getClassTypeId ()))
00454 {
00455 float oldfocaldist = cam->focalDistance.getValue ();
00456 cam->focalDistance = oldfocaldist * multiplicator;
00457
00458 SbVec3f direction;
00459 cam->orientation.getValue ().multVec (SbVec3f (0, 0, -1), direction);
00460 cam->position = cam->position.getValue () +
00461 (cam->focalDistance.getValue () - oldfocaldist) * -direction;
00462
00463 }
00464 else
00465 {
00466 static SbBool first = TRUE;
00467 if (first)
00468 {
00469 SoDebugError::postWarning("Ig3DBaseBrowser::zoom",
00470 "unknown camera type, "
00471 "attempts to zoom will have no effect");
00472 first = FALSE;
00473 }
00474 }
00475 }
00476
00477 void
00478 Ig3DBaseBrowser::toggleCameraType (void)
00479 {
00480 SoQtExaminerViewer::toggleCameraType ();
00481 SoCamera * const camera = this->getCamera ();
00482 camera->farDistance = SHRT_MAX;
00483 camera->nearDistance = 0.1;
00484 m_farDistanceSensor->attach (&camera->farDistance);
00485 m_nearDistanceSensor->attach (&camera->nearDistance);
00486 cameraToggled();
00487 }
00488
00489 void
00490 Ig3DBaseBrowser::invertCamera (void)
00491 {
00492 SoCamera * const camera = this->getCamera ();
00493 if (!camera) return;
00494
00495 camera->position = camera->position.getValue () * -1.0F;
00496 camera->orientation.setValue (
00497 SbRotation (SbVec3f (0.F,-1.F,0.F), M_PI)
00498 * camera->orientation.getValue ());
00499 }
00500
00501 void
00502 Ig3DBaseBrowser::farDistanceSensorCB (void *me, SoSensor *)
00503 {
00504 Ig3DBaseBrowser *self = static_cast<Ig3DBaseBrowser *> (me);
00505 if (self->isAutoClipping ())
00506 {
00507 SoCamera * const camera = self->getCamera ();
00508 self->m_farDistanceSensor->detach ();
00509 camera->farDistance = SHRT_MAX;
00510 self->m_farDistanceSensor->attach (&camera->farDistance);
00511 }
00512 }
00513
00514 void
00515 Ig3DBaseBrowser::nearDistanceSensorCB (void *me, SoSensor *)
00516 {
00517 Ig3DBaseBrowser *self = static_cast<Ig3DBaseBrowser *> (me);
00518 if (self->isAutoClipping ())
00519 {
00520 SoCamera * const camera = self->getCamera ();
00521 self->m_nearDistanceSensor->detach ();
00522 camera->nearDistance = 0.1;
00523 self->m_nearDistanceSensor->attach (&camera->nearDistance);
00524 }
00525 }
00526
00527 void
00528 Ig3DBaseBrowser::browse (IgRepresentable *object)
00529 {
00530
00531 IgRep *rep = IgRepSet::lookup (object, m_model, true);
00532
00533
00534 if (rep && m_first_time)
00535 {
00536 m_first_time = false;
00537 viewAll ();
00538 }
00539
00540
00541 }
00542
00543 void
00544 Ig3DBaseBrowser::select (Ig3DBaseRep *rep)
00545 {
00546
00547 if (m_selection != rep)
00548 {
00549 m_selection = rep;
00550
00551 LOG(0, trace, LF3dbrowser, "changing selection to " << rep << '\n');
00552 ASSERT(m_model->sceneGraph()->isOfType(SoSelection::getClassTypeId()));
00553 SoSelection *top = static_cast<SoSelection *>
00554 (m_model->sceneGraph ());
00555 SoNode *node = rep;
00556 SoPathList *selected = const_cast<SoPathList *> (top->getList ());
00557 bool alreadySelected = false;
00558 int path = 0;
00559
00560 LOG(0, trace, LF3dbrowser, " -- looping over selected items\n");
00561
00562 while (node && ! alreadySelected && path < selected->getLength ())
00563 alreadySelected = (*selected) [path++]->containsNode (node);
00564
00565 if (! node)
00566 top->deselectAll ();
00567 else if (! alreadySelected)
00568 {
00569
00570 SoSearchAction finder;
00571 finder.setNode (node);
00572 finder.setFind (SoSearchAction::NODE);
00573 finder.apply (top);
00574
00575 if (finder.getPath ())
00576 {
00577 LOG(0, trace, LF3dbrowser, " -- selecting object\n");
00578
00579
00580
00581
00582
00583 LOG(0, trace, LF3dbrowser, " -- firing callback\n");
00584 top->deselectAll ();
00585 top->select (finder.getPath ());
00586 }
00587 }
00588 }
00589 }
00590
00591 Ig3DBaseRep *
00592 Ig3DBaseBrowser::getCurrentPick (void) const
00593 {
00594 return m_currentPick;
00595 }
00596
00597 Ig3DBaseRep *
00598 Ig3DBaseBrowser::getSelection (void) const
00599 {
00600 return m_selection;
00601 }
00602
00603 void
00604 Ig3DBaseBrowser::setCurrentPick (Ig3DBaseRep *rep)
00605 {
00606
00607
00608 m_selection = rep;
00609
00610
00611 }
00612
00613 const SoPickedPoint *
00614 Ig3DBaseBrowser::getCurrentPickPoint (void) const
00615 {
00616 return m_pick;
00617 }
00618
00619 void
00620 Ig3DBaseBrowser::setCurrentPickPoint (const SoPickedPoint *pick)
00621 {
00622
00623 m_pick = pick;
00624 }
00625
00627 SoPath *
00628 Ig3DBaseBrowser::onPick (void *cb, const SoPickedPoint *pick)
00629 {
00630
00631 Ig3DBaseBrowser *self = static_cast<Ig3DBaseBrowser *> (cb);
00632
00633
00634 self->setCurrentPickPoint (pick);
00635
00636
00637 SoPath *selection = pick->getPath ();
00638 const int length = selection->getLength ();
00639 int index = 0;
00640 Ig3DBaseRep *match = 0;
00641 for ( ; index < length; ++index)
00642 {
00643
00644 SoNode *node = selection->getNodeFromTail (index);
00645 LOG(0, trace, LF3dbrowser, "pick node [-" << index << "] = "
00646 << node->getName ().getString () << '\n');
00647
00648 if ((match = Ig3DBaseRep::asRep (node)))
00649 break;
00650 }
00651
00652
00653 LOG(0, trace, LF3dbrowser, "pick --> " << index << " of " << length);
00654 if (index < length && self->getSelection () != match)
00655 {
00656 LOG(0, trace, LF3dbrowser, ": match and different from previous\n");
00657 return selection->copy (0, length - index);
00658 }
00659 else
00660 {
00661 LOG(0, trace, LF3dbrowser, ": no match or same as previously\n");
00662 return 0;
00663 }
00664 }
00665
00666 void
00667 Ig3DBaseBrowser::onSelect (void *cb, SoPath *selection)
00668 {
00669
00670
00671 ASSERT (selection);
00672 ASSERT (selection->getLength () > 0);
00673
00674 LOG(0, trace, LF3dbrowser, "select: " << selection->getLength () << '\n');
00675 Ig3DBaseBrowser *self = static_cast<Ig3DBaseBrowser *> (cb);
00676 Ig3DBaseRep *match = Ig3DBaseRep::asRep (selection->getNodeFromTail (0));
00677 ASSERT (match);
00678
00679 self->setCurrentPick (match);
00680 IgSelectionService::get (self->m_state)
00681 ->broadcast (IgSelectionMessage (match->context ()));
00682 }
00683
00684 void
00685 Ig3DBaseBrowser::onDeselect (void *cb, SoPath *selection)
00686 {
00687
00688 LOG(0, trace, LF3dbrowser, "deselect: " << selection->getLength () << '\n');
00689 Ig3DBaseBrowser *self = static_cast<Ig3DBaseBrowser *> (cb);
00690 Ig3DBaseRep *match = Ig3DBaseRep::asRep (selection->getNodeFromTail (0));
00691 ASSERT (match);
00692 self->setCurrentPick (0);
00693 IgSelectionService::get (self->m_state)
00694 ->broadcast (IgSelectionMessage (0));
00695 }
00696
00698 void
00699 Ig3DBaseBrowser::selectMessage (IgSelectionMessage message)
00700 {
00701 if (! message.context ())
00702 select (0);
00703 else if (Ig3DBaseRep *rep = dynamic_cast<Ig3DBaseRep *>
00704 (IgRepSet::lookup (message.context (), m_model, true)))
00705 select (rep);
00706 }
00707
00709 void
00710 Ig3DBaseBrowser::modelChanged (Ig3DBaseModelEvent)
00711 {
00712 getSceneManager ()->scheduleRedraw ();
00713 }
00714
00715
00717 void
00718 Ig3DBaseBrowser::save (void)
00719 { saveNode (m_model->sceneGraph (), "Save Scene As...", getShellWidget ()); }
00720
00721 void
00722 Ig3DBaseBrowser::print (void)
00723 {
00724 QString vector2EPS ("Vector EPS [Level 2] (*.eps)");
00725 QString vector3EPS ("Vector EPS [Level 3] (*.eps)");
00726 QString vectorPDF ("Portable Document Format (*.pdf)");
00727 QStringList filters (vector2EPS);
00728 filters.append (vector3EPS);
00729 filters.append (vectorPDF);
00730
00731 SoOffscreenRenderer *renderer =
00732 new SoOffscreenRenderer (this->getViewportRegion ());
00733
00734 int num =renderer->getNumWriteFiletypes();
00735
00736 if (num == 0)
00737 {
00738 filters.append ("Encapsulated postscript (*.eps)");
00739 filters.append ("Encapsulated postscript (*.ps)");
00740 filters.append ("The SGI RGB file format (*.rgb)");
00741 filters.append ("The SGI RGB file format (*.rgba)");
00742 filters.append ("The SGI RGB file format (*.bw)");
00743 filters.append ("The SGI RGB file format (*.inta)");
00744 filters.append ("The SGI RGB file format (*.int)");
00745 }
00746 else
00747 {
00748 for (int i=0; i < num; i++)
00749 {
00750 SbPList extlist;
00751 SbString fullname, description;
00752 renderer->getWriteFiletypeInfo(i, extlist, fullname, description);
00753 QString filter (fullname.getString());
00754 filter+=" (*.";
00755 for (int j=0; j < extlist.getLength(); j++)
00756 filters.append (filter+(const char*)extlist[j]+")");
00757 }
00758 }
00759 delete renderer;
00760
00761
00762 QFileDialog dialog (getShellWidget (), "Print To File", true);
00763 dialog.setFilters (filters);
00764 dialog.setMode (QFileDialog::AnyFile);
00765 bool tryagain = true;
00766 QString f;
00767 while (tryagain)
00768 {
00769 if (dialog.exec () != QDialog::Accepted)
00770 return;
00771
00772 f = dialog.selectedFile ();
00773 if (f.isEmpty ())
00774 return;
00775 else
00776 {
00777 lat::Filename sealf (f.latin1());
00778 QString dir (sealf.directory().name());
00779 if (sealf.exists ())
00780 {
00781 LOG(0, trace, LF3dbrowser, QString(f+": File already exists.\n").latin1());
00782 int button = QMessageBox::warning (getShellWidget (), "File already exists",
00783 "File \""+f+"\" already exists.\n"
00784 "Do you want to overwrite it?",
00785 "Yes", "No");
00786 if (button == 0)
00787 {
00788 if (!sealf.isWritable ())
00789 {
00790 LOG(0, trace, LF3dbrowser, QString(f+": File not write able.\n").latin1());
00791 int button = QMessageBox::warning (getShellWidget (), "Access denied",
00792 "File \""+f+"\" not write able.\n"
00793 "Do you want to select some other file?",
00794 "Yes", "No");
00795 if (button == 1)
00796 return;
00797 }
00798 else
00799 tryagain = false;
00800 }
00801 }
00802 else if (!lat::Filename(dir.latin1()).isWritable ())
00803 {
00804 LOG(0, trace, LF3dbrowser, QString(dir+": Directory not write able.\n").latin1());
00805 int button = QMessageBox::warning (getShellWidget (), "Access denied",
00806 "You do not have permissions to write in \""+dir+"\" directory.\n"
00807 "Do you want to select some other file?",
00808 "Yes", "No");
00809 if (button == 1)
00810 return;
00811 }
00812 else
00813 tryagain = false;
00814 }
00815 }
00816
00817 float ppi = SoOffscreenRenderer::getScreenPixelsPerInch ();
00818
00819 float dpi = ppi;
00820 QString format ("jpg");
00821
00822 QString sfilter = dialog.selectedFilter ();
00823 int extIndexStart = sfilter.findRev(".")+1;
00824 int extIndexEnd = sfilter.findRev(")");
00825 format = sfilter.mid (extIndexStart, extIndexEnd-extIndexStart);
00826
00827
00828 QString suffix ("." + format);
00829 if (f.find (suffix, -(format.length()+1)) == -1)
00830 f += suffix;
00831
00832
00833 QDir::setCurrent (QFileInfo (f).dirPath ());
00834
00835 QApplication::setOverrideCursor (Qt::waitCursor);
00836 if (sfilter == vector2EPS)
00837 printVector (f, format, 2);
00838 else if (sfilter == vector3EPS)
00839 printVector (f, format, 3);
00840 else if (sfilter == vectorPDF)
00841 printVector (f, format, 0);
00842 else
00843 printBitmap (f, ppi, dpi, format);
00844 QApplication::restoreOverrideCursor ();
00845 }
00846
00848 void
00849 Ig3DBaseBrowser::printBitmap (QString file, float ppi,
00850 float dpi, QString format)
00851 {
00852
00853
00854 float r, g, b;
00855 SbViewportRegion outvr = this->getViewportRegion ();
00856
00857 SbVec2s pixels (outvr.getViewportSizePixels ());
00858 SbVec2s size ((short) (pixels [0] * dpi / ppi + 0.5), (short) (pixels [1] * dpi / ppi + 0.5));
00859 SbVec2s origin = outvr.getViewportOriginPixels();
00860 outvr.setViewportPixels (origin, size);
00861
00862
00863
00864
00865
00866
00867 SoGLRenderAction *ra = new SoGLRenderAction (outvr);
00868 SoOffscreenRenderer *renderer = new SoOffscreenRenderer (outvr);
00869
00870
00871 getSceneManager ()->getBackgroundColor ().getValue (r, g, b);
00872 renderer->setBackgroundColor (SbColor (r, g, b));
00873 renderer->setGLRenderAction (ra);
00874 ra->setTransparencyType (SoGLRenderAction::SORTED_OBJECT_BLEND);
00875 ra->setSmoothing (TRUE);
00876 ra->setNumPasses (8);
00877
00878
00879
00880
00881 SoNode *root = getSceneManager ()->getSceneGraph ();
00882
00883 SbBool ok = renderer->render (root);
00884 if (!ok)
00885 {
00886 QMessageBox::information (0, "IGUANA Print Info",
00887 tr ("Printing of the %1 format works only\n"
00888 "if you run locally installed software\n"
00889 "If iguana is running remotely, please,\n"
00890 "print as vector Postscript.").arg (file.right (3)));
00891 }
00892 else if (!renderer->writeToFile (file.latin1 (), format.latin1 ()))
00893 {
00894 LOG(0, trace, LF3dbrowser,
00895 QString(file + ": Failed to open file for writing.\n").latin1());
00896 QMessageBox::warning (getShellWidget (), "System Error",
00897 "Failed to open file \""+file+"\" for writing.",
00898 "Ok");
00899 }
00900 delete renderer;
00901 delete ra;
00902 }
00903
00904 void
00905 Ig3DBaseBrowser::printVector (QString file, QString format, int level)
00906 {
00907
00908
00909 static IgSoGL2PSAction *gl2psAction = 0;
00910 if (FILE *output = fopen (file.utf8 (), "wb+"))
00911 {
00912 int type = GL2PS_EPS;
00913 if (format == "pdf")
00914 type = GL2PS_PDF;
00915 else if (format == "eps")
00916 type = GL2PS_EPS;
00917 else
00918 ASSERT (0);
00919
00920 if (! gl2psAction )
00921 gl2psAction = new IgSoGL2PSAction (this->getViewportRegion ());
00922
00923 gl2psAction->setViewportRegion (this->getViewportRegion ());
00924 SoGLRenderAction* prevAction = getGLRenderAction ();
00925 setGLRenderAction (gl2psAction);
00926
00927 int state = GL2PS_OVERFLOW;
00928 while (state == GL2PS_OVERFLOW)
00929 {
00930 int gl2psOptions = GL2PS_SILENT | GL2PS_USE_CURRENT_VIEWPORT
00931 | (level < 3 ? GL2PS_NO_PS3_SHADING : 0)
00932 | getGL2PSOptions ();
00933 gl2psBeginPage ("IGUANA Scene Graph", "IGUANA", NULL,
00934 type, GL2PS_BSP_SORT,
00935 gl2psOptions,
00936 GL_RGBA, 0, NULL,0, 0, 0,
00937 m_gl2psFBBufferSize, output, NULL);
00938 actualRedraw ();
00939 state = gl2psEndPage();
00940 if (state == GL2PS_OVERFLOW)
00941 m_gl2psFBBufferSize += 1024*1024;
00942 }
00943 fclose (output);
00944 setGLRenderAction (prevAction);
00945 }
00946 else
00947 {
00948 LOG(0, trace, LF3dbrowser, QString(file +": Failed to open file for writing.\n").latin1());
00949 QMessageBox::warning (getShellWidget (), "System Error",
00950 "Failed to open file \""+file+"\" for writing.",
00951 "Ok");
00952 }
00953 }
00954
00955 int
00956 Ig3DBaseBrowser::getGL2PSOptions (void)
00957 { return m_gl2psOptions; }
00958
00959 void
00960 Ig3DBaseBrowser::setGL2PSOptions (int options)
00961 { m_gl2psOptions = options; }
00962
00964 void
00965 Ig3DBaseBrowser::repMenu (IgQtObjectMenuMessage message)
00966 {
00967 static const IgQtObjectMenu::ItemDef defs [] = {
00968 { -1, MENU_3D_OPS, 0, 0, -1 },
00969 { -1, MENU_SEEKTO, "Seek To", SLOT(repSeekTo()), -1 },
00970 };
00971 static const int ndefs = sizeof (defs)/sizeof (defs [0]);
00972 IgRepresentable *object = message.object ();
00973
00974 m_menuRep = dynamic_cast<Ig3DBaseRep *>
00975 (IgRepSet::lookup (object, m_model, false));
00976
00977 message.menu ()->removeFromDefs (defs, ndefs);
00978 if (m_menuRep)
00979
00980 message.menu ()->insertFromDefs (this, defs, ndefs);
00981
00982
00983
00984
00985
00986
00987
00988
00989 }
00990
00991 void
00992 Ig3DBaseBrowser::repSeekTo (void)
00993 {
00994 ASSERT (m_menuRep);
00995
00996
00997 SoGetBoundingBoxAction bbaction (getViewportRegion ());
00998 SoSearchAction search;
00999 search.setNode (m_menuRep);
01000 search.apply (m_model->sceneGraph ());
01001 ASSERT (search.getPath ());
01002 bbaction.apply (search.getPath ());
01003
01004
01005
01006 SoCamera *camera = getCamera ();
01007 SbBox3f bbox = bbaction.getBoundingBox ();
01008 SbVec3f hitpoint = bbox.getCenter ();
01009 SbVec3f here = camera->position.getValue ();
01010 SbVec3f dir = hitpoint - here;
01011 SbVec3f olddir;
01012 float fd = (bbox.getMin () - bbox.getMax ()).length () / 2;
01013
01014 dir.normalize ();
01015
01016 camera->orientation.getValue ().multVec (SbVec3f (0, 0, -1), olddir);
01017 SbRotation diffrot (olddir, dir);
01018
01019
01020 camera->focalDistance = fd;
01021 camera->orientation.setValue (camera->orientation.getValue () * diffrot);
01022 camera->position.setValue (hitpoint - fd * dir);
01023 }
01024
01025 void
01026 Ig3DBaseBrowser::leftWheelPressed (void)
01027 { leftWheelStart (); }
01028
01029 void
01030 Ig3DBaseBrowser::leftWheelChanged (float by)
01031 { leftWheelMotion (by); }
01032
01033 void
01034 Ig3DBaseBrowser::leftWheelReleased (void)
01035 { leftWheelFinish (); }
01036
01037
01038
01039 Ig3DBaseRep *
01040 Ig3DBaseBrowser::menuRep (void)
01041 { return m_menuRep; }
01042
01043 void
01044 Ig3DBaseBrowser::resetToHomePosition (void)
01045 {
01046 SoQtViewer::resetToHomePosition ();
01047 }
01048
01049 void
01050 Ig3DBaseBrowser::saveHomePosition (void)
01051 {
01052 SoQtViewer::saveHomePosition ();
01053 }
01054
01055 void
01056 Ig3DBaseBrowser::viewAll (void)
01057 {
01058 SoQtViewer::viewAll ();
01059 }
01060
01061 void
01062 Ig3DBaseBrowser::seek (void)
01063 {
01064 if (SoQtViewer::isSeekMode())
01065 {
01066 SoQtViewer::setSeekMode (false);
01067 setComponentCursor (SoQtCursor::getRotateCursor ());
01068 }
01069 else
01070 {
01071 SoQtViewer::setSeekMode (true);
01072 if (isAnimating ()) { stopAnimating (); }
01073 setComponentCursor(SoQtCursor(SoQtCursor::CROSSHAIR));
01074 }
01075 }
01076
01077 void
01078 Ig3DBaseBrowser::view (void)
01079 {
01080 if (isViewing () != true)
01081 {
01082 m_whatsThisPicking = false;
01083 SoQtViewer::setViewing (true);
01084 setComponentCursor (SoQtCursor::getRotateCursor ());
01085 }
01086 }
01087
01088 void
01089 Ig3DBaseBrowser::pick (void)
01090 {
01091 if (isViewing () != false)
01092 {
01093 SoQtViewer::setViewing (false);
01094 SoQtViewer::setSeekMode (false);
01095 setComponentCursor(SoQtCursor(SoQtCursor::DEFAULT));
01096 }
01097 }
01098
01099 void
01100 Ig3DBaseBrowser::autoPrint (void)
01101 {
01102 autoPrint (this->getTitle ());
01103 }
01104
01105 void
01106 Ig3DBaseBrowser::autoPrint (const std::string text)
01107 {
01108 QDateTime dt = QDateTime::currentDateTime ();
01109 QString fName = "screenShot-" + dt.toString ("hh:mm:ss.zzz-dd.MM.yyyy") + ".png";
01110 QString dName = "screenShot-" + dt.toString ("hh:mm:ss.zzz-dd.MM.yyyy") + ".date";
01111
01112 SbColor c = getBackgroundColor ();
01113 SoOffscreenRenderer osr (this->getViewportRegion ());
01114 osr.setBackgroundColor (c);
01115 SoNode *root = getSceneManager ()->getSceneGraph ();
01116 SbBool ok = osr.render (root);
01117
01118 if (!ok) { return; }
01119
01120 ok = osr.writeToFile (fName.latin1 (), "png");
01121 if (!ok) { return; }
01122
01123 dt = QDateTime::currentDateTime ();
01124 QFile file (dName);
01125 if (file.open (IO_WriteOnly))
01126 {
01127 QTextStream stream (&file);
01128 stream << dt.toString ("ddd MMM d hh:mm:ss.zzz yyyy") << "\n";
01129 stream << text << "\n";
01130 file.close ();
01131 }
01132 }
01133
01134 void
01135 Ig3DBaseBrowser::viewPlaneX (void)
01136 {
01137 SoCamera * const camera = this->getCamera ();
01138 if (!camera) return;
01139
01140 camera->position = SbVec3f(-1,0,0) * camera->position.getValue().length();
01141 camera->orientation = SbRotation(SbVec3f(0,1,0),-M_PI/2.f);
01142 }
01143
01144 void
01145 Ig3DBaseBrowser::viewPlaneY (void)
01146 {
01147 SoCamera * const camera = this->getCamera ();
01148 if (!camera) return;
01149
01150 SbVec3f norient = SbVec3f(0,-1,0);
01151 camera->position = -norient * camera->position.getValue().length();
01152 camera->orientation = SbRotation(SbVec3f(0,0,1),-M_PI/2.f) *
01153 SbRotation(SbVec3f(0,0,-1),norient);
01154 }
01155
01156 void
01157 Ig3DBaseBrowser::viewPlaneZ (void)
01158 {
01159 SoCamera * const camera = this->getCamera ();
01160 if (!camera) return;
01161
01162 camera->position = SbVec3f(0,0,1) * camera->position.getValue().length();
01163 camera->orientation = SbRotation::identity();
01164 }
01165
01166 bool
01167 Ig3DBaseBrowser::saveNode (SoNode *node, const QString& title,
01168 QWidget* parent , const char* file )
01169 {
01170 QString f = "";
01171 bool binaryfile = false;
01172
01173 if (file == 0)
01174 {
01175 QFileDialog dialog (QString::null, QString::null, parent,
01176 title, true);
01177 QString binary ("Binary OIV Files (*.iv)");
01178 QString ascii ("ASCII OIV Files (*.iv)");
01179 QStringList filters (ascii);
01180 filters.append (binary);
01181 dialog.setFilters (filters);
01182 dialog.setMode (QFileDialog::AnyFile);
01183
01184 bool tryagain = true;
01185 while (tryagain)
01186 {
01187 if (dialog.exec () != QDialog::Accepted)
01188 return false;
01189
01190 f = dialog.selectedFile ();
01191 if (f.isEmpty ())
01192 return false;
01193 else
01194 {
01195 lat::Filename sealf (f.latin1());
01196 if (sealf.exists ())
01197 {
01198 int button = QMessageBox::warning (parent, "File already exists",
01199 "File \""+f+"\" already exists.\n"
01200 "Do you want to overwrite it?",
01201 "Yes", "No");
01202 if (button == 0)
01203 {
01204 if (!sealf.isWritable ())
01205 {
01206 int button = QMessageBox::warning (parent, "Access denied",
01207 "File \""+f+"\" not write able.\n"
01208 "Do you want to select some other file?",
01209 "Yes", "No");
01210 if (button == 1)
01211 return false;
01212 }
01213 else
01214 tryagain = false;
01215 }
01216 }
01217 else if (!lat::Filename(dialog.dirPath ().latin1()).isWritable ())
01218 {
01219 int button = QMessageBox::warning (parent, "Access denied",
01220 "You do not have permissions to write in \""+dialog.dirPath ()+"\" directory.\n"
01221 "Do you want to select some other file?",
01222 "Yes", "No");
01223 if (button == 1)
01224 return false;
01225 }
01226 else
01227 tryagain = false;
01228 }
01229 }
01230 if (dialog.selectedFilter () == binary)
01231 binaryfile = true;
01232 }
01233 else
01234 f = file;
01235
01236 if (! (f.length () > 3 && f.find (".iv", -3) != -1))
01237 f += ".iv";
01238
01239 QDir::setCurrent (QFileInfo (f).dirPath ());
01240 return writeNode (node, f.utf8 (), binaryfile);
01241 }
01242
01243 bool
01244 Ig3DBaseBrowser::writeNode (SoNode *node, const QString& file, bool binary,
01245 QWidget* parent )
01246 {
01247 SoOutput out;
01248 QApplication::setOverrideCursor (Qt::waitCursor);
01249 bool ret = false;
01250 if (out.openFile (file.utf8 ()))
01251 {
01252 out.setBinary (binary);
01253 SoWriteAction writer (&out);
01254 writer.apply (node);
01255 ret = true;
01256 }
01257 else
01258 {
01259 QMessageBox::warning (parent, "System Error",
01260 "Failed to open file \""+file+"\" for writing."
01261 "Ok");
01262 }
01263 QApplication::restoreOverrideCursor ();
01264 return ret;
01265 }
01266
01267 SoNode*
01268 Ig3DBaseBrowser::openNode (const QString& nodeName, const QString& title,
01269 QWidget* parent , const char* file )
01270 {
01271
01272
01273
01274
01275
01276
01277 QString filename = "";
01278 if (file != 0)
01279 {
01280 filename = file;
01281 }
01282 else
01283 {
01284 filename =
01285 QFileDialog::getOpenFileName ("./",
01286 "Open Inventor files (*.iv)",
01287 0,
01288 QString::null,
01289 0,
01290 0,
01291 title);
01292 }
01293
01294 if (filename != QString::null && !filename.isEmpty())
01295 {
01296 SoNode *node = 0;
01297 SoInput file;
01298 if (! file.openFile (filename.latin1 ())
01299 || ! SoDB::read (&file, node)
01300 || ! node)
01301 {
01302 QMessageBox::warning (parent,
01303 "File Access Error",
01304 "Can not open file \""+ filename + "\" for reading.", "Ok");
01305 return 0;
01306 }
01307 QDir::setCurrent (QFileInfo (filename).dirPath ());
01308
01309 if (nodeName.isEmpty ()) return node;
01310
01311 node = findGroup (node, nodeName.latin1 ());
01312
01313 if (!node)
01314 QMessageBox::warning (parent,
01315 "Wrong file",
01316 "Can not find node \""+nodeName+"\" in file \""+ filename + "\".", "Ok");
01317 return node;
01318 }
01319 return 0;
01320 }
01321
01322 SoNode*
01323 Ig3DBaseBrowser::findGroup (SoNode *node, const char* name)
01324 {
01325 if (node->isOfType(SoGroup::getClassTypeId()))
01326 {
01327 if (node->getName () == name)
01328 return node;
01329 else
01330 {
01331 SoGroup * group = dynamic_cast<SoGroup*>(node);
01332 int count = group->getNumChildren ();
01333 for (int i = 0; i < count; i++)
01334 {
01335 SoNode *n = findGroup (group->getChild(i), name);
01336 if (n) return n;
01337 }
01338 }
01339 }
01340 return 0;
01341 }
01342
01343 bool
01344 Ig3DBaseBrowser::isWhatsThisPicking (void)
01345 { return m_whatsThisPicking; }
01346
01347 void
01348 Ig3DBaseBrowser::setWhatsThisPicking (bool enable )
01349 {
01350 if (m_whatsThisPicking != enable)
01351 {
01352 m_whatsThisPicking = enable;
01353 if (enable)
01354 {
01355 m_oldView = isViewing ();
01356 m_oldSeek = isSeekMode();
01357 pick ();
01358 getGLWidget ()->setCursor (QCursor (Qt::WhatsThisCursor));
01359 }
01360 else
01361 {
01362 if (m_oldView)
01363 {
01364 view ();
01365 if (m_oldSeek)
01366 seek ();
01367 }
01368 else
01369 {
01370 pick ();
01371 setComponentCursor(SoQtCursor(SoQtCursor::DEFAULT));
01372 }
01373 }
01374 }
01375 }