Geant4 Cross Reference

Cross-Referencing   Geant4
Geant4/visualization/OpenInventor/src/G4OpenInventorQtExaminerViewer.cc

Version: [ ReleaseNotes ] [ 1.0 ] [ 1.1 ] [ 2.0 ] [ 3.0 ] [ 3.1 ] [ 3.2 ] [ 4.0 ] [ 4.0.p1 ] [ 4.0.p2 ] [ 4.1 ] [ 4.1.p1 ] [ 5.0 ] [ 5.0.p1 ] [ 5.1 ] [ 5.1.p1 ] [ 5.2 ] [ 5.2.p1 ] [ 5.2.p2 ] [ 6.0 ] [ 6.0.p1 ] [ 6.1 ] [ 6.2 ] [ 6.2.p1 ] [ 6.2.p2 ] [ 7.0 ] [ 7.0.p1 ] [ 7.1 ] [ 7.1.p1 ] [ 8.0 ] [ 8.0.p1 ] [ 8.1 ] [ 8.1.p1 ] [ 8.1.p2 ] [ 8.2 ] [ 8.2.p1 ] [ 8.3 ] [ 8.3.p1 ] [ 8.3.p2 ] [ 9.0 ] [ 9.0.p1 ] [ 9.0.p2 ] [ 9.1 ] [ 9.1.p1 ] [ 9.1.p2 ] [ 9.1.p3 ] [ 9.2 ] [ 9.2.p1 ] [ 9.2.p2 ] [ 9.2.p3 ] [ 9.2.p4 ] [ 9.3 ] [ 9.3.p1 ] [ 9.3.p2 ] [ 9.4 ] [ 9.4.p1 ] [ 9.4.p2 ] [ 9.4.p3 ] [ 9.4.p4 ] [ 9.5 ] [ 9.5.p1 ] [ 9.5.p2 ] [ 9.6 ] [ 9.6.p1 ] [ 9.6.p2 ] [ 9.6.p3 ] [ 9.6.p4 ] [ 10.0 ] [ 10.0.p1 ] [ 10.0.p2 ] [ 10.0.p3 ] [ 10.0.p4 ] [ 10.1 ] [ 10.1.p1 ] [ 10.1.p2 ] [ 10.1.p3 ] [ 10.2 ] [ 10.2.p1 ] [ 10.2.p2 ] [ 10.2.p3 ] [ 10.3 ] [ 10.3.p1 ] [ 10.3.p2 ] [ 10.3.p3 ] [ 10.4 ] [ 10.4.p1 ] [ 10.4.p2 ] [ 10.4.p3 ] [ 10.5 ] [ 10.5.p1 ] [ 10.6 ] [ 10.6.p1 ] [ 10.6.p2 ] [ 10.6.p3 ] [ 10.7 ] [ 10.7.p1 ] [ 10.7.p2 ] [ 10.7.p3 ] [ 10.7.p4 ] [ 11.0 ] [ 11.0.p1 ] [ 11.0.p2 ] [ 11.0.p3, ] [ 11.0.p4 ] [ 11.1 ] [ 11.1.1 ] [ 11.1.2 ] [ 11.1.3 ] [ 11.2 ] [ 11.2.1 ] [ 11.2.2 ] [ 11.3.0 ]

  1 //
  2 // ********************************************************************
  3 // * License and Disclaimer                                           *
  4 // *                                                                  *
  5 // * The  Geant4 software  is  copyright of the Copyright Holders  of *
  6 // * the Geant4 Collaboration.  It is provided  under  the terms  and *
  7 // * conditions of the Geant4 Software License,  included in the file *
  8 // * LICENSE and available at  http://cern.ch/geant4/license .  These *
  9 // * include a list of copyright holders.                             *
 10 // *                                                                  *
 11 // * Neither the authors of this software system, nor their employing *
 12 // * institutes,nor the agencies providing financial support for this *
 13 // * work  make  any representation or  warranty, express or implied, *
 14 // * regarding  this  software system or assume any liability for its *
 15 // * use.  Please see the license in the file  LICENSE  and URL above *
 16 // * for the full disclaimer and the limitation of liability.         *
 17 // *                                                                  *
 18 // * This  code  implementation is the result of  the  scientific and *
 19 // * technical work of the GEANT4 collaboration.                      *
 20 // * By using,  copying,  modifying or  distributing the software (or *
 21 // * any work based  on the software)  you  agree  to acknowledge its *
 22 // * use  in  resulting  scientific  publications,  and indicate your *
 23 // * acceptance of all terms of the Geant4 Software license.          *
 24 // ********************************************************************
 25 //
 26 
 27 // Frederick Jones TRIUMF 07 January 2018
 28 
 29 #include "G4OpenInventorQtExaminerViewer.hh"
 30 
 31 #include "ui_OIQtListsDialog.h"
 32 
 33 #include "saveViewPt.h"
 34 #include "pickext.h"
 35 #include "pickref.h"
 36 #include "wireframe.h"
 37 
 38 #include <algorithm> // For using sort on a vector
 39 
 40 #include "G4ios.hh"
 41 #include "G4UImanager.hh"
 42 #include "G4UIQt.hh"
 43 
 44 #include <Inventor/Qt/SoQt.h>
 45 #include <Inventor/Qt/SoQtCursor.h>
 46 #include <Inventor/events/SoKeyboardEvent.h>
 47 #include <Inventor/events/SoMouseButtonEvent.h>
 48 #include <Inventor/events/SoLocation2Event.h>
 49 #include <Inventor/nodes/SoSeparator.h>
 50 #include <Inventor/nodes/SoOrthographicCamera.h>
 51 #include <Inventor/nodes/SoPerspectiveCamera.h>
 52 
 53 // FWJ moved to header file
 54 //#include <Inventor/nodes/SoEventCallback.h>
 55 #include <Inventor/nodes/SoLineSet.h>
 56 #include <Inventor/nodes/SoMaterial.h>
 57 #include <Inventor/errors/SoDebugError.h>
 58 #include <Inventor/SoPickedPoint.h>
 59 #include <Inventor/actions/SoWriteAction.h>
 60 #include <Inventor/projectors/SbPlaneProjector.h>
 61 
 62 #include <Inventor/sensors/SoTimerSensor.h>   // Animation
 63 #include <Inventor/sensors/SoNodeSensor.h>    // Detect start of run
 64 
 65 #include "Geant4_SoPolyhedron.h"
 66 #include "G4TrajectoryPoint.hh"
 67 #include "G4AttHolder.hh"
 68 #include "G4AttCheck.hh"
 69 #if 0x060000 <= QT_VERSION
 70 #include "G4StateManager.hh"
 71 #endif
 72 
 73 #include <Inventor/nodes/SoCallback.h>
 74 #include <Inventor/nodes/SoSwitch.h>
 75 #include <Inventor/nodes/SoScale.h>
 76 #include <Inventor/nodes/SoTranslation.h>
 77 #include <Inventor/actions/SoSearchAction.h>
 78 #include <Inventor/actions/SoGetBoundingBoxAction.h>
 79 
 80 #include <Inventor/nodes/SoCoordinate3.h>
 81 // For rendering distance during animation:
 82 #include <Inventor/nodes/SoText2.h>
 83 #include <Inventor/nodes/SoFont.h>
 84 #include <Inventor/nodes/SoPointSet.h>
 85 #include <Inventor/nodes/SoDrawStyle.h>
 86 #include <Inventor/nodes/SoBaseColor.h>
 87 
 88 // For searching for nodes within kits:
 89 #include <Inventor/nodekits/SoBaseKit.h>
 90 
 91 #include <QMenuBar>
 92 #include <QPushButton>
 93 #include <QRadioButton>
 94 #include <QToolButton>
 95 #include <QListWidget>
 96 #include <QListWidgetItem>
 97 #include <QInputDialog>
 98 #include <QMessageBox>
 99 #include <QFileDialog>
100 #include <QStyle>
101 #include <QCommonStyle>
102 //#include <QMainWindow>
103 
104 #ifndef G4GMAKE
105 #include "moc_G4OpenInventorQtExaminerViewer.cpp"
106 #endif
107 
108 #define G4warn G4cout
109 
110 #if QT_VERSION < 0x060000
111 G4OpenInventorQtExaminerViewer* G4OpenInventorQtExaminerViewer::viewer = 0;
112 #endif
113 
114 #define MIN_SPEED  2.1        // Lower number means faster
115 #define START_STEP 0.3
116 #define SPEED_INDICATOR_STEP 0.045
117 #define MAX_SPEED_INDICATOR  0.81
118 // Number of steps 90 degree rotation around an element is split into
119 #define ROT_CNT 6
120 
121 
122 // Constructor
123 G4OpenInventorQtExaminerViewer::
124 G4OpenInventorQtExaminerViewer(QWidget* parent, const char* name, SbBool embed,
125                                SoQtFullViewer::BuildFlag flag,
126                                SoQtViewer::Type type)
127    : SoQtExaminerViewer(parent, name, embed, flag, type),
128 #if 0x060000 <= QT_VERSION
129      fName(name),
130 #endif
131      externalQtApp(0), processSoEventCount(0)
132 {
133    // FWJ DEBUG
134    //  G4cout << "G4OpenInventorQtExaminerViewer CONSTRUCTOR CALLED" << G4endl;
135    //  G4cout << "G4OpenInventorQtExaminerViewer parent=" << parent << G4endl;
136 
137    // FWJ THIS DOESN'T WORK APPARENTLY NO MAINWINDOW
138    //   QMenuBar* menubar = ((QMainWindow*)parent)->menuBar();
139 
140 #if QT_VERSION < 0x060000
141    fName = new QString(name);
142    viewer = this;
143 #endif
144    construct(TRUE);
145 }
146 
147 // Destructor
148 G4OpenInventorQtExaminerViewer::~G4OpenInventorQtExaminerViewer()
149 {
150    //   if (superimposition != NULL) {
151    //      removeSuperimposition(superimposition);
152    //      superimposition->unref();
153    //      superimposition = NULL;
154    //   }
155    //   if (animateSensor->isScheduled())
156    //      animateSensor->unschedule();
157    //   delete animateSensor;
158    //   delete sceneChangeSensor;
159    //   delete[] curViewPtName;
160    //   delete searcher;
161 
162 #if QT_VERSION < 0x060000
163    viewer = 0;
164 #else
165    delete hookBeamOn;
166 #endif
167 }
168 
169 
170 void G4OpenInventorQtExaminerViewer::construct(const SbBool)
171 {
172    setFeedbackSize(40);
173 
174    hookBeamOn = new HookEventProcState(this);
175    newEvents = false;
176 
177    buildWidget(getParentWidget());
178 
179    fileName = "bookmarkFile"; // Default viewpoint file name
180    viewPtIdx = -1; // index of the most recent viewpoint in viewPtList vector
181 
182    animateSensor = new SoTimerSensor(animateSensorCB, this);
183    animateSensorRotation = new SoTimerSensor(animateSensorRotationCB, this);
184    animateBtwPtsPeriod = MIN_SPEED;
185 
186    currentState = GENERAL;
187    myCam = new SoPerspectiveCamera;
188    MAX_VP_IDX = 3;
189    MAX_VP_NAME = 35; // Max length of a viewpoint name, padded with spaces
190 #if QT_VERSION < 0x060000
191    curViewPtName = new char[MAX_VP_NAME + 1];
192 #else
193    curViewPtName.clear();
194 #endif
195    left_right = up_down = 0; // For movements around the beam during animation
196    speedStep = START_STEP; // For smoother animation speed increase/decrease
197    rotUpVec = false; // Used during scene element rotations
198    step = 1;  //By default
199    // Used for moving along the beam with the
200    // mouse instead of rotating the view
201    lshiftdown = rshiftdown = false;
202    // Used for rotating the view with the camera
203    // staying in place
204    lctrldown = rctrldown = false;
205    // Used to send abbreviated output to the console when
206    abbrOutputFlag = false;
207    pickRefPathFlag = false;
208    prevColorField = NULL;
209    //   warningFlag = false; // We come from the warning dialog
210    //   myElementList = NULL;
211    // FWJ default path look-ahead
212    pathLookahead = 5;
213 
214    newSceneGraph = NULL;
215    zcoordSetFlag = false;
216 
217    //////////////////////////SUPERIMPOSED SCENE//////////////////////////
218    searcher = NULL;
219    // Used in animation; progressively scaled for gradual speed change
220    maxSpeed = 0.0f;
221 
222    static const char * superimposed[] = {
223       "#Inventor V2.1 ascii", "",
224       "Separator ",
225       "{",
226       " MaterialBinding ",
227       " {",
228       "         value OVERALL",
229       " }",
230       "         OrthographicCamera ",
231       " {",
232       "         height 1",
233       "         nearDistance 0",
234       "         farDistance 1",
235       " }",
236       "         DEF soxt->callback Callback { }",
237       "         Separator ",
238       " {",
239       "         DEF soxt->translation Translation ",
240       "         {",
241       "                 translation 0 0 0",
242       "     }",
243       "     DEF soxt->scale Scale ",
244       "         {",
245       "                 scaleFactor 1 1 1",
246       "     }",
247       "         DEF soxt->geometry Coordinate3 ",
248       "         {",
249       "             point ",
250       "                 [",
251       "                         -0.81   -0.04   0,      -0.81   0             0,",
252       "                 -0.81   0.04    0,      0       -0.04   0,",
253       "                 0       0       0,  0       0.04        0,",
254       "                 0.81    -0.04   0,  0.81        0           0,",
255       "                 0.81    0.04    0,",
256       "                 0       0.02    0,", // idx 9
257       "                 0.81    0.02    0,  0.81        -0.02   0,",
258       "                 0       -0.02   0,",
259       "                 0       0.01    0,", // idx 13
260       "                 0.4     0.01    0,  0.4         -0.01   0,",
261       "                 0       -0.01   0",
262       "                 ]",
263       "         }",
264       // current speed indicator (outline)
265       "         DEF soxt->animSpeedOutlineSwitch Switch ",
266       "         {",
267       "                 whichChild -3",
268       "                 Material ",
269       "                 {",
270       "                    emissiveColor 0 0 0",
271       "             }",
272       "                 IndexedFaceSet ",
273       "                 {",
274       "                 coordIndex ",
275       "                         [",
276       "                                 12, 11, 10, 9, -1",
277       "                         ]",
278       "         }",
279       "                  }",
280       // the coordinate system
281       "         DEF soxt->axisSwitch Switch ",
282       "         {",
283       "                 whichChild -3",
284       "                 BaseColor ",
285       "                 {",
286       "                     rgb 1 1 1",
287       "                 }",
288       "                 IndexedLineSet ",
289       "                 {",
290       "                         coordIndex ",
291       "                         [",
292       "                                 0, 2, -1,",
293       "                                 3, 5, -1,",
294       "                                 6, 8, -1,",
295       "                                 1, 7, -1",
296       "                         ]",
297       "                     }",
298       "                 }",
299       // current speed indicator
300       "         DEF soxt->animSpeedSwitch Switch ",
301       "         {",
302       "                     whichChild -3",
303       "                 Material ",
304       "                 {",
305       "                 emissiveColor 0 1 0",
306       "         }",
307       "                 IndexedFaceSet ",
308       "                 {",
309       "                 coordIndex ",
310       "                         [",
311       "                                 16, 15, 14, 13, -1",
312       "                         ]",
313       "                 }",
314       "         }",
315       "         }",
316       // For displaying either z position (during animation) or current viewpoint name
317       " DEF soxt->curInfoSwitch Switch ",
318       " {",
319       "         whichChild -3",
320       "         DEF soxt->curInfoTrans Translation ",
321       "         {",
322       "                 translation 0 0 0    ",
323       //      "                 translation 10 20 30    ",
324       "         }",
325       "         DEF soxt->curInfoFont Font ",
326       "         {",
327       "                 name defaultFont:Bold",
328       "                 size 16",
329       "                 }",
330       "         DEF soxt->curInfoText Text2 ",
331       "         {",
332       "                 string Hello",
333       "     }",
334       " }",
335       // Need to use different fields for mouseover
336       // because newlines are ignored when the scene is rendered
337       " Separator ",
338       " {",
339       "         DEF soxt->mouseOverTransLogName Translation ",
340       "         {",
341       "                 translation 0 0 0    ",
342       "         }",
343       "         DEF soxt->mouseOverFontLogName Font ",
344       "         {",
345       "                 name defaultFont:Bold",
346       "                 size 16",
347       "                 }",
348       "         DEF soxt->mouseOverTextLogName Text2 { } ",
349       " }",
350       " Separator ",
351       " {",
352       "         DEF soxt->mouseOverTransSolid Translation ",
353       "         {",
354       "                 translation 0 0 0    ",
355       "         }",
356       "         DEF soxt->mouseOverFontSolid Font ",
357       "         {",
358       "                 name defaultFont:Bold",
359       "                 size 16",
360       "                 }",
361       "         DEF soxt->mouseOverTextSolid Text2 { } ",
362       " }",
363       " Separator ",
364       " {",
365       "         DEF soxt->mouseOverTransMaterial Translation ",
366       "         {",
367       "                 translation 0 0 0    ",
368       "         }",
369       "         DEF soxt->mouseOverFontMaterial Font ",
370       "         {",
371       "                 name defaultFont:Bold",
372       "                 size 16",
373       "                 }",
374       "         DEF soxt->mouseOverTextMaterial Text2 { } ",
375       " }",
376       " Separator ",
377       " {",
378       "         DEF soxt->mouseOverTransZPos Translation ",
379       "         {",
380       "                 translation 0 0 0    ",
381       "         }",
382       "         DEF soxt->mouseOverFontZPos Font ",
383       "         {",
384       "                 name defaultFont:Bold",
385       "                 size 16",
386       "                 }",
387       "         DEF soxt->mouseOverTextZPos Text2 { } ",
388       " }",
389       "}", NULL
390    };
391 
392    int i, bufsize;
393    for (i = bufsize = 0; superimposed[i]; i++)
394       bufsize += strlen(superimposed[i]) + 1;
395    char * buf = new char[bufsize + 1];
396    for (i = bufsize = 0; superimposed[i]; i++) {
397       strcpy(buf + bufsize, superimposed[i]);
398       bufsize += strlen(superimposed[i]);
399       buf[bufsize] = '\n';
400       bufsize++;
401    }
402    SoInput * input = new SoInput;
403    input->setBuffer(buf, bufsize);
404    SbBool ok = SoDB::read(input, superimposition);
405    (void)ok;   // FWJ added to avoid compiler warning
406    assert(ok);
407    delete input;
408    delete[] buf;
409    superimposition->ref();
410 
411    sscale = (SoScale *) getSuperimpositionNode(superimposition, "soxt->scale");
412    stranslation = (SoTranslation *) getSuperimpositionNode(superimposition, "soxt->translation");
413    sgeometry = (SoCoordinate3 *) getSuperimpositionNode(superimposition, "soxt->geometry");
414    axisSwitch = (SoSwitch *) getSuperimpositionNode(superimposition, "soxt->axisSwitch");
415    animSpeedOutlineSwitch = (SoSwitch *) getSuperimpositionNode(superimposition, "soxt->animSpeedOutlineSwitch");
416    animSpeedSwitch = (SoSwitch *) getSuperimpositionNode(superimposition, "soxt->animSpeedSwitch");
417    curInfoSwitch = (SoSwitch *) getSuperimpositionNode(superimposition, "soxt->curInfoSwitch");
418    curInfoTrans = (SoTranslation *) getSuperimpositionNode(superimposition, "soxt->curInfoTrans");
419    curInfoFont = (SoFont *) getSuperimpositionNode(superimposition, "soxt->curInfoFont");
420    curInfoText = (SoText2 *) getSuperimpositionNode(superimposition, "soxt->curInfoText");
421    mouseOverTransLogName = (SoTranslation*)getSuperimpositionNode(superimposition, "soxt->mouseOverTransLogName");
422    mouseOverFontLogName = (SoFont *) getSuperimpositionNode(superimposition, "soxt->mouseOverFontLogName");
423    mouseOverTextLogName = (SoText2 *) getSuperimpositionNode(superimposition, "soxt->mouseOverTextLogName");
424    mouseOverTransSolid = (SoTranslation *) getSuperimpositionNode(superimposition, "soxt->mouseOverTransSolid");
425    mouseOverFontSolid = (SoFont *) getSuperimpositionNode(superimposition, "soxt->mouseOverFontSolid");
426    mouseOverTextSolid = (SoText2 *) getSuperimpositionNode(superimposition, "soxt->mouseOverTextSolid");
427    mouseOverTransMaterial = (SoTranslation*)getSuperimpositionNode(superimposition, "soxt->mouseOverTransMaterial");
428    mouseOverFontMaterial = (SoFont *) getSuperimpositionNode(superimposition, "soxt->mouseOverFontMaterial");
429    mouseOverTextMaterial = (SoText2 *) getSuperimpositionNode(superimposition, "soxt->mouseOverTextMaterial");
430    mouseOverTransZPos = (SoTranslation *) getSuperimpositionNode(superimposition, "soxt->mouseOverTransZPos");
431    mouseOverFontZPos = (SoFont *) getSuperimpositionNode(superimposition, "soxt->mouseOverFontZPos");
432    mouseOverTextZPos = (SoText2 *) getSuperimpositionNode(superimposition, "soxt->mouseOverTextZPos");
433 
434    SoCallback * cb = (SoCallback *) getSuperimpositionNode(superimposition, "soxt->callback");
435    cb->setCallback(superimpositionCB, this);
436 
437    addSuperimposition(superimposition);
438    setSuperimpositionEnabled(superimposition, FALSE);
439    axisSwitch->whichChild.setValue(SO_SWITCH_NONE);
440    animSpeedOutlineSwitch->whichChild.setValue(SO_SWITCH_NONE);
441    animSpeedSwitch->whichChild.setValue(SO_SWITCH_NONE);
442 
443    /////////////////////\SUPERIMPOSED SCENE///////////////////////////////////
444 
445 }
446 
447 
448 // Adds a menu bar and menu items to the viewer.
449 void G4OpenInventorQtExaminerViewer::buildWidget(QWidget* parent)
450 {
451    if (!parent)
452       SoDebugError::post("G4OpenInventorQtExaminerViewer::buildWidget",
453                          "Error: Parent is null.");
454 
455    // Common font for (almost) all widgets
456    font = new QFont;
457    font->setPointSize(12);
458    // This font setting does not propagate to added child widgets - Why?
459    parent->setFont(*font);
460    // This propagates everywhere but would affect UIQt!
461    //   QApplication::setFont(*font);
462 
463 // MENU BAR
464 
465 #if QT_VERSION < 0x060000
466    menubar = new QMenuBar(getRenderAreaWidget());
467 #else
468    menubar = new QMenuBar();
469    menubar->setNativeMenuBar(false);
470    addAppPushButton(menubar);
471 #endif
472    // FWJ DEBUG
473    //   G4cout << "G4OpenInventorQtExaminerViewer: GOT A menubar=" <<
474    //      menubar << G4endl; 
475    
476    filemenu = new QMenu("File");
477    menubar->addMenu(filemenu); 
478 
479    FileOpenBookmark = new QAction("Open Bookmark File", this);
480    FileOpenBookmark->setFont(*font);
481    connect(FileOpenBookmark, SIGNAL(triggered()), this,
482            SLOT(FileOpenBookmarkCB()));
483    filemenu->addAction(FileOpenBookmark);
484 
485    FileNewBookmark = new QAction("New Bookmark File", this);
486    FileNewBookmark->setFont(*font);
487    connect(FileNewBookmark, SIGNAL(triggered()), this,
488            SLOT(FileNewBookmarkCB()));
489    filemenu->addAction(FileNewBookmark);
490 
491    FileLoadRefPath = new QAction("Load Reference Path", this);
492    FileLoadRefPath->setFont(*font);
493    connect(FileLoadRefPath, SIGNAL(triggered()), this,
494            SLOT(FileLoadRefPathCB()));
495    filemenu->addAction(FileLoadRefPath);
496 
497    FileSaveRefPath = new QAction("Save Reference Path", this);
498    FileSaveRefPath->setFont(*font);
499    connect(FileSaveRefPath, SIGNAL(triggered()), this,
500            SLOT(FileSaveRefPathCB()));
501    filemenu->addAction(FileSaveRefPath);
502 
503    FileLoadSceneGraph = new QAction("Load scene graph", this);
504    FileLoadSceneGraph->setFont(*font);
505    connect(FileLoadSceneGraph, SIGNAL(triggered()), this,
506            SLOT(FileLoadSceneGraphCB()));
507    filemenu->addAction(FileLoadSceneGraph);
508 
509    FileSaveSceneGraph = new QAction("Save scene graph", this);
510    FileSaveSceneGraph->setFont(*font);
511    connect(FileSaveSceneGraph, SIGNAL(triggered()), this,
512            SLOT(FileSaveSceneGraphCB()));
513    filemenu->addAction(FileSaveSceneGraph);
514 
515    // Rest of File menu is done in G4OpenInventorQtViewer
516 
517    toolsmenu = new QMenu("Tools");
518    menubar->addMenu(toolsmenu); 
519 
520    ToolsAnimateRefParticle = new QAction("Fly on Ref Path", this);
521    ToolsAnimateRefParticle->setFont(*font);
522    connect(ToolsAnimateRefParticle, SIGNAL(triggered()), this,
523            SLOT(ToolsAnimateRefParticleCB()));
524    toolsmenu->addAction(ToolsAnimateRefParticle);
525 
526    ToolsRefPathStart = new QAction("Go to start of Ref Path", this);
527    ToolsRefPathStart->setFont(*font);
528    connect(ToolsRefPathStart, SIGNAL(triggered()), this,
529            SLOT(ToolsRefPathStartCB()));
530    toolsmenu->addAction(ToolsRefPathStart);
531 
532    ToolsRefPathInvert = new QAction("Invert Ref Path", this);
533    ToolsRefPathInvert->setFont(*font);
534    connect(ToolsRefPathInvert, SIGNAL(triggered()), this,
535            SLOT(ToolsRefPathInvertCB()));
536    toolsmenu->addAction(ToolsRefPathInvert);
537 
538    etcmenu = new QMenu("Etc");
539    menubar->addMenu(etcmenu); 
540 
541    // All Etc menu items are done in G4OpenInventorQtViewer
542 
543    helpmenu = new QMenu("Help");
544    menubar->addMenu(helpmenu); 
545 
546    HelpControls = new QAction("Controls", this);
547    HelpControls->setFont(*font);
548    connect(HelpControls, SIGNAL(triggered()), this, SLOT(HelpControlsCB()));
549    helpmenu->addAction(HelpControls);
550 
551 #if QT_VERSION < 0x060000
552    menubar->show();
553 #endif
554 
555    //   SoQtExaminerViewer::buildWidget(parent);
556 
557    // APP VIEWER BUTTONS have their own box on upper left
558    // The built in viewer button list is PRIVATE
559 
560    saveViewPtButton = new QPushButton;
561    saveViewPtButton->setIcon(QPixmap((const char **)saveViewPt_xpm));
562    saveViewPtButton->setIconSize(QSize(24,24));
563    saveViewPtButton->setToolTip("Bookmark this view");
564    connect(saveViewPtButton, SIGNAL(clicked()), this,
565            SLOT(SaveViewPtCB()));
566    addAppPushButton(saveViewPtButton);
567 
568    nextViewPtButton = new QPushButton;
569    nextViewPtButton->setIconSize(QSize(24,24));
570    QCommonStyle style;
571    nextViewPtButton->setIcon(style.standardIcon(QStyle::SP_ArrowRight));
572    nextViewPtButton->setToolTip("Next bookmark");
573    connect(nextViewPtButton, SIGNAL(clicked()), this,
574            SLOT(NextViewPtCB()));
575    addAppPushButton(nextViewPtButton);
576 
577    prevViewPtButton = new QPushButton;
578    prevViewPtButton->setIconSize(QSize(24,24));
579    prevViewPtButton->setIcon(style.standardIcon(QStyle::SP_ArrowLeft));
580    prevViewPtButton->setToolTip("Previous bookmark");
581    connect(prevViewPtButton, SIGNAL(clicked()), this,
582            SLOT(PrevViewPtCB()));
583    addAppPushButton(prevViewPtButton);
584 
585    abbrOutputButton = new QPushButton;
586    abbrOutputButton->setCheckable(true);
587    abbrOutputButton->setIconSize(QSize(24,24));
588    abbrOutputButton->setIcon(QPixmap((const char **)pickext_xpm));
589    abbrOutputButton->setToolTip("Extended picking & readout");
590    connect(abbrOutputButton, SIGNAL(toggled(bool)), this,
591            SLOT(AbbrOutputCB(bool)));
592    addAppPushButton(abbrOutputButton);
593 
594    pickRefPathButton = new QPushButton;
595    pickRefPathButton->setIconSize(QSize(24,24));
596    pickRefPathButton->setIcon(QPixmap((const char **)pickref_xpm));
597    pickRefPathButton->setToolTip("Pick ref trajectory");
598    connect(pickRefPathButton, SIGNAL(clicked()), this,
599            SLOT(PickRefPathCB()));
600    addAppPushButton(pickRefPathButton);
601 
602    switchWireFrameButton = new QPushButton;
603    switchWireFrameButton->setCheckable(true);
604    switchWireFrameButton->setIconSize(QSize(24,24));
605    switchWireFrameButton->setIcon(QPixmap((const char **)wireframe_xpm));
606    switchWireFrameButton->setToolTip("Switch wireframe/solid");
607    connect(switchWireFrameButton, SIGNAL(toggled(bool)), this,
608            SLOT(SwitchWireFrameCB(bool)));
609    addAppPushButton(switchWireFrameButton);
610 
611    switchAxesButton = new QPushButton;
612    switchAxesButton->setCheckable(true);
613    switchAxesButton->setText(QString("A"));
614    switchAxesButton->setToolTip("Axes on/off");
615    connect(switchAxesButton, SIGNAL(toggled(bool)), this,
616            SLOT(SwitchAxesCB(bool)));
617    addAppPushButton(switchAxesButton);
618 
619    detachButton = new QPushButton;
620    detachButton->setIconSize(QSize(24,24));
621    detachButton->setIcon(style.standardIcon(QStyle::SP_CommandLink));
622    detachButton->setToolTip("Detach viewer window");
623    connect(detachButton, SIGNAL(clicked()), this,
624            SLOT(DetachCB()));
625    // Used for UIQt only so check and add later
626    //   addAppPushButton(detachButton);
627 
628    // HELP WINDOW
629 
630    helpmsgbox = new QMessageBox(getParentWidget());
631    helpmsgbox->setWindowTitle("OIQt Controls");
632    helpmsgbox->setFont(*font);
633    QString messagetxt =
634 "\nVIEWING mode (Hand cursor):\n\n\
635    Left-button + pointer move:  rotate\n\
636    Shift+Left-button + pointer move:  pan\n\
637    Middle-button + pointer move:  pan\n\
638    Ctrl+Shift+Left-button + pointer move:  zoom\n\
639    Mouse wheel:  zoom\n\
640    Right-button:  popup menu\n\n\
641 PICKING mode (Arrow cursor):\n\n\
642    Click on a volume:  geometry readout\n\
643    Click on a trajectory:  particle & trajectory readout\n\
644    Ctrl + click on a volume:  see daughters.\n\
645    Shift + click on a volume:  see mother.\n\n\
646 EXTENDED PICKING mode (Arrow+ viewer button):\n\n\
647    Hover the mouse over a volume or trajectory for\n\
648    overlayed readout.\n\n\
649 ELEMENT NAVIGATION (requires Reference Path):\n\n\
650    Click on element in list:  centers view on element\n\
651    Arrow keys:  rotate in 90 degree steps around element  \n\
652    Shift + Right Arrow:  move to next element\n\
653    Shift + Left Arrow:  move to previous element\n\n\
654 FLY mode (requires Reference Path):\n\n\
655    Page Up:  Increase speed\n\
656    Page Down:  Decrease speed (& reverse if wanted)\n\
657    Up Arrow:  raise camera above path\n\
658    Down Arror:  lower camera below path\n\
659    Escape:  Exit fly mode";
660    helpmsgbox->setText(messagetxt);
661    helpmsgbox->setModal(false);
662    //   helpmsgbox->setWindowModality(Qt::NonModal);
663 
664    // AUXILIARY LISTS WINDOW
665 
666    // Bypass the namespace in order to make a persistent object
667    AuxWindowDialog = new Ui_Dialog;
668    AuxWindow = new QDialog(parent);
669    AuxWindowDialog->setupUi(AuxWindow);
670 
671    // SIGNALS
672    connect(AuxWindowDialog->listWidget, SIGNAL(itemClicked(QListWidgetItem*)),
673            this, SLOT(LoadBookmarkCB(QListWidgetItem*)));
674    connect(AuxWindowDialog->listWidget1, SIGNAL(itemClicked(QListWidgetItem*)),
675            this, SLOT(LookAtSceneElementCB(QListWidgetItem*)));
676    connect(AuxWindowDialog->pushButton_2, SIGNAL(clicked()),
677            this, SLOT(DeleteBookmarkCB()));
678    connect(AuxWindowDialog->pushButton_3, SIGNAL(clicked()),
679            this, SLOT(RenameBookmarkCB()));
680    connect(AuxWindowDialog->pushButton, SIGNAL(clicked()),
681            this, SLOT(SortBookmarksCB()));
682    
683    // FWJ Better to do this after viewer window is realized
684    //   AuxWindow->show();
685    //   AuxWindow->raise();
686    //   AuxWindow->activateWindow();
687 }
688 
689 
690 #if QT_VERSION < 0x060000
691 // Called right after buttons and widgets get realized.
692 // It sets the viewpoint last accessed.
693 void G4OpenInventorQtExaminerViewer::afterRealizeHook()
694 {
695    SoQtExaminerViewer::afterRealizeHook();
696 #else
697 void G4OpenInventorQtExaminerViewer::setupSceneGraph()
698 {
699 #endif
700    // Default height is used when selecting and viewing scene elements
701    // FWJ Added defaultHeight for Ortho camera
702    SoCamera *cam = getCamera();
703    if (cam) {
704       if (cam->isOfType(SoPerspectiveCamera::getClassTypeId())) {
705          defaultHeightAngle =
706             ((SoPerspectiveCamera *) cam)->heightAngle.getValue();
707          toggleCameraType();
708          defaultHeight =
709             ((SoOrthographicCamera *) cam)->height.getValue();
710          toggleCameraType();
711       } else {
712          defaultHeight =
713             ((SoOrthographicCamera *) cam)->height.getValue();
714          toggleCameraType();
715          cam = getCamera();
716          if (cam->isOfType(SoPerspectiveCamera::getClassTypeId()))
717             defaultHeightAngle =
718                ((SoPerspectiveCamera *) cam)->heightAngle.getValue();
719          toggleCameraType();
720       }
721    }
722 
723    // Open the default bookmark file
724    fileIn.open(fileName.c_str());
725    if (!fileIn.fail()) {
726       if (!loadViewPts()) {
727          QMessageBox msgbox;
728          msgbox.setFont(*font);
729          QString messagetxt = "Error reading bookmark file ";
730          messagetxt.append(QString(fileName.c_str()));
731          msgbox.setText(messagetxt);
732          msgbox.exec();
733       } else {
734          // Opens a file without erasing it
735          fileOut.open(fileName.c_str(), std::ios::in);
736          fileOut.seekp(0, std::ios::end); // For appending new data to the end
737          // FWJ DEBUG
738          // G4cout << "afterRealizeHook: opened EXISTING bookmark file"
739          //        << G4endl;
740          if (viewPtList.size()) {
741             // FWJ disabled auto-selection of first viewpoint.
742             // Initial view should be user-controllable & not forced
743             //    setViewPt();
744             addViewPoints();
745          }
746       }
747       fileIn.close();
748    } else {
749       // Creates a new default bookmark file
750       fileOut.open(fileName.c_str());
751       // FWJ DEBUG
752       // G4cout << "afterRealizeHook: Opened a NEW bookmark file" << G4endl;
753    }
754 
755    fileIn.clear();
756 
757    SoSeparator* root = (SoSeparator*) (getSceneManager()->getSceneGraph());
758    if (root == NULL)
759       SoDebugError::post("G4OpenInventorQtExaminerViewer::afterRealizeHook", "Root is null.");
760    else {
761       root->addChild(myCam); // For position/orientation calculation during animation
762    }
763 
764 #if 0x060000 <= QT_VERSION
765    if(root!=nullptr) {
766 #endif
767    sceneChangeSensor = new SoNodeSensor;
768    sceneChangeSensor->setFunction(sceneChangeCB);
769    sceneChangeSensor->attach(root);
770    sceneChangeSensor->setData(this);
771 
772    ///////////////////////////// MOUSEOVER & PICK /////////////////////
773 
774    // Monitor mouseover events for displaying the name of scene elements
775    // An SoEventCallback is needed instead of using the default processSoEvent
776    // because that last one does not provide us with an SoPath to the object
777    // that was picked
778    SoEventCallback *moCB = new SoEventCallback;
779    moCB->addEventCallback(
780                           SoLocation2Event::getClassTypeId(),
781                           mouseoverCB, static_cast<void *>(this));
782    root->addChild(moCB);
783 
784    // Override the default picking mechanism present in G4OpenInventorViewer
785    // because we want abbreviated output when picking a trajectory
786    SoEventCallback *pickCB = new SoEventCallback;
787    pickCB->addEventCallback(
788                             SoMouseButtonEvent::getClassTypeId(),
789                             pickingCB, static_cast<void *>(this));
790    root->addChild(pickCB);
791 #if 0x060000 <= QT_VERSION
792    }
793 #endif
794 
795    ///////////////////////////// MOUSEOVER & PICK /////////////////////
796 
797 #if QT_VERSION < 0x060000
798    AuxWindow->show();
799    AuxWindow->raise();
800    AuxWindow->activateWindow();
801 
802    auto UI = G4UImanager::GetUIpointer();
803    uiQt = dynamic_cast<G4UIQt*>(UI->GetG4UIWindow());
804    // This explicitly sets the TabWidget as parent before addTab():
805    if (uiQt) {
806       viewerParent = getParentWidget();
807       viewerParent2 = viewerParent->parentWidget();
808       uiQt->AddTabWidget(getParentWidget(), *fName);
809       uiQtTabIndex = uiQt->GetViewerTabWidget()->currentIndex();
810       //      attached = TRUE;
811       addAppPushButton(detachButton);
812    }
813 #else
814    //G.Barrand: map the AuxWindow "at need", it is done in the SaveViewPtCB.
815    //           Having one appearing for each viewer creation is cumbersome.
816    //           Also, if "closing" the AuxWindow with the mouse, we have no
817    //           way to map it again. Moreover, if done here, on macOS at sartup,
818    //           it inactivates the Apple menu bar. An idea: since there is one
819    //           AuxWindow per viewer, why not having them as tabs in the UI
820    //           tab widget? It will go toward a more compact GUI.
821 #endif
822 }
823 
824 #if 0x060000 <= QT_VERSION
825 void G4OpenInventorQtExaminerViewer::addInTab() {
826   auto UI = G4UImanager::GetUIpointer();
827   uiQt = dynamic_cast<G4UIQt*>(UI->GetG4UIWindow());
828   if(uiQt) {
829     uiQt->AddTabWidget(getParentWidget(),fName);
830     addAppPushButton(detachButton);
831   }
832 }
833 #endif
834 
835 // This method locates a named node in the superimposed or original scene.
836 // FWJ RENAME THIS
837 SoNode*
838 G4OpenInventorQtExaminerViewer::getSuperimpositionNode(SoNode* root,
839                                                        const char* name)
840 {
841    if (!searcher)
842       searcher = new SoSearchAction;
843    searcher->reset();
844    searcher->setName(SbName(name));
845    searcher->setInterest(SoSearchAction::FIRST);
846    searcher->setSearchingAll(TRUE);
847    searcher->apply(root);
848    assert(searcher->getPath());
849    return searcher->getPath()->getTail();
850 }
851 
852 
853 // FWJ don't know why userdata is called "closure"
854 // It contains the this pointer!
855 void G4OpenInventorQtExaminerViewer::superimpositionCB(void * closure,
856                                                        SoAction * action)
857 {
858    if (closure)
859       ((G4OpenInventorQtExaminerViewer*)closure)->superimpositionEvent(action);
860 }
861 
862 
863 // Renders and positions speed indicator and longitudinal
864 // distance/viewpoint name on the drawing canvas
865 void G4OpenInventorQtExaminerViewer::superimpositionEvent(SoAction * action)
866 {
867 
868    if (!action->isOfType(SoGLRenderAction::getClassTypeId()))
869       return;
870    SbViewportRegion vpRegion =
871       ((SoGLRenderAction*)action)->getViewportRegion();
872    SbVec2s viewportSize = vpRegion.getViewportSizePixels();
873    
874    // Aspect is WIDTH/HEIGHT
875    float aspect = float(viewportSize[0]) / float(viewportSize[1]);
876 
877    // FWJ DEBUG
878    //   G4cout << "SPEVENT X0 Y0 DX DY aspect: " << vpRegion.getViewportOrigin()[0] <<
879    //      " " << vpRegion.getViewportOrigin()[1] <<
880    //      " " << viewportSize[0] <<
881    //      " " << viewportSize()[1] <<
882    //      " " << aspect << G4endl;
883 
884    // Translation and scale factor for animation speed indicator...
885 
886    float factorx = 1.0f / float(viewportSize[1]) * 220.0f;
887    float factory = factorx;
888 
889    if (aspect > 1.0f) {
890       stranslation->translation.setValue(SbVec3f(0.0f, -0.4f, 0.0f));
891    } else {
892       stranslation->translation.setValue(SbVec3f(0.0f, -0.4f / aspect, 0.0f));
893       factorx /= aspect;
894       factory /= aspect;
895    }
896    if (viewportSize[0] > 500)
897       factorx *= 500.0f / 400.0f;
898    else
899       factorx *= float(viewportSize[0]) / 400.0f;
900 
901    sscale->scaleFactor.setValue(SbVec3f(factorx, factory, 1.0f));
902 
903    // TEXT OVERLAY...
904 
905    // FWJ Simplified and rewrote the following section to ease problems
906    // with the overlayed text after a viewer window resize.
907    // Result is now readable but needs further refinement of the scaling.
908 
909    float xInfo, yInfo, xLogName, yLogName, xSolid, ySolid,
910       xMaterial, yMaterial, xZPos, yZPos;
911 
912    // Base point for navigation distance or viewpoint name
913    // Origin is at center of render area.
914    xInfo = -.475;
915    yInfo = .475;
916    // Menu bar height in same coordinates:
917    float mbgap = 0.03;
918    if (aspect > 1.) xInfo = xInfo*aspect;
919    if (aspect < 1.) yInfo = yInfo/aspect;
920    yInfo = yInfo - mbgap*aspect;
921 
922    // Following are relative to above base point
923    xLogName = 0.0;
924    yLogName = -.88 + mbgap*aspect;
925    xSolid = 0.0;
926    ySolid = -.91 + mbgap*aspect;
927    xMaterial = 0.0;
928    yMaterial = -.94 + mbgap*aspect;
929    xZPos = 0.0;
930    yZPos = -.97 + mbgap*aspect;
931 
932    // Top line
933    curInfoTrans->translation.setValue(SbVec3f(xInfo, yInfo, 0.0));
934 
935    // Bottom lines
936    mouseOverTransLogName->translation.setValue(SbVec3f(xLogName, yLogName, 0.0));
937    mouseOverTransSolid->translation.setValue(SbVec3f(xSolid, ySolid, 0.0));
938    mouseOverTransMaterial->translation.setValue(SbVec3f(xMaterial, yMaterial, 0.0));
939    mouseOverTransZPos->translation.setValue(SbVec3f(xZPos, yZPos, 0.0));
940 
941    if (currentState == VIEWPOINT) { // Displaying viewpoint name
942       curInfoFont->size.setValue(15);
943       curInfoFont->name.setValue("defaultFont:Italic");
944 #if QT_VERSION < 0x060000
945       curInfoText->string.setValue(SbString(curViewPtName));
946 #else
947       curInfoText->string.setValue(SbString(curViewPtName.c_str()));
948 #endif
949    }
950    else if(currentState == GENERAL) { // Displaying longitudinal distance
951       curInfoFont->size.setValue(16);
952       curInfoFont->name.setValue("defaultFont:Bold");
953       curInfoText->string.setValue(SbString(""));
954    }
955    else {
956       if (refParticleIdx < (int) refParticleTrajectory.size() - 1) {
957          curInfoFont->size.setValue(16);
958          curInfoFont->name.setValue("defaultFont:Bold");
959          char zPos[20];
960          // FWJ need a better format here
961          snprintf(zPos, sizeof zPos, "%-7.2f [m]", refZPositions[refParticleIdx] / 1000);
962          curInfoText->string.setValue(SbString(zPos));
963       }
964    }
965 }
966 
967 
968 //  Loads view point data from a file into a vector.
969 
970 bool G4OpenInventorQtExaminerViewer::loadViewPts() 
971 {
972    bool error = false;
973    viewPtData tmp;
974    std::string token;
975    SbVec3f axis;
976    SbRotation orient;
977    float x{0.0}, y{0.0}, z{0.0}, angle{0.0};
978 
979    // Gets the last view point accessed, stored in the first line of the data file.
980    fileIn >> token;
981    parseString<int>(viewPtIdx, token, error);
982    getline(fileIn, token); // Remove "\n"
983    // Converts data from string type into necessary types
984    while (getline(fileIn, token)) {
985 
986       std::size_t end = token.find_last_not_of(' '); // Remove padded spaces
987       token = token.substr(0, end + 1);
988 
989 #if QT_VERSION < 0x060000
990       char *vpName = new char[token.size() + 1];
991       strcpy(vpName, token.c_str());
992       tmp.viewPtName = vpName;
993 #else
994       tmp.viewPtName = token;
995 #endif
996       fileIn >> token;
997 
998       parseString<float>(x, token, error);
999       fileIn >> token;
1000       parseString<float>(y, token, error);
1001       fileIn >> token;
1002       parseString<float>(z, token, error);
1003       fileIn >> token;
1004       tmp.position = axis.setValue(x, y, z);
1005 
1006       parseString<float>(x, token, error);
1007       fileIn >> token;
1008       parseString<float>(y, token, error);
1009       fileIn >> token;
1010       parseString<float>(z, token, error);
1011       fileIn >> token;
1012       parseString<float>(angle, token, error);
1013       fileIn >> token;
1014       orient.setValue(axis.setValue(x, y, z), angle);
1015       tmp.orientation = orient.getValue();
1016 
1017       int camType{0};
1018       parseString<int>(camType, token, error);
1019       fileIn >> token;
1020       tmp.camType = (CameraType) camType;
1021 
1022       parseString<float>(tmp.height, token, error);
1023       fileIn >> token;
1024       parseString<float>(tmp.focalDistance, token, error);
1025       fileIn >> token;
1026       parseString<float>(tmp.nearDistance, token, error);
1027       fileIn >> token;
1028       parseString<float>(tmp.farDistance, token, error);
1029       fileIn >> token;
1030       parseString<int>(tmp.viewportMapping, token, error);
1031       fileIn >> token;
1032       parseString<float>(tmp.aspectRatio, token, error);
1033 
1034       getline(fileIn, token); // To remove "\n" characters
1035       getline(fileIn, token);
1036 
1037       if (error) {
1038 #if QT_VERSION < 0x060000
1039          viewPtIdx = 0;
1040 #else
1041          viewPtIdx = -1;
1042 #endif
1043          viewPtList.clear();
1044          return false;
1045       }
1046       viewPtList.push_back(tmp);
1047    }
1048 
1049    return true;
1050 }
1051 
1052 
1053 // Rotates camera 90 degrees around a scene element.
1054 // Rotation is animated for smoothness.
1055 void G4OpenInventorQtExaminerViewer::rotateCamera()
1056 {
1057    SoCamera *cam = getCamera();
1058 
1059    SbRotation rot(rotAxis, M_PI / (2 * ROT_CNT));
1060    rot.multVec(camDir, camDir);
1061    rot.multVec(camUpVec, camUpVec);
1062 
1063    SbVec3f camPosNew = prevPt - (camDir*distance);
1064    cam->position = camPosNew;
1065    cam->pointAt(prevPt, camUpVec);
1066    cam->focalDistance = (prevPt - camPosNew).length();
1067 
1068    rotCnt--;
1069 
1070    if (animateSensorRotation->isScheduled()) {
1071       animateSensorRotation->unschedule();
1072    }
1073 
1074    animateSensorRotation->setBaseTime(SbTime::getTimeOfDay());
1075    animateSensorRotation->setInterval(SbTime(0.02));
1076    animateSensorRotation->schedule();
1077 
1078 }
1079 
1080 
1081 // Slides camera along the beamline.
1082 void G4OpenInventorQtExaminerViewer::moveCamera(float dist, bool lookdown)
1083 {
1084 
1085    SoCamera *cam = getCamera();
1086    SbVec3f p1, p2;   // The particle moves from p1 to p2
1087    SbVec3f particleDir;  // Direction vector from p1 to p2
1088    SbVec3f camPosNew{0.0f, 0.0f, 0.0f};  // New position of the camera
1089 
1090    if(refParticleTrajectory.size() == 0) {
1091       //refParticleTrajectory hasn't been set yet
1092       if(dist)
1093          distance = dist;
1094       else
1095          distance = (cam->position.getValue() - center).length();
1096 
1097       cam->position.setValue(center + offsetFromCenter*distance);
1098       cam->focalDistance = (cam->position.getValue() - center).length();
1099       cam->pointAt(center, upVector);
1100    }
1101    else {
1102 
1103       // If we move forward past the last trajectory point,
1104       // go back to the beginning
1105       if (refParticleIdx >= (int) refParticleTrajectory.size() - 1) {
1106          prevPt = refParticleTrajectory[refParticleIdx - step];
1107          dist = (prevPt - cam->position.getValue()).length();
1108          refParticleIdx = 0;
1109       }
1110       // If we move backward past the beginning,
1111       // go to the last trajectory point
1112       if (refParticleIdx < 0) {
1113          prevPt = refParticleTrajectory[refParticleIdx + step];
1114          dist = (prevPt - cam->position.getValue()).length();
1115          refParticleIdx = (int) refParticleTrajectory.size() - 2;
1116       }
1117 
1118       // Set start and end points
1119       p1 = refParticleTrajectory[refParticleIdx];
1120       p2 = refParticleTrajectory[refParticleIdx + step];
1121 
1122       // Get the direction from p1 to p2
1123       particleDir = p2 - p1;
1124       particleDir.normalize();
1125 
1126       if(prevParticleDir == SbVec3f(0,0,0)) {
1127          // First time entering BEAMLINE mode, look at
1128          // the element from the front, with camera upright
1129          if(lookdown)
1130             camDir = SbVec3f(0,0,1);
1131          else
1132             camDir = SbVec3f(1,0,0);
1133          camUpVec = SbVec3f(0,1,0);
1134 
1135          // In case the start of the goes in a
1136          // direction other than +z, rotate the camera accordingly
1137          SbRotation rot(SbVec3f(0,0,1), particleDir);
1138          rot.multVec(camDir, camDir);
1139          rot.multVec(camUpVec, camUpVec);
1140 
1141       }
1142       else if(particleDir != prevParticleDir) {
1143          // The beamline has changed direction
1144 
1145          SbRotation rot(prevParticleDir, particleDir);
1146          rot.multVec(camDir, camDir);
1147          rot.multVec(camUpVec, camUpVec);
1148 
1149       }
1150 
1151       if (cam->isOfType(SoPerspectiveCamera::getClassTypeId())) {
1152          if (!dist)
1153             distance = (prevPt - cam->position.getValue()).length();
1154          else
1155             distance = dist;
1156       }
1157 
1158       // FWJ distance not relevant -- use focalDistance
1159       // if (cam->isOfType(SoOrthographicCamera::getClassTypeId())) {
1160       //    if (!dist)
1161       //       distance = (prevPt - cam->position.getValue()).length();
1162       //    else
1163       //       distance = dist;
1164       // }
1165 
1166 
1167       float x,y,z;
1168       prevPt.getValue(x,y,z);
1169 
1170 
1171       if (cam->isOfType(SoPerspectiveCamera::getClassTypeId())) {
1172          camPosNew = p2 - (camDir*distance);
1173       }
1174       if (cam->isOfType(SoOrthographicCamera::getClassTypeId())) {
1175          // FWJ maintain focal distance
1176          camPosNew = p2 - (camDir*cam->focalDistance.getValue());
1177          //         camPosNew = p2 - (camDir);
1178       }
1179 
1180       cam->position = camPosNew;
1181       cam->pointAt(p2, camUpVec);
1182       cam->focalDistance = (p2 - camPosNew).length();
1183 
1184       p2.getValue(x,y,z);
1185       camPosNew.getValue(x,y,z);
1186 
1187       prevParticleDir = particleDir;
1188       prevPt = p1; // For accurate distance calculation
1189 
1190    }
1191 
1192 }
1193 
1194 
1195 void G4OpenInventorQtExaminerViewer::pickingCB(void *aThis, 
1196                                                SoEventCallback *eventCB)
1197 {
1198    SoHandleEventAction* action = eventCB->getAction();
1199    const SoPickedPoint *pp = action->getPickedPoint();
1200    G4OpenInventorQtExaminerViewer* This = (G4OpenInventorQtExaminerViewer*)aThis;
1201 
1202    if(pp != NULL) {
1203 
1204       SoPath* path = pp->getPath();
1205       SoNode* node = ((SoFullPath*)path)->getTail();
1206 
1207       if(node->getTypeId() == SoLineSet::getClassTypeId()) {
1208 
1209          if(This->pickRefPathFlag) {
1210             This->pickRefPathFlag = false;
1211             if(This->viewingBeforePickRef != This->isViewing())
1212                This->setViewing(This->viewingBeforePickRef);
1213             else
1214                This->setComponentCursor(SoQtCursor(SoQtCursor::DEFAULT));
1215 
1216             // The trajectory is a set of lines stored in a LineSet
1217             SoLineSet * trajectory = (SoLineSet *)node;
1218             // FWJ DEBUG
1219             // G4cout << "FOUND trajectory LineSet" << trajectory << G4endl;
1220 
1221        // The set of all trajectories is stored in a Seperator group node
1222        // one level above the LineSet that was picked. The nodes under that
1223        // seperator are as follows (in this order): Material, LightModel,
1224        // ResetTransform, MatrixTransform, Coordinate3, DrawStyle, LineSet
1225             SoSeparator * grpNode = 
1226                (SoSeparator*)(((SoFullPath*)path)->getNodeFromTail(1));
1227 
1228    // The node that contains the coordinates for the trajectory is a
1229    // Coordinate3 node which occurs before the LineSet node.  We iterate
1230    // back through the nodes in the group until we find the Coordinate3 node
1231             int nodeIndex = grpNode->findChild(trajectory);
1232             SoNode * tmpNode;
1233             // FWJ needs initialization
1234             SoCoordinate3 * coords = 0;
1235             //            SoCoordinate3 * coords;
1236             // We allow only 100 iterations, in case the node isn't found
1237             // (should take only a few iterations)
1238             for(int i = 0; i < 100; ++i) {
1239                --nodeIndex;
1240 
1241                tmpNode = grpNode->getChild(nodeIndex);
1242                if(tmpNode->getTypeId() == SoCoordinate3::getClassTypeId()) {
1243                   //node found
1244                   coords = (SoCoordinate3 *)tmpNode;
1245                   break;
1246                }
1247             }
1248 
1249             if(coords == NULL) {
1250                G4warn << "Could not find the coordinates node"
1251                   " for the picked trajectory." << G4endl;
1252                G4warn << " Reference trajectory not set" << G4endl;
1253                return;
1254             }
1255             // FWJ DEBUG
1256             // G4cout << "FOUND SoCoordinate3 node " << coords << G4endl;
1257 
1258 
1259             if ((This->lshiftdown)  || (This->rshiftdown))
1260                This->setReferencePath(trajectory, coords, true);  //APPENDING
1261             else
1262                This->setReferencePath(trajectory, coords, false);
1263 
1264             return;
1265 
1266          }
1267          else if(This->abbrOutputFlag) {
1268 
1269             G4AttHolder* attHolder = dynamic_cast<G4AttHolder*>(node);
1270             if(attHolder && attHolder->GetAttDefs().size()) {
1271 
1272                std::string strTrajPoint = "G4TrajectoryPoint:";
1273                std::ostringstream oss;
1274                for (std::size_t i = 0; i < attHolder->GetAttDefs().size(); ++i) {
1275                   G4cout << G4AttCheck(attHolder->GetAttValues()[i],
1276                                        attHolder->GetAttDefs()[i]);
1277                   oss << G4AttCheck(attHolder->GetAttValues()[i],
1278                                     attHolder->GetAttDefs()[i]);
1279                   if(oss.str().find(strTrajPoint) != std::string::npos) {
1280 
1281            // Last attribute displayed was a trajectory point.  Since we
1282            // want abbreviated output, display the last one and exit
1283            // (unless we're already at the last (and only) trajectory point)
1284                      if(i != attHolder->GetAttDefs().size()-1) {
1285                         G4cout << G4AttCheck(
1286               attHolder->GetAttValues()[attHolder->GetAttDefs().size()-1],
1287               attHolder->GetAttDefs()[attHolder->GetAttDefs().size()-1]);
1288                      }
1289                      break;
1290                   }
1291                }
1292             } else {
1293                G4String name((char*)node->getName().getString());
1294                G4String cls((char*)node->getTypeId().getName().getString());
1295                G4warn << "SoNode : " << node
1296                       << " SoType : " << cls
1297                       << " name : " << name
1298                       << G4endl;
1299                G4warn << "No attributes attached." << G4endl;
1300             }
1301 
1302             return;
1303          }
1304          else{
1305             //Go to default behavior
1306          }
1307       }
1308       else {
1309          //Go to default behavior
1310       }
1311 
1312       // Default behavior in G4OpenInventorViewer::SelectionCB
1313       G4AttHolder* attHolder = dynamic_cast<G4AttHolder*>(node);
1314       if(attHolder && attHolder->GetAttDefs().size()) {
1315          for (std::size_t i = 0; i < attHolder->GetAttDefs().size(); ++i) {
1316             G4cout << G4AttCheck(attHolder->GetAttValues()[i],
1317                                  attHolder->GetAttDefs()[i]);
1318          }
1319       } else {
1320          G4String name((char*)node->getName().getString());
1321          G4String cls((char*)node->getTypeId().getName().getString());
1322          G4warn << "SoNode : " << node
1323                 << " SoType : " << cls
1324                 << " name : " << name
1325                 << G4endl;
1326          G4warn << "No attributes attached." << G4endl;
1327       }
1328 
1329       //Suppress other event handlers
1330       eventCB->setHandled();
1331    }
1332 }
1333 
1334 
1335 void G4OpenInventorQtExaminerViewer::mouseoverCB(void *aThis, SoEventCallback *eventCB)
1336 {
1337    SoHandleEventAction* action = eventCB->getAction();
1338    const SoPickedPoint* pp = action->getPickedPoint();
1339    G4OpenInventorQtExaminerViewer* This = (G4OpenInventorQtExaminerViewer*)aThis;
1340 
1341    if(!This->abbrOutputFlag)
1342       return;
1343 
1344    if(pp != NULL) {
1345 
1346       const SbViewportRegion & viewportRegion = action->getViewportRegion();
1347 
1348       std::string sLogName;
1349       float x,y,z;
1350       std::stringstream ssZPos;
1351       std::stringstream ssSolids;
1352       std::stringstream ssMaterials;
1353       SoPath * path = pp->getPath();
1354       SoNode* node = ((SoFullPath*)path)->getTail();
1355 
1356       if(node->getTypeId() == Geant4_SoPolyhedron::getClassTypeId()) {
1357 
1358          sLogName = "Logical Volume:  ";
1359          sLogName += ((Geant4_SoPolyhedron *)node)->getName().getString();
1360 
1361          SoGetBoundingBoxAction bAction(viewportRegion);
1362          bAction.apply((SoFullPath*)path);
1363          SbBox3f bBox = bAction.getBoundingBox();
1364          SbVec3f centr = bBox.getCenter();
1365          centr.getValue(x,y,z);
1366          ssZPos << "Pos:  " << x << "  " << y << "  " << z;
1367 
1368          G4AttHolder* attHolder = dynamic_cast<G4AttHolder*>(node);
1369          if(attHolder && attHolder->GetAttDefs().size()) {
1370 
1371             std::vector<const std::map<G4String,G4AttDef>*> vecDefs =
1372                attHolder->GetAttDefs();
1373             std::vector<const std::vector<G4AttValue>*> vecVals =
1374                attHolder->GetAttValues();
1375             for (std::size_t i = 0; i < vecDefs.size(); ++i) {
1376                const std::vector<G4AttValue> * vals = vecVals[i];
1377 
1378                std::vector<G4AttValue>::const_iterator iValue;
1379 
1380                for (iValue = vals->begin(); iValue != vals->end(); ++iValue) {
1381                   const G4String& valueName = iValue->GetName();
1382                   const G4String& value = iValue->GetValue();
1383 
1384                   if(valueName == "Solid") {
1385                      if(ssSolids.str() == "")
1386                         ssSolids << "Solid Name:  " << value;
1387                      else
1388                         ssSolids << ", " << value;
1389                   }
1390 
1391                   if(valueName == "Material") {
1392                      if(ssMaterials.str() == "")
1393                         ssMaterials << "Material Name:  " << value;
1394                      else
1395                         ssMaterials << ", " << value;
1396                   }
1397                }
1398             }
1399          }
1400       }
1401       // FWJ Mouseover for trajectories
1402       else if(node->getTypeId() == SoLineSet::getClassTypeId()) {
1403          // G4cout << "Trajectory!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << G4endl;
1404          G4AttHolder* attHolder = dynamic_cast<G4AttHolder*>(node);
1405          if(attHolder && attHolder->GetAttDefs().size()) {
1406             std::string strTrajPoint = "G4TrajectoryPoint:";
1407             std::ostringstream oss;
1408             G4String t1, t1Ch, t2, t3, t4;
1409             for (std::size_t i = 0; i < attHolder->GetAttDefs().size(); ++i) {
1410                // G4cout << "Getting index " << i << " from attHolder" << G4endl;
1411                // No, returns a vector!
1412                //   G4AttValue* attValue = attHolder->GetAttValues()[i];
1413                const std::vector<G4AttValue>* vals = attHolder->GetAttValues()[i];
1414                std::vector<G4AttValue>::const_iterator iValue;
1415                for (iValue = vals->begin(); iValue != vals->end(); ++iValue) {
1416                   const G4String& valueName = iValue->GetName();
1417                   const G4String& value = iValue->GetValue();
1418                   // G4cout << "  valueName = " << valueName << G4endl;
1419                   // G4cout << "  value = " << value << G4endl;
1420                   // LINE 1
1421                   if (valueName == "PN") t1 = value;
1422                   if (valueName == "Ch") {
1423                      if (atof(value.c_str()) > 0)
1424                         t1Ch = "    +";
1425                      else
1426                         t1Ch = "    ";
1427                      t1Ch += value;
1428                   }
1429                   if (valueName == "PDG") {
1430                      t1 += "    ";
1431                      t1 += value;
1432                      t1 += t1Ch;
1433                      This->mouseOverTextLogName->string.setValue(t1);
1434                   }
1435                   //                  G4cout << "  t1 = " << t1 << G4endl;
1436                   // LINE 2
1437                   if (valueName == "EventID") t2 = "Evt " + value;
1438                   if (valueName == "ID") t2 += "    Trk " + value;
1439                   if (valueName == "PID") {
1440                      t2 += "    Prt " + value;
1441                      This->mouseOverTextSolid->string.setValue(t2);
1442                   }
1443                   // LINE 3
1444                   if (valueName == "IKE") t3 = "KE " + value;
1445                   if (valueName == "IMom") {
1446                      // Remove units
1447                      std::size_t ipos = value.rfind(" ");
1448                      G4String value1 = value;
1449                      value1.erase(ipos);
1450                      t3 += "    P (" + value1 + ")";
1451                   }
1452                   if (valueName == "IMag") {
1453                      t3 += " " + value + "/c";
1454                      //                     t3 += " " + value;
1455                      This->mouseOverTextMaterial->string.setValue(t3);
1456                   }
1457                   // LINE 4
1458                   if (valueName == "NTP") {
1459                      std::ostringstream t4oss;
1460                      t4oss << "TrjPts " <<  value;
1461                      t4oss << "    Pos " << pp->getPoint()[0] << " " << pp->getPoint()[1] <<
1462                         " " << pp->getPoint()[2];
1463                      This->mouseOverTextZPos->string.setValue(SbString(t4oss.str().c_str()));
1464                   }
1465                }
1466 //             G4cout << "  NOW CALLING G4AttCheck" << G4endl;
1467 //             G4cout << G4AttCheck(attHolder->GetAttValues()[i],
1468 //                                     attHolder->GetAttDefs()[i]);
1469 //             oss << G4AttCheck(attHolder->GetAttValues()[i],
1470 //                                  attHolder->GetAttDefs()[i]);
1471 //             if(oss.str().find(strTrajPoint) != std::string::npos) {
1472 //                // Last attribute displayed was a trajectory point.  Since we
1473 //                // want abbreviated output, display the last one and exit
1474 //                // (unless we're already at the last (and only) trajectory point)
1475 //                if(i != attHolder->GetAttDefs().size()-1) {
1476 //                   G4cout << G4AttCheck(
1477 //                      attHolder->GetAttValues()[attHolder->GetAttDefs().size()-1],
1478 //                      attHolder->GetAttDefs()[attHolder->GetAttDefs().size()-1]);
1479 //                   }
1480 //                   break;
1481 //                }
1482             }
1483          }
1484          This->setSuperimpositionEnabled(This->superimposition, TRUE);
1485          This->scheduleRedraw();
1486          eventCB->setHandled();
1487          return;
1488       }
1489 
1490       bool redraw = false;
1491       if(std::string(This->mouseOverTextLogName->string.getValues(0)->getString()) != sLogName) {
1492          This->mouseOverTextLogName->string.setValue(SbString(sLogName.c_str()));
1493          redraw = true;
1494       }
1495       if(std::string(This->mouseOverTextSolid->string.getValues(0)->getString()) != ssSolids.str()) {
1496          This->mouseOverTextSolid->string.setValue(SbString(ssSolids.str().c_str()));
1497          redraw = true;
1498       }
1499       if(std::string(This->mouseOverTextMaterial->string.getValues(0)->getString()) != ssMaterials.str()) {
1500          This->mouseOverTextMaterial->string.setValue(SbString(ssMaterials.str().c_str()));
1501          redraw = true;
1502       }
1503       if(std::string(This->mouseOverTextZPos->string.getValues(0)->getString()) != ssZPos.str()) {
1504          This->mouseOverTextZPos->string.setValue(SbString(ssZPos.str().c_str()));
1505          redraw = true;
1506       }
1507 
1508       if(redraw) {
1509          This->setSuperimpositionEnabled(This->superimposition, TRUE);
1510          This->scheduleRedraw();
1511       }
1512 
1513       eventCB->setHandled();
1514    }
1515    else {
1516       if(std::string(This->mouseOverTextLogName->string.getValues(0)->getString()) != "") {
1517          This->mouseOverTextLogName->string.setValue(SbString(""));
1518          This->scheduleRedraw();
1519       }
1520       if(std::string(This->mouseOverTextSolid->string.getValues(0)->getString()) != "") {
1521          This->mouseOverTextSolid->string.setValue(SbString(""));
1522          This->scheduleRedraw();
1523       }
1524       if(std::string(This->mouseOverTextMaterial->string.getValues(0)->getString()) != "") {
1525          This->mouseOverTextMaterial->string.setValue(SbString(""));
1526          This->scheduleRedraw();
1527       }
1528       if(std::string(This->mouseOverTextZPos->string.getValues(0)->getString()) != "") {
1529          This->mouseOverTextZPos->string.setValue(SbString(""));
1530          This->scheduleRedraw();
1531       }
1532    }
1533 }
1534 
1535 
1536 // Called by hitting PageUp during animation.
1537 void G4OpenInventorQtExaminerViewer::incSpeed() {
1538    if (std::ceil(animateBtwPtsPeriod * 100) >= 4) {
1539       if (speedStep > 0.08)
1540          speedStep -= 0.02;
1541       else
1542          speedStep = 0.02;
1543       animateBtwPtsPeriod -= speedStep;
1544    } else
1545       animateBtwPtsPeriod = 0.0;
1546 
1547    if (currentState != PAUSED_ANIMATION) {
1548       int lastIdx = (int) refParticleTrajectory.size() - 1;
1549       if (refParticleIdx < lastIdx && !animateSensor->isScheduled())
1550          animateRefParticle();
1551    }
1552 }
1553 
1554 // Called by hitting PageDown during animation.
1555 void G4OpenInventorQtExaminerViewer::decSpeed() {
1556    animateBtwPtsPeriod += speedStep;
1557    if (animateBtwPtsPeriod < MIN_SPEED) {
1558       if (std::floor(animateBtwPtsPeriod * 100) == 12) { // Errors in double representation
1559     speedStep = 0.08;
1560       } else if (animateBtwPtsPeriod > 0.12)
1561          speedStep += 0.02;
1562    } else {
1563       animateBtwPtsPeriod = MIN_SPEED;
1564       speedStep = START_STEP;
1565       maxSpeed = 0.0f;
1566       if (animateSensor->isScheduled())
1567          animateSensor->unschedule();
1568    }
1569 }
1570 
1571 
1572 // Based on the user's interaction the speed indicator bar needs to be adjusted
1573 
1574 void G4OpenInventorQtExaminerViewer::updateSpeedIndicator(void)
1575 {
1576    assert(this->sgeometry != NULL);
1577 
1578    SbVec3f * points = this->sgeometry->point.startEditing();
1579 
1580    if (points[10][0] == 0.0f)
1581       this->animSpeedOutlineSwitch->whichChild.setValue(SO_SWITCH_ALL);
1582    if (points[14][0] == 0.0f)
1583       this->animSpeedSwitch->whichChild.setValue(SO_SWITCH_ALL);
1584    points[10][0] = this->maxSpeed;
1585    points[11][0] = this->maxSpeed;
1586    points[14][0] = this->maxSpeed;
1587    points[15][0] = this->maxSpeed;
1588    this->sgeometry->point.finishEditing();
1589 
1590    if (this->maxSpeed == 0.0f) {
1591       this->animSpeedOutlineSwitch->whichChild.setValue(SO_SWITCH_NONE);
1592       this->animSpeedSwitch->whichChild.setValue(SO_SWITCH_NONE);
1593    }
1594 }
1595 
1596 
1597 void G4OpenInventorQtExaminerViewer::actualRedraw(void) {
1598   switch (currentState) {
1599   case ANIMATION:
1600   case REVERSED_ANIMATION:
1601   case PAUSED_ANIMATION:
1602     updateSpeedIndicator();
1603     SoQtExaminerViewer::actualRedraw();
1604     break;
1605   default:
1606     SoQtExaminerViewer::actualRedraw();
1607     break;
1608   }
1609 }
1610 
1611 
1612 void G4OpenInventorQtExaminerViewer::setReferencePath(SoLineSet *lineset,
1613        SoCoordinate3 *coords, bool append)
1614 {
1615    // TODO:  Color the reference path
1616    // Disable the color stuff for now: changes all trajectories
1617    // FWJ See G4OpenInventorXtExaminerViewer.cc for test code
1618 
1619    // The trajectory is composed of all the polyline segments in the
1620    // multiple value field (SoMFInt32) numVertices.
1621    // For each of the numVertices.getNum()* polyline segments,
1622    // retrieve the points from the SoCoordinate3 node
1623 
1624    SbVec3f refParticlePt;
1625 
1626    if(!append)
1627       refParticleTrajectory.clear();
1628 
1629    for(int i = 0; i < lineset->numVertices.getNum(); ++i) {
1630       for(int j = 0; j < lineset->numVertices[i]; ++j) {
1631          refParticlePt = coords->point[j];
1632          refParticleTrajectory.push_back(refParticlePt);
1633       }
1634    }
1635    // Remove points that are too close to each other
1636    evenOutRefParticlePts();
1637    setReferencePathZPos();
1638    getSceneElements();
1639    sortElements();
1640 }
1641 
1642 
1643 void G4OpenInventorQtExaminerViewer::setReferencePathZPos()
1644 {
1645    refZPositions.clear();
1646    refZPositions.push_back(0);
1647    float dist;
1648    for(unsigned int i=0; i < refParticleTrajectory.size() - 1; ++i) {
1649       dist = (refParticleTrajectory[i] - 
1650               refParticleTrajectory[i + 1]).length();
1651       refZPositions.push_back(refZPositions[i] + dist);
1652    }
1653 }
1654 
1655 
1656 void G4OpenInventorQtExaminerViewer::findAndSetRefPath()
1657 {
1658    SoSearchAction action;
1659    action.setType(SoLineSet::getClassTypeId(),false);
1660    action.setInterest(SoSearchAction::ALL);
1661    action.apply(getSceneGraph());
1662 
1663    SoPathList &pathList = action.getPaths();
1664 
1665    if(pathList.getLength() != 0) {
1666 
1667       SoCoordinate3 * coords = NULL;
1668       std::vector<SoCoordinate3 *> coordvec;
1669       std::vector<SoLineSet *> linevec;
1670 
1671       bool refPathFound = false;
1672       for(int i = 0; i < pathList.getLength(); ++i) {
1673          SoFullPath *path = (SoFullPath *)pathList[i];
1674 
1675          G4AttHolder* attHolder = dynamic_cast<G4AttHolder*>(path->getTail());
1676          if(attHolder != nullptr)
1677          {
1678          for (std::size_t j = 0; j < attHolder->GetAttDefs().size(); ++j) {
1679             std::ostringstream oss;
1680             oss << G4AttCheck(attHolder->GetAttValues()[j],
1681                               attHolder->GetAttDefs()[j]);
1682 
1683             std::string findStr = "Type of trajectory (Type): ";
1684             std::string compareValue = "REFERENCE";
1685             std::size_t idx = oss.str().find(findStr);
1686 
1687             if(idx != std::string::npos) {
1688                if(oss.str().substr(idx + findStr.size(),
1689                                    compareValue.size()) == compareValue) {
1690                   coords = getCoordsNode(path);
1691                   if(coords != NULL) {
1692                      refPathFound = true;
1693                      coordvec.push_back(coords);
1694                      linevec.push_back((SoLineSet *)path->getTail());
1695                   }
1696                   break;
1697                }
1698             }
1699 
1700             findStr = "Track ID (ID): ";
1701             idx = oss.str().find(findStr);
1702             if(idx != std::string::npos) {
1703                //index all primary tracks
1704                std::string tmpstr = oss.str().substr(idx + findStr.size(),1);
1705                std::istringstream buffer(tmpstr);
1706                int num;
1707                buffer >> num;
1708                if(num == 1) {
1709 
1710                   // Check if next character is a number, 
1711                   // in which case we don't have Track ID 1
1712                   // FWJ attempt to fix Coverity issue.
1713                   char nextChar = oss.str().at(idx+findStr.size()+1);
1714                   // const char * nextChar = 
1715                   // oss.str().substr(idx + findStr.size() + 1,1).c_str();
1716                   if(std::isdigit(nextChar))
1717                      break; //Not a primary track, continue with next track
1718 
1719                   coords = getCoordsNode(path);
1720                   if(coords != NULL) {
1721                      coordvec.push_back(coords);
1722                      linevec.push_back((SoLineSet *)path->getTail());
1723                      break; //Found coords node, continue with next track
1724                   }
1725                }
1726                else
1727                   break;  //Not a primary track, continue with next track
1728             }
1729             else{
1730                //Not a Track ID attribute, fall through
1731             }
1732          }
1733          }
1734 
1735          if(refPathFound)
1736             break;
1737       }
1738 
1739       if(coordvec.empty())
1740          return;    //No track with a Coordinate3 node found
1741 
1742       if(refPathFound) {
1743          //set ref path to last traj, coord in the vecs
1744          setReferencePath(linevec.back(), coordvec.back());
1745          return;
1746       }
1747       //else
1748 
1749       int longestIdx = 0;
1750       float longestLength = 0.0;
1751       // For all paths
1752       for(unsigned int i=0;i < linevec.size(); ++i) {
1753 
1754          //First generate a vector with all the points in this lineset
1755          std::vector<SbVec3f> trajectory;
1756          // For all lines in the i path
1757          for(int j=0; j < linevec[i]->numVertices.getNum(); ++j) {
1758             // For all points in line j
1759             for(int k=0; k < linevec[i]->numVertices[j]; ++k) {
1760                trajectory.push_back(coordvec[i]->point[k]);
1761             }
1762          }
1763 
1764          // Then calculate the total length
1765          float tmpLength=0.0;
1766          for(unsigned int j=0; j < trajectory.size() - 1; ++j) {
1767             tmpLength += (trajectory[j] - trajectory[j + 1]).length();
1768          }
1769 
1770          if(tmpLength > longestLength) {
1771             longestIdx = i;
1772             longestLength = tmpLength;
1773          }
1774       }
1775 
1776       // Set the longest path as the reference path
1777       setReferencePath(linevec[longestIdx], coordvec[longestIdx]);
1778    }
1779 }
1780 
1781 
1782 SoCoordinate3 * G4OpenInventorQtExaminerViewer::getCoordsNode(SoFullPath *path)
1783 {
1784    SoLineSet *trajectory = (SoLineSet *)path->getTail();
1785    SoSeparator * grpNode = (SoSeparator*)(((SoFullPath*)path)->getNodeFromTail(1));
1786    int nodeIndex = grpNode->findChild(trajectory);
1787    SoNode * tmpNode;
1788 
1789    // We allow only 100 iterations, in case the node isn't found
1790    // (should take only a few iterations)
1791    for (int i = 0; i < 100; ++i) {
1792       --nodeIndex;
1793 
1794       tmpNode = grpNode->getChild(nodeIndex);
1795       if(tmpNode->getTypeId() == SoCoordinate3::getClassTypeId()) {
1796          //node found
1797          return (SoCoordinate3 *)tmpNode;
1798       }
1799    }
1800    return NULL; //coords node not found
1801 }
1802 
1803 
1804 // Displays scene elements on the right side of listsDialog.
1805 // else: scene graph is searched for Geant4_SoPolyhedron type nodes
1806 void G4OpenInventorQtExaminerViewer::getSceneElements()
1807 {
1808    std::string field, eltName;
1809 
1810    std::map<std::string, int> duplicates;
1811    std::map<std::string, int> sceneElts;
1812    SoSearchAction search;
1813    Geant4_SoPolyhedron *node;
1814    SoGroup *root = (SoGroup *)getSceneManager()->getSceneGraph();
1815 
1816    SoBaseKit::setSearchingChildren(TRUE);
1817 
1818    search.reset();
1819    search.setSearchingAll(TRUE);
1820    search.setInterest(SoSearchAction::ALL);
1821    search.setType(Geant4_SoPolyhedron::getClassTypeId(), 0);
1822 
1823    // FWJ DEBUG
1824    //   G4cout << "Searching for elements....." << G4endl;
1825    search.apply(root);
1826 
1827    SoPathList &pl = search.getPaths();
1828 
1829 
1830    // First find which names occur more than once so we can append a counter to them
1831    for (int i = 0; i < pl.getLength(); i++) {
1832       SoFullPath *path = (SoFullPath *)pl[i];
1833       node = (Geant4_SoPolyhedron *)path->getTail();
1834       eltName = node->getName();
1835       //      G4cout << "  FOUND " << i << "  " << eltName << G4endl;
1836       if(duplicates.count(eltName))
1837          duplicates[eltName]++;
1838       else
1839          duplicates[eltName] = 1;
1840    }
1841 
1842    for(int i = 0; i < pl.getLength(); i++) {
1843       float x,y,z;
1844       std::stringstream ssCount;
1845       SoFullPath *path = (SoFullPath *)pl[i];
1846       node = (Geant4_SoPolyhedron *)path->getTail();
1847       eltName = node->getName();
1848       field = eltName;
1849       if(duplicates[eltName] == 1)
1850          ssCount << "";//duplicates[field]
1851       else {
1852          if(sceneElts.count(eltName))
1853             sceneElts[eltName]++;
1854          else
1855             sceneElts[eltName] = 1;
1856 
1857          ssCount << sceneElts[eltName];
1858          field += "_";
1859       }
1860 
1861       field += ssCount.str();
1862 
1863       SoGetBoundingBoxAction bAction(getViewportRegion());
1864       bAction.apply(path);
1865       SbBox3f bBox = bAction.getBoundingBox();
1866 
1867       SbVec3f centr = bBox.getCenter();
1868       centr.getValue(x,y,z);
1869 
1870       path->ref();
1871       sceneElement el = { field, path, centr, 0.0 };
1872       sceneElements.push_back(el);
1873    }
1874 }
1875 
1876 
1877 float G4OpenInventorQtExaminerViewer::sqrlen(const SbVec3f &a)
1878 {
1879    float x,y,z;
1880    a.getValue(x,y,z);
1881    return x*x + y*y + z*z;
1882 }
1883 
1884 
1885 void G4OpenInventorQtExaminerViewer::distanceToTrajectory(const SbVec3f &q,
1886                                                           float &dist,
1887                                                 SbVec3f &closestPoint,
1888                                                           int &index)
1889 {
1890    // a : Previous point on trajectory
1891    // b : Next point on trajectory
1892    // q : the point in space
1893    // dab, daq, dbq: distance between a & b, a & q, b & q
1894    //    
1895    // Theory:  A point p on a line ab is defined as:
1896    //
1897    //         p(t) = a+t?(b?a)
1898    //
1899    //       note: All are vectors except the parameter t
1900    //
1901    // When t is between 0 and 1 the point p is situated between a and b on ab.
1902    // The point p is defined in terms of the parameter t, subsequently so does
1903    // the distance from the query point q to the point p. To find the minimum
1904    // of that distance we differentiate it and set equal to zero:
1905    //
1906    //       diff(Norm(p(t)- q)) = 0
1907    //
1908    //     note: diff means taking the derivative with regard to t
1909    //
1910    // The resulting t is given in the code below. The square of the distance
1911    // between p and q is given by:
1912    //
1913    //       d^2 = (Norm(p(t)-q))^2
1914    //
1915    // The expression found is given in the code below (current_dist)
1916    //
1917    // Ref: http://programmizm.sourceforge.net/blog/2012/
1918    //           distance-from-a-point-to-a-polyline
1919    //
1920    //    --PLG
1921 
1922    const std::size_t count = refParticleTrajectory.size();
1923    assert(count>0);
1924 
1925    SbVec3f b = refParticleTrajectory[0];
1926    SbVec3f dbq = b - q;
1927    float sqrDist = sqrlen(dbq);
1928    closestPoint = b;
1929    index = 0;
1930    for (std::size_t i = 1; i < count; ++i) {
1931       const SbVec3f a = b;
1932       const SbVec3f daq = dbq;
1933       b = refParticleTrajectory[i];
1934       dbq = b - q;
1935       const SbVec3f dab = a - b;
1936 
1937       float dab_x, dab_y, dab_z;
1938       dab.getValue(dab_x,dab_y,dab_z);
1939       float daq_x, daq_y, daq_z;
1940       daq.getValue(daq_x, daq_y, daq_z);
1941       float dbq_x, dbq_y, dbq_z;
1942       dbq.getValue(dbq_x, dbq_y, dbq_z);
1943 
1944       const float inv_sqrlen = 1./sqrlen(dab);
1945       const float t = (dab_x*daq_x + dab_y*daq_y + dab_z*daq_z)*inv_sqrlen;
1946 
1947       if (t<0.) {
1948          // The trajectory point occurs before point a
1949          // Go to the next point
1950          continue;
1951       }
1952       float current_dist;
1953       if (t<=1.) {
1954          // The trajectory point occurs between a and b.
1955          // Compute the distance to that point
1956          current_dist = daq_x*daq_x + daq_y*daq_y + daq_z*daq_z
1957             - t*(daq_x*dab_x + daq_y*dab_y + daq_z*dab_z)
1958             + t*t*(dab_x*dab_x + dab_y*dab_y + dab_z*dab_z);
1959       }
1960       else { //t>1.
1961          // The trajectory point occurs after b.
1962          // Get the distance to point b
1963          current_dist = sqrlen(dbq);
1964       }
1965 
1966       if (current_dist < sqrDist) {
1967          sqrDist = current_dist;
1968          closestPoint = a + t*(b-a);
1969          index = (int) i;
1970       }
1971    }
1972 
1973    dist = std::sqrt(sqrDist);
1974 }
1975 
1976 
1977 void G4OpenInventorQtExaminerViewer::sortElements()
1978 {
1979    if(refParticleTrajectory.empty())
1980       return;
1981 
1982    float * trajLength = new float[refParticleTrajectory.size()];
1983    typedef std::map<elementForSorting, sceneElement> sortedMap;
1984    sortedMap sorted;
1985 
1986    // For every point on the reference trajectory, compute
1987    // the total length from the start
1988    SbVec3f prevPoint;
1989    std::vector<SbVec3f>::iterator itRef = refParticleTrajectory.begin();
1990    int trajIndex = 0;
1991    prevPoint = *itRef;
1992    trajLength[trajIndex] = 0.0;
1993    ++itRef;
1994    ++trajIndex;
1995    for(; itRef != refParticleTrajectory.end(); ++itRef, ++trajIndex) {
1996       trajLength[trajIndex] = trajLength[trajIndex-1] + (*itRef - prevPoint).length();
1997       prevPoint = *itRef;
1998    }
1999 
2000    // Compute the smallest distance between the element
2001    // and the reference trajectory (find the closest point),
2002    // then map the element to the trajectory length of that
2003    // point (calculated above)
2004    SoGetBoundingBoxAction bAction(getViewportRegion());
2005    SbVec3f elementCoord;
2006    std::vector<sceneElement>::iterator itEl;
2007    int elementIndex;
2008    elementForSorting el;
2009    for(itEl = sceneElements.begin(), elementIndex = 0;
2010        itEl != sceneElements.end(); ++itEl, ++elementIndex) {
2011       bAction.apply(itEl->path);
2012 
2013       // FWJ sceneElement already has a center
2014       elementCoord = itEl->center;
2015       // ... and this sometimes returns an empty box!
2016       //      elementCoord = bAction.getBoundingBox().getCenter();
2017       //      if (bAction.getBoundingBox().isEmpty()) {
2018       //         G4cout << "sortElements: Box is empty!" << G4endl;
2019       //         G4cout << "   element name=" << itEl->name << G4endl;
2020       //      }
2021 
2022       int index;
2023       distanceToTrajectory(elementCoord, el.smallestDistance, el.closestPoint, index);
2024       itEl->closestPointZCoord = el.closestPointZCoord = trajLength[index];
2025       el.distanceToBeamlineStart = (itEl->center - refParticleTrajectory[0]).length();
2026 
2027       // This map of the scene elements (or their coordinates rather)
2028       // is automatically sorted by trajectory length (Z coord), then
2029       // by the distance between the element and the point in case the Z coord
2030       // is the same as another element.  This is done by using as a key
2031       // an element structure which implements the operator for weak ordering
2032       sorted.insert(std::make_pair(el,*itEl));
2033    }
2034 
2035    // store the sorted elements into the vector field
2036    sceneElements.clear();
2037 
2038    sortedMap::iterator itSorted = sorted.begin();
2039    for(; itSorted != sorted.end(); itSorted++)
2040       sceneElements.push_back(itSorted->second);
2041 
2042    zcoordSetFlag = true;
2043 
2044    createElementsList();
2045 
2046    delete[] trajLength;
2047 }
2048 
2049 
2050 void G4OpenInventorQtExaminerViewer::createElementsList()
2051 {
2052    // FWJ DEBUG
2053    //   G4cout << "Populating ELEMENT LIST..." << G4endl;
2054 
2055    AuxWindowDialog->listWidget1->clear();
2056    //   int size = sceneElements.size();
2057 
2058    std::vector<sceneElement>::const_iterator it;
2059    std::stringstream ss;
2060 
2061    for(it=sceneElements.begin(); it!=sceneElements.end(); ++it) {
2062       ss << it->name;
2063       if(zcoordSetFlag)
2064          ss << " [" << it->closestPointZCoord << "]";
2065 
2066       new QListWidgetItem(ss.str().c_str(), AuxWindowDialog->listWidget1); 
2067       ss.str("");
2068    }
2069 }
2070 
2071 
2072 // Called when user clicks a scene element in listsDialog.
2073 // Zooms onto that element.
2074 void
2075 G4OpenInventorQtExaminerViewer::LookAtSceneElementCB(QListWidgetItem* item)
2076 {
2077    // FWJ DEBUG
2078    //   G4cout << "AuxWindow: listWidget1 select element CALLBACK" << G4endl;
2079 
2080    SoCamera * cam = getCamera();
2081 
2082    if (SoQtExaminerViewer::isAnimating())
2083       stopAnimating();
2084 
2085    if (currentState == ANIMATION || currentState == REVERSED_ANIMATION
2086        || currentState == PAUSED_ANIMATION ) {
2087       if (animateSensor->isScheduled())
2088          animateSensor->unschedule();
2089       setSuperimpositionEnabled(superimposition, FALSE);
2090       maxSpeed = 0.0f;
2091       scheduleRedraw();
2092       restoreCamera();
2093       currentState = prevState;
2094    } else if (currentState == VIEWPOINT)
2095       setSuperimpositionEnabled(superimposition, FALSE);
2096 
2097    std::string elementField = qPrintable(item->text());
2098 
2099    std::size_t idx = elementField.find_last_of("[");
2100    if(idx == std::string::npos)
2101       idx = elementField.size(); //if "[" not found for whatever reason (list not sorted)
2102    else
2103       idx--; // To get rid of the space that is between the name and '['
2104 
2105    bool error = false;
2106    SoFullPath *path;
2107    SoSearchAction search;
2108    SoNode *root = getSceneManager()->getSceneGraph();
2109    int counter = 0;
2110    std::size_t idxUnderscore = elementField.find_last_of("_");
2111 
2112    parseString<int>(counter, 
2113                           elementField.substr(idxUnderscore + 1, idx), error);
2114 
2115    SoBaseKit::setSearchingChildren(TRUE);
2116    search.reset();
2117    search.setSearchingAll(TRUE);
2118 
2119    // G4cout << "  Starting search for elementField " << elementField 
2120    //        << G4endl;
2121 
2122    if(error) { // No counter is present => element name was not modified
2123       curEltName = elementField.substr(0, idx);
2124       search.setName(curEltName.c_str());
2125       search.apply(root);
2126 
2127       path = (SoFullPath *)search.getPath();
2128    }
2129    else {
2130       curEltName = elementField.substr(0, idxUnderscore);
2131       search.setInterest(SoSearchAction::ALL);
2132       search.setName(curEltName.c_str());
2133       search.apply(root);
2134 
2135       SoPathList &pl = search.getPaths();
2136       path = (SoFullPath *)pl[counter - 1]; // Since counter starts at 1, not 0
2137    }
2138 
2139    G4ThreeVector global;
2140 
2141    // FWJ FLIP THIS
2142    if ((idx > 0) && (path)) {
2143 
2144       if(!refParticleTrajectory.empty()) {
2145 
2146          SoGetBoundingBoxAction bAction(getViewportRegion());
2147          bAction.apply(path);
2148          SbBox3f bBox = bAction.getBoundingBox();
2149          SbVec3f elementCoord = bBox.getCenter();
2150 
2151          refParticleIdx = 0;
2152          SbVec3f p;
2153 
2154          float absLengthNow, absLengthMin;
2155          int maxIdx = (int) refParticleTrajectory.size() - 2;
2156          int targetIdx = 0;
2157          SbVec3f dir;
2158 
2159          p = refParticleTrajectory[refParticleIdx];
2160          absLengthMin = (p - elementCoord).length();
2161          refParticleIdx++;
2162 
2163          // Find a ref. particle's point closest to element's global coords
2164          while (refParticleIdx < maxIdx) {
2165             p = refParticleTrajectory[refParticleIdx];
2166             absLengthNow = (p - elementCoord).length();
2167 
2168             if (absLengthNow < absLengthMin) {
2169                absLengthMin = absLengthNow;
2170                targetIdx = refParticleIdx;
2171             }
2172             refParticleIdx++;
2173          }
2174 
2175          if (currentState != BEAMLINE) { // Set up default zoom
2176             SbVec3f p1, pN;
2177             currentState = BEAMLINE;
2178             prevParticleDir = SbVec3f(0,0,0); //so that moveCamera() knows sets default parameters
2179             
2180             p1 = prevPt = refParticleTrajectory[0];
2181             pN = refParticleTrajectory[refParticleTrajectory.size() - 1];
2182             distance = (pN - p1).length() / 10;
2183 
2184             // FWJ Rather than switching to a default height, it is more flexible
2185             // to keep the same height(magnification) while moving the camera.
2186             // if (cam->isOfType(SoOrthographicCamera::getClassTypeId())) {
2187             //    ((SoOrthographicCamera *) cam)->height.setValue(defaultHeight);
2188             // // FWJ Restore the default height instead of hard-wired value
2189             // // ((SoOrthographicCamera *) cam)->height.setValue(10000.0f);
2190             // }
2191             // else if (cam->isOfType(SoPerspectiveCamera::getClassTypeId()))
2192 
2193             // FWJ required to avoid extreme perspective after camera move:
2194             if (cam->isOfType(SoPerspectiveCamera::getClassTypeId()))
2195                ((SoPerspectiveCamera*)cam)->heightAngle.setValue(defaultHeightAngle);
2196 
2197          } else {
2198             if (cam->isOfType(SoPerspectiveCamera::getClassTypeId()))
2199                distance = (prevPt - cam->position.getValue()).length();
2200          }
2201          refParticleIdx = targetIdx;
2202 
2203          //////////////////////////////////////////////////////////////
2204          setSuperimpositionEnabled(superimposition, TRUE);
2205          axisSwitch->whichChild.setValue(SO_SWITCH_NONE);
2206          animSpeedOutlineSwitch->whichChild.setValue(SO_SWITCH_NONE);
2207          animSpeedSwitch->whichChild.setValue(SO_SWITCH_NONE);
2208          scheduleRedraw();
2209          //////////////////////////////////////////////////////////////
2210 
2211          moveCamera(distance);
2212 
2213       }
2214       
2215       else {
2216          offsetFromCenter.setValue(0, 0, 1);
2217          distance = 50;// small number since using viewAll() for default zoom
2218          upVector.setValue(0, 1, 0);
2219          moveCamera(distance);
2220          cam->viewAll(path, getViewportRegion());
2221       }
2222    }
2223 
2224 }
2225 
2226 
2227 void G4OpenInventorQtExaminerViewer::FileLoadRefPathCB()
2228 {
2229    //   G4cout << "File: Load Ref Path CALLBACK" << G4endl;
2230 
2231    QFileDialog filedialog(getParentWidget(), tr("Load Reference Path"));
2232    filedialog.setFileMode(QFileDialog::AnyFile);
2233    filedialog.setFont(*font);
2234    if (!filedialog.exec()) return;
2235    QStringList filenameinlist = filedialog.selectedFiles();
2236    QString filenamein = filenameinlist[0];
2237 
2238    std::ifstream ifs(qPrintable(filenamein));
2239    if(ifs.is_open()) {
2240       refParticleTrajectory.clear();
2241       float x,y,z;
2242       while(ifs >> x >> y >> z) {
2243          refParticleTrajectory.push_back(SbVec3f(x,y,z));
2244       }
2245       ifs.close();
2246    } else {
2247       QMessageBox msgbox;
2248       msgbox.setFont(*font);
2249       QString messagetxt = "Reference Path file not found: ";
2250       messagetxt.append(filenamein);
2251       msgbox.setText(messagetxt);
2252       msgbox.exec();
2253       return;
2254    }
2255    if (refParticleTrajectory.size() < 2) {
2256       QMessageBox msgbox;
2257       msgbox.setFont(*font);
2258       QString messagetxt = "Invalid Reference Path";
2259       msgbox.setText(messagetxt);
2260       msgbox.exec();
2261       return;
2262    }
2263    // Following setReferencePath() ...
2264    evenOutRefParticlePts();
2265    setReferencePathZPos();
2266    getSceneElements();
2267    sortElements();
2268 }
2269 
2270 
2271 void G4OpenInventorQtExaminerViewer::FileSaveRefPathCB()
2272 {
2273    //   G4cout << "File: Save Ref Path CALLBACK" << G4endl;
2274 
2275    QFileDialog filedialog(getParentWidget(), tr("Save Reference Path"));
2276    filedialog.setFileMode(QFileDialog::AnyFile);
2277    // To enable confirmation of overwriting
2278    filedialog.setAcceptMode(QFileDialog::AcceptSave);
2279    filedialog.setFont(*font);
2280    if (!filedialog.exec()) return;
2281    QStringList filenameinlist = filedialog.selectedFiles();
2282    QString filenamein = filenameinlist[0];
2283 
2284    std::ofstream ofs(qPrintable(filenamein));
2285    if (ofs.is_open()) {
2286       float x,y,z;
2287       for (unsigned int i=0; i < refParticleTrajectory.size(); ++i) {
2288          refParticleTrajectory[i].getValue(x,y,z);
2289          ofs << x << " " << y << " " << z << "\n";
2290       }
2291       ofs.close();
2292    } else {
2293       QMessageBox msgbox;
2294       msgbox.setFont(*font);
2295       QString messagetxt = "Error opening file ";
2296       messagetxt.append(filenamein);
2297       msgbox.setText(messagetxt);
2298       msgbox.exec();
2299    }
2300 
2301 }
2302 
2303 void G4OpenInventorQtExaminerViewer::evenOutRefParticlePts()
2304 {
2305    if(refParticleTrajectory.empty())
2306       return;
2307 
2308    SbVec3f p1, p2, p3, dirNow, dirNxt, dir, p2_tmp, p_start, p_corner, p_nxt;
2309    float avgDistBtwPts = 0;
2310    float totalDistBtwPts = 0;
2311    std::vector<SbVec3f> newRefParticleTrajectory;
2312    SbVec3f refPoint;
2313    std::size_t size = refParticleTrajectory.size() - 1;
2314    int numOfPts = 0;
2315    for (std::size_t i = 0; i < size; ++i) {
2316       p1 = refParticleTrajectory[i];
2317       p2 = refParticleTrajectory[i + 1];
2318       if (p1 == p2)
2319          continue;
2320       numOfPts++;
2321       totalDistBtwPts += (p2 - p1).length();
2322    }
2323    // Nothing useful to do (and fix Coverity)
2324    if (numOfPts <= 2) return;
2325 
2326    avgDistBtwPts = totalDistBtwPts / numOfPts;
2327    float minDistAllowed = 0.75 * avgDistBtwPts;
2328    // float maxDistAllowed = 1.25 * avgDistBtwPts; // Pts tend to be close not far
2329 
2330    float x, y, z;
2331    std::size_t i = 0, j = 0;
2332    while (i < size) {
2333       p1 = refParticleTrajectory[i];
2334       p2 = refParticleTrajectory[i + 1];
2335 
2336       refPoint = p1;
2337       p1.getValue(x, y, z);
2338 
2339       newRefParticleTrajectory.push_back(refPoint);
2340 
2341       j = i;
2342       while ((p2 - p1).length() < minDistAllowed && j < (size - 1)) {
2343          j++;
2344 
2345          p1 = refParticleTrajectory[j];
2346          p2 = refParticleTrajectory[j + 1];
2347       }
2348       if (j != i)
2349          i = j + 1;
2350       else
2351          i++;
2352    }
2353 
2354    refParticleTrajectory.clear();
2355    refParticleTrajectory = newRefParticleTrajectory;
2356 }
2357 
2358 
2359 void G4OpenInventorQtExaminerViewer::saveCurCamera()
2360 {
2361    SoCamera *cam = getCamera();
2362    camB4Animation.viewportMapping = cam->viewportMapping.getValue();
2363    camB4Animation.position = cam->position.getValue();
2364    camB4Animation.orientation = cam->orientation.getValue();
2365    camB4Animation.aspectRatio = cam->aspectRatio.getValue();
2366    camB4Animation.nearDistance = cam->nearDistance.getValue();
2367    camB4Animation.farDistance = cam->farDistance.getValue();
2368    camB4Animation.focalDistance = cam->focalDistance.getValue();
2369 
2370    if (cam->isOfType(SoPerspectiveCamera::getClassTypeId())) {
2371       camB4Animation.height =
2372          ((SoPerspectiveCamera *) cam)->heightAngle.getValue();
2373       camB4Animation.camType = PERSPECTIVE;
2374    } else if (cam->isOfType(SoOrthographicCamera::getClassTypeId())) {
2375       camB4Animation.height =
2376          ((SoOrthographicCamera *) cam)->height.getValue();
2377       camB4Animation.camType = ORTHOGRAPHIC;
2378    }
2379 }
2380 
2381 
2382 void G4OpenInventorQtExaminerViewer::restoreCamera()
2383 {
2384    SoCamera *cam = getCamera();
2385 
2386    cam->viewportMapping = camB4Animation.viewportMapping;
2387    cam->position = camB4Animation.position;
2388    cam->orientation = camB4Animation.orientation;
2389    cam->aspectRatio = camB4Animation.aspectRatio;
2390    cam->nearDistance = camB4Animation.nearDistance;
2391    cam->farDistance = camB4Animation.farDistance;
2392    cam->focalDistance = camB4Animation.focalDistance;
2393 
2394    if (cam->isOfType(SoPerspectiveCamera::getClassTypeId())) {
2395       if (camB4Animation.camType == ORTHOGRAPHIC) {
2396          toggleCameraType();
2397          cam = getCamera();
2398          ((SoOrthographicCamera *) cam)->height.setValue(
2399                                                          camB4Animation.height);
2400       } else
2401          ((SoPerspectiveCamera *) cam)->heightAngle.setValue(
2402                                                              camB4Animation.height);
2403    } else if (cam->isOfType(SoOrthographicCamera::getClassTypeId())) {
2404       if (camB4Animation.camType == PERSPECTIVE) {
2405          toggleCameraType();
2406          cam = getCamera();
2407          ((SoPerspectiveCamera *) cam)->heightAngle.setValue(
2408                                                              camB4Animation.height);
2409       } else
2410          ((SoOrthographicCamera *) cam)->height.setValue(
2411                                                          camB4Animation.height);
2412    }
2413 }
2414 
2415 
2416 void G4OpenInventorQtExaminerViewer::animateSensorRotationCB(void *data, 
2417                                                              SoSensor *sensor)
2418 {
2419    SbTime curTime = SbTime::getTimeOfDay();
2420    G4OpenInventorQtExaminerViewer* This = (G4OpenInventorQtExaminerViewer*) data;
2421 
2422    SoTimerSensor* s = (SoTimerSensor*) sensor;
2423 
2424    float t = float((curTime - s->getBaseTime()).getValue())
2425       / This->animateBtwPtsPeriod;
2426 
2427    if ((t > 1.0f) || (t + s->getInterval().getValue() > 1.0f))
2428       t = 1.0f;
2429    SbBool end = (t == 1.0f);
2430 
2431    if (end) {
2432       This->animateSensorRotation->unschedule();
2433       if(This->rotCnt) {
2434          // rotations left
2435          This->rotateCamera();
2436       }
2437       else {
2438          // rotation over
2439          This->currentState = This->prevState;
2440          return;
2441       }
2442    }
2443 
2444 }
2445 
2446 
2447 // Called repeatedly during reference particle animation
2448 
2449 void G4OpenInventorQtExaminerViewer::animateSensorCB(void *data, 
2450                                                      SoSensor *sensor)
2451 {
2452    SbTime curTime = SbTime::getTimeOfDay();
2453    G4OpenInventorQtExaminerViewer* This = (G4OpenInventorQtExaminerViewer*) data;
2454    SoCamera *cam = This->getCamera();
2455    SoTimerSensor* s = (SoTimerSensor*) sensor;
2456 
2457    float t = float((curTime - s->getBaseTime()).getValue())
2458       / This->animateBtwPtsPeriod;
2459 
2460    if ((t > 1.0f) || (t + s->getInterval().getValue() > 1.0f))
2461       t = 1.0f;
2462    SbBool end = (t == 1.0f);
2463 
2464    cam->orientation = SbRotation::slerp(This->camStartOrient, This->camEndOrient, t);
2465    cam->position = This->camStartPos + (This->camEndPos - This->camStartPos) * t;
2466 
2467    if (end) {
2468       This->animateSensor->unschedule();
2469 
2470       if (This->currentState == ANIMATION) {
2471          if (This->refParticleIdx < (int) (This->refParticleTrajectory.size() - 1))
2472             This->animateRefParticle();
2473          else {
2474             This->animateBtwPtsPeriod = MIN_SPEED;
2475             This->speedStep = START_STEP;
2476          }
2477       }
2478       if (This->currentState == REVERSED_ANIMATION) {
2479          if (This->refParticleIdx >= 1)
2480             This->animateRefParticle();
2481          else {
2482             This->animateBtwPtsPeriod = MIN_SPEED;
2483             This->speedStep = START_STEP;
2484          }
2485       }
2486    }
2487 }
2488 
2489 
2490 void G4OpenInventorQtExaminerViewer::setStartingPtForAnimation()
2491 {
2492    if (SoQtExaminerViewer::isAnimating())
2493       stopAnimating();
2494 
2495    SbRotation rot;
2496    SbVec3f p1{0.0, 0.0, 0.0}, p2{0.0, 0.0, 0.0}, p2_tmp, camUpV, camD, camD_tmp, leftRightAxis;
2497    float x1, y1, z1, x2, y2, z2;
2498 
2499    if (currentState == ANIMATION) {
2500       p1 = refParticleTrajectory[refParticleIdx];
2501       p2 = refParticleTrajectory[++(refParticleIdx)];
2502    } else if (currentState == REVERSED_ANIMATION) {
2503       p2 = refParticleTrajectory[refParticleIdx];
2504       p1 = refParticleTrajectory[--(refParticleIdx)];
2505    } else if (currentState == PAUSED_ANIMATION) {
2506       if (refParticleIdx < (int) refParticleTrajectory.size()) {
2507          p1 = refParticleTrajectory[refParticleIdx];
2508          p2 = refParticleTrajectory[refParticleIdx + 1];
2509       } else {
2510          p1 = refParticleTrajectory[refParticleIdx - 1];
2511          p2 = refParticleTrajectory[refParticleIdx];
2512       }
2513    }
2514    p1.getValue(x1, y1, z1);
2515    p2.getValue(x2, y2, z2);
2516 
2517    camD = p2 - p1;
2518    camD.normalize();
2519 
2520    p2_tmp.setValue(x2, y1, z2);
2521    camD_tmp = p2_tmp - p1;
2522    camD_tmp.normalize();
2523 
2524    camUpV.setValue(0, 1, 0);
2525    rot.setValue(camD_tmp, camD);
2526    rot.multVec(camUpV, camUpV);
2527 
2528    leftRightAxis = camD.cross(camUpV);
2529 
2530    myCam->position = p1;
2531    myCam->pointAt(p2, camUpV);
2532 
2533    // Update camera position
2534    p1 = p1 + (up_down * camUpV) + (left_right * leftRightAxis);
2535    myCam->position = p1;
2536    // FWJ Try look-ahead here
2537    int idx = refParticleIdx + pathLookahead;
2538    idx = std::min(idx, (int)refParticleTrajectory.size() - 1);
2539    myCam->pointAt(refParticleTrajectory[idx], camUpV);
2540    //   myCam->pointAt(refParticleTrajectory[idx], camUpVec);
2541    myCam->focalDistance = 0.1f;
2542 }
2543 
2544 
2545 void G4OpenInventorQtExaminerViewer::gotoRefPathStart()
2546 {
2547    G4OpenInventorQtExaminerViewer::ToolsRefPathStartCB();
2548 }
2549 
2550 
2551 void G4OpenInventorQtExaminerViewer::ToolsRefPathStartCB()
2552 {
2553    if (!refParticleTrajectory.size()) {
2554       QMessageBox msgbox;
2555       msgbox.setFont(*font);
2556       QString messagetxt = "No current reference path";
2557       msgbox.setText(messagetxt);
2558       msgbox.exec();
2559       return;
2560    }
2561 
2562    if (currentState == ROTATING)
2563       return;
2564    if (currentState == ANIMATION || currentState == REVERSED_ANIMATION
2565        || currentState == PAUSED_ANIMATION) {
2566       if (animateSensor->isScheduled())
2567          animateSensor->unschedule();
2568       setSuperimpositionEnabled(superimposition, FALSE);
2569       maxSpeed = 0.0f;
2570       scheduleRedraw();
2571    } else {
2572       saveCurCamera();
2573       prevState = currentState;
2574       prevRefIdx = refParticleIdx;
2575    }
2576 
2577    if (SoQtExaminerViewer::isAnimating())
2578       stopAnimating();
2579 
2580    up_down = 0;
2581    left_right = 0;
2582    step = 1;
2583 
2584    refParticleIdx = 0;
2585    currentState = BEAMLINE;
2586    setSuperimpositionEnabled(superimposition, TRUE);
2587    axisSwitch->whichChild.setValue(SO_SWITCH_NONE);
2588    animSpeedOutlineSwitch->whichChild.setValue(SO_SWITCH_NONE);
2589    animSpeedSwitch->whichChild.setValue(SO_SWITCH_NONE);
2590    scheduleRedraw();
2591 
2592    // FWJ Disabled: this is set in moveCamera()
2593    // Zoom in
2594    //   SoCamera *cam = getCamera();
2595    //   cam->focalDistance = 0.1f;
2596 
2597    prevParticleDir = SbVec3f(0,0,0);
2598 
2599    //Default zoom
2600    SbVec3f p1 = refParticleTrajectory[0];
2601    SbVec3f pN = refParticleTrajectory[refParticleTrajectory.size() - 1];
2602    distance = (pN - p1).length() / 10;
2603 
2604    moveCamera(distance, true);
2605 }
2606 
2607 
2608 void G4OpenInventorQtExaminerViewer::ToolsRefPathInvertCB()
2609 {
2610    invertRefPath();
2611 }
2612 
2613 
2614 void G4OpenInventorQtExaminerViewer::invertRefPath()
2615 {
2616    std::reverse(refParticleTrajectory.begin(),
2617                 refParticleTrajectory.end());
2618    setReferencePathZPos();
2619    sortElements();
2620 }
2621 
2622 
2623 void G4OpenInventorQtExaminerViewer::animateRefParticle()
2624 {
2625    SoCamera *cam = getCamera();
2626 
2627    camStartPos = cam->position.getValue();
2628    camStartOrient = cam->orientation.getValue();
2629 
2630    if (currentState != BEAMLINE)
2631       setStartingPtForAnimation();
2632 
2633    camEndPos = myCam->position.getValue();
2634    camEndOrient = myCam->orientation.getValue();
2635 
2636    if (animateSensor->isScheduled())
2637       animateSensor->unschedule();
2638 
2639    animateSensor->setBaseTime(SbTime::getTimeOfDay());
2640    animateSensor->setInterval(SbTime(0.02));
2641 
2642    animateSensor->schedule();
2643 }
2644 
2645 
2646 #if QT_VERSION < 0x060000
2647 void G4OpenInventorQtExaminerViewer::addEscapeCallback(void (*callback)())
2648 {
2649    escapeCallback = callback;
2650 }
2651 #endif
2652 
2653 void G4OpenInventorQtExaminerViewer::sceneChangeCB(void* userData, SoSensor*)
2654 {
2655    // FWJ DEBUG
2656    //   G4cout << "SCENE CHANGE callback" << G4endl;
2657    // NOTE: could/should be disabled during animation
2658 
2659    G4OpenInventorQtExaminerViewer* This =
2660       (G4OpenInventorQtExaminerViewer*)userData;
2661    if(This->newEvents) {
2662       This->findAndSetRefPath();
2663       This->newEvents = false;
2664    }
2665 }
2666 
2667 
2668 //////////////////////////////////// BOOKMARKS ///////////////////////////
2669 
2670 // Adds bookmarks to listsDialog.
2671 
2672 void G4OpenInventorQtExaminerViewer::addViewPoints()
2673 {
2674    std::size_t size = viewPtList.size();
2675    if (!size) return;
2676 
2677    for (std::size_t i = 0; i < size; ++i) {
2678 #if QT_VERSION < 0x060000
2679       new QListWidgetItem(viewPtList[i].viewPtName,
2680                           AuxWindowDialog->listWidget);
2681 #else
2682       new QListWidgetItem(viewPtList[i].viewPtName.c_str(),
2683                           AuxWindowDialog->listWidget);
2684 #endif
2685    }
2686 }
2687 
2688 
2689 // Converts a string type word into a float type.
2690 
2691 template<class T> 
2692 void G4OpenInventorQtExaminerViewer::parseString(T &t, const std::string &s,
2693                                                  bool &error) 
2694 {
2695    std::istringstream str(s);
2696    if ((str >> t).fail())
2697       error = true;
2698 }
2699 
2700 
2701 void
2702 G4OpenInventorQtExaminerViewer::FileOpenBookmarkCB()
2703 {
2704    // FWJ DEBUG
2705    //   G4cout << "File: Open Bookmark File CALLBACK" << G4endl;
2706    QFileDialog filedialog(getParentWidget(), tr("Open bookmark file"));
2707    filedialog.setFileMode(QFileDialog::ExistingFile);
2708    filedialog.setFont(*font);
2709    if (!filedialog.exec()) return;
2710    QStringList filenameinlist = filedialog.selectedFiles();
2711    QString filenamein = filenameinlist[0];
2712 
2713    fileIn.close();
2714    fileIn.open(qPrintable(filenamein));
2715    if (fileIn.fail()) {
2716       QMessageBox msgbox;
2717       msgbox.setFont(*font);
2718       QString messagetxt = "Error opening file: ";
2719       messagetxt.append(filenamein);
2720       msgbox.setText(messagetxt);
2721       msgbox.exec();
2722       //      G4cout << "ERROR opening file " << filename << G4endl;
2723       fileIn.clear();
2724       return;
2725    }
2726    // Opens a file without erasing it
2727    cleanUpAfterPrevFile();
2728 
2729    if (!loadViewPts()) {
2730       QMessageBox msgbox;
2731       msgbox.setFont(*font);
2732       QString messagetxt = "Error reading bookmark file: ";
2733       messagetxt.append(filenamein);
2734       msgbox.setText(messagetxt);
2735       msgbox.exec();
2736       //      G4cout << "ERROR reading bookmark file " << filename << G4endl;
2737       fileIn.clear();
2738       return;
2739    }
2740 
2741    fileName = qPrintable(filenamein);
2742    fileOut.open(fileName.c_str(), std::ios::in);
2743    fileOut.seekp(0, std::ios::end);
2744 
2745    addViewPoints();
2746 
2747    // LATER: display filename in lists window
2748 
2749    fileIn.close();
2750    fileIn.clear();
2751 }
2752 
2753 // Called before loading a new viewpoint file. 
2754 // Resets member fields to default values.
2755 
2756 void G4OpenInventorQtExaminerViewer::cleanUpAfterPrevFile()
2757 {
2758    viewPtIdx = -1;
2759    viewPtList.clear();
2760    //   setSuperimpositionEnabled(superimposition, FALSE);
2761    //   scheduleRedraw();
2762    currentState = GENERAL;
2763    if (fileOut.is_open()) fileOut.close();
2764 
2765    AuxWindowDialog->listWidget->clear();   
2766    AuxWindowDialog->lineEdit->setText(QString(""));
2767 }
2768 
2769 
2770 void
2771 G4OpenInventorQtExaminerViewer::FileNewBookmarkCB()
2772 {
2773    //   G4cout << "File: Open New Bookmark File CALLBACK" << G4endl;
2774    QFileDialog filedialog(getParentWidget(), tr("Open new bookmark file"));
2775    filedialog.setFileMode(QFileDialog::AnyFile);
2776    // To enable confirmation of overwriting
2777    filedialog.setAcceptMode(QFileDialog::AcceptSave);
2778    // But change the "Save" button text
2779    filedialog.setLabelText(QFileDialog::Accept, QString("New"));
2780    filedialog.setFont(*font);
2781    if (!filedialog.exec()) return;
2782    QStringList filenameinlist = filedialog.selectedFiles();
2783    QString filenamein = filenameinlist[0];
2784 
2785    cleanUpAfterPrevFile();
2786    fileName = qPrintable(filenamein);
2787    fileOut.open(fileName.c_str());
2788    if (fileOut.fail()) {
2789       QMessageBox msgbox;
2790       msgbox.setFont(*font);
2791       QString messagetxt = "Error opening new bookmark file: ";
2792       messagetxt.append(filenamein);
2793       msgbox.setText(messagetxt);
2794       msgbox.exec();
2795       // G4cout << "ERROR opening new bookmark file " << filename << G4endl;
2796    }
2797 }
2798 
2799 
2800 void
2801 G4OpenInventorQtExaminerViewer::ToolsAnimateRefParticleCB()
2802 {
2803    //   G4cout << "Tools: Animate Ref Particle CALLBACK" << G4endl;
2804    if (!refParticleTrajectory.size()) {
2805       returnToAnim = true;
2806       G4warn << "No Reference Trajectory" << G4endl;
2807       return;
2808    }
2809 
2810    ///////////////////////////////////////////////////////////////
2811    setSuperimpositionEnabled(superimposition, TRUE);
2812    maxSpeed = SPEED_INDICATOR_STEP;
2813    axisSwitch->whichChild.setValue(SO_SWITCH_ALL);
2814    animSpeedOutlineSwitch->whichChild.setValue(SO_SWITCH_ALL);
2815    animSpeedSwitch->whichChild.setValue(SO_SWITCH_ALL);
2816    scheduleRedraw();
2817    ///////////////////////////////////////////////////////////////
2818 
2819    SoCamera *cam = getCamera();
2820    //   SbVec3f camDirOld, camDirNew, camDirNew_tmp, camUpVec, P0, P1, P1_tmp;
2821 
2822    if (currentState == ANIMATION || currentState == REVERSED_ANIMATION
2823        || currentState == ROTATING)
2824       return;
2825 
2826    if (currentState != PAUSED_ANIMATION) {
2827 
2828       saveCurCamera();
2829       prevState = currentState;
2830       prevRefIdx = refParticleIdx;
2831 
2832       if (cam->isOfType(SoOrthographicCamera::getClassTypeId())) {
2833          toggleCameraType();
2834          cam = getCamera();
2835       }
2836 
2837       refParticleIdx = 0; // Set the camera to the starting point of the animation
2838       animateBtwPtsPeriod = MIN_SPEED;
2839       speedStep = START_STEP;
2840       left_right = up_down = 0;
2841 
2842       cam->focalDistance = 0.1f;
2843       ((SoPerspectiveCamera *) cam)->heightAngle = 0.50f;
2844    }
2845 
2846    currentState = ANIMATION;
2847    setStartingPtForAnimation();
2848 
2849    cam->position = (myCam)->position.getValue();
2850    cam->orientation = (myCam)->orientation.getValue();
2851    animateRefParticle(); // Animate the camera
2852 }
2853 
2854 
2855 void
2856 G4OpenInventorQtExaminerViewer::SaveViewPtCB()
2857 {
2858    //   G4cout << "AppButton: Save Viewpoint CALLBACK" << G4endl;
2859    // First get viewpoint name ...
2860    // EMULATING getViewPtNameCB ...
2861    //   bool ok;
2862    // Note QString() returns an empty string
2863 #if 0x060000 <= QT_VERSION
2864    //G.Barrand: map the AuxWindow here. Then it is mapped "at need"
2865    //           and not systematically when creating a new viewer.
2866    //           Moreover, if "closing" the AuxWindow with the mouse,
2867    //           it permits to map it back.
2868    AuxWindow->show();
2869    AuxWindow->raise();
2870    AuxWindow->activateWindow();
2871 #endif
2872 
2873    // NONE OF THE FOLLOWING CHANGES THE FONT: FORGET IT FOR NOW
2874    QInputDialog* inputdialog = new QInputDialog(getParentWidget());
2875    inputdialog->setFont(*font);
2876    inputdialog->setWindowTitle(tr("Enter a name for the bookmark"));
2877    inputdialog->setLabelText("Bookmark name");
2878    //   inputdialog->setTextEchoMode(QLineEdit::Normal);
2879    inputdialog->adjustSize();
2880    QString namein;
2881    if (inputdialog->exec() == QDialog::Accepted)
2882       namein=inputdialog->textValue().trimmed();
2883    else
2884       return;
2885    if (namein.isEmpty()) return;
2886 
2887    // This easier approach failed: unable to set font size
2888    //   QString namein = 
2889    //      QInputDialog::getText(getParentWidget(),
2890    //                            tr("Enter a name for the bookmark"),
2891    //                            tr("Bookmark name"), QLineEdit::Normal,
2892    //                            QString(), &ok);
2893 
2894    namein.truncate(MAX_VP_NAME);
2895 
2896    char* name = strdup(qPrintable(namein));
2897 
2898    // FWJ DEBUG
2899    //   G4cout << "QString is " << qPrintable(namein) << G4endl;
2900    //   G4cout << "char[] is  " << name << G4endl;
2901 
2902    for (int i = 0; i < (int)viewPtList.size(); i++) {
2903 #if QT_VERSION < 0x060000
2904       if (!strcmp(name, viewPtList[i].viewPtName)) {
2905 #else
2906       if (!strcmp(name, viewPtList[i].viewPtName.c_str())) {
2907 #endif
2908          QMessageBox msgbox;
2909          msgbox.setText("Bookmark name is already in use");
2910          msgbox.exec();
2911          free(name);
2912          return;
2913       }
2914    }
2915 
2916    if (viewPtIdx == -1) viewPtIdx = 0;
2917    saveViewPt(name);
2918 
2919    saveViewPtItem = new QListWidgetItem(namein,
2920                                         AuxWindowDialog->listWidget);
2921    AuxWindowDialog->listWidget->setCurrentItem(saveViewPtItem);
2922    AuxWindowDialog->lineEdit->setText(namein);
2923    free(name);
2924 }
2925 
2926 
2927 // Saves current camera parameters to a viewpoint file.
2928 
2929 void G4OpenInventorQtExaminerViewer::saveViewPt(char *name)
2930 {
2931    SbVec3f axis;
2932    viewPtData tmp;
2933    float x, y, z, angle;
2934    SoCamera* camera = getCamera();
2935 
2936    // NOTE: Xt VSN increments this at end of procedure
2937    //   viewPtIdx++;
2938 
2939    //  FWJ DEBUG
2940    //   G4cout << "saveViewPt: saving bookmark " << viewPtIdx << " " << name
2941    //          << G4endl;
2942 
2943    if (viewPtList.size() == 0) {
2944       writeViewPtIdx();
2945    }
2946 
2947 #if QT_VERSION < 0x060000
2948    tmp.viewPtName = name;
2949 #else
2950    tmp.viewPtName = std::string(name);
2951 #endif
2952    tmp.viewportMapping = camera->viewportMapping.getValue();
2953    tmp.position = camera->position.getValue();
2954    tmp.orientation = camera->orientation.getValue();
2955    tmp.aspectRatio = camera->aspectRatio.getValue();
2956    tmp.nearDistance = camera->nearDistance.getValue();
2957    tmp.farDistance = camera->farDistance.getValue();
2958    tmp.focalDistance = camera->focalDistance.getValue();
2959 
2960    // Save camera height (changed by zooming)
2961    if (camera->isOfType(SoPerspectiveCamera::getClassTypeId())) {
2962       tmp.height = ((SoPerspectiveCamera *) camera)->heightAngle.getValue();
2963       tmp.camType = PERSPECTIVE;
2964    } else if (camera->isOfType(SoOrthographicCamera::getClassTypeId())) {
2965       tmp.height = ((SoOrthographicCamera *) camera)->height.getValue();
2966       tmp.camType = ORTHOGRAPHIC;
2967    } else {
2968       SoDebugError::post("G4OpenInventorQtExaminerViewer::saveViewPtCB",
2969                          "Only Perspective and Orthographic cameras are supported.");
2970       return;
2971    }
2972 
2973    viewPtList.push_back(tmp);
2974 
2975    // Now save the view point to a .txt file
2976    // FWJ DEBUG
2977    // G4cout << "saveViewPt: writing to Bookmark file " << fileName << G4endl;
2978 
2979    std::string vpName = name;
2980 
2981    while ((int) vpName.size() <= MAX_VP_NAME)
2982       vpName += " ";
2983 
2984    fileOut << vpName << std::endl;
2985    tmp.position.getValue(x, y, z);
2986    fileOut << x << " " << y << " " << z << std::endl;
2987 
2988    // Reusing x, y and z for storing the axis
2989    tmp.orientation.getValue(axis, angle);
2990    axis.getValue(x, y, z);
2991    fileOut << x << " " << y << " " << z << " " << angle << std::endl;
2992 
2993    fileOut << tmp.camType << " " << tmp.height << std::endl;
2994    fileOut << tmp.focalDistance << " ";
2995    fileOut << tmp.nearDistance << " ";
2996    fileOut << tmp.farDistance << std::endl;
2997    fileOut << tmp.viewportMapping << " ";
2998    fileOut << tmp.aspectRatio << "\n" << std::endl;
2999    fileOut.flush();
3000 
3001    viewPtIdx++;
3002 
3003    // FWJ DEBUG
3004    //   G4cout << "saveViewPt: finished writing to file" << G4endl <<
3005    //      "  Next viewPtIdx is " << viewPtIdx << G4endl;
3006 }
3007 
3008 
3009 // Updates the viewPtIdx in a viewpoint file.
3010 
3011 void G4OpenInventorQtExaminerViewer::writeViewPtIdx()
3012 {
3013    std::string idxStr;
3014    std::stringstream out;
3015 
3016    out << viewPtIdx;
3017    idxStr = out.str();
3018    fileOut.seekp(0, std::ios::beg);
3019 
3020    while ((int) idxStr.length() < MAX_VP_IDX) {
3021       idxStr += " ";
3022    }
3023 
3024    // FWJ DEBUG
3025    //   G4cout << "writeViewPtIdx: " << viewPtIdx << G4endl;
3026    fileOut << idxStr << "\n";
3027    fileOut.flush();
3028    fileOut.seekp(0, std::ios::end);
3029 }
3030 
3031 
3032 // Receives the name of the bookmark clicked and searches for it in viewPtList.
3033 
3034 void G4OpenInventorQtExaminerViewer::LoadBookmarkCB(QListWidgetItem* item)
3035 {
3036    // FWJ DEBUG
3037    //   G4cout << "AuxWindow: listWidget LoadBookmark CALLBACK" << G4endl;
3038 
3039    for (int i = 0; i < (int)viewPtList.size(); i++) {
3040 #if QT_VERSION < 0x060000
3041       if (!strcmp(viewPtList[i].viewPtName, qPrintable(item->text()))) {
3042 #else
3043       if (!strcmp(viewPtList[i].viewPtName.c_str(), qPrintable(item->text()))) {
3044 #endif
3045          viewPtIdx = i;
3046          break;
3047       }
3048    }
3049    //   G4cout << "  FOUND viewPtIdx " << viewPtIdx << G4endl;
3050 
3051    writeViewPtIdx();
3052    setViewPt();
3053    AuxWindowDialog->lineEdit->setText(item->text());
3054 }
3055 
3056 
3057 // Sets the viewpoint based on camera data that viewPtIdx is pointing to.
3058 
3059 void G4OpenInventorQtExaminerViewer::setViewPt()
3060 {
3061    if (currentState == ANIMATION || currentState == REVERSED_ANIMATION
3062        || currentState == ROTATING) {
3063       if (animateSensor->isScheduled()) animateSensor->unschedule();
3064       setSuperimpositionEnabled(superimposition, FALSE);
3065       maxSpeed = 0.0f;
3066       scheduleRedraw();
3067    }
3068 
3069    SoCamera * camera = getCamera();
3070    if (camera == NULL) {
3071       G4warn << "setViewPt: Camera is null. Unable to set the viewpoint." <<
3072          G4endl;
3073       //      String dialogName = (char *) "Missing Camera Node";
3074       //      std::string msg = "Camera is null. Unable to set the viewpoint.";
3075       //      warningMsgDialog(msg, dialogName, NULL);
3076       return;
3077    }
3078 
3079    if (!viewPtList.size()) {
3080       G4warn << "setViewPt: There are no viewpoints to load." << G4endl;
3081       //      String dialogName = (char *) "Missing Viewpoints";
3082       //      std::string msg = "There are no viewpoints to load.";
3083       //      warningMsgDialog(msg, dialogName, NULL);
3084       return;
3085    }
3086 
3087    if (SoQtExaminerViewer::isAnimating()) stopAnimating();
3088 
3089    if (currentState != VIEWPOINT) {
3090       currentState = VIEWPOINT;
3091       //////////////////////////////////////////////////////////////
3092       setSuperimpositionEnabled(superimposition, TRUE);
3093       axisSwitch->whichChild.setValue(SO_SWITCH_NONE);
3094       animSpeedOutlineSwitch->whichChild.setValue(SO_SWITCH_NONE);
3095       animSpeedSwitch->whichChild.setValue(SO_SWITCH_NONE);
3096       scheduleRedraw();
3097       ///////////////////////////////////////////////////////////////
3098    }
3099 
3100 #if QT_VERSION < 0x060000
3101 #else
3102    if((viewPtIdx<0)||(viewPtIdx>=int(viewPtList.size()))) {
3103      G4warn << "setViewPt: inconsitent viewPtIdx " << viewPtIdx << ", viewPtList.size() " << viewPtList.size() << G4endl;
3104      return;
3105    }
3106 #endif
3107 
3108    curViewPtName = viewPtList[viewPtIdx].viewPtName;
3109    camera->viewportMapping = viewPtList[viewPtIdx].viewportMapping;
3110    camera->position = viewPtList[viewPtIdx].position;
3111    camera->orientation = viewPtList[viewPtIdx].orientation;
3112    camera->aspectRatio = viewPtList[viewPtIdx].aspectRatio;
3113    camera->nearDistance = viewPtList[viewPtIdx].nearDistance;
3114    camera->farDistance = viewPtList[viewPtIdx].farDistance;
3115    camera->focalDistance = viewPtList[viewPtIdx].focalDistance;
3116 
3117    // Restore camera height (changed by zooming)
3118    if (camera->isOfType(SoPerspectiveCamera::getClassTypeId())) {
3119       if (viewPtList[viewPtIdx].camType == ORTHOGRAPHIC) {
3120          toggleCameraType();
3121          camera = getCamera();
3122          ((SoOrthographicCamera *) camera)->height.setValue(
3123                                                             viewPtList[viewPtIdx].height);
3124       } else
3125          ((SoPerspectiveCamera *) camera)->heightAngle.setValue(
3126                                                                 viewPtList[viewPtIdx].height);
3127    } else if (camera->isOfType(SoOrthographicCamera::getClassTypeId())) {
3128       if (viewPtList[viewPtIdx].camType == PERSPECTIVE) {
3129          toggleCameraType();
3130          camera = getCamera();
3131          ((SoPerspectiveCamera *) camera)->heightAngle.setValue(
3132                                                                 viewPtList[viewPtIdx].height);
3133       } else
3134          ((SoOrthographicCamera *) camera)->height.setValue(
3135                                                             viewPtList[viewPtIdx].height);
3136    } else {
3137       SoDebugError::post("G4OpenInventorQtExaminerViewer::setViewPt",
3138                          "Only Perspective and Orthographic cameras are supported.");
3139       return;
3140    }
3141 
3142 }
3143 
3144 
3145 void G4OpenInventorQtExaminerViewer::NextViewPtCB()
3146 {
3147    // FWJ DEBUG
3148    //   G4cout << "App Button: nextViewPt CALLBACK" << G4endl;
3149 
3150    if (!viewPtList.size()) return;
3151    if (viewPtIdx >= (int)viewPtList.size() - 1)
3152       viewPtIdx = 0;
3153 #if 0x060000 <= QT_VERSION
3154    else if (viewPtIdx<0)
3155       viewPtIdx = 0;
3156 #endif
3157    else
3158       viewPtIdx++;
3159 
3160    writeViewPtIdx();
3161    setViewPt();
3162 #if QT_VERSION < 0x060000
3163    char* viewptname = viewPtList[viewPtIdx].viewPtName;
3164    AuxWindowDialog->lineEdit->setText(QString(viewptname));
3165 #else
3166    AuxWindowDialog->lineEdit->setText(QString(viewPtList[viewPtIdx].viewPtName.c_str()));
3167 #endif
3168 }
3169 
3170 void G4OpenInventorQtExaminerViewer::PrevViewPtCB()
3171 {
3172    // FWJ DEBUG
3173    //   G4cout << "App Button: prevViewPt CALLBACK" << G4endl;
3174 
3175    if (!viewPtList.size()) return;
3176 #if QT_VERSION < 0x060000
3177    if (viewPtIdx == 0)
3178 #else
3179    if (viewPtIdx <= 0)
3180 #endif
3181       viewPtIdx = (int) viewPtList.size() - 1;
3182    else
3183       viewPtIdx--;
3184 
3185    writeViewPtIdx();
3186    setViewPt();
3187 #if QT_VERSION < 0x060000
3188    char* viewptname = viewPtList[viewPtIdx].viewPtName;
3189    AuxWindowDialog->lineEdit->setText(QString(viewptname));
3190 #else
3191    AuxWindowDialog->lineEdit->setText(QString(viewPtList[viewPtIdx].viewPtName.c_str()));
3192 #endif
3193 }
3194 
3195 
3196 void G4OpenInventorQtExaminerViewer::AbbrOutputCB(bool checked)
3197 {
3198    // FWJ DEBUG
3199    //   G4cout << "App Button: abbrOutput CALLBACK" << G4endl;
3200 
3201    abbrOutputFlag = checked;
3202 }
3203 
3204 
3205 void G4OpenInventorQtExaminerViewer::PickRefPathCB()
3206 {
3207    // FWJ DEBUG
3208    //   G4cout << "App Button: pickRefPath CALLBACK" << G4endl;
3209 
3210    // Save viewing state and go to picking mode
3211    viewingBeforePickRef = isViewing();
3212    if(isViewing())
3213       setViewing(false);
3214    setComponentCursor(SoQtCursor(SoQtCursor::CROSSHAIR));
3215    pickRefPathFlag = true;
3216 }
3217 
3218 
3219 void G4OpenInventorQtExaminerViewer::SwitchWireFrameCB(bool checked)
3220 {
3221    // FWJ DEBUG
3222    //   G4cout << "App Button: switchWireFrame CALLBACK" << G4endl;
3223 
3224    //   if (switchWireFrameButton->isDown()) {
3225    if (checked) {
3226       setDrawStyle(SoQtViewer::STILL, SoQtViewer::VIEW_LINE);
3227       setDrawStyle(SoQtViewer::INTERACTIVE, SoQtViewer::VIEW_LINE);
3228    } else {
3229       setDrawStyle(SoQtViewer::STILL, SoQtViewer::VIEW_AS_IS);
3230       setDrawStyle(SoQtViewer::INTERACTIVE,
3231                          SoQtViewer::VIEW_SAME_AS_STILL);
3232    }
3233 }
3234 
3235 
3236 void G4OpenInventorQtExaminerViewer::SwitchAxesCB(bool checked)
3237 {
3238    // FWJ DEBUG
3239    //   G4cout << "App Button: switchAxes CALLBACK" << G4endl;
3240    setFeedbackVisibility(checked);
3241    //   if (checked) {
3242    //      setFeedbackVisibility(TRUE);
3243    //   } else {
3244    //      setFeedbackVisibility(FALSE);
3245    //   }
3246 }
3247 
3248 
3249 void G4OpenInventorQtExaminerViewer::DetachCB()
3250 {
3251 #if QT_VERSION < 0x060000
3252    //   FWJ DEBUG
3253    //   G4cout << "App Button: detach CALLBACK" << G4endl;
3254    uiQt->GetViewerTabWidget()->removeTab(uiQtTabIndex);
3255    viewerParent->setParent(viewerParent2);
3256    removeAppPushButton(detachButton);
3257    show();
3258 #else
3259    //G.Barrand: have the viewer in a detached window.
3260    //           We have the title window reflecting from where
3261    //           it comes from, then "Detached <viewer name>".
3262    //           Ask to destroy the detached viewer in case
3263    //           of closing the window with the mouse; if not
3264    //           we have a dandling hidden viewer without a way
3265    //           to map it again.
3266    G4int index = -1;
3267   {int tabn = uiQt->GetViewerTabWidget()->count();
3268    for (G4int c = 0; c < tabn; ++c) {
3269      if (uiQt->GetViewerTabWidget()->tabText(c)==fName) {
3270        index = c;
3271      }
3272    }}
3273    if(index==(-1)) return;
3274    removeAppPushButton(detachButton);
3275    uiQt->GetViewerTabWidget()->removeTab(index);
3276   {short w,h;
3277    getSize().getValue(w,h);
3278    QWidget* dialog = new QDialog();
3279    dialog->setWindowTitle(QString("Detached "+fName));
3280    dialog->setAttribute(Qt::WA_DeleteOnClose);
3281   {QHBoxLayout* layout = new QHBoxLayout();
3282    layout->setContentsMargins(0,0,0,0);
3283    layout->setSpacing(0);
3284    layout->addWidget(getParentWidget());
3285    dialog->setLayout(layout);}
3286    dialog->resize(w,h);
3287    getParentWidget()->show();
3288    dialog->show();
3289   }
3290 #endif
3291 }
3292 
3293 
3294 void G4OpenInventorQtExaminerViewer::DeleteBookmarkCB()
3295 {
3296    // FWJ DEBUG
3297    //   G4cout << "Delete Button: DeleteBookmark CALLBACK" << G4endl;
3298 
3299    // Maybe nothing selected yet
3300    QListWidgetItem* listitem = AuxWindowDialog->listWidget->currentItem();
3301    if (!listitem) return;
3302    if (!(listitem->isSelected())) return;
3303 
3304    QString vpnamein = listitem->text();
3305 
3306    char* vpName = strdup(qPrintable(vpnamein));
3307    //   G4cout << "DELETING bookmark " << vpName << G4endl;
3308 
3309    deleteViewPt(vpName);
3310    delete listitem;
3311    free(vpName);
3312 }
3313 
3314 // Deletes current viewpoint the user is looking at.
3315 // Updates the input file and bookmarks as well.
3316 
3317 void G4OpenInventorQtExaminerViewer::deleteViewPt(char *vpName)
3318 {
3319    std::string line;
3320    std::size_t end;
3321    fileIn.open(fileName.c_str());
3322    std::ofstream out("temporaryFile.txt");
3323 
3324 #if QT_VERSION < 0x060000
3325    if (!vpName)
3326       vpName = viewPtList[viewPtIdx].viewPtName;
3327 #endif
3328 
3329    getline(fileIn, line); // Printing the viewpoint idx
3330    out << line << "\n";
3331 
3332    while (getline(fileIn, line)) {
3333       end = line.find_last_not_of(' ');
3334       line = line.substr(0, end + 1);
3335       if (!strcmp(line.c_str(), vpName)) { // Equal
3336          while (line.size()) {
3337             getline(fileIn, line);
3338          }
3339 
3340          while (getline(fileIn, line))
3341             out << line << "\n";
3342       } else {
3343          while (line.size()) {
3344             out << line << "\n";
3345             getline(fileIn, line);
3346          }
3347          out << "\n";
3348       }
3349    }
3350 
3351    std::size_t idx = 0; // Remove viewpoint from the vector
3352    std::size_t size = viewPtList.size();
3353    while (idx < size) {
3354 #if QT_VERSION < 0x060000
3355       if (!strcmp(viewPtList[idx].viewPtName, vpName)) {
3356 #else
3357       if (!strcmp(viewPtList[idx].viewPtName.c_str(), vpName)) {
3358 #endif
3359          viewPtList.erase(viewPtList.begin() + idx);
3360          break;
3361       }
3362       idx++;
3363    }
3364 
3365    out.close();
3366    fileOut.close();
3367    fileIn.clear();
3368    fileIn.close();
3369 
3370    // FWJ check return status: error popups needed here
3371    int istat = remove(fileName.c_str());
3372    if (istat == -1) {
3373       QMessageBox msgbox;
3374       msgbox.setFont(*font);
3375       QString messagetxt = "Error removing bookmarks file";
3376       //      messagetxt.append(filenamein);
3377       msgbox.setText(messagetxt);
3378       msgbox.exec();
3379       //      G4cout << "Error removing bookmarks file" << G4endl;
3380    }
3381    istat = rename("temporaryFile.txt", fileName.c_str());
3382    if (istat == -1) {
3383       QMessageBox msgbox;
3384       msgbox.setFont(*font);
3385       QString messagetxt = "Error renaming bookmarks file";
3386       //      messagetxt.append(filenamein);
3387       msgbox.setText(messagetxt);
3388       msgbox.exec();
3389       //      G4cout << "Error renaming bookmarks file" << G4endl;
3390    }
3391    fileOut.open(fileName.c_str(), std::ios::in);
3392    fileOut.seekp(0, std::ios::end);
3393 
3394    if (!viewPtList.size()) { // viewPtList is empty
3395 #if QT_VERSION < 0x060000
3396       curViewPtName = (char *) empty.c_str();
3397 #else
3398       curViewPtName.clear();
3399 #endif
3400       scheduleRedraw();
3401    } else {
3402       if (viewPtIdx >= (int) viewPtList.size())
3403          viewPtIdx--;
3404       writeViewPtIdx();
3405       setViewPt();
3406    }
3407 }
3408 
3409 
3410 void G4OpenInventorQtExaminerViewer::RenameBookmarkCB()
3411 {
3412    // FWJ DEBUG
3413    //   G4cout << "Rename Button: RenameBookmark CALLBACK" << G4endl;
3414    // Maybe nothing selected yet
3415    QListWidgetItem* listitem = AuxWindowDialog->listWidget->currentItem();
3416    if (!listitem) return;
3417    if (!(listitem->isSelected())) return;
3418 
3419    QString vpnamein = listitem->text();
3420 
3421    QInputDialog* inputdialog = new QInputDialog(getParentWidget());
3422    inputdialog->setFont(*font);
3423    inputdialog->setWindowTitle(tr("Enter"));
3424    inputdialog->setLabelText("New bookmark name");
3425    inputdialog->adjustSize();
3426    QString newnamein;
3427    if (inputdialog->exec() == QDialog::Accepted)
3428       newnamein=inputdialog->textValue().trimmed();
3429    else
3430       return;
3431    if (newnamein.isEmpty()) return;
3432 
3433    char* newname = strdup(qPrintable(newnamein));
3434 
3435    std::size_t size = viewPtList.size();
3436    for (std::size_t i = 0; i < size; ++i) {
3437 #if QT_VERSION < 0x060000
3438       if (!strcmp(newname, viewPtList[i].viewPtName)) {
3439 #else
3440       if (!strcmp(newname, viewPtList[i].viewPtName.c_str())) {
3441 #endif
3442          QMessageBox msgbox;
3443          msgbox.setFont(*font);
3444          msgbox.setText("Bookmark name is already in use");
3445          msgbox.exec();
3446       }
3447    }
3448 
3449    //   G4cout << "RENAMING to " << newname << G4endl;
3450    renameViewPt(newname);
3451    listitem->setText(QString(newname));
3452    AuxWindowDialog->lineEdit->setText(newname);
3453    //   if (currentState == VIEWPOINT)
3454    //      scheduleRedraw();
3455 
3456    free(newname);
3457 }
3458 
3459 // Renames currently selected viewpoint.
3460 
3461 void G4OpenInventorQtExaminerViewer::renameViewPt(char *vpName)
3462 {
3463    std::size_t idx = 0, end, pos;
3464    std::size_t size = viewPtList.size();
3465    std::string line, newName;
3466    fileIn.open(fileName.c_str());
3467 
3468    newName = vpName;
3469    while ((int) newName.size() < MAX_VP_NAME)
3470       newName += " ";
3471 
3472    getline(fileIn, line);
3473    pos = fileIn.tellg();
3474    while (getline(fileIn, line)) {
3475       end = line.find_last_not_of(' ');
3476       line = line.substr(0, end + 1);
3477 #if QT_VERSION < 0x060000
3478       if (!strcmp(line.c_str(), curViewPtName)) {
3479 #else
3480       if (!strcmp(line.c_str(), curViewPtName.c_str())) {
3481 #endif
3482          fileOut.seekp(pos);
3483          fileOut << newName;
3484          fileOut.seekp(0, std::ios::end); // Set the file pointer to the end of the file
3485          break;
3486       }
3487       while (line.size())
3488          getline(fileIn, line);
3489       pos = fileIn.tellg();
3490    }
3491 
3492    fileIn.close();
3493    fileIn.clear();
3494 
3495    while (idx < size) {
3496 #if QT_VERSION < 0x060000
3497       if (!strcmp(viewPtList[idx].viewPtName, curViewPtName)) {
3498          strcpy(viewPtList[idx].viewPtName, vpName);
3499 #else
3500       if (!strcmp(viewPtList[idx].viewPtName.c_str(), curViewPtName.c_str())) {
3501          viewPtList[idx].viewPtName = std::string(vpName);
3502 #endif
3503          break;
3504       }
3505       idx++;
3506    }
3507 }
3508 
3509 
3510 void G4OpenInventorQtExaminerViewer::SortBookmarksCB()
3511 {
3512    // FWJ NOTE: Xt version of this does not work (does nothing)
3513 
3514    //   G4cout << "Sort Button: SortBookmarks CALLBACK" << G4endl;
3515 
3516    // FWJ List for sorting
3517    // The dialog list and bookmark file will be rewritten.
3518    // Simpler to populate this list from the data structure.
3519 
3520    std::vector<std::string> charList;
3521 
3522    if (viewPtList.size() < 2) return;
3523 
3524    // Get current entries from the list
3525 
3526    for (int i = 0; i < (int)viewPtList.size(); i++) {
3527 
3528       charList.push_back(viewPtList[i].viewPtName);
3529       //      G4cout << "  Pushed " << i << " " << charList[i] << G4endl;
3530    }
3531 
3532    std::sort(charList.begin(), charList.end());
3533 
3534    // FWJ POPULATE the new dialog list
3535    //   G4cout << "  Populating Bookmark listWidget..." << G4endl;
3536    AuxWindowDialog->listWidget->clear();
3537 
3538    for (int i = 0; i < (int)viewPtList.size(); i++) {
3539       // viewPtIdx has to be changed to account for a different order in viewPtList
3540 #if QT_VERSION < 0x060000
3541       if (!strcmp(charList[i].c_str(), curViewPtName))
3542 #else
3543       if (!strcmp(charList[i].c_str(), curViewPtName.c_str()))
3544 #endif
3545          viewPtIdx = i;
3546       new QListWidgetItem(charList[i].c_str(), AuxWindowDialog->listWidget); 
3547 
3548    }
3549 
3550    sortViewPts(charList);
3551 
3552 }
3553 
3554 // Rewrites entire viewpoint file with sorted viewpoints.
3555 
3556 void G4OpenInventorQtExaminerViewer::sortViewPts(std::vector<std::string> sortedViewPts) 
3557 {
3558    SbVec3f axis;
3559    float x, y, z, angle;
3560    std::size_t sortIdx = 0, unsortIdx = 0;
3561 
3562    if (fileOut.is_open())
3563       fileOut.close();
3564 
3565    fileOut.open(fileName.c_str()); // Erase current viewpoint file
3566 
3567    writeViewPtIdx();
3568 
3569    std::size_t size = sortedViewPts.size();
3570    while (sortIdx < size) {
3571       while (strcmp(sortedViewPts[sortIdx].c_str(),
3572 #if QT_VERSION < 0x060000
3573                     viewPtList[unsortIdx].viewPtName))
3574 #else
3575                     viewPtList[unsortIdx].viewPtName.c_str()))
3576 #endif
3577          unsortIdx++;
3578 
3579       std::string vpName = viewPtList[unsortIdx].viewPtName;
3580 
3581       while ((int) vpName.size() < MAX_VP_NAME)
3582          vpName += " ";
3583       fileOut << vpName << std::endl;
3584       viewPtList[unsortIdx].position.getValue(x, y, z);
3585       fileOut << x << " " << y << " " << z << std::endl;
3586 
3587       // Reusing x, y and z for storing the axis
3588       viewPtList[unsortIdx].orientation.getValue(axis, angle);
3589       axis.getValue(x, y, z);
3590       fileOut << x << " " << y << " " << z << " " << angle << std::endl;
3591 
3592       fileOut << viewPtList[unsortIdx].camType << " "
3593               << viewPtList[unsortIdx].height << std::endl;
3594       fileOut << viewPtList[unsortIdx].focalDistance << " ";
3595 
3596       fileOut << viewPtList[unsortIdx].nearDistance << " ";
3597 
3598       fileOut << viewPtList[unsortIdx].farDistance << std::endl;
3599 
3600       fileOut << viewPtList[unsortIdx].viewportMapping << " ";
3601       fileOut << viewPtList[unsortIdx].aspectRatio << "\n" << std::endl;
3602       fileOut.flush();
3603 
3604       unsortIdx = 0;
3605       sortIdx++;
3606    }
3607 }
3608 
3609 // Needed to implement mouse wheel zoom direction change.
3610 // Does not work with MacOS trackpad: use Coin3d default handler.
3611 // Emulating private method SoGuiFullViewerP::zoom()
3612 #ifndef __APPLE__
3613 void
3614 G4OpenInventorQtExaminerViewer::zoom(const float diffvalue)
3615 {
3616    float multiplicator = float(std::exp(diffvalue));
3617    SoCamera *cam = getCamera();
3618 
3619    if (cam->isOfType(SoPerspectiveCamera::getClassTypeId())) {
3620       const float oldfocaldist = cam->focalDistance.getValue();
3621       const float newfocaldist = oldfocaldist * multiplicator;
3622 
3623       SbVec3f direction;
3624       cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction);
3625 
3626       const SbVec3f oldpos = cam->position.getValue();
3627       const SbVec3f newpos = oldpos + (newfocaldist - oldfocaldist) * -direction;
3628       cam->position = newpos;
3629       cam->focalDistance = newfocaldist;
3630    } else if (cam->isOfType(SoOrthographicCamera::getClassTypeId())) {
3631       SoOrthographicCamera * oc = (SoOrthographicCamera *)cam;
3632       oc->height = oc->height.getValue() * multiplicator;
3633    }
3634 }
3635 #endif
3636 
3637 // Handling mouse and keyboard events
3638 
3639 SbBool
3640 G4OpenInventorQtExaminerViewer::processSoEvent(const SoEvent* const ev)
3641 {
3642 
3643    // FWJ DEBUG
3644    //   G4cout << "processSoEvent ############" << ++processSoEventCount << G4endl;
3645 
3646    SoCamera *cam = getCamera();
3647    const SoType type(ev->getTypeId());
3648 
3649 // Needed to implement mouse wheel zoom direction change.
3650 // Does not work with MacOS trackpad: use Coin3d default handler.
3651 #ifndef __APPLE__
3652    if (type.isDerivedFrom(SoMouseButtonEvent::getClassTypeId())) {
3653       SoMouseButtonEvent * me = (SoMouseButtonEvent *) ev;
3654 
3655       //      if (currentState == ANIMATION || currentState == REVERSED_ANIMATION
3656       //          || currentState == PAUSED_ANIMATION) {
3657 
3658       switch (me->getButton()) {
3659 
3660          case SoMouseButtonEvent::BUTTON4: // Scroll wheel up
3661             if (me->getState() == SoButtonEvent::DOWN) {
3662                //               G4cout << "SCROLL WHEEL UP" << G4endl;
3663                zoom(-0.1f);
3664                return TRUE;
3665             }
3666             break;
3667 
3668          case SoMouseButtonEvent::BUTTON5: // Scroll wheel down
3669             if (me->getState() == SoButtonEvent::DOWN) {
3670                //               G4cout << "SCROLL WHEEL DOWN" << G4endl;
3671                zoom(0.1f);
3672                return TRUE;
3673             }
3674             break;
3675 
3676          default:
3677             break;
3678       }
3679          //      }
3680       if (currentState == GENERAL) {
3681 
3682       }
3683    }
3684 #endif
3685 
3686    if (type.isDerivedFrom(SoKeyboardEvent::getClassTypeId())) {
3687       SoKeyboardEvent* ke = (SoKeyboardEvent*)ev;
3688 
3689       if (SoKeyboardEvent::isKeyPressEvent(ev, ke->getKey())) {
3690          switch (ke->getKey()) {
3691          case SoKeyboardEvent::E:
3692             if (externalQtApp) {
3693                // G4cout << "E KEY PRESSED" << G4endl;
3694                return TRUE;
3695             } else {
3696                G4cout <<
3697                   "E KEY PRESSED, EXITING OIQT VIEWER SECONDARY LOOP" <<
3698                   G4endl;
3699                SoQt::exitMainLoop();
3700             //            escapeCallback();
3701                return TRUE;
3702             }
3703          case SoKeyboardEvent::LEFT_SHIFT:
3704             this->lshiftdown = true;
3705             return TRUE;
3706          case SoKeyboardEvent::RIGHT_SHIFT:
3707             this->rshiftdown = true;
3708             return TRUE;
3709          case SoKeyboardEvent::LEFT_CONTROL:
3710             this->lctrldown = true;
3711             return TRUE;
3712          case SoKeyboardEvent::RIGHT_CONTROL:
3713             this->rctrldown = true;
3714             return TRUE;
3715          case SoKeyboardEvent::SPACE:
3716             if (currentState == ANIMATION
3717                 || currentState == REVERSED_ANIMATION) {
3718                beforePausing = currentState;
3719                currentState = PAUSED_ANIMATION;
3720                if (animateSensor->isScheduled())
3721                   animateSensor->unschedule();
3722                return TRUE;
3723             } else if (currentState == PAUSED_ANIMATION) {
3724                if (maxSpeed) {
3725                   if ((beforePausing == ANIMATION
3726                        && refParticleIdx
3727                        < (int) refParticleTrajectory.size() - 1)
3728                       || (beforePausing == REVERSED_ANIMATION
3729                           && refParticleIdx > 0)) {
3730                      currentState = beforePausing;
3731                      animateRefParticle();
3732                   }
3733                }
3734                return TRUE;
3735             }
3736             break;
3737          case SoKeyboardEvent::ESCAPE:
3738             if (currentState == ANIMATION
3739                 || currentState == REVERSED_ANIMATION
3740                 || currentState == PAUSED_ANIMATION) {
3741 
3742                if (animateSensor->isScheduled())
3743                   animateSensor->unschedule();
3744                currentState = prevState;
3745                refParticleIdx = prevRefIdx;
3746                setSuperimpositionEnabled(superimposition, FALSE);
3747                maxSpeed = 0.0f;
3748                step = 1;
3749 
3750                scheduleRedraw();
3751                if (currentState == VIEWPOINT) {
3752                   setSuperimpositionEnabled(superimposition, TRUE);
3753                   axisSwitch->whichChild.setValue(SO_SWITCH_NONE);
3754                   animSpeedOutlineSwitch->whichChild.setValue(
3755                                                               SO_SWITCH_NONE);
3756                   animSpeedSwitch->whichChild.setValue(SO_SWITCH_NONE);
3757 
3758                   scheduleRedraw();
3759                }
3760                restoreCamera();
3761                return TRUE;
3762             }
3763             break;
3764          case SoKeyboardEvent::DELETE:
3765             if (viewPtList.size()
3766                 && (currentState != ANIMATION
3767                     && currentState != REVERSED_ANIMATION
3768                     && currentState != PAUSED_ANIMATION)) {
3769                // FWJ IMPLEMENT LATER
3770                // String dialogName = (char *) "Delete Viewpoint";
3771                // std::string msg = "Are you sure you want to delete current viewpoint?";
3772                // warningMsgDialog(msg, dialogName, deleteViewPtCB);
3773                return TRUE;
3774             }
3775             break;
3776          case SoKeyboardEvent::LEFT_ARROW:
3777             switch (currentState) {
3778             case BEAMLINE:
3779                if ((this->lshiftdown)   || (this->rshiftdown)) {
3780                   refParticleIdx -= step;
3781                   moveCamera();
3782                }
3783                else if ((this->lctrldown)       || (this->rctrldown)) {
3784                   if (SoQtExaminerViewer::isAnimating())
3785                      stopAnimating();
3786                   prevState = currentState;
3787                   currentState = ROTATING;
3788                   animateBtwPtsPeriod = 0.08f;
3789 
3790                   SbVec3f tmp = camDir;
3791                   tmp.negate();
3792                   rotAxis = tmp;
3793 
3794                   rotCnt = ROT_CNT;
3795                   moveCamera(); // To make sure camera is perpendicular to the beamline
3796                   rotateCamera();
3797                }
3798                else {
3799                   if (SoQtExaminerViewer::isAnimating())
3800                      stopAnimating();
3801                   prevState = currentState;
3802                   currentState = ROTATING;
3803                   animateBtwPtsPeriod = 0.08f;
3804 
3805                   SbVec3f tmp = camUpVec;
3806                   tmp.negate();
3807                   rotAxis = tmp;
3808 
3809                   rotCnt = ROT_CNT;
3810                   moveCamera(); // To make sure camera is perpendicular to the beamline
3811                   rotateCamera();
3812 
3813                }
3814                return TRUE;
3815 
3816             case ANIMATION:
3817             case REVERSED_ANIMATION:
3818                left_right -= 1.5f;
3819                return TRUE;
3820             case PAUSED_ANIMATION:
3821                left_right -= 1.5f;
3822                setStartingPtForAnimation();
3823                cam->position = myCam->position;
3824                return TRUE;
3825             case GENERAL:
3826             case VIEWPOINT:
3827                if ((!this->lshiftdown) && (!this->rshiftdown)) {
3828                   // Using this allows us to look around without
3829                   // changing the camera parameters (camDir, camUpVec)
3830                   this->bottomWheelMotion(
3831                                           this->getBottomWheelValue() + 0.1f);
3832 
3833                   return TRUE;
3834                }
3835                break;
3836             case ROTATING:
3837                // For this state, let the keyboard event
3838                // be handled by superclass
3839                break;
3840             default:
3841                SoDebugError::post("G4OpenInventorQtExaminerViewer::processSoEvent",
3842                                   "Unhandled viewer state");
3843                break;
3844             }
3845             break;
3846 
3847          case SoKeyboardEvent::RIGHT_ARROW:
3848             switch(currentState) {
3849             case BEAMLINE:
3850                if ((this->lshiftdown)   || (this->rshiftdown)) {
3851                   refParticleIdx += step;
3852                   moveCamera();
3853                }
3854                else if ((this->lctrldown)       || (this->rctrldown)) {
3855                   if (SoQtExaminerViewer::isAnimating())
3856                      stopAnimating();
3857                   prevState = currentState;
3858                   currentState = ROTATING;
3859                   animateBtwPtsPeriod = 0.08f;
3860 
3861                   rotAxis = camDir;
3862 
3863                   rotCnt = ROT_CNT;
3864                   moveCamera(); // To make sure camera is perpendicular to the beamline
3865                   rotateCamera();
3866                }
3867                else{
3868                   if (SoQtExaminerViewer::isAnimating())
3869                      stopAnimating();
3870                   prevState = currentState;
3871                   currentState = ROTATING;
3872                   animateBtwPtsPeriod = 0.08f;
3873 
3874                   rotAxis = camUpVec;
3875 
3876                   rotCnt = ROT_CNT;
3877                   moveCamera(); // To make sure camera is perpendicular to the beamline
3878                   rotateCamera();
3879                }
3880                return TRUE;
3881 
3882             case ANIMATION:
3883             case REVERSED_ANIMATION:
3884                left_right += 1.5f;
3885                return TRUE;
3886             case PAUSED_ANIMATION:
3887                left_right += 1.5f;
3888                setStartingPtForAnimation();
3889                cam->position = myCam->position;
3890                return TRUE;
3891             case GENERAL:
3892             case VIEWPOINT:
3893                if ((!this->lshiftdown) && (!this->rshiftdown)) {
3894                   // Using this allows us to look around without
3895                   // changing the camera parameters (camDir, camUpVec)
3896                   this->bottomWheelMotion(
3897                                           this->getBottomWheelValue() - 0.1f);
3898                   return TRUE;
3899                }
3900                break;
3901             case ROTATING:
3902                // For this state, let the keyboard event
3903                // be handled by superclass
3904                break;
3905             default:
3906                SoDebugError::post("G4OpenInventorQtExaminerViewer::processSoEvent",
3907                                   "Unhandled viewer state");
3908                break;
3909             }
3910             break;
3911 
3912          case SoKeyboardEvent::DOWN_ARROW:
3913             switch(currentState) {
3914             case BEAMLINE:
3915 
3916                if ((this->lshiftdown)   || (this->rshiftdown)) {
3917                   refParticleIdx -= step;
3918                   moveCamera();
3919                }
3920                else{
3921                   if (SoQtExaminerViewer::isAnimating())
3922                      stopAnimating();
3923                   prevState = currentState;
3924                   currentState = ROTATING;
3925                   animateBtwPtsPeriod = 0.08f;
3926 
3927                   rotAxis = camDir.cross(camUpVec);
3928 
3929                   rotCnt = ROT_CNT;
3930                   moveCamera(); // To make sure camera is perpendicular to the beamline
3931                   rotateCamera();
3932 
3933                }
3934                return TRUE;
3935 
3936             case ANIMATION:
3937             case REVERSED_ANIMATION:
3938                up_down -= 1.5f;
3939                return TRUE;
3940             case PAUSED_ANIMATION:
3941                up_down -= 1.5f;
3942                setStartingPtForAnimation();
3943                cam->position = myCam->position;
3944                return TRUE;
3945             case GENERAL:
3946             case VIEWPOINT:
3947                // Using this allows us to look around without
3948                // changing the camera parameters (camDir, camUpVec)
3949                if ((!this->lshiftdown) && (!this->rshiftdown)) {
3950                   this->leftWheelMotion(this->getLeftWheelValue() - 0.1f);
3951                   return TRUE;
3952                }
3953                break;
3954             case ROTATING:
3955                // For this state, let the keyboard event
3956                // be handled by superclass
3957                break;
3958             default:
3959                SoDebugError::post("G4OpenInventorQtExaminerViewer::processSoEvent",
3960                                   "Unhandled viewer state");
3961                break;
3962             }
3963             break;
3964 
3965          case SoKeyboardEvent::UP_ARROW:
3966             switch(currentState) {
3967             case BEAMLINE:
3968                if ((this->lshiftdown)   || (this->rshiftdown)) {
3969                   refParticleIdx -= step;
3970                   moveCamera();
3971                }
3972                else{
3973                   if (SoQtExaminerViewer::isAnimating())
3974                      stopAnimating();
3975                   prevState = currentState;
3976                   currentState = ROTATING;
3977                   animateBtwPtsPeriod = 0.08f;
3978 
3979                   rotAxis = camUpVec.cross(camDir);
3980 
3981                   rotCnt = ROT_CNT;
3982                   moveCamera();
3983 
3984                   rotateCamera();
3985 
3986 
3987                }
3988                return TRUE;
3989             case ANIMATION:
3990             case REVERSED_ANIMATION:
3991                up_down += 1.5f;
3992                return TRUE;
3993             case PAUSED_ANIMATION:
3994                up_down += 1.5f;
3995                setStartingPtForAnimation();
3996                cam->position = myCam->position;
3997                return TRUE;
3998             case GENERAL:
3999             case VIEWPOINT:
4000                // Using this allows us to look around without
4001                // changing the camera parameters (camDir, camUpVec)
4002                if ((!this->lshiftdown) && (!this->rshiftdown)) {
4003                   this->leftWheelMotion(this->getLeftWheelValue() + 0.1f);
4004                   return TRUE;
4005                }
4006                break;
4007             case ROTATING:
4008                // For this state, let the keyboard event
4009                // be handled by superclass
4010                break;
4011             default:
4012                SoDebugError::post("G4OpenInventorQtExaminerViewer::processSoEvent",
4013                                   "Unhandled viewer state");
4014                break;
4015             }
4016             break;
4017 
4018          case SoKeyboardEvent::PAGE_UP:
4019             switch(currentState) {
4020             case BEAMLINE:
4021                if (step < (int) refParticleTrajectory.size() / 5) // Magic number
4022                   step++;
4023                return TRUE;
4024             case ANIMATION:
4025                incSpeed();
4026                maxSpeed += SPEED_INDICATOR_STEP;
4027                if (maxSpeed > 0.8)
4028                   maxSpeed = MAX_SPEED_INDICATOR;
4029                scheduleRedraw();
4030 
4031                return TRUE;
4032             case REVERSED_ANIMATION:
4033                if(!animateSensor->isScheduled()) {
4034                   currentState = ANIMATION;
4035                   if (refParticleIdx
4036                       < (int) refParticleTrajectory.size() - 1) {
4037                      refParticleIdx++;
4038                      maxSpeed = SPEED_INDICATOR_STEP;
4039                      scheduleRedraw();
4040                      animateRefParticle();
4041                   }
4042                }
4043                else{
4044                   maxSpeed += SPEED_INDICATOR_STEP;
4045                   decSpeed();
4046                   scheduleRedraw();
4047                }
4048                return TRUE;
4049             case PAUSED_ANIMATION:
4050                maxSpeed += SPEED_INDICATOR_STEP;
4051                if (maxSpeed > 0.8)
4052                   maxSpeed = MAX_SPEED_INDICATOR;
4053 
4054                if (beforePausing == ANIMATION) {
4055                   incSpeed();
4056                } else {
4057                   decSpeed();
4058                   if (animateBtwPtsPeriod >= MIN_SPEED)
4059                      beforePausing = ANIMATION;
4060                }
4061 
4062                scheduleRedraw();
4063                return TRUE;
4064             default:    //fall through
4065                break;
4066             }
4067             break;
4068 
4069          case SoKeyboardEvent::PAGE_DOWN:
4070             switch(currentState) {
4071             case BEAMLINE:
4072                if (step > 1)
4073                   step--;
4074                return TRUE;
4075             case ANIMATION:
4076                if(!animateSensor->isScheduled()) {
4077                   currentState = REVERSED_ANIMATION;
4078                   if (refParticleIdx > 1) {
4079                      refParticleIdx--;
4080                      maxSpeed = -SPEED_INDICATOR_STEP;
4081                      scheduleRedraw();
4082                      animateRefParticle();
4083                   }
4084                }
4085                else{
4086                   maxSpeed -= SPEED_INDICATOR_STEP;
4087                   decSpeed();
4088                   scheduleRedraw();
4089                }
4090                return TRUE;
4091             case REVERSED_ANIMATION:
4092                incSpeed();
4093                maxSpeed -= SPEED_INDICATOR_STEP;
4094                if (maxSpeed < -0.8)
4095                   maxSpeed = -MAX_SPEED_INDICATOR;
4096                scheduleRedraw();
4097                return TRUE;
4098             case PAUSED_ANIMATION:
4099                maxSpeed -= SPEED_INDICATOR_STEP;
4100                if (maxSpeed < -0.8)
4101                   maxSpeed = -MAX_SPEED_INDICATOR;
4102                if (beforePausing == REVERSED_ANIMATION) {
4103                   incSpeed();
4104                } else {
4105                   decSpeed();
4106                   if (animateBtwPtsPeriod >= MIN_SPEED)
4107                      beforePausing = REVERSED_ANIMATION;
4108                }
4109                scheduleRedraw();
4110                return TRUE;
4111             default:
4112                //fall through
4113                break;
4114             }
4115             break;
4116 
4117             // FROM XT VIEWER
4118             //         case SoKeyboardEvent::E:
4119             //            this->escapeCallback(this->examinerObject);
4120             //            break;
4121 
4122          default:
4123             break; // To get rid of compiler warnings
4124          }
4125       }
4126       if (SoKeyboardEvent::isKeyReleaseEvent(ev, ke->getKey())) {
4127          switch (ke->getKey()) {
4128          case SoKeyboardEvent::LEFT_SHIFT:
4129             this->lshiftdown = false;
4130             return TRUE;
4131          case SoKeyboardEvent::RIGHT_SHIFT:
4132             this->rshiftdown = false;
4133             return TRUE;
4134          case SoKeyboardEvent::LEFT_CONTROL:
4135             this->lctrldown = false;
4136             return TRUE;
4137          case SoKeyboardEvent::RIGHT_CONTROL:
4138             this->rctrldown = false;
4139             return TRUE;
4140          default:
4141             break;
4142          }
4143       }
4144    }
4145 
4146    // Pass the event on to the viewer
4147    // Need some checks here as in Xt viewer?
4148 
4149    if (currentState == ANIMATION || currentState == REVERSED_ANIMATION
4150        || currentState == ROTATING)
4151       return FALSE;
4152    else
4153       return SoQtExaminerViewer::processSoEvent(ev);
4154 }
4155 
4156 
4157 // REMAINDER OF MENU BAR FUNCTIONS...
4158 
4159 
4160 void G4OpenInventorQtExaminerViewer::FileLoadSceneGraphCB()
4161 {
4162    //   G4cout << "File: Load scene graph CALLBACK" << G4endl;
4163 
4164    QFileDialog filedialog(getParentWidget(), tr("Load Scene Graph"));
4165    filedialog.setFileMode(QFileDialog::AnyFile);
4166    filedialog.setFont(*font);
4167    if (!filedialog.exec()) return;
4168    QStringList filenameinlist = filedialog.selectedFiles();
4169    QString filenamein = filenameinlist[0];
4170 
4171    SoInput sceneInput;
4172 
4173    if (sceneInput.openFile(qPrintable(filenamein))) {
4174       // Read the whole file into the database
4175       newSceneGraph = SoDB::readAll(&sceneInput);
4176       if (newSceneGraph == NULL) {
4177          QMessageBox msgbox;
4178          msgbox.setFont(*font);
4179          QString messagetxt = "Error reading scene graph file ";
4180          messagetxt.append(filenamein);
4181          msgbox.setText(messagetxt);
4182          msgbox.exec();
4183          sceneInput.closeFile();
4184          return;
4185       }
4186    } else {
4187       QMessageBox msgbox;
4188       msgbox.setFont(*font);
4189       QString messagetxt = "Error opening scene graph file ";
4190       messagetxt.append(filenamein);
4191       msgbox.setText(messagetxt);
4192       msgbox.exec();
4193       return;
4194    }
4195 
4196    SoSeparator* root = (SoSeparator*)getSceneGraph();
4197    root->unref();
4198    newSceneGraph->ref();
4199    setSceneGraph(newSceneGraph);
4200 }
4201 
4202 void G4OpenInventorQtExaminerViewer::FileSaveSceneGraphCB()
4203 {
4204    //   G4cout << "File: Save scene graph CALLBACK" << G4endl;
4205 
4206    QFileDialog filedialog(getParentWidget(), tr("Save scene graph"));
4207    filedialog.setFileMode(QFileDialog::AnyFile);
4208    // To enable confirmation of overwriting
4209    filedialog.setAcceptMode(QFileDialog::AcceptSave);
4210    filedialog.setFont(*font);
4211    if (!filedialog.exec()) return;
4212    QStringList filenameinlist = filedialog.selectedFiles();
4213    QString filenamein = filenameinlist[0];
4214 
4215    SoWriteAction writeAction;
4216    SoSeparator* root = (SoSeparator*)getSceneGraph();
4217 
4218    SoOutput* out = writeAction.getOutput();
4219 
4220    if (out->openFile(qPrintable(filenamein))) {
4221       out->setBinary(FALSE);
4222       writeAction.apply(root);
4223       out->closeFile();
4224    } else {
4225       QMessageBox msgbox;
4226       msgbox.setFont(*font);
4227       QString messagetxt = "Error opening file ";
4228       messagetxt.append(filenamein);
4229       msgbox.setText(messagetxt);
4230       msgbox.exec();
4231    }
4232 }
4233 
4234 
4235 void G4OpenInventorQtExaminerViewer::HelpControlsCB()
4236 {
4237    //   G4cout << "Help: Help Controls CALLBACK" << G4endl;
4238    helpmsgbox->show();
4239 }
4240 
4241 
4242 HookEventProcState::HookEventProcState(G4OpenInventorQtExaminerViewer* vwr)
4243 {
4244    viewer = vwr;
4245 }
4246 
4247 HookEventProcState::~HookEventProcState()
4248 {;}
4249 
4250 G4bool HookEventProcState::Notify(G4ApplicationState requestedState)
4251 {
4252 #if QT_VERSION < 0x060000
4253    if (requestedState == G4State_EventProc) viewer->newEvents = true;
4254 #else
4255 #ifdef G4MULTITHREADED
4256    //G.Barrand: on the master thread, we are not notified of end of events,
4257    //           we raise the newEvents flag on the end of run.
4258    G4StateManager* stateManager = G4StateManager::GetStateManager();
4259    G4ApplicationState previousState = stateManager->GetPreviousState();
4260  //if (previousState == G4State_Idle       &&  requestedState == G4State_GeomClosed)  //BeginOfRun
4261  //if (previousState == G4State_GeomClosed &&  requestedState == G4State_EventProc)   //BeginOfEvent
4262  //if (previousState == G4State_EventProc  &&  requestedState == G4State_GeomClosed)  //EndOfEvent
4263    if (previousState == G4State_GeomClosed &&  requestedState == G4State_Idle) {      //EndOfRun
4264      viewer->newEvents = true;
4265    }
4266 #else
4267    if (requestedState == G4State_EventProc) viewer->newEvents = true;
4268 #endif
4269 #endif
4270    return true;
4271 }
4272