Geant4 Cross Reference

Cross-Referencing   Geant4
Geant4/visualization/OpenGL/src/G4OpenGLQtViewer.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 // G4OpenGLQtViewer : Class to provide Qt specific
 27 //                     functionality for OpenGL in GEANT4
 28 //
 29 // 27/06/2003 : G.Barrand : implementation (at last !).
 30 // 30/06/2014 : M.Kelsey :  Change QPixmap objects to pointers
 31 
 32 #include "G4LogicalVolumeStore.hh"
 33 #include "G4PhysicalVolumeStore.hh"
 34 #include "G4VisCommandsGeometrySet.hh"
 35 #include "G4PhysicalVolumeModel.hh"
 36 #include "G4Text.hh"
 37 #include "G4UnitsTable.hh"
 38 #include "G4OpenGLStoredQtViewer.hh"
 39 #include "G4Threading.hh"
 40 
 41 #include "G4OpenGLQtViewer.hh"
 42 #include "G4OpenGLSceneHandler.hh"
 43 #include "G4OpenGLQtExportDialog.hh"
 44 #include "G4OpenGLQtMovieDialog.hh"
 45 #include "G4Qt.hh"
 46 #include "G4UIQt.hh"
 47 #include "G4UImanager.hh"
 48 #include "G4UIcommandTree.hh"
 49 
 50 #include <CLHEP/Units/SystemOfUnits.h>
 51 
 52 #include <typeinfo>
 53 #include <mutex>
 54 
 55 #include <qlayout.h>
 56 #include <qlabel.h>
 57 #include <qdialog.h>
 58 #include <qpushbutton.h>
 59 #include <qprocess.h>
 60 #include <qmenu.h>
 61 #include <qimagewriter.h>
 62 
 63 #include <qtextedit.h>
 64 #include <qtreewidget.h>
 65 #include <qapplication.h>
 66 #include <qmessagebox.h>
 67 #include <qfiledialog.h>
 68 #include <qdatetime.h>
 69 #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
 70   #include <qelapsedtimer.h>
 71 #endif
 72 #if (QT_VERSION < QT_VERSION_CHECK(5, 10, 0))
 73   #include "qdesktopwidget.h"
 74 #endif
 75 #include <qpainter.h>
 76 #include <qdialog.h>
 77 #include <qcolordialog.h>
 78 #include <qevent.h> //include <qcontextmenuevent.h>
 79 #include <qobject.h>
 80 #include <qgroupbox.h>
 81 #include <qcombobox.h>
 82 #include <qlineedit.h>
 83 #include <qscreen.h>
 84 #include <qmainwindow.h>
 85 #include <qtablewidget.h>
 86 #include <qheaderview.h>
 87 #include <qscrollarea.h>
 88 #include <qsplitter.h>
 89 #include <qcheckbox.h>
 90 #include <qcursor.h>
 91 #include <qthread.h>
 92 
 93 #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
 94 #include <QGLContext>
 95 #else
 96 #include <QOpenGLContext>
 97 #endif
 98 
 99 #ifndef G4GMAKE
100 #include "moc_G4OpenGLQtViewer.cpp"
101 #endif
102 
103 //////////////////////////////////////////////////////////////////////////////
104 void G4OpenGLQtViewer::CreateMainWindow (
105  G4QGLWidgetType* glWidget
106  ,const QString& name
107 )
108 //////////////////////////////////////////////////////////////////////////////
109 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!//
110 {
111 
112   if(fGLWidget) return; //Done.
113 
114   fGLWidget = glWidget ;
115 
116 #if QT_VERSION < 0x060000
117   ResizeWindow(fVP.GetWindowSizeHintX(),fVP.GetWindowSizeHintY());
118 #else
119   ResizeWindow(glWidget->devicePixelRatio()*fVP.GetWindowSizeHintX(),glWidget->devicePixelRatio()*fVP.GetWindowSizeHintY());
120 #endif
121 
122   // FIXME L.Garnier 9/11/09 Has to be check !!!
123   // Qt UI with Qt Vis
124   // Qt UI with X Vis
125   // X UI with Qt Vis
126   // X UI with X Vis
127   // Ne marche pas avec un UIBatch !! (ecran blanc)
128 
129   // return false if G4UIQt was not launch
130 
131   G4UImanager* UI = G4UImanager::GetUIpointer();
132   if (UI == NULL) return;
133 
134   if (! static_cast<G4UIQt*> (UI->GetG4UIWindow())) {
135     // NO UI, should be batch mode
136     fBatchMode = true;
137     return;
138   }
139   fUiQt = static_cast<G4UIQt*> (UI->GetG4UIWindow());
140 
141   bool isTabbedView = false;
142   if ( fUiQt) {
143     if (!fBatchMode) {
144       G4Qt* interactorManager = G4Qt::getInstance ();
145       if (!interactorManager->IsExternalApp()) {
146         // INIT size
147         fWinSize_x = fVP.GetWindowSizeHintX();
148         fWinSize_y = fVP.GetWindowSizeHintY();
149 
150         isTabbedView = fUiQt->AddTabWidget((QWidget*)fGLWidget,name);
151         QObject::connect(fUiQt->GetViewerTabWidget(),
152                          SIGNAL(currentChanged(int)),
153                          this,
154                          SLOT(currentTabActivated(int)));
155 
156 #if QT_VERSION < 0x060000
157 #else
158         createViewerPropertiesWidget();
159 #endif
160 
161       }
162       createSceneTreeWidget();
163       // activate them
164     }
165   }
166 
167   if (!isTabbedView) { // we have to do a dialog
168 
169     QWidget *glDialogWidget = getParentWidget();
170     if (glDialogWidget == NULL) {
171       return;
172     }
173     glWidget->setParent(glDialogWidget);
174     QHBoxLayout *mainLayout = new QHBoxLayout();
175 
176     mainLayout->setContentsMargins(0,0,0,0);
177     mainLayout->setSpacing(0);
178     mainLayout->addWidget(fGLWidget);
179     if (fGLWidget->inherits("QMainWindow")) {
180       fGLWidget->setWindowTitle( name);
181     }
182     glDialogWidget->setLayout(mainLayout);
183 
184 
185     //useful for MACOSX, we have to compt the menuBar height
186 #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
187       G4int offset = QGuiApplication::primaryScreen()->availableGeometry().height()
188           - QGuiApplication::screenAt(QPoint(20,20))->availableGeometry().height();
189 #else
190       G4int offset = QApplication::desktop()->height()
191           - QApplication::desktop()->availableGeometry().height();
192 #endif
193 
194     G4int YPos= fVP.GetWindowAbsoluteLocationHintY(QGuiApplication::primaryScreen()->availableGeometry().height());
195     if (fVP.GetWindowAbsoluteLocationHintY(QGuiApplication::primaryScreen()->availableGeometry().height())< offset) {
196       YPos = offset;
197     }
198     glDialogWidget->resize(getWinWidth(), getWinHeight());
199     glDialogWidget->move(fVP.GetWindowAbsoluteLocationHintX(QGuiApplication::primaryScreen()->availableGeometry().width()),YPos);
200     glDialogWidget->show();
201   }
202 
203   if(!fGLWidget) return;
204 
205   if (!fContextMenu)
206     createPopupMenu();
207 
208 }
209 
210 
211 //////////////////////////////////////////////////////////////////////////////
212 G4OpenGLQtViewer::G4OpenGLQtViewer (
213  G4OpenGLSceneHandler& scene
214 )
215   :G4VViewer (scene, -1)
216   ,G4OpenGLViewer (scene)
217   ,fUiQt(NULL)
218   ,fGLWidget(NULL)
219   ,fRecordFrameNumber(0)
220   ,fMouseOnSceneTree(false)
221   ,fContextMenu(0)
222   ,fLastPickPoint(-1,-1)
223   ,fDeltaDepth(0.01)
224   ,fDeltaZoom(0.05)
225   ,fHoldKeyEvent(false)
226   ,fHoldMoveEvent(false)
227   ,fHoldRotateEvent(false)
228   ,fAutoMove(false)
229   ,fEncoderPath("")
230   ,fTempFolderPath("")
231   ,fMovieTempFolderPath("")
232   ,fSaveFileName("")
233   ,fParameterFileName("ppmtompeg_encode_parameter_file.par")
234   ,fMovieParametersDialog(NULL)
235   ,fRecordingStep(WAIT)
236   ,fProcess(NULL)
237   ,fNbMaxFramesPerSec(100)
238   ,fNbMaxAnglePerSec(360)
239   ,fLaunchSpinDelay(100)
240   ,fUISceneTreeWidget(NULL)
241   ,fUIViewerPropertiesWidget(NULL)
242   ,fUIPickInfosWidget(NULL)
243   ,fNoKeyPress(true)
244   ,fAltKeyPress(false)
245   ,fControlKeyPress(false)
246   ,fShiftKeyPress(false)
247   ,fBatchMode(false)
248   ,fCheckSceneTreeComponentSignalLock(false)
249   ,fViewerPropertiesTableWidgetIsInit(false)
250   ,fSceneTreeComponentTreeWidget(NULL)
251   ,fSceneTreeWidget(NULL)
252   ,fPVRootNodeCreate(false)
253   ,fFilterOutput(NULL)
254   ,fNbRotation(0)
255   ,fTimeRotation(0)
256   ,fTouchableVolumes("Touchables")
257   ,fShortcutsDialog(NULL)
258   ,fViewerPropertiesTableWidget(NULL)
259   ,fPickInfosWidget(NULL)
260   ,fPickInfosScrollArea(NULL)
261   ,fTreeWidgetInfosIgnoredCommands(0)
262   ,fSceneTreeDepthSlider(NULL)
263   ,fSceneTreeDepth(1)
264   ,fModelShortNameItem(NULL)
265   ,fMaxPOindexInserted(-1)
266   ,fTreeIconOpen(NULL)
267   ,fTreeIconClosed(NULL)
268   ,fLastExportSliderValue(80)
269   ,fLastHighlightColor(G4Color(0,0,0,0))
270   ,fLastHighlightName(0)
271   ,fIsDeleting(false)
272 {
273   // launch Qt if not
274   if (QCoreApplication::instance () == NULL) {
275     fBatchMode = true;
276   }
277   G4Qt::getInstance ();
278 
279   fLastPos3 = QPoint(-1,-1);
280   fLastPos2 = QPoint(-1,-1);
281   fLastPos1 = QPoint(-1,-1);
282 
283   initMovieParameters();
284 
285 #if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0))
286   fLastEventTime = new QTime();
287 #else
288   fLastEventTime = new QElapsedTimer();
289 #endif
290   // Set default path and format
291   fFileSavePath = QDir::currentPath();
292 
293   // add available export format
294   QList<QByteArray> formats =  QImageWriter::supportedImageFormats ();
295   for (int i = 0; i < formats.size(); ++i) {
296     addExportImageFormat(formats.at(i).data());
297   }
298 
299   const char * const icon1[]={
300     /* columns rows colors chars-per-pixel */
301           "20 20 34 1",
302      "  c None",
303      ". c #7C7C7C7C7C7C",
304      "X c #7D7D7D7D7D7D",
305      "o c #828282828282",
306      "O c #838383838383",
307      "+ c #848484848484",
308      "@ c #858585858585",
309      "# c #878787878787",
310      "$ c #888888888888",
311      "% c #8B8B8B8B8B8B",
312      "& c #8C8C8C8C8C8C",
313      "* c #8F8F8F8F8F8F",
314      "= c #909090909090",
315      "- c #919191919191",
316      "; c #999999999999",
317      ": c #9D9D9D9D9D9D",
318      "> c #A2A2A2A2A2A2",
319      ", c #A3A3A3A3A3A3",
320      "< c #A5A5A5A5A5A5",
321      "1 c #A6A6A6A6A6A6",
322      "2 c #B3B3B3B3B3B3",
323      "3 c #B6B6B6B6B6B6",
324      "4 c #C2C2C2C2C2C2",
325      "5 c #C6C6C6C6C6C6",
326      "6 c #CACACACACACA",
327      "7 c #CFCFCFCFCFCF",
328      "8 c #D0D0D0D0D0D0",
329      "9 c #D4D4D4D4D4D4",
330      "0 c #D7D7D7D7D7D7",
331      "q c #DEDEDEDEDEDE",
332      "w c #E0E0E0E0E0E0",
333      "e c #E7E7E7E7E7E7",
334      "r c #F4F4F4F4F4F4",
335      "t c #F7F7F7F7F7F7",
336      "                               ",
337      "                               ",
338      "                               ",
339      "                               ",
340      "                               ",
341      "                               ",
342      "    =========>                 ",
343      "    7&X+++Oo<e                 ",
344      "     2o+@@+-8                  ",
345      "     w;.#@+3                   ",
346      "      4$o@:q                   ",
347      "      r1X%5                    ",
348      "       9*,t                    ",
349      "        60                     ",
350      "                               ",
351      "                               ",
352      "                               ",
353      "                               ",
354      "                               ",
355      "                               "
356   };
357   const char * const icon2[]={
358     "20 20 68 1",
359     "  c None",
360     ". c #5F5F10102323",
361     "X c #40405F5F1010",
362     "o c #696963632E2E",
363     "O c #101019194C4C",
364     "+ c #101023237070",
365     "@ c #70702D2D6363",
366     "# c #73732D2D6464",
367     "$ c #79792E2E6767",
368     "% c #19194C4C5353",
369     "& c #2D2D63636161",
370     "* c #2E2E61617070",
371     "= c #6F6F6E6E4343",
372     "- c #707065655F5F",
373     "; c #727279795454",
374     ": c #535341417070",
375     "> c #797954547979",
376     ", c #434361617474",
377     "< c #414170707070",
378     "1 c #686869696363",
379     "2 c #6C6C69696363",
380     "3 c #656567676F6F",
381     "4 c #69696F6F6E6E",
382     "5 c #747465656767",
383     "6 c #757562626C6C",
384     "7 c #70706C6C6969",
385     "8 c #616174746565",
386     "9 c #656573736969",
387     "0 c #616174746969",
388     "q c #707075756262",
389     "w c #797970706565",
390     "e c #636361617474",
391     "r c #67676F6F7272",
392     "t c #727261617070",
393     "y c #616170707070",
394     "u c #6F6F72727979",
395     "i c #67676E6ED1D1",
396     "p c #808080808080",
397     "a c #828282828282",
398     "s c #838383838383",
399     "d c #848484848484",
400     "f c #858585858585",
401     "g c #868686868686",
402     "h c #888888888888",
403     "j c #8A8A8A8A8A8A",
404     "k c #8D8D8D8D8D8D",
405     "l c #8F8F8F8F8F8F",
406     "z c #909090909090",
407     "x c #949494949494",
408     "c c #9C9C9C9C9C9C",
409     "v c #9F9F9F9F9F9F",
410     "b c #A2A2A2A2A2A2",
411     "n c #AEAEAEAEAEAE",
412     "m c #B7B7B7B7B7B7",
413     "M c #C7C7C7C7C7C7",
414     "N c #C9C9C9C9C9C9",
415     "B c #D1D1D1D1D1D1",
416     "V c #D4D4D4D4D4D4",
417     "C c #D9D9D9D9D9D9",
418     "Z c #E0E0E0E0E0E0",
419     "A c #E2E2E2E2E2E2",
420     "S c #EEEEEEEEEEEE",
421     "D c #F0F0F0F0F0F0",
422     "F c #F5F5F5F5F5F5",
423     "G c #F6F6F6F6F6F6",
424     "H c #F9F9F9F9F9F9",
425     "J c #FCFCFCFCFCFC",
426     "K c #FDFDFDFDFDFD",
427     "                    ",
428     "                    ",
429     "                    ",
430     "                    ",
431     "                    ",
432     "     bC             ",
433     "     zjnD           ",
434     "     ldjjMK         ",
435     "     zdhdjcA        ",
436     "     zddhdddVK      ",
437     "     zghdalBH       ",
438     "     zghamSK        ",
439     "     lubZH          ",
440     "     xMF            ",
441     "     G              ",
442     "                    ",
443     "                    ",
444     "                    ",
445     "                    ",
446     "                    ",
447 
448   };
449 
450   const char * const search[]  = {
451     /* columns rows colors chars-per-pixel */
452     "19 19 8 1",
453     "  c #5C5C5C",
454     ". c #7D7D7D",
455     "X c #9B9B9B",
456     "o c #C3C3C3",
457     "O c None",
458     "+ c #000000",
459     "@ c #000000",
460     "# c None",
461     /* pixels */
462     "OOOOOOOOOOOOOOOOOOO",
463     "OOOOOOOOOOOOOOOOOOO",
464     "OOOOOOOo.  .oOOOOOO",
465     "OOOOOOX      XOOOOO",
466     "OOOOOo  XOOX  oOOOO",
467     "OOOOO. XOOOOX .OOOO",
468     "OOOOO  OOOOOO  OOOO",
469     "OOOOO  OOOOOO  OOOO",
470     "OOOOO. XOOOOo .OOOO",
471     "OOOOOo  oOOo  oOOOO",
472     "OOOOOOX       XOOOO",
473     "OOOOOOOo.  .   XOOO",
474     "OOOOOOOOOOOOO.  XOO",
475     "OOOOOOOOOOOOOO. XOO",
476     "OOOOOOOOOOOOOOOoOOO",
477     "OOOOOOOOOOOOOOOOOOO",
478     "OOOOOOOOOOOOOOOOOOO",
479     "OOOOOOOOOOOOOOOOOOO",
480     "OOOOOOOOOOOOOOOOOOO"
481   };
482 
483   fSearchIcon = new QPixmap(search);
484   fTreeIconOpen = new QPixmap(icon1);
485   fTreeIconClosed = new QPixmap(icon2);
486 
487 }
488 
489 //////////////////////////////////////////////////////////////////////////////
490 G4OpenGLQtViewer::~G4OpenGLQtViewer (
491 )
492 //////////////////////////////////////////////////////////////////////////////
493 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!//
494 {
495   fIsDeleting = true;
496 
497   // remove scene tree from layout
498   // Delete all the existing buttons in the layout
499   QLayoutItem *wItem;
500   if (fSceneTreeWidget != NULL) {
501     if (fSceneTreeWidget->layout() != NULL) {
502       while ((wItem = fSceneTreeWidget->layout()->takeAt(0)) != 0) {
503   delete wItem->widget();
504   delete wItem;
505       }
506     }
507   }
508 
509   // Delete the open/close icons
510   delete fTreeIconOpen;
511   delete fTreeIconClosed;
512 
513 #if QT_VERSION < 0x060000
514   G4cout <<removeTempFolder().toStdString().c_str() <<G4endl; //G.Barrand: with Qt6, it crashes at exit if the viewer is in a detached dialog window.
515 #endif
516 
517 }
518 
519 G4bool G4OpenGLQtViewer::ReadyToDraw() {
520 #if QT_VERSION < 0x060000
521   return true;
522 #else
523   if(!fGLWidget) return false;
524   auto* qGLW = dynamic_cast<G4QGLWidgetType*>(fGLWidget);
525   if (!qGLW) return false;
526   return qGLW->isValid();
527 #endif
528 }
529 
530 //
531 //   Create a popup menu for the widget. This menu is activated by right-mouse click
532 //
533 void G4OpenGLQtViewer::createPopupMenu()    {
534 
535   fContextMenu = new QMenu("All");
536 
537   QMenu *mMouseAction = fContextMenu->addMenu("&Mouse actions");
538   fMouseRotateAction = mMouseAction->addAction("Rotate", this, [this](){ this->toggleMouseAction(1); });
539   fMouseMoveAction = mMouseAction->addAction("Move", this, [this](){ this->toggleMouseAction(2); });
540   fMousePickAction = mMouseAction->addAction("Pick", this, [this](){ this->toggleMouseAction(3); });
541   fMouseZoomOutAction = mMouseAction->addAction("Zoom out", this, [this](){ this->toggleMouseAction(4); });
542   fMouseZoomInAction = mMouseAction->addAction("Zoom in", this, [this](){ this->toggleMouseAction(5); });
543   QAction *shortcutsAction = mMouseAction->addAction("Show shortcuts");
544 
545   fMouseRotateAction->setCheckable(true);
546   fMouseMoveAction->setCheckable(true);
547   fMousePickAction->setCheckable(true);
548   fMouseZoomOutAction->setCheckable(true);
549   fMouseZoomInAction->setCheckable(true);
550   shortcutsAction->setCheckable(false);
551 
552   QObject::connect(shortcutsAction,
553                     SIGNAL(triggered(bool)),
554                     this,
555                     SLOT(showShortcuts()));
556 
557   // === Style Menu ===
558   QMenu *mStyle = fContextMenu->addMenu("&Style");
559 
560   QMenu *mProjection = mStyle->addMenu("&Projection");
561 
562   // no more radioAction, not realy useful and could be confusing to use context menu and icon at the same time
563   fProjectionOrtho = mProjection->addAction("Orthographic", this, [this](){ this->toggleProjection(true); });
564   fProjectionPerspective = mProjection->addAction("Perspective", this, [this](){ this->toggleProjection(false); });
565   // === Drawing Menu ===
566   QMenu *mDrawing = mStyle->addMenu("&Drawing");
567   fDrawingWireframe = mDrawing->addAction("Wireframe", this, [this](){ this->toggleSurfaceAction(1); });
568   fDrawingLineRemoval = mDrawing->addAction("Hidden line removal", this, [this](){ this->toggleSurfaceAction(2); });
569   fDrawingSurfaceRemoval = mDrawing->addAction("Hidden Surface removal", this, [this](){ this->toggleSurfaceAction(3); });
570   fDrawingLineSurfaceRemoval = mDrawing->addAction("Hidden line and surface removal", this, [this](){ this->toggleSurfaceAction(4); });
571 
572   fDrawingWireframe->setCheckable(true);
573   fDrawingLineRemoval->setCheckable(true);
574   fDrawingSurfaceRemoval->setCheckable(true);
575   fDrawingLineSurfaceRemoval->setCheckable(true);
576 
577   // Background Color
578 
579   QAction *backgroundColorChooser ;
580   // === Action Menu ===
581   backgroundColorChooser = mStyle->addAction("Background color");
582   QObject ::connect(backgroundColorChooser,
583                     SIGNAL(triggered()),
584                     this,
585                     SLOT(actionChangeBackgroundColor()));
586 
587   // Text Color
588 
589   QAction *textColorChooser ;
590   // === Action Menu ===
591   textColorChooser = mStyle->addAction("Text color");
592   QObject ::connect(textColorChooser,
593                     SIGNAL(triggered()),
594                     this,
595                     SLOT(actionChangeTextColor()));
596 
597   // Default Color
598 
599   QAction *defaultColorChooser ;
600   // === Action Menu ===
601   defaultColorChooser = mStyle->addAction("Default color");
602   QObject ::connect(defaultColorChooser,
603                     SIGNAL(triggered()),
604                     this,
605                     SLOT(actionChangeDefaultColor()));
606 
607 
608   // === Action Menu ===
609   QMenu *mActions = fContextMenu->addMenu("&Actions");
610   QAction *createEPS = mActions->addAction("Save as ...");
611   QObject ::connect(createEPS,
612                     SIGNAL(triggered()),
613                     this,
614                     SLOT(actionSaveImage()));
615 
616   // === Action Menu ===
617   QAction *movieParameters = mActions->addAction("Save as movie...");
618   QObject ::connect(movieParameters,
619                     SIGNAL(triggered()),
620                     this,
621                     SLOT(actionMovieParameters()));
622 
623 
624 
625 
626   // === Special Menu ===
627   QMenu *mSpecial = fContextMenu->addMenu("S&pecial");
628   QMenu *mTransparency = mSpecial->addMenu("Transparency");
629   QAction *transparencyOn = mTransparency->addAction("On");
630   QAction *transparencyOff = mTransparency->addAction("Off");
631 
632   if (transparency_enabled == false) {
633     createRadioAction(transparencyOn,transparencyOff,SLOT(toggleTransparency(bool)),2);
634   } else if (transparency_enabled == true) {
635     createRadioAction(transparencyOn,transparencyOff,SLOT(toggleTransparency(bool)),1);
636   } else {
637     mSpecial->clear();
638   }
639 
640 
641   QMenu *mAntialiasing = mSpecial->addMenu("Antialiasing");
642   QAction *antialiasingOn = mAntialiasing->addAction("On");
643   QAction *antialiasingOff = mAntialiasing->addAction("Off");
644 
645   if (antialiasing_enabled == false) {
646     createRadioAction(antialiasingOn,antialiasingOff,SLOT(toggleAntialiasing(bool)),2);
647   } else if (antialiasing_enabled == true) {
648     createRadioAction(antialiasingOn,antialiasingOff,SLOT(toggleAntialiasing(bool)),1);
649   } else {
650     mAntialiasing->clear();
651   }
652 
653   QMenu *mHaloing = mSpecial->addMenu("Haloing");
654   QAction *haloingOn = mHaloing->addAction("On");
655   QAction *haloingOff = mHaloing->addAction("Off");
656   if (haloing_enabled == false) {
657     createRadioAction(haloingOn,haloingOff,SLOT(toggleHaloing(bool)),2);
658   } else if (haloing_enabled == true) {
659     createRadioAction(haloingOn,haloingOff,SLOT(toggleHaloing(bool)),1);
660   } else {
661     mHaloing->clear();
662   }
663 
664   QMenu *mAux = mSpecial->addMenu("Auxiliary edges");
665   QAction *auxOn = mAux->addAction("On");
666   QAction *auxOff = mAux->addAction("Off");
667   if (!fVP.IsAuxEdgeVisible()) {
668     createRadioAction(auxOn,auxOff,SLOT(toggleAux(bool)),2);
669   } else {
670     createRadioAction(auxOn,auxOff,SLOT(toggleAux(bool)),1);
671   }
672 
673 
674   QMenu *mHiddenMarkers = mSpecial->addMenu("Hidden markers");
675   QAction *hiddenMarkersOn = mHiddenMarkers->addAction("On");
676   QAction *hiddenMarkersOff = mHiddenMarkers->addAction("Off");
677   if (fVP.IsMarkerNotHidden()) {
678     createRadioAction(hiddenMarkersOn,hiddenMarkersOff,SLOT(toggleHiddenMarkers(bool)),2);
679   } else {
680     createRadioAction(hiddenMarkersOn,hiddenMarkersOff,SLOT(toggleHiddenMarkers(bool)),1);
681   }
682 
683 
684 
685   QMenu *mFullScreen = mSpecial->addMenu("&Full screen");
686   fFullScreenOn = mFullScreen->addAction("On");
687   fFullScreenOff = mFullScreen->addAction("Off");
688   createRadioAction(fFullScreenOn,fFullScreenOff,SLOT(toggleFullScreen(bool)),2);
689 
690   // INIT All
691   updateToolbarAndMouseContextMenu();
692 }
693 
694 void G4OpenGLQtViewer::G4manageContextMenuEvent(QContextMenuEvent *e)
695 {
696   if (!fGLWidget) {
697     G4cerr << "Visualization window not defined, please choose one before" << G4endl;
698   } else {
699 
700     if (!fContextMenu)
701       createPopupMenu();
702 
703     // launch menu
704     if ( fContextMenu ) {
705       fContextMenu->exec( e->globalPos() );
706       //    delete fContextMenu;
707     }
708   }
709   e->accept();
710 }
711 
712 
713 /**
714    Create a radio button menu. The two menu will be connected. When click on one,
715    eatch state will be invert and callback method will be called.
716    @param action1 first action to connect
717    @param action2 second action to connect
718    @param method callback method
719    @param nCheck: 1 : first action will be set true. 2 : second action will be set true
720 */
721 void G4OpenGLQtViewer::createRadioAction(QAction *action1,QAction *action2, const std::string& method,unsigned int nCheck) {
722 
723   action1->setCheckable(true);
724   action2->setCheckable(true);
725 
726   if (nCheck ==1)
727     action1->setChecked (true);
728   else
729     action2->setChecked (true);
730 
731   QObject ::connect(action1, SIGNAL(triggered(bool)),action2, SLOT(toggle()));
732   QObject ::connect(action2, SIGNAL(triggered(bool)),action1, SLOT(toggle()));
733 
734   QObject ::connect(action1, SIGNAL(toggled(bool)),this, method.c_str());
735 
736 }
737 
738 
739 
740 /**
741    Show shortcuts for this mouse action
742 */
743 void G4OpenGLQtViewer::showShortcuts() {
744   G4String text;
745 
746   text = "========= Mouse Shortcuts =========\n";
747   if (fUiQt != NULL) {
748     if (fUiQt->IsIconRotateSelected()) {  // rotate
749       text += "Click and move mouse to rotate volume \n";
750       text += "ALT + Click and move mouse to rotate volume (Toggle View/Theta-Phi Direction) \n";
751       text += "CTRL + Click and move mouse to zoom in/out \n";
752       text += "SHIFT + Click and move mouse to change camera point of view \n";
753     } else  if (fUiQt->IsIconMoveSelected()) { //move
754       text += "Move camera point of view with mouse \n";
755     } else  if (fUiQt->IsIconPickSelected()) { //pick
756       text += "Click and pick \n";
757     }
758   } else {
759     text += "Click and move mouse to rotate volume \n";
760     text += "ALT + Click and move mouse to rotate volume (Toggle View/Theta-Phi Direction) \n";
761     text += "CTRL + Click and zoom mouse to zoom in/out \n";
762     text += "SHIFT + Click and zoommove camera point of view \n";
763   }
764   text += "========= Move Shortcuts ========= \n";
765   text += "Press left/right arrows to move volume left/right \n";
766   text += "Press up/down arrows to move volume up/down \n";
767   text += "Press '+'/'-' to move volume toward/forward \n";
768   text += "\n";
769   text += "========= Rotation (Theta/Phi) Shortcuts ========= \n";
770   text += "Press SHIFT + left/right arrows to rotate volume left/right \n";
771   text += "Press SHIFT + up/down arrows to rotate volume up/down \n";
772   text += "\n";
773   text += "========= Rotation (View Direction) Shortcuts ========= \n";
774   text += "Press ALT + left/right to rotate volume around vertical direction \n";
775   text += "Press ALT + up/down to rotate volume around horizontal direction \n";
776   text += "\n";
777   text += "========= Zoom View ========= \n";
778   text += "Press CTRL + '+'/'-' to zoom into volume \n";
779   text += "\n";
780   text += "========= Misc ========= \n";
781   text += "Press ALT +/- to slow/speed rotation/move \n";
782   text += "Press H to reset view \n";
783   text += "Press Esc to exit FullScreen \n";
784   text += "\n";
785   text += "========= Video ========= \n";
786   text += "In video mode : \n";
787   text += " Press SPACE to Start/Pause video recording \n";
788   text += " Press RETURN to Stop video recording \n";
789   text += "\n";
790 
791   G4cout << text;
792 
793   if (  fShortcutsDialog == NULL) {
794     fShortcutsDialog = new QDialog();
795     fShortcutsDialogInfos = new QTextEdit() ;
796     QVBoxLayout *mainLayout = new QVBoxLayout;
797     mainLayout->addWidget(fShortcutsDialogInfos);
798     fShortcutsDialog->setLayout(mainLayout);
799     fShortcutsDialog->setWindowTitle(tr("Shortcuts"));
800   }
801 
802   fShortcutsDialogInfos->setPlainText(text.data());
803   fShortcutsDialog->show();
804 }
805 
806 
807 
808 /**
809    Slot activated when mouse action is toggle
810    @param aAction : 1 rotate, 2 move, 3 pick, 4 zoom out, 5 zoom in
811    @see G4OpenGLStoredQtViewer::DrawView
812    @see G4XXXStoredViewer::CompareForKernelVisit
813 */
814 void G4OpenGLQtViewer::toggleMouseAction(int aAction) {
815 
816   if (aAction == 1) {
817     fUiQt->SetIconRotateSelected();
818   } else  if (aAction == 2) {
819     fUiQt->SetIconMoveSelected();
820   } else  if (aAction == 3) {
821     togglePicking();
822   } else  if (aAction == 4) {
823     fUiQt->SetIconZoomOutSelected();
824   } else  if (aAction == 5) {
825     fUiQt->SetIconZoomInSelected();
826   }
827 
828   updateQWidget();
829   updateToolbarAndMouseContextMenu();
830 }
831 
832 
833 /**
834    Slot activated when drawing menu is toggle
835    Warning : When G4OpenGLStoredQtViewer::DrawView() method call,
836    KernelVisitDecision () will be call and will set the fNeedKernelVisit
837    to 1. See G4XXXStoredViewer::CompareForKernelVisit for explanations.
838    It will cause a redraw of the view
839    @param aAction : 1 wireframe, 2 line removal, 3 surface removal, 4 line & surface removal
840    @see G4OpenGLStoredQtViewer::DrawView
841    @see G4XXXStoredViewer::CompareForKernelVisit
842 */
843 void G4OpenGLQtViewer::toggleSurfaceAction(int aAction) {
844 
845   G4ViewParameters::DrawingStyle d_style = G4ViewParameters::wireframe;
846 
847   if (aAction ==1) {
848     d_style = G4ViewParameters::wireframe;
849 
850   } else  if (aAction ==2) {
851     d_style = G4ViewParameters::hlr;
852 
853   } else  if (aAction ==3) {
854     d_style = G4ViewParameters::hsr;
855 
856   } else  if (aAction ==4) {
857     d_style = G4ViewParameters::hlhsr;
858   }
859   fVP.SetDrawingStyle(d_style);
860 
861   updateToolbarAndMouseContextMenu();
862   updateQWidget();
863 }
864 
865 
866 /**
867    SLOT Activate by a click on the projection menu
868    Warning : When G4OpenGLStoredQtViewer::DrawView() method call,
869    KernelVisitDecision () will be call and will set the fNeedKernelVisit
870    to 1. See G4XXXStoredViewer::CompareForKernelVisit for explanations.
871    It will cause a redraw of the view
872    @param check : 1 orthographic, 2 perspective
873    @see G4OpenGLStoredQtViewer::DrawView
874    @see G4XXXStoredViewer::CompareForKernelVisit
875 */
876 void G4OpenGLQtViewer::toggleProjection(bool check) {
877 
878   if (check) {
879     fVP.SetOrthogonalProjection ();
880   } else {
881     fVP.SetPerspectiveProjection();
882   }
883   updateToolbarAndMouseContextMenu();
884   updateQWidget();
885 }
886 
887 
888 /**
889    SLOT Activate by a click on the transparency menu
890    @param check : 1 , 0
891 */
892 void G4OpenGLQtViewer::toggleTransparency(bool check) {
893 
894   if (check) {
895     transparency_enabled = true;
896   } else {
897     transparency_enabled = false;
898   }
899   SetNeedKernelVisit (true);
900   updateToolbarAndMouseContextMenu();
901   updateQWidget();
902 }
903 
904 /**
905    SLOT Activate by a click on the antialiasing menu
906    @param check : 1 , 0
907 */
908 void G4OpenGLQtViewer::toggleAntialiasing(bool check) {
909 
910   if (!check) {
911     antialiasing_enabled = false;
912     glDisable (GL_LINE_SMOOTH);
913     glDisable (GL_POLYGON_SMOOTH);
914   } else {
915     antialiasing_enabled = true;
916     glEnable (GL_LINE_SMOOTH);
917     glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
918     glEnable (GL_POLYGON_SMOOTH);
919     glHint (GL_POLYGON_SMOOTH_HINT, GL_NICEST);
920   }
921 
922   updateToolbarAndMouseContextMenu();
923   updateQWidget();
924 }
925 
926 /**
927    SLOT Activate by a click on the haloing menu
928    @param check : 1 , 0
929 */
930 //FIXME : I SEE NOTHING...
931 void G4OpenGLQtViewer::toggleHaloing(bool check) {
932   if (check) {
933     haloing_enabled = false;
934   } else {
935     haloing_enabled = true;
936   }
937 
938   updateToolbarAndMouseContextMenu();
939   updateQWidget();
940 
941 }
942 
943 /**
944    SLOT Activate by a click on the auxiliaire edges menu
945    @param check : 1 , 0
946 */
947 void G4OpenGLQtViewer::toggleAux(bool check) {
948   if (check) {
949     fVP.SetAuxEdgeVisible(true);
950   } else {
951     fVP.SetAuxEdgeVisible(false);
952   }
953   SetNeedKernelVisit (true);
954   updateToolbarAndMouseContextMenu();
955   updateQWidget();
956 }
957 
958 
959 void G4OpenGLQtViewer::togglePicking() {
960   // FIXME : Not the good way to do, we should handle the multiple cases of Icon/ContextMenu and CheckBox in a better way
961   if (fUiQt) {
962     if (!fVP.IsPicking()) {
963       fUiQt->SetIconPickSelected();
964     } else {
965       fUiQt->SetIconRotateSelected();
966     }
967   }
968 
969   G4UImanager* UI = G4UImanager::GetUIpointer();
970   if(UI != NULL)  {
971     if (!fVP.IsPicking()) {
972       UI->ApplyCommand(std::string("/vis/viewer/set/picking true"));
973     } else {
974       UI->ApplyCommand(std::string("/vis/viewer/set/picking false"));
975     }
976   }
977 
978 }
979 
980 
981 /**
982    SLOT Activate by a click on the hidden marker menu
983    @param check : 1 , 0
984 */
985 void G4OpenGLQtViewer::toggleHiddenMarkers(bool check) {
986   if (check) {
987     fVP.SetMarkerHidden();
988   } else {
989     fVP.SetMarkerNotHidden();
990   }
991   //  SetNeedKernelVisit (true);
992   updateToolbarAndMouseContextMenu();
993   updateQWidget();
994 }
995 
996 /**
997    SLOT Activate by a click on the full screen menu
998 */
999 void G4OpenGLQtViewer::toggleFullScreen(bool check) {
1000   if (check != fGLWidget->isFullScreen()) { //toggle
1001     fGLWidget->setWindowState(fGLWidget->windowState() ^ Qt::WindowFullScreen);
1002   }
1003 }
1004 
1005 
1006 void G4OpenGLQtViewer::savePPMToTemp() {
1007   if (fMovieTempFolderPath == "") {
1008     return;
1009   }
1010   auto qGLW = dynamic_cast<G4QGLWidgetType*> (fGLWidget) ;
1011   if (! qGLW) {
1012     return;
1013   }
1014   QString fileName ="Test"+QString::number(fRecordFrameNumber)+".ppm";
1015   QString filePath =fMovieTempFolderPath+fileName;
1016 
1017   QImage image;
1018 #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
1019   image = qGLW->grabFrameBuffer();
1020 #else
1021   image = qGLW->grabFramebuffer();
1022 #endif
1023   bool res = false;
1024 
1025   res = image.save(filePath,0);
1026   if (res == false) {
1027     resetRecording();
1028     setRecordingInfos("Can't save tmp file "+filePath);
1029     return;
1030   }
1031 
1032   setRecordingInfos("File "+fileName+" saved");
1033   fRecordFrameNumber++;
1034 }
1035 
1036 
1037 
1038 void G4OpenGLQtViewer::actionSaveImage() {
1039   QString filters;
1040   for (unsigned int i = 0; i < fExportImageFormatVector.size(); ++i) {
1041     filters += QString("*.") + fExportImageFormatVector.at(i).c_str() + ";;";
1042   }
1043 
1044   QString* selectedFormat = new QString(fDefaultExportImageFormat.c_str());
1045   QString qFilename;
1046   qFilename =  QFileDialog::getSaveFileName ( fGLWidget,
1047                                          tr("Save as ..."),
1048                                          fFileSavePath,
1049                                          filters,
1050                                          selectedFormat );
1051 
1052 
1053   std::string name = qFilename.toStdString().c_str();
1054 
1055   // bmp jpg jpeg png ppm xbm xpm
1056   if (name.empty()) {
1057     return;
1058   }
1059 
1060   fFileSavePath = QFileInfo(qFilename).path();
1061 
1062   std::string format = selectedFormat->toLower().toStdString().c_str();
1063 
1064   // set the format to current
1065   fExportImageFormat = format.substr(format.find_last_of(".") + 1);
1066 
1067   std::string filename = name;
1068   std::string extension = "";
1069   if (name.find_last_of(".") != std::string::npos) {
1070     filename = name.substr(0,name.find_last_of(".") + 1);
1071     extension = name.substr(name.find_last_of(".") + 1);
1072   } else {
1073     extension = fExportImageFormat;
1074   }
1075 
1076   filename+= "."+ extension;
1077 
1078   if (!setExportFilename(filename.c_str(),0)) {
1079     return;
1080   }
1081 
1082   G4OpenGLQtExportDialog* exportDialog= new G4OpenGLQtExportDialog(fGLWidget,format.c_str(),fGLWidget->height(),fGLWidget->width());
1083   if(  exportDialog->exec()) {
1084 
1085     if ((exportDialog->getWidth() !=fGLWidget->width()) ||
1086         (exportDialog->getHeight() !=fGLWidget->height())) {
1087       setExportSize(exportDialog->getWidth(),exportDialog->getHeight());
1088 
1089     }
1090     if (fExportImageFormat == "eps") {
1091       fVectoredPs = exportDialog->getVectorEPS();
1092     } else if (fExportImageFormat == "ps") {
1093       fVectoredPs = true;
1094     }
1095     fLastExportSliderValue = exportDialog->getSliderValue();
1096 
1097     if (exportImage(filename)) {
1098       // set the default format to current
1099       fDefaultExportImageFormat = format;
1100     }
1101   } else { // cancel selected
1102     return;
1103   }
1104 
1105 }
1106 
1107 
1108 void G4OpenGLQtViewer::actionChangeBackgroundColor() {
1109 
1110   //   //I need to revisit the kernel if the background colour changes and
1111   //   //hidden line removal is enabled, because hlr drawing utilises the
1112   //   //background colour in its drawing...
1113   //   // (Note added by JA 13/9/2005) Background now handled in view
1114   //   // parameters.  A kernel visit is triggered on change of background.
1115   const QColor color =
1116     QColorDialog::getColor(Qt::black,
1117                            fGLWidget,
1118                            " Get background color and transparency",
1119                            QColorDialog::ShowAlphaChannel);
1120   if (color.isValid()) {
1121     G4Colour colour(((G4double)color.red())/255,
1122                     ((G4double)color.green())/255,
1123                     ((G4double)color.blue())/255,
1124                     ((G4double)color.alpha())/255);
1125     fVP.SetBackgroundColour(colour);
1126 
1127     updateToolbarAndMouseContextMenu();
1128     updateQWidget();
1129   }
1130 }
1131 
1132 void G4OpenGLQtViewer::actionChangeTextColor() {
1133   const QColor& color =
1134     QColorDialog::getColor(Qt::yellow,
1135                            fGLWidget,
1136                            " Get text color and transparency",
1137                            QColorDialog::ShowAlphaChannel);
1138   if (color.isValid()) {
1139     G4Colour colour(((G4double)color.red())/255,
1140                     ((G4double)color.green())/255,
1141                     ((G4double)color.blue())/255,
1142                     ((G4double)color.alpha())/255);
1143 
1144     fVP.SetDefaultTextColour(colour);
1145 
1146     updateToolbarAndMouseContextMenu();
1147     updateQWidget();
1148   }
1149 }
1150 
1151 void G4OpenGLQtViewer::actionChangeDefaultColor() {
1152   const QColor& color =
1153     QColorDialog::getColor(Qt::white,
1154                            fGLWidget,
1155                            " Get default color and transparency",
1156                            QColorDialog::ShowAlphaChannel);
1157   if (color.isValid()) {
1158     G4Colour colour(((G4double)color.red())/255,
1159                     ((G4double)color.green())/255,
1160                     ((G4double)color.blue())/255,
1161                     ((G4double)color.alpha())/255);
1162 
1163     fVP.SetDefaultColour(colour);
1164 
1165     updateToolbarAndMouseContextMenu();
1166     updateQWidget();
1167   }
1168 }
1169 
1170 
1171 void G4OpenGLQtViewer::actionMovieParameters() {
1172   showMovieParametersDialog();
1173 }
1174 
1175 
1176 void G4OpenGLQtViewer::showMovieParametersDialog() {
1177   if (!fMovieParametersDialog) {
1178     fMovieParametersDialog= new G4OpenGLQtMovieDialog(this,fGLWidget);
1179     displayRecordingStatus();
1180     fMovieParametersDialog->checkEncoderSwParameters();
1181     fMovieParametersDialog->checkSaveFileNameParameters();
1182     fMovieParametersDialog->checkTempFolderParameters();
1183     if (getEncoderPath() == "") {
1184       setRecordingInfos("ppmtompeg is needed to encode in video format. It is available here: http://netpbm.sourceforge.net ");
1185     }
1186   }
1187   fMovieParametersDialog->show();
1188 }
1189 
1190 
1191 
1192 void G4OpenGLQtViewer::FinishView()
1193 {
1194  /* From Apple doc:
1195   CGLFlushDrawable : Copies the back buffer of a double-buffered context to the front buffer.
1196   If the backing store attribute is set to false, the buffers can be exchanged rather than copied
1197  */
1198   glFlush ();
1199 
1200   // L. Garnier 10/2009 : Not necessary and cause problems on mac OS X 10.6
1201   //  fGLWidget->swapBuffers ();
1202 }
1203 
1204 /**
1205    Save the current mouse press point
1206    @param p mouse click point
1207 */
1208 void G4OpenGLQtViewer::G4MousePressEvent(QMouseEvent *evnt)
1209 {
1210   if (evnt->button() == Qt::RightButton) {
1211     return;
1212   }
1213   if ((evnt->button() & Qt::LeftButton) && (! (evnt->modifiers() & Qt::ControlModifier ))){
1214     fGLWidget->setMouseTracking(true);
1215     fAutoMove = false; // stop automove
1216     fLastPos1 = evnt->pos();
1217     fLastPos2 = fLastPos1;
1218     fLastPos3 = fLastPos2;
1219     fLastEventTime->start();
1220     if (fUiQt != NULL) {
1221 
1222       if (fUiQt->IsIconZoomInSelected()) {  // zoomIn
1223         // Move click point to center of OGL
1224 
1225         float deltaX = ((float)getWinWidth()/2-evnt->pos().x());
1226         float deltaY = ((float)getWinHeight()/2-evnt->pos().y());
1227 
1228         G4double coefTrans = 0;
1229         coefTrans = ((G4double)getSceneNearWidth())/((G4double)getWinWidth());
1230         if (getWinHeight() <getWinWidth()) {
1231           coefTrans = ((G4double)getSceneNearWidth())/((G4double)getWinHeight());
1232         }
1233         fVP.IncrementPan(-deltaX*coefTrans,deltaY*coefTrans,0);
1234         fVP.SetZoomFactor(1.5 * fVP.GetZoomFactor());
1235 
1236         updateQWidget();
1237 
1238       } else if (fUiQt->IsIconZoomOutSelected()) {  // zoomOut
1239         // Move click point to center of OGL
1240         moveScene(((float)getWinWidth()/2-evnt->pos().x()),((float)getWinHeight()/2-evnt->pos().y()),0,true);
1241 
1242         fVP.SetZoomFactor(0.75 * fVP.GetZoomFactor());
1243         updateQWidget();
1244 
1245       } else if (fUiQt->IsIconRotateSelected() ) {
1246 
1247         if (fShiftKeyPress) { // move
1248           fGLWidget->setCursor(QCursor(Qt::SizeAllCursor));
1249 
1250         } else {  // rotate
1251           fGLWidget->setCursor(QCursor(Qt::ClosedHandCursor));
1252         }
1253       } else if (fUiQt->IsIconMoveSelected()) {
1254         fGLWidget->setCursor(QCursor(Qt::SizeAllCursor));
1255       } else if (fUiQt->IsIconPickSelected()) {
1256         fGLWidget->setCursor(QCursor(Qt::PointingHandCursor));
1257       }
1258     }
1259   }
1260 }
1261 
1262 /**
1263  */
1264 void G4OpenGLQtViewer::G4MouseReleaseEvent(QMouseEvent *evnt)
1265 {
1266 #if QT_VERSION < 0x060000
1267 #else
1268  {auto* qGLW = dynamic_cast<G4QGLWidgetType*> (fGLWidget) ;
1269   if (qGLW) qGLW->makeCurrent();}
1270   ResizeGLView();
1271 #endif
1272   GLint viewport[4];
1273   glGetIntegerv(GL_VIEWPORT, viewport);
1274 
1275   // factorX == factorY
1276   double factorX =  ((double)viewport[2]/fGLWidget->width());
1277   double factorY =  ((double)viewport[3]/fGLWidget->height());
1278   fSpinningDelay = (int)fLastEventTime->elapsed();
1279   QPoint delta = (fLastPos3-fLastPos1)*factorX;
1280 
1281   // reset cursor state
1282   fGLWidget->setCursor(QCursor(Qt::ArrowCursor));
1283 
1284   if (fVP.IsPicking()){  // pick
1285     if ((delta.x() != 0) || (delta.y() != 0)) {
1286       return;
1287     }
1288     updatePickInfosWidget(evnt->pos().x()*factorX,evnt->pos().y()*factorY);
1289 
1290   } else if (fSpinningDelay < fLaunchSpinDelay ) {
1291     if ((delta.x() == 0) && (delta.y() == 0)) {
1292       return;
1293     }
1294 
1295     fAutoMove = true;
1296 #if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0))
1297     QTime lastMoveTime;
1298 #else
1299     QElapsedTimer lastMoveTime;
1300 #endif
1301     lastMoveTime.start();
1302     // try to addapt speed move/rotate looking to drawing speed
1303     float correctionFactor = 5;
1304     while (fAutoMove) {
1305       if ( lastMoveTime.elapsed() >= (int)(1000/fNbMaxFramesPerSec)) {
1306         float lTime = 1000.0f/lastMoveTime.elapsed();
1307         if (((((float)delta.x())/correctionFactor)*lTime > fNbMaxAnglePerSec) ||
1308             ((((float)delta.x())/correctionFactor)*lTime < -fNbMaxAnglePerSec) ) {
1309           correctionFactor = (float)delta.x()*(lTime/fNbMaxAnglePerSec);
1310           if (delta.x() <0 ) {
1311             correctionFactor = -correctionFactor;
1312           }
1313         }
1314         if (((((float)delta.y())/correctionFactor)*lTime > fNbMaxAnglePerSec) ||
1315             ((((float)delta.y())/correctionFactor)*lTime < -fNbMaxAnglePerSec) ) {
1316           correctionFactor = (float)delta.y()*(lTime/fNbMaxAnglePerSec);
1317           if (delta.y() <0 ) {
1318             correctionFactor = -correctionFactor;
1319           }
1320         }
1321 
1322         // Check Qt Versions for META Keys
1323 
1324         // Click and move mouse to rotate volume
1325         // ALT + Click and move mouse to rotate volume (View Direction)
1326         // SHIFT + Click and move camera point of view
1327         // CTRL + Click and zoom mouse to zoom in/out
1328 
1329         lastMoveTime.start();
1330 
1331         bool rotate = false;
1332         bool move = false;
1333 
1334         if (fUiQt != NULL) {
1335           if (fUiQt->IsIconRotateSelected()) {  // rotate
1336             rotate = true;
1337           } else if (fUiQt->IsIconMoveSelected()) {  // move
1338             move = true;
1339           }
1340         } else {
1341           rotate = true;
1342         }
1343         // prevent from closing widget when rotating (cause a crash)
1344         if (fIsDeleting) {
1345           return;
1346         }
1347 
1348         if (rotate) {  // rotate
1349           if (fNoKeyPress) {
1350             rotateQtScene(((float)delta.x())/correctionFactor,((float)delta.y())/correctionFactor);
1351           } else if (fAltKeyPress) {
1352             rotateQtSceneToggle(((float)delta.x())/correctionFactor,((float)delta.y())/correctionFactor);
1353           }
1354 
1355         } else if (move) {  // move
1356           moveScene(-((float)delta.x())/correctionFactor,-((float)delta.y())/correctionFactor,0,true);
1357         }
1358       }
1359       ((QApplication*)G4Qt::getInstance ())->processEvents();
1360     }
1361   }
1362   fGLWidget->setMouseTracking(false);
1363 
1364 }
1365 
1366 
1367 void G4OpenGLQtViewer::G4MouseDoubleClickEvent()
1368 {
1369   fGLWidget->setMouseTracking(true);
1370 }
1371 
1372 
1373 /**
1374    @param pos_x mouse x position
1375    @param pos_y mouse y position
1376    @param mButtons mouse button active
1377    @param mAutoMove true: apply this move till another evnt came, false :one time move
1378 */
1379 
1380 void G4OpenGLQtViewer::G4MouseMoveEvent(QMouseEvent *evnt)
1381 {
1382 
1383   Qt::MouseButtons mButtons = evnt->buttons();
1384 
1385   updateKeyModifierState(evnt->modifiers());
1386 
1387   if (fAutoMove) {
1388     return;
1389   }
1390 
1391   fLastPos3 = fLastPos2;
1392   fLastPos2 = fLastPos1;
1393 
1394 #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
1395   fLastPos1 = QPoint(evnt->x(), evnt->y());
1396 #else
1397   fLastPos1 = QPoint(evnt->position().x(), evnt->position().y());
1398 #endif
1399 
1400   int deltaX = fLastPos2.x()-fLastPos1.x();
1401   int deltaY = fLastPos2.y()-fLastPos1.y();
1402 
1403   bool move = false;
1404   if (fUiQt != NULL) {
1405     if (fUiQt->IsIconMoveSelected()) {  // move
1406       move = true;
1407     }
1408   }
1409   if (!move) {  // rotate, pick, zoom...
1410     if (mButtons & Qt::LeftButton) {
1411       if (fNoKeyPress) {
1412         rotateQtScene(((float)deltaX),((float)deltaY));
1413       } else if (fAltKeyPress) {
1414         rotateQtSceneToggle(((float)deltaX),((float)deltaY));
1415       } else if (fShiftKeyPress) {
1416         unsigned int sizeWin;
1417         sizeWin = getWinWidth();
1418         if (getWinHeight() < getWinWidth()) {
1419           sizeWin = getWinHeight();
1420         }
1421 
1422         // L.Garnier : 08/2010 100 is the good value, but don't ask me why !
1423         float factor = ((float)100/(float)sizeWin) ;
1424         moveScene(-(float)deltaX*factor,-(float)deltaY*factor,0,false);
1425       } else if (fControlKeyPress) {
1426         fVP.SetZoomFactor(fVP.GetZoomFactor()*(1+((float)deltaY)));
1427       }
1428     }
1429   } else if (move) {  // move
1430     if (mButtons & Qt::LeftButton) {
1431       moveScene(-(float)deltaX,-(float)deltaY,0,true);
1432     }
1433   }
1434 
1435   fLastEventTime->start();
1436 }
1437 
1438 
1439 /**
1440    Move the scene of dx, dy, dz values.
1441    @param dx delta mouse x position
1442    @param dy delta mouse y position
1443    @param mouseMove : true if event comes from a mouse move, false if event comes from key action
1444 */
1445 
1446 void G4OpenGLQtViewer::moveScene(float dx,float dy, float dz,bool mouseMove)
1447 {
1448   if (fHoldMoveEvent)
1449     return;
1450   fHoldMoveEvent = true;
1451 
1452   G4double coefTrans = 0;
1453   GLdouble coefDepth = 0;
1454   if(mouseMove) {
1455     coefTrans = ((G4double)getSceneNearWidth())/((G4double)getWinWidth());
1456     if (getWinHeight() <getWinWidth()) {
1457       coefTrans = ((G4double)getSceneNearWidth())/((G4double)getWinHeight());
1458     }
1459   } else {
1460     coefTrans = getSceneNearWidth()*fPan_sens;
1461     coefDepth = getSceneDepth()*fDeltaDepth;
1462   }
1463   fVP.IncrementPan(-dx*coefTrans,dy*coefTrans,dz*coefDepth);
1464 
1465   updateQWidget();
1466   if (fAutoMove)
1467     ((QApplication*)G4Qt::getInstance ())->processEvents();
1468 
1469   fHoldMoveEvent = false;
1470 }
1471 
1472 
1473 /**
1474    @param dx delta mouse x position
1475    @param dy delta mouse y position
1476 */
1477 
1478 void G4OpenGLQtViewer::rotateQtScene(float dx, float dy)
1479 {
1480   if (fHoldRotateEvent)
1481     return;
1482   fHoldRotateEvent = true;
1483 
1484   rotateScene(dx,dy);
1485 
1486   updateQWidget();
1487 
1488   fHoldRotateEvent = false;
1489 }
1490 
1491 /**
1492    @param dx delta mouse x position
1493    @param dy delta mouse y position
1494 */
1495 
1496 void G4OpenGLQtViewer::rotateQtSceneToggle(float dx, float dy)
1497 {
1498   if (fHoldRotateEvent)
1499     return;
1500   fHoldRotateEvent = true;
1501 
1502   rotateSceneToggle(dx,dy);
1503 
1504   updateQWidget();
1505 
1506   fHoldRotateEvent = false;
1507 }
1508 
1509 
1510 
1511 
1512 
1513 /** This is the benning of a rescale function. It does nothing for the moment
1514     @param aWidth : new width
1515     @param aHeight : new height
1516 */
1517 void G4OpenGLQtViewer::rescaleImage(
1518  int /* aWidth */
1519 ,int /* aHeight */
1520 ){
1521   //  GLfloat* feedback_buffer;
1522   //  GLint returned;
1523   //  FILE* file;
1524 
1525   //   feedback_buffer = new GLfloat[size];
1526   //   glFeedbackBuffer (size, GL_3D_COLOR, feedback_buffer);
1527   //   glRenderMode (GL_FEEDBACK);
1528 
1529   //   DrawView();
1530   //   returned = glRenderMode (GL_RENDER);
1531 
1532 }
1533 
1534 
1535 
1536 
1537 void G4OpenGLQtViewer::G4wheelEvent (QWheelEvent * evnt)
1538 {
1539 #if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0))
1540   fVP.SetZoomFactor(fVP.GetZoomFactor()+(fVP.GetZoomFactor()*(evnt->delta())/1200));
1541 #else
1542   fVP.SetZoomFactor(fVP.GetZoomFactor()+(fVP.GetZoomFactor()*(evnt->angleDelta().y())/1200));
1543 #endif
1544   updateQWidget();
1545 }
1546 
1547 
1548 void G4OpenGLQtViewer::G4keyPressEvent (QKeyEvent * evnt)
1549 {
1550   if (fHoldKeyEvent)
1551     return;
1552 
1553   fHoldKeyEvent = true;
1554 
1555 
1556   // with no modifiers
1557   updateKeyModifierState(evnt->modifiers());
1558   if ((fNoKeyPress) || (evnt->modifiers() == Qt::KeypadModifier )) {
1559     if (evnt->key() == Qt::Key_Down) { // go down
1560       moveScene(0,1,0,false);
1561     }
1562     else if (evnt->key() == Qt::Key_Up) {  // go up
1563       moveScene(0,-1,0,false);
1564     }
1565     if (evnt->key() == Qt::Key_Left) { // go left
1566       moveScene(-1,0,0,false);
1567     }
1568     else if (evnt->key() == Qt::Key_Right) { // go right
1569       moveScene(1,0,0,false);
1570     }
1571     if (evnt->key() == Qt::Key_Minus) { // go backward
1572       moveScene(0,0,1,false);
1573     }
1574     else if (evnt->key() == Qt::Key_Plus) { // go forward
1575       moveScene(0,0,-1,false);
1576     }
1577     // escaped from full screen
1578     if (evnt->key() == Qt::Key_Escape) {
1579       toggleFullScreen(false);
1580     }
1581   }
1582   // several case here : If return is pressed, in every case -> display the movie parameters dialog
1583   // If one parameter is wrong -> put it in red (only save filenam could be wrong..)
1584   // If encoder not found-> does nothing.Only display a message in status box
1585   // If all ok-> generate parameter file
1586   // If ok -> put encoder button enabled
1587 
1588   if ((evnt->key() == Qt::Key_Return) || (evnt->key() == Qt::Key_Enter)){ // end of video
1589     stopVideo();
1590   }
1591   if (evnt->key() == Qt::Key_Space){ // start/pause of video
1592     startPauseVideo();
1593   }
1594 
1595   // H : Return Home view
1596   if (evnt->key() == Qt::Key_H){ // go Home
1597     ResetView();
1598 
1599     updateQWidget();
1600   }
1601 
1602   // Shift Modifier
1603   if (fShiftKeyPress) {
1604     fGLWidget->setCursor(QCursor(Qt::SizeAllCursor));
1605 
1606     if (evnt->key() == Qt::Key_Down) { // rotate phi
1607       rotateQtScene(0,-fRot_sens);
1608     }
1609     else if (evnt->key() == Qt::Key_Up) { // rotate phi
1610       rotateQtScene(0,fRot_sens);
1611     }
1612     if (evnt->key() == Qt::Key_Left) { // rotate theta
1613       rotateQtScene(fRot_sens,0);
1614     }
1615     else if (evnt->key() == Qt::Key_Right) { // rotate theta
1616       rotateQtScene(-fRot_sens,0);
1617     }
1618     if (evnt->key() == Qt::Key_Plus) { // go forward  ("Plus" imply
1619       // "Shift" on Mac French keyboard
1620       moveScene(0,0,-1,false);
1621     }
1622 
1623     // Alt Modifier
1624   }
1625   if ((fAltKeyPress)) {
1626     fGLWidget->setCursor(QCursor(Qt::ClosedHandCursor));
1627 
1628     if (evnt->key() == Qt::Key_Down) { // rotate phi
1629       rotateQtSceneToggle(0,-fRot_sens);
1630     }
1631     else if (evnt->key() == Qt::Key_Up) { // rotate phi
1632       rotateQtSceneToggle(0,fRot_sens);
1633     }
1634     if (evnt->key() == Qt::Key_Left) { // rotate theta
1635       rotateQtSceneToggle(fRot_sens,0);
1636     }
1637     else if (evnt->key() == Qt::Key_Right) { // rotate theta
1638       rotateQtSceneToggle(-fRot_sens,0);
1639     }
1640 
1641     // Rotatio +/-
1642     if (evnt->key() == Qt::Key_Plus) {
1643       fRot_sens = fRot_sens/0.7;
1644       G4cout << "Auto-rotation set to : " << fRot_sens << G4endl;
1645     }
1646     else if (evnt->key() == Qt::Key_Minus) {
1647       fRot_sens = fRot_sens*0.7;
1648       G4cout << "Auto-rotation set to : " << fRot_sens << G4endl;
1649     }
1650 
1651     // Control Modifier OR Command on MAC
1652   }
1653   if ((fControlKeyPress)) {
1654     if (evnt->key() == Qt::Key_Plus) {
1655       fVP.SetZoomFactor(fVP.GetZoomFactor()*(1+fDeltaZoom));
1656       updateQWidget();
1657     }
1658     else if (evnt->key() == Qt::Key_Minus) {
1659       fVP.SetZoomFactor(fVP.GetZoomFactor()*(1-fDeltaZoom));
1660       updateQWidget();
1661     }
1662   }
1663 
1664   fHoldKeyEvent = false;
1665 }
1666 
1667 
1668 void G4OpenGLQtViewer::G4keyReleaseEvent (QKeyEvent *)
1669 {
1670   fGLWidget->setCursor(QCursor(Qt::ArrowCursor));
1671 }
1672 
1673 
1674 void  G4OpenGLQtViewer::updateKeyModifierState(const Qt::KeyboardModifiers& modifier) {
1675   // Check Qt Versions for META Keys
1676 
1677   fNoKeyPress = true;
1678   fAltKeyPress = false;
1679   fShiftKeyPress = false;
1680   fControlKeyPress = false;
1681 
1682   if (modifier & Qt::AltModifier ) {
1683     fAltKeyPress = true;
1684     fNoKeyPress = false;
1685   }
1686   if (modifier & Qt::ShiftModifier ) {
1687     fShiftKeyPress = true;
1688     fNoKeyPress = false;
1689   }
1690   if (modifier & Qt::ControlModifier ) {
1691     fControlKeyPress = true;
1692     fNoKeyPress = false;
1693   }
1694 }
1695 
1696 
1697 /** Stop the video. Check all parameters and enable encoder button if all is ok.
1698  */
1699 void G4OpenGLQtViewer::stopVideo() {
1700 
1701   // if encoder parameter is wrong, display parameters dialog and return
1702   if (!fMovieParametersDialog) {
1703     showMovieParametersDialog();
1704   }
1705   setRecordingStatus(STOP);
1706 
1707   if (fRecordFrameNumber >0) {
1708     // check parameters if they were modified (Re APPLY them...)
1709     if (!(fMovieParametersDialog->checkEncoderSwParameters())) {
1710       setRecordingStatus(BAD_ENCODER);
1711     }  else if (!(fMovieParametersDialog->checkSaveFileNameParameters())) {
1712       setRecordingStatus(BAD_OUTPUT);
1713     }
1714   } else {
1715     resetRecording();
1716     setRecordingInfos("No frame to encode.");
1717   }
1718 }
1719 
1720 /** Stop the video. Check all parameters and enable encoder button if all is ok.
1721  */
1722 void G4OpenGLQtViewer::saveVideo() {
1723 
1724   // if encoder parameter is wrong, display parameters dialog and return
1725   if (!fMovieParametersDialog) {
1726     showMovieParametersDialog();
1727   }
1728 
1729   fMovieParametersDialog->checkEncoderSwParameters();
1730   fMovieParametersDialog->checkSaveFileNameParameters();
1731 
1732   if (fRecordingStep == STOP) {
1733     setRecordingStatus(SAVE);
1734     generateMpegEncoderParameters();
1735     encodeVideo();
1736   }
1737 }
1738 
1739 
1740 /** Start/Pause the video..
1741  */
1742 void G4OpenGLQtViewer::startPauseVideo() {
1743 
1744   // first time, if temp parameter is wrong, display parameters dialog and return
1745 
1746   if ( fRecordingStep == WAIT) {
1747     if ( fRecordFrameNumber == 0) {
1748       if (getTempFolderPath() == "") { // BAD_OUTPUT
1749         showMovieParametersDialog();
1750         setRecordingInfos("You should specified the temp folder in order to make movie");
1751         return;
1752       } else  {
1753         // remove temp folder if it was create
1754         QString tmp = removeTempFolder();
1755         if (tmp !="") {
1756           setRecordingInfos(tmp);
1757           return;
1758         }
1759         tmp = createTempFolder();
1760         if (tmp != "") {
1761           setRecordingInfos("Can't create temp folder."+tmp);
1762           return;
1763         }
1764       }
1765     }
1766   }
1767   if (fRecordingStep == WAIT) {
1768     setRecordingStatus(START);
1769   } else if (fRecordingStep == START) {
1770     setRecordingStatus(PAUSE);
1771   } else if (fRecordingStep == PAUSE) {
1772     setRecordingStatus(CONTINUE);
1773   } else if (fRecordingStep == CONTINUE) {
1774     setRecordingStatus(PAUSE);
1775   }
1776 }
1777 
1778 void G4OpenGLQtViewer::setRecordingStatus(RECORDING_STEP step) {
1779 
1780   fRecordingStep = step;
1781   displayRecordingStatus();
1782 }
1783 
1784 
1785 void G4OpenGLQtViewer::displayRecordingStatus() {
1786 
1787   QString txtStatus = "";
1788   if (fRecordingStep == WAIT) {
1789     txtStatus  = "Waiting to start...";
1790     fRecordFrameNumber = 0; // reset the frame number
1791   } else if (fRecordingStep == START) {
1792     txtStatus  = "Start Recording...";
1793   } else if (fRecordingStep == PAUSE) {
1794     txtStatus  = "Pause Recording...";
1795   } else if (fRecordingStep == CONTINUE) {
1796     txtStatus  = "Continue Recording...";
1797   } else if (fRecordingStep == STOP) {
1798     txtStatus  = "Stop Recording...";
1799   } else if (fRecordingStep == READY_TO_ENCODE) {
1800     txtStatus  = "Ready to Encode...";
1801   } else if (fRecordingStep == ENCODING) {
1802     txtStatus  = "Encoding...";
1803   } else if (fRecordingStep == FAILED) {
1804     txtStatus  = "Failed to encode...";
1805   } else if ((fRecordingStep == BAD_ENCODER)
1806              || (fRecordingStep == BAD_OUTPUT)
1807              || (fRecordingStep == BAD_TMP)) {
1808     txtStatus  = "Correct above errors first";
1809   } else if (fRecordingStep == SUCCESS) {
1810     txtStatus  = "File encoded successfully";
1811   } else {
1812   }
1813 
1814   if (fMovieParametersDialog) {
1815     fMovieParametersDialog->setRecordingStatus(txtStatus);
1816   } else {
1817     G4cout << txtStatus.toStdString().c_str() << G4endl;
1818   }
1819   setRecordingInfos("");
1820 }
1821 
1822 
1823 void G4OpenGLQtViewer::setRecordingInfos(const QString& txt) {
1824   if (fMovieParametersDialog) {
1825     fMovieParametersDialog->setRecordingInfos(txt);
1826   } else {
1827     G4cout << txt.toStdString().c_str() << G4endl;
1828   }
1829 }
1830 
1831 /** Init the movie parameters. Temp dir and encoder path
1832  */
1833 void G4OpenGLQtViewer::initMovieParameters() {
1834   //init encoder
1835 
1836   //look for encoderPath
1837   fProcess = new QProcess();
1838 
1839   QObject ::connect(fProcess,SIGNAL(finished ( int)),
1840                     this,SLOT(processLookForFinished()));
1841   fProcess->setProcessChannelMode(QProcess::MergedChannels);
1842 #if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0))
1843   fProcess->start ("which ppmtompeg");
1844 #else
1845   fProcess->start ("which ppmtompeg", QStringList());
1846 #endif
1847 }
1848 
1849 /** @return encoder path or "" if it does not exist
1850  */
1851 QString G4OpenGLQtViewer::getEncoderPath() {
1852   return fEncoderPath;
1853 }
1854 
1855 
1856 /**
1857  * set the new encoder path
1858  * @return "" if correct. The error otherwise
1859  */
1860 QString G4OpenGLQtViewer::setEncoderPath(QString path) {
1861   if (path == "") {
1862     return "ppmtompeg is needed to encode in video format. It is available here: http://netpbm.sourceforge.net ";
1863   }
1864 
1865   path =  QDir::cleanPath(path);
1866   QFileInfo *f = new QFileInfo(path);
1867   if (!f->exists()) {
1868     return "File does not exist";
1869   } else if (f->isDir()) {
1870     return "This is a directory";
1871   } else if (!f->isExecutable()) {
1872     return "File exist but is not executable";
1873   } else if (!f->isFile()) {
1874     return "This is not a file";
1875   }
1876   fEncoderPath = path;
1877 
1878   if (fRecordingStep == BAD_ENCODER) {
1879     setRecordingStatus(STOP);
1880   }
1881   return "";
1882 }
1883 
1884 
1885 bool G4OpenGLQtViewer::isRecording(){
1886   if ((fRecordingStep == START) || (fRecordingStep == CONTINUE)) {
1887     return true;
1888   }
1889   return false;
1890 }
1891 
1892 bool G4OpenGLQtViewer::isPaused(){
1893   if (fRecordingStep == PAUSE) {
1894     return true;
1895   }
1896   return false;
1897 }
1898 
1899 bool G4OpenGLQtViewer::isEncoding(){
1900   if (fRecordingStep == ENCODING) {
1901     return true;
1902   }
1903   return false;
1904 }
1905 
1906 bool G4OpenGLQtViewer::isWaiting(){
1907   if (fRecordingStep == WAIT) {
1908     return true;
1909   }
1910   return false;
1911 }
1912 
1913 bool G4OpenGLQtViewer::isStopped(){
1914   if (fRecordingStep == STOP) {
1915     return true;
1916   }
1917   return false;
1918 }
1919 
1920 bool G4OpenGLQtViewer::isFailed(){
1921   if (fRecordingStep == FAILED) {
1922     return true;
1923   }
1924   return false;
1925 }
1926 
1927 bool G4OpenGLQtViewer::isSuccess(){
1928   if (fRecordingStep == SUCCESS) {
1929     return true;
1930   }
1931   return false;
1932 }
1933 
1934 bool G4OpenGLQtViewer::isBadEncoder(){
1935   if (fRecordingStep == BAD_ENCODER) {
1936     return true;
1937   }
1938   return false;
1939 }
1940 bool G4OpenGLQtViewer::isBadTmp(){
1941   if (fRecordingStep == BAD_TMP) {
1942     return true;
1943   }
1944   return false;
1945 }
1946 bool G4OpenGLQtViewer::isBadOutput(){
1947   if (fRecordingStep == BAD_OUTPUT) {
1948     return true;
1949   }
1950   return false;
1951 }
1952 
1953 void G4OpenGLQtViewer::setBadEncoder(){
1954   fRecordingStep = BAD_ENCODER;
1955   displayRecordingStatus();
1956 }
1957 void G4OpenGLQtViewer::setBadTmp(){
1958   fRecordingStep = BAD_TMP;
1959   displayRecordingStatus();
1960 }
1961 void G4OpenGLQtViewer::setBadOutput(){
1962   fRecordingStep = BAD_OUTPUT;
1963   displayRecordingStatus();
1964 }
1965 
1966 void G4OpenGLQtViewer::setWaiting(){
1967   fRecordingStep = WAIT;
1968   displayRecordingStatus();
1969 }
1970 
1971 
1972 bool G4OpenGLQtViewer::isReadyToEncode(){
1973   if (fRecordingStep == READY_TO_ENCODE) {
1974     return true;
1975   }
1976   return false;
1977 }
1978 
1979 void G4OpenGLQtViewer::resetRecording() {
1980   setRecordingStatus(WAIT);
1981 }
1982 
1983 /**
1984  * set the temp folder path
1985  * @return "" if correct. The error otherwise
1986  */
1987 QString G4OpenGLQtViewer::setTempFolderPath(QString path) {
1988 
1989   if (path == "") {
1990     return "Path does not exist";
1991   }
1992   path =  QDir::cleanPath(path);
1993   QFileInfo *d = new QFileInfo(path);
1994   if (!d->exists()) {
1995     return "Path does not exist";
1996   } else if (!d->isDir()) {
1997     return "This is not a directory";
1998   } else if (!d->isReadable()) {
1999     return path +" is read protected";
2000   } else if (!d->isWritable()) {
2001     return path +" is write protected";
2002   }
2003 
2004   if (fRecordingStep == BAD_TMP) {
2005     setRecordingStatus(WAIT);
2006   }
2007   fTempFolderPath = path;
2008   return "";
2009 }
2010 
2011 /** @return the temp folder path or "" if it does not exist
2012  */
2013 QString G4OpenGLQtViewer::getTempFolderPath() {
2014   return fTempFolderPath;
2015 }
2016 
2017 /**
2018  * set the save file name path
2019  * @return "" if correct. The error otherwise
2020  */
2021 QString G4OpenGLQtViewer::setSaveFileName(QString path) {
2022 
2023   if (path == "") {
2024     return "Path does not exist";
2025   }
2026 
2027   QFileInfo *file = new QFileInfo(path);
2028   QDir dir = file->dir();
2029   path =  QDir::cleanPath(path);
2030   if (file->exists()) {
2031     return "File already exist, please choose a new one";
2032   } else if (!dir.exists()) {
2033     return "Dir does not exist";
2034   } else if (!dir.isReadable()) {
2035     return path +" is read protected";
2036   }
2037 
2038   if (fRecordingStep == BAD_OUTPUT) {
2039     setRecordingStatus(STOP);
2040   }
2041   fSaveFileName = path;
2042   return "";
2043 }
2044 
2045 /** @return the save file path
2046  */
2047 QString G4OpenGLQtViewer::getSaveFileName() {
2048   return fSaveFileName ;
2049 }
2050 
2051 /** Create a Qt_temp folder in the temp folder given
2052  * The temp folder will be like this /tmp/QtMovie_12-02-2008_12_12_58/
2053  * @return "" if success. Error message if not.
2054  */
2055 QString G4OpenGLQtViewer::createTempFolder() {
2056   fMovieTempFolderPath = "";
2057   //check
2058   QString tmp = setTempFolderPath(fTempFolderPath);
2059   if (tmp != "") {
2060     return tmp;
2061   }
2062   QString sep = QString(QDir::separator());
2063   QString path = sep+"QtMovie_"+QDateTime::currentDateTime ().toString("dd-MM-yyyy_hh-mm-ss")+sep;
2064   QDir *d = new QDir(QDir::cleanPath(fTempFolderPath));
2065   // check if it is already present
2066   if (d->exists(path)) {
2067     return "Folder "+path+" already exists.Please remove it first";
2068   }
2069   if (d->mkdir(fTempFolderPath+path)) {
2070     fMovieTempFolderPath = fTempFolderPath+path;
2071     return "";
2072   }
2073   return "Can't create "+fTempFolderPath+path;
2074 }
2075 
2076 /** Remove the Qt_temp folder in the temp folder
2077  */
2078 QString G4OpenGLQtViewer::removeTempFolder() {
2079   // remove files in Qt_temp folder
2080   if (fMovieTempFolderPath == "") {
2081     return "";
2082   }
2083   QDir *d = new QDir(QDir::cleanPath(fMovieTempFolderPath));
2084   if (!d->exists()) {
2085     return "";  // already remove
2086   }
2087 
2088   d->setFilter( QDir::Files );
2089   QStringList subDirList = d->entryList();
2090   int res = true;
2091   QString error = "";
2092   for (QStringList::ConstIterator it = subDirList.begin() ;(it != subDirList.end()) ; it++) {
2093     const QString currentFile = *it;
2094     if (!d->remove(currentFile)) {
2095       res = false;
2096       QString file = fMovieTempFolderPath+currentFile;
2097       error +="Removing file failed : "+file;
2098     } else {
2099     }
2100   }
2101   if (res) {
2102     if (d->rmdir(fMovieTempFolderPath)) {
2103       fMovieTempFolderPath = "";
2104       return "";
2105     } else {
2106       return "Dir "+fMovieTempFolderPath+" should be empty, but could not remove it";
2107     }
2108 
2109   }
2110   return "Could not remove "+fMovieTempFolderPath+" because of the following errors :"+error;
2111 }
2112 
2113 /**
2114   Export image. Try to get the format according to the file extention.
2115   If not present, the last one choosen by /vis/ogl/set/exportFormat
2116   If not, will take the default format : eps
2117   Best format actually available is pdf (vectored and allow transparency)
2118   If name is not set, it will take the default name value given by /vis/ogl/set/printFilename
2119  */
2120 bool G4OpenGLQtViewer::exportImage(std::string name, int width, int height) {
2121 
2122   auto* qGLW = dynamic_cast<G4QGLWidgetType*> (fGLWidget) ;
2123   if (! qGLW) {
2124     return false;
2125   }
2126   // If there is already an extention
2127   bool increaseFileNumber = true;
2128   // if
2129   if (name.size() != name.substr(name.find_last_of(".") + 1).size()) {
2130     increaseFileNumber = false;
2131   }
2132   if (! setExportFilename(name,increaseFileNumber)) {
2133     return false;
2134   }
2135   if ((width !=-1) && (height != -1)) {
2136     setExportSize(width, height);
2137   }
2138   // first, try to do it with generic function
2139   if (G4OpenGLViewer::exportImage(name, width, height)) {
2140     return true;
2141 
2142   // Then try Qt saving functions
2143   } else {
2144     QImage image;
2145 #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
2146     image = qGLW->grabFrameBuffer();
2147 #else
2148     image = qGLW->grabFramebuffer();
2149 #endif
2150 
2151     bool res = image.save(QString(getRealPrintFilename().c_str()),0,fLastExportSliderValue);
2152 
2153     if (!res) {
2154       G4cerr << "Error saving file... " << getRealPrintFilename().c_str() << G4endl;
2155       return false;
2156     } else {
2157       G4cout << "File " << getRealPrintFilename().c_str() << " size: " << fGLWidget->width() << "x" << fGLWidget->height() << " has been saved " << G4endl;
2158       fExportFilenameIndex++;
2159     }
2160   }
2161   return true;
2162 }
2163 
2164 bool G4OpenGLQtViewer::generateMpegEncoderParameters () {
2165 
2166   // save the parameter file
2167   FILE* fp;
2168   fp = fopen (QString(fMovieTempFolderPath+fParameterFileName).toStdString().c_str(), "w");
2169 
2170   if (fp == NULL) {
2171     setRecordingInfos("Generation of parameter file failed");
2172     return false;
2173   }
2174 
2175   fprintf (fp,"# Pattern affects speed, quality and compression. See the User's Guide\n");
2176   fprintf (fp,"# for more info.\n");
2177   fprintf (fp,"\n");
2178   fprintf (fp,"PATTERN    I\n");
2179   fprintf (fp,"OUTPUT   %s\n",getSaveFileName().toStdString().c_str());
2180   fprintf (fp,"\n");
2181   fprintf (fp,"# You must specify the type of the input files.  The choices are:\n");
2182   fprintf (fp,"#    YUV, PPM, JMOVIE, Y, JPEG, PNM\n");
2183   fprintf (fp,"#  (must be upper case)\n");
2184   fprintf (fp,"#\n");
2185   fprintf (fp,"BASE_FILE_FORMAT PPM\n");
2186   fprintf (fp,"\n");
2187   fprintf (fp,"\n");
2188   fprintf (fp,"# If you are using YUV, there are different supported file formats.\n");
2189   fprintf (fp,"# EYUV or UCB are the same as previous versions of this encoder.\n");
2190   fprintf (fp,"# (All the Y's, then U's then V's, in 4:2:0 subsampling.)\n");
2191   fprintf (fp,"# Other formats, such as Abekas, Phillips, or a general format are\n");
2192   fprintf (fp,"# permissible, the general format is a string of Y's, U's, and V's\n");
2193   fprintf (fp,"# to specify the file order.\n");
2194   fprintf (fp,"\n");
2195   fprintf (fp,"INPUT_FORMAT UCB\n");
2196   fprintf (fp,"\n");
2197   fprintf (fp,"# the conversion statement\n");
2198   fprintf (fp,"#\n");
2199   fprintf (fp,"# Each occurrence of '*' will be replaced by the input file\n");
2200   fprintf (fp,"#\n");
2201   fprintf (fp,"# e.g., if you have a bunch of GIF files, then this might be:\n");
2202   fprintf (fp,"#  INPUT_CONVERT giftoppm *\n");
2203   fprintf (fp,"#\n");
2204   fprintf (fp,"# e.g., if you have a bunch of files like a.Y a.U a.V, etc., then:\n");
2205   fprintf (fp,"#  INPUT_CONVERT cat *.Y *.U *.V\n");
2206   fprintf (fp,"#\n");
2207   fprintf (fp,"# e.g., if you are grabbing from laser disc you might have something like\n");
2208   fprintf (fp,"#  INPUT_CONVERT goto frame *; grabppm\n");
2209   fprintf (fp,"# 'INPUT_CONVERT *' means the files are already in the base file format\n");
2210   fprintf (fp,"#\n");
2211   fprintf (fp,"INPUT_CONVERT  * \n");
2212   fprintf (fp,"\n");
2213   fprintf (fp,"# number of frames in a GOP.\n");
2214   fprintf (fp,"#\n");
2215   fprintf (fp,"# since each GOP must have at least one I-frame, the encoder will find the\n");
2216   fprintf (fp,"# the first I-frame after GOP_SIZE frames to start the next GOP\n");
2217   fprintf (fp,"#\n");
2218   fprintf (fp,"# later, will add more flexible GOP signalling\n");
2219   fprintf (fp,"#\n");
2220   fprintf (fp,"GOP_SIZE 1\n");
2221   fprintf (fp,"\n");
2222   fprintf (fp,"# number of slices in a frame\n");
2223   fprintf (fp,"#\n");
2224   fprintf (fp,"# 1 is a good number.  another possibility is the number of macroblock rows\n");
2225   fprintf (fp,"# (which is the height divided by 16)\n");
2226   fprintf (fp,"#\n");
2227   fprintf (fp,"SLICES_PER_FRAME 1\n");
2228   fprintf (fp,"PIXEL HALF");
2229   fprintf (fp,"\n");
2230   fprintf (fp,"# directory to get all input files from (makes this file easier to read)\n");
2231   fprintf (fp,"INPUT_DIR  %s\n",fMovieTempFolderPath.toStdString().c_str());
2232   fprintf (fp,"\n");
2233   fprintf (fp,"# There are a bunch of ways to specify the input files.\n");
2234   fprintf (fp,"# from a simple one-per-line listing, to the following \n");
2235   fprintf (fp,"# way of numbering them.  See the manual for more information.\n");
2236   fprintf (fp,"INPUT\n");
2237   fprintf (fp,"# '*' is replaced by the numbers 01, 02, 03, 04\n");
2238   fprintf (fp,"# if I instead do [01-11], it would be 01, 02, ..., 09, 10, 11\n");
2239   fprintf (fp,"# if I instead do [1-11], it would be 1, 2, 3, ..., 9, 10, 11\n");
2240   fprintf (fp,"# if I instead do [1-11+3], it would be 1, 4, 7, 10\n");
2241   fprintf (fp,"# the program assumes none of your input files has a name ending in ']'\n");
2242   fprintf (fp,"# if you do, too bad!!!\n");
2243   fprintf (fp,"#\n");
2244   fprintf (fp,"#\n");
2245   fprintf (fp,"Test*.ppm  [0-%d]\n",fRecordFrameNumber-1);
2246   fprintf (fp,"# can have more files here if you want...there is no limit on the number\n");
2247   fprintf (fp,"# of files\n");
2248   fprintf (fp,"END_INPUT\n");
2249   fprintf (fp,"\n");
2250   fprintf (fp,"\n");
2251   fprintf (fp,"\n");
2252   fprintf (fp,"# Many of the remaining options have to do with the motion search and qscale\n");
2253   fprintf (fp,"\n");
2254   fprintf (fp,"# FULL or HALF -- must be upper case\n");
2255   fprintf (fp,"# Should be FULL for computer generated images\n");
2256   fprintf (fp,"PIXEL    FULL\n");
2257   fprintf (fp,"\n");
2258   fprintf (fp,"# means +/- this many pixels for both P and B frame searches\n");
2259   fprintf (fp,"# specify two numbers if you wish to serc different ranges in the two.\n");
2260   fprintf (fp,"RANGE    10\n");
2261   fprintf (fp,"\n");
2262   fprintf (fp,"# The two search algorithm parameters below mostly affect speed,\n");
2263   fprintf (fp,"# with some affect on compression and almost none on quality.\n");
2264   fprintf (fp,"\n");
2265   fprintf (fp,"# this must be one of {EXHAUSTIVE, SUBSAMPLE, LOGARITHMIC}\n");
2266   fprintf (fp,"PSEARCH_ALG  LOGARITHMIC\n");
2267   fprintf (fp,"\n");
2268   fprintf (fp,"# this must be one of {SIMPLE, CROSS2, EXHAUSTIVE}\n");
2269   fprintf (fp,"#\n");
2270   fprintf (fp,"# note that EXHAUSTIVE is really, really, really slow\n");
2271   fprintf (fp,"#\n");
2272   fprintf (fp,"BSEARCH_ALG  SIMPLE\n");
2273   fprintf (fp,"\n");
2274   fprintf (fp,"#\n");
2275   fprintf (fp,"# these specify the q-scale for I, P, and B frames\n");
2276   fprintf (fp,"# (values must be between 1 and 31)\n");
2277   fprintf (fp,"# These are the Qscale values for the entire frame in variable bit-rate\n");
2278   fprintf (fp,"# mode, and starting points (but not important) for constant bit rate\n");
2279   fprintf (fp,"#\n");
2280   fprintf (fp,"\n");
2281   fprintf (fp,"# Qscale (Quantization scale) affects quality and compression,\n");
2282   fprintf (fp,"# but has very little effect on speed.\n");
2283   fprintf (fp,"\n");
2284   fprintf (fp,"IQSCALE    4\n");
2285   fprintf (fp,"PQSCALE    5\n");
2286   fprintf (fp,"BQSCALE    12\n");
2287   fprintf (fp,"\n");
2288   fprintf (fp,"# this must be ORIGINAL or DECODED\n");
2289   fprintf (fp,"REFERENCE_FRAME  ORIGINAL\n");
2290   fprintf (fp,"\n");
2291   fprintf (fp,"# for parallel parameters see parallel.param in the examples subdirectory\n");
2292   fprintf (fp,"\n");
2293   fprintf (fp,"# if you want constant bit-rate mode, specify it as follows (number is bits/sec):\n");
2294   fprintf (fp,"#BIT_RATE  1000000\n");
2295   fprintf (fp,"\n");
2296   fprintf (fp,"# To specify the buffer size (327680 is default, measused in bits, for 16bit words)\n");
2297   fprintf (fp,"BUFFER_SIZE 327680\n");
2298   fprintf (fp,"\n");
2299   fprintf (fp,"# The frame rate is the number of frames/second (legal values:\n");
2300   fprintf (fp,"# 23.976, 24, 25, 29.97, 30, 50 ,59.94, 60\n");
2301   fprintf (fp,"FRAME_RATE 30\n");
2302   fprintf (fp,"\n");
2303   fprintf (fp,"# There are many more options, see the users manual for examples....\n");
2304   fprintf (fp,"# ASPECT_RATIO, USER_DATA, GAMMA, IQTABLE, etc.\n");
2305   fprintf (fp,"\n");
2306   fprintf (fp,"\n");
2307   fclose (fp);
2308 
2309   setRecordingInfos("Parameter file "+fParameterFileName+" generated in "+fMovieTempFolderPath);
2310   setRecordingStatus(READY_TO_ENCODE);
2311   return true;
2312 }
2313 
2314 void G4OpenGLQtViewer::encodeVideo()
2315 {
2316   if ((getEncoderPath() != "") && (getSaveFileName() != "")) {
2317     setRecordingStatus(ENCODING);
2318 
2319     fProcess = new QProcess();
2320     QObject ::connect(fProcess,SIGNAL(finished ( int,QProcess::ExitStatus)),
2321                       this,SLOT(processEncodeFinished()));
2322     QObject ::connect(fProcess,SIGNAL(readyReadStandardOutput ()),
2323                       this,SLOT(processEncodeStdout()));
2324 #if (QT_VERSION < QT_VERSION_CHECK(5, 10, 0))
2325     fProcess->setReadChannelMode(QProcess::MergedChannels);
2326 #else
2327     fProcess->setProcessChannelMode(QProcess::MergedChannels);
2328 #endif
2329     fProcess->start (fEncoderPath, QStringList(fMovieTempFolderPath+fParameterFileName));
2330   }
2331 }
2332 
2333 
2334 // FIXME : does not work on Qt3
2335 void G4OpenGLQtViewer::processEncodeStdout()
2336 {
2337   QString tmp = fProcess->readAllStandardOutput ().data();
2338   auto start = tmp.lastIndexOf("ESTIMATED TIME");
2339   tmp = tmp.mid(start,tmp.indexOf("\n",start)-start);
2340   setRecordingInfos(tmp);
2341 }
2342 
2343 
2344 void G4OpenGLQtViewer::processEncodeFinished()
2345 {
2346 
2347   QString txt = "";
2348   txt = getProcessErrorMsg();
2349   if (txt == "") {
2350     setRecordingStatus(SUCCESS);
2351   } else {
2352     setRecordingStatus(FAILED);
2353   }
2354   //  setRecordingInfos(txt+removeTempFolder());
2355 }
2356 
2357 
2358 void G4OpenGLQtViewer::processLookForFinished()
2359 {
2360 
2361   QString txt = getProcessErrorMsg();
2362   if (txt != "") {
2363     fEncoderPath = "";
2364   } else {
2365     fEncoderPath = QString(fProcess->readAllStandardOutput ().data()).trimmed();
2366     // if not found, return "not found"
2367     if (fEncoderPath.contains(" ")) {
2368       fEncoderPath = "";
2369     } else if (!fEncoderPath.contains("ppmtompeg")) {
2370       fEncoderPath = "";
2371     }
2372     setEncoderPath(fEncoderPath);
2373   }
2374   // init temp folder
2375   setTempFolderPath(QDir::temp ().absolutePath ());
2376 }
2377 
2378 
2379 QString G4OpenGLQtViewer::getProcessErrorMsg()
2380 {
2381   QString txt = "";
2382   if (fProcess->exitCode() != 0) {
2383     switch (fProcess->error()) {
2384     case QProcess::FailedToStart:
2385       txt = "The process failed to start. Either the invoked program is missing, or you may have insufficient permissions to invoke the program.\n";
2386       break;
2387     case QProcess::Crashed:
2388       txt = "The process crashed some time after starting successfully.\n";
2389       break;
2390     case QProcess::Timedout:
2391       txt = "The last waitFor...() function timed out. The state of QProcess is unchanged, and you can try calling waitFor...() again.\n";
2392       break;
2393     case QProcess::WriteError:
2394       txt = "An error occurred when attempting to write to the process. For example, the process may not be running, or it may have closed its input channel.\n";
2395       break;
2396     case QProcess::ReadError:
2397       txt = "An error occurred when attempting to read from the process. For example, the process may not be running.\n";
2398       break;
2399     case QProcess::UnknownError:
2400       txt = "An unknown error occurred. This is the default return value of error().\n";
2401       break;
2402     }
2403   }
2404   return txt;
2405 }
2406 
2407 
2408 
2409 
2410 QWidget *G4OpenGLQtViewer::getParentWidget()
2411 {
2412   // launch Qt if not
2413   G4Qt* interactorManager = G4Qt::getInstance ();
2414 
2415   bool found = false;
2416   QDialog* dialog = NULL;
2417   // create window
2418   if (((QApplication*)interactorManager->GetMainInteractor())) {
2419     // look for the main window
2420     QWidgetList wl = QApplication::allWidgets();
2421     QWidget *widget = NULL;
2422     for (int i=0; i < wl.size(); i++) {
2423       widget = wl.at(i);
2424       if ((found== false) && (widget->inherits("QMainWindow"))) {
2425         dialog = new QDialog(widget,Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint);
2426         found = true;
2427       }
2428     }
2429 
2430     if (found==false) {
2431       dialog = new QDialog();
2432     }
2433   } else {
2434     dialog= new QDialog();
2435   }
2436   if (found) {
2437     return dialog;
2438   } else {
2439     return NULL;
2440   }
2441 }
2442 
2443 
2444 void G4OpenGLQtViewer::createSceneTreeWidget() {
2445   fUISceneTreeWidget = fUiQt->GetSceneTreeWidget();
2446 
2447   if (!fUISceneTreeWidget) {
2448     return;
2449   }
2450 
2451   // do not remove previous widgets, hide them!
2452   QLayoutItem * wItem;
2453   bool found = false;
2454   if (fUISceneTreeWidget->layout()->count() ) {
2455     for(int idx = 0; idx < fUISceneTreeWidget->layout()->count(); idx++){
2456       wItem = fUISceneTreeWidget->layout()->itemAt(idx);
2457       if (fSceneTreeWidget) {
2458         if(dynamic_cast<QWidget *>(wItem->widget())) {
2459           if (wItem->widget()->windowTitle() == fSceneTreeWidget->windowTitle()) {
2460             wItem->widget()->show();
2461             found = true;
2462           } else {
2463             wItem->widget()->hide();
2464           }
2465         }
2466       } else {
2467 //        wItem->widget()->hide();
2468       }
2469     }
2470   }
2471 
2472   if (!found) {
2473     // initialize scene tree / viewer properties / picking
2474     fSceneTreeWidget = new QWidget();
2475     QVBoxLayout* layoutSceneTree = new QVBoxLayout();
2476     fSceneTreeWidget->setStyleSheet ("padding: 0px ");
2477 
2478     fSceneTreeWidget->setLayout(layoutSceneTree);
2479     fSceneTreeWidget->layout()->setContentsMargins(5,5,5,5);
2480     fSceneTreeWidget->setWindowTitle(QString(GetName().data()));
2481 
2482     if (fUISceneTreeWidget != NULL) {
2483 //      fUISceneTreeWidget->layout()->addWidget(fSceneTreeWidget);
2484     }
2485 
2486     // not available for Immediate mode
2487     if (dynamic_cast<G4OpenGLStoredQtViewer*> (this)) {
2488       createSceneTreeComponent();
2489     }
2490   }
2491 }
2492 
2493 
2494 void G4OpenGLQtViewer::createSceneTreeComponent(){
2495 
2496   QLayout* vLayout = fSceneTreeWidget->layout();
2497 
2498   // Search line
2499   QWidget* coutButtonWidget = new QWidget();
2500   QHBoxLayout* layoutCoutTBButtons = new QHBoxLayout();
2501 
2502   fFilterOutput = new QLineEdit();
2503   fFilterOutput->setToolTip("Filter output by...");
2504   fFilterOutput->setStyleSheet ("padding: 0px ");
2505 
2506   QPixmap* searchIcon = fUiQt->getSearchIcon();
2507   fFilterOutput->addAction(*searchIcon,QLineEdit::TrailingPosition);
2508   fFilterOutput->setStyleSheet ("border-radius:7px;");
2509   layoutCoutTBButtons->addWidget(fFilterOutput);
2510 
2511   coutButtonWidget->setLayout(layoutCoutTBButtons);
2512   vLayout->addWidget(coutButtonWidget);
2513 
2514   // reduce margins
2515   vLayout->setContentsMargins(0,0,0,0);
2516 
2517 
2518   fSceneTreeComponentTreeWidget = new QTreeWidget();
2519   fSceneTreeComponentTreeWidget->setSelectionMode(QAbstractItemView::SingleSelection);
2520   fSceneTreeComponentTreeWidget->setHeaderLabel ("Scene tree : "+QString(GetName().data()));
2521   fSceneTreeComponentTreeWidget->setColumnHidden (1,true);  // copy number
2522   fSceneTreeComponentTreeWidget->setColumnHidden (2,true);  // PO index
2523   fSceneTreeComponentTreeWidget->setColumnHidden (3,true);  // Informations
2524   //   data(0) : POindex
2525   //   data(1) : copy number
2526   //   data(2) : g4color
2527 
2528   vLayout->addWidget(fSceneTreeComponentTreeWidget);
2529 
2530   connect(fSceneTreeComponentTreeWidget,SIGNAL(itemChanged(QTreeWidgetItem*, int)),SLOT(sceneTreeComponentItemChanged(QTreeWidgetItem*, int)));
2531   connect(fSceneTreeComponentTreeWidget,SIGNAL(itemSelectionChanged ()),SLOT(sceneTreeComponentSelected()));
2532   connect(fSceneTreeComponentTreeWidget,SIGNAL(itemDoubleClicked ( QTreeWidgetItem*, int)),SLOT(changeColorAndTransparency( QTreeWidgetItem*, int)));
2533 
2534 
2535   // Depth slider
2536   QWidget *helpWidget = new QWidget();
2537   QHBoxLayout *helpLayout = new QHBoxLayout();
2538 
2539   QWidget* depthWidget = new QWidget();
2540   QWidget *showBox = new QWidget(depthWidget);
2541   QHBoxLayout *showBoxLayout = new QHBoxLayout();
2542 
2543   // reduce margins
2544   showBoxLayout->setContentsMargins(5,5,5,5);
2545 
2546   QLabel *zero = new QLabel();
2547   zero->setText("Show all");
2548   QLabel *one = new QLabel();
2549   one->setText("Hide all");
2550   fSceneTreeDepthSlider = new QSlider ( Qt::Horizontal);
2551   fSceneTreeDepthSlider->setMaximum (1000);
2552   fSceneTreeDepthSlider->setMinimum (0);
2553   fSceneTreeDepthSlider->setTickPosition(QSlider::TicksAbove);
2554   // set a minimum size
2555   fSceneTreeDepthSlider->setMinimumWidth (40);
2556 
2557   showBoxLayout->addWidget(zero);
2558   showBoxLayout->addWidget(fSceneTreeDepthSlider);
2559   showBoxLayout->addWidget(one);
2560 
2561   showBox->setLayout(showBoxLayout);
2562 
2563   helpLayout->addWidget(showBox);
2564   helpWidget->setLayout(helpLayout);
2565   helpLayout->setContentsMargins(0,0,0,0);
2566 
2567   vLayout->addWidget(helpWidget);
2568 
2569   connect( fSceneTreeDepthSlider, SIGNAL( valueChanged(int) ), this, SLOT( changeDepthInSceneTree(int) ) );
2570   connect( fFilterOutput, SIGNAL( textEdited ( const QString &) ), this, SLOT(changeSearchSelection()));
2571   fTreeItemModels.clear();
2572 
2573   fPVRootNodeCreate = false;
2574 
2575   fMaxPOindexInserted = -1;
2576 
2577 
2578 }
2579 
2580 
2581 void G4OpenGLQtViewer::createViewerPropertiesWidget() {
2582 
2583   // Get the pointer to the Viewer Properties widget
2584   fUIViewerPropertiesWidget = fUiQt->GetViewerPropertiesWidget();
2585 
2586   if (!fUIViewerPropertiesWidget) {
2587     return;
2588   }
2589 
2590   // remove previous widgets
2591   QLayoutItem * wItem;
2592   if (fUIViewerPropertiesWidget->layout()->count()) {
2593     while ((wItem = fUIViewerPropertiesWidget->layout()->takeAt(0)) != 0) {
2594       delete wItem->widget();
2595       delete wItem;
2596     }
2597   }
2598 
2599   // add properties
2600   QGroupBox *groupBox = new QGroupBox();
2601   groupBox->setTitle(GetName().data());
2602   QVBoxLayout *vbox = new QVBoxLayout;
2603 
2604   // add properties content
2605   fViewerPropertiesTableWidget = new QTableWidget();
2606 
2607   QSizePolicy vPolicy = fViewerPropertiesTableWidget->sizePolicy();
2608   vPolicy.setVerticalStretch(4);
2609 
2610   vbox->addWidget(fViewerPropertiesTableWidget);
2611   groupBox->setLayout(vbox);
2612   fUIViewerPropertiesWidget->layout()->addWidget(groupBox);
2613 
2614   connect(fViewerPropertiesTableWidget, SIGNAL(itemChanged(QTableWidgetItem*)),this, SLOT(tableWidgetViewerSetItemChanged(QTableWidgetItem *)));
2615 
2616   updateViewerPropertiesTableWidget();
2617 
2618   QDialog* dial = static_cast<QDialog*> (fUIViewerPropertiesWidget->parent());
2619   if (dial) {
2620     // change name
2621     dial->setWindowTitle(QString("Viewer properties - ")+GetName());
2622   }
2623 }
2624 
2625 
2626 void G4OpenGLQtViewer::createPickInfosWidget(){
2627 
2628   // Get the pointer to the Pick infos widget
2629   fUIPickInfosWidget = fUiQt->GetPickInfosWidget();
2630 
2631   if (!fUIPickInfosWidget) {
2632     return;
2633   }
2634 
2635   // remove previous widgets
2636   QLayoutItem * wItem;
2637   if (fUIPickInfosWidget->layout()->count()) {
2638     while ((wItem = fUIPickInfosWidget->layout()->takeAt(0)) != 0) {
2639       delete wItem->widget();
2640       delete wItem;
2641     }
2642   }
2643 
2644   QGroupBox *groupBox = new QGroupBox("");
2645   QVBoxLayout *vbox = new QVBoxLayout;
2646 
2647   // add picking infos
2648   QWidget *pickingInfoWidget = new QWidget();
2649   QHBoxLayout *pickingInfoLayout = new QHBoxLayout();
2650 
2651   pickingInfoWidget->setStyleSheet ("padding-left: 0px; border:0px;");
2652   pickingInfoWidget->setLayout(pickingInfoLayout);
2653 
2654   vbox->addWidget(pickingInfoWidget);
2655   // add picking content
2656 
2657   fPickInfosScrollArea = new QScrollArea();
2658   fPickInfosScrollArea->setWidgetResizable(true);
2659 
2660 
2661   fPickInfosWidget = new QWidget();
2662   fPickInfosWidget->setStyleSheet ("padding: 0px ");
2663 
2664   QVBoxLayout* vLayout = new QVBoxLayout();
2665   fPickInfosWidget->setLayout (vLayout);
2666   fPickInfosScrollArea->setWidget(fPickInfosWidget);
2667 
2668   QSizePolicy vPolicy = fPickInfosWidget->sizePolicy();
2669   vPolicy.setVerticalStretch(4);
2670   vbox->addWidget(fPickInfosScrollArea);
2671   pickingInfoLayout->setContentsMargins(0,0,0,0);
2672   vLayout->setContentsMargins(0,0,0,0);
2673   vbox->setContentsMargins(1,1,1,1);
2674 
2675   groupBox->setLayout(vbox);
2676   fUIPickInfosWidget->layout()->addWidget(groupBox);
2677 
2678   updatePickInfosWidget(fLastPickPoint.x(),fLastPickPoint.y());
2679 }
2680 
2681 
2682 // set the component to check/unchecked, also go into its child
2683 // and set the same status to all his childs
2684 void G4OpenGLQtViewer::setCheckComponent(QTreeWidgetItem* item,bool check)
2685 {
2686   if (item) {
2687 
2688     const PVPath& fullPath = fTreeItemModels[item->data(0,Qt::UserRole).toInt()];
2689     // If a physical volume
2690     if (fullPath.size() > 0) {
2691       SetTouchable(fullPath);
2692       TouchableSetVisibility(fullPath, check);
2693       fMouseOnSceneTree = true;
2694     }
2695   }
2696 
2697   if (item != NULL) {
2698     if (check) {
2699       item->setCheckState(0,Qt::Checked);
2700     } else {
2701       item->setCheckState(0,Qt::Unchecked);
2702     }
2703     updatePositivePoIndexSceneTreeWidgetQuickMap(item->data(0,Qt::UserRole).toInt(),item);
2704     int nChildCount = item->childCount();
2705     for (int i = 0; i < nChildCount; i++) {
2706       setCheckComponent(item->child(i),check);
2707     }
2708   }
2709 }
2710 
2711 #if QT_VERSION < 0x060000
2712 #else
2713 //G.Barrand : from stackoverflow "How to render text with QOpenGLWidget":
2714 static void transform_point(GLdouble out[4], const GLdouble m[16], const GLdouble in[4])
2715 {
2716 #define M(row,col)  m[col*4+row]
2717     out[0] =
2718         M(0, 0) * in[0] + M(0, 1) * in[1] + M(0, 2) * in[2] + M(0, 3) * in[3];
2719     out[1] =
2720         M(1, 0) * in[0] + M(1, 1) * in[1] + M(1, 2) * in[2] + M(1, 3) * in[3];
2721     out[2] =
2722         M(2, 0) * in[0] + M(2, 1) * in[1] + M(2, 2) * in[2] + M(2, 3) * in[3];
2723     out[3] =
2724         M(3, 0) * in[0] + M(3, 1) * in[1] + M(3, 2) * in[2] + M(3, 3) * in[3];
2725 #undef M
2726 }
2727 inline GLint project_point(GLdouble objx, GLdouble objy, GLdouble objz,
2728     const GLdouble model[16], const GLdouble proj[16],
2729     const GLint viewport[4],
2730     GLdouble * winx, GLdouble * winy, GLdouble * winz)
2731 {
2732     GLdouble in[4], out[4];
2733 
2734     in[0] = objx;
2735     in[1] = objy;
2736     in[2] = objz;
2737     in[3] = 1.0;
2738     transform_point(out, model, in);
2739     transform_point(in, proj, out);
2740 
2741     if (in[3] == 0.0)
2742         return GL_FALSE;
2743 
2744     in[0] /= in[3];
2745     in[1] /= in[3];
2746     in[2] /= in[3];
2747 
2748     *winx = viewport[0] + (1 + in[0]) * viewport[2] / 2;
2749     *winy = viewport[1] + (1 + in[1]) * viewport[3] / 2;
2750 
2751     *winz = (1 + in[2]) / 2;
2752     return GL_TRUE;
2753 }
2754 static void render_text(QOpenGLWidget& widget,
2755                  double world_x, double world_y, double world_z,
2756                  double offset_x, double offset_y,
2757                  const QFont& font,
2758                  const QColor& color,
2759                  const char* text)
2760 {
2761     GLdouble model[4][4];
2762     glGetDoublev(GL_MODELVIEW_MATRIX, &model[0][0]);
2763     GLdouble proj[4][4];
2764     glGetDoublev(GL_PROJECTION_MATRIX, &proj[0][0]);
2765     GLint view[4];
2766     glGetIntegerv(GL_VIEWPORT, &view[0]);
2767 
2768     GLdouble textPosX = 0, textPosY = 0, textPosZ = 0;
2769     project_point(world_x, world_y, world_z,
2770                   &model[0][0], &proj[0][0], &view[0],
2771                   &textPosX, &textPosY, &textPosZ);
2772 
2773     textPosX /= GLdouble(widget.devicePixelRatio());
2774     textPosY /= GLdouble(widget.devicePixelRatio());
2775 
2776     textPosY = GLdouble(widget.height()) - textPosY; // y is inverted
2777 
2778     textPosX += offset_x;
2779     textPosY += offset_y;
2780 
2781     GLboolean GL_BLEND_enabled = glIsEnabled(GL_BLEND);
2782 
2783     QPainter painter(&widget);
2784     painter.setPen(color);
2785     painter.setFont(font);
2786     painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
2787     painter.drawText(textPosX, textPosY, text);
2788     painter.end();
2789 
2790     if(GL_BLEND_enabled==GL_TRUE) {
2791       ::glEnable(GL_BLEND);
2792       ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2793     }
2794 }
2795 #endif
2796 
2797 void G4OpenGLQtViewer::DrawText(const G4Text& g4text)
2798 {
2799   auto* qGLW = dynamic_cast<G4QGLWidgetType*> (fGLWidget) ;
2800   if (! qGLW) {
2801     return;
2802   }
2803   if (isGl2psWriting()) {
2804 
2805     G4OpenGLViewer::DrawText(g4text);
2806 
2807   } else {
2808 
2809     if (!fGLWidget) return;
2810 
2811     if (!G4Threading::IsMasterThread()) return;
2812 
2813     G4VSceneHandler::MarkerSizeType sizeType;
2814     G4double size = fSceneHandler.GetMarkerSize(g4text,sizeType);
2815 
2816     QFont font = QFont();
2817     font.setPointSizeF(size);
2818 
2819     const G4Colour& c = fSceneHandler.GetTextColour(g4text);
2820 
2821     G4Point3D position = g4text.GetPosition();
2822 
2823     const G4String& textString = g4text.GetText();
2824     const char* textCString = textString.c_str();
2825 
2826     // Calculate move for centre and right adjustment
2827     QFontMetrics f(font);
2828     G4double span = f.boundingRect(textCString).width();
2829 
2830     G4double xmove = 0.;
2831     G4double ymove = 0.;
2832 
2833     switch (g4text.GetLayout()) {
2834     case G4Text::left: break;
2835     case G4Text::centre: xmove -= span / 2.; break;
2836     case G4Text::right: xmove -= span;
2837     }
2838 
2839     //Add offsets
2840     xmove += g4text.GetXOffset();
2841     ymove += g4text.GetYOffset();
2842 
2843 #if QT_VERSION < 0x060000
2844     glColor4d(c.GetRed(),c.GetGreen(),c.GetBlue(),c.GetAlpha());
2845     glRasterPos3d(position.x(),position.y(),position.z());
2846     // xmove, ymove in pixels - or are they?
2847 #ifdef __APPLE__
2848     const G4double fudgeFactor = 2.;
2849 #else
2850     const G4double fudgeFactor = 1.;
2851 #endif
2852     xmove *= fudgeFactor;
2853     ymove *= fudgeFactor;
2854     qGLW->renderText
2855       ((position.x()+(2*xmove)/getWinWidth()),
2856        (position.y()+(2*ymove)/getWinHeight()),
2857        position.z(),
2858        textCString,
2859        font);
2860 #else
2861     QColor color((int)(c.GetRed()*255),
2862                  (int)(c.GetGreen()*255),
2863                  (int)(c.GetBlue()*255),
2864                  (int)(c.GetAlpha()*255));
2865     render_text(*qGLW,
2866                 position.x(),position.y(),position.z(),
2867                 xmove,ymove,
2868                 font,color,textCString);
2869 #endif
2870   }
2871 }
2872 
2873 
2874 void G4OpenGLQtViewer::ResetView () {
2875   G4OpenGLViewer::ResetView();
2876   fDeltaDepth = 0.01;
2877   fDeltaZoom = 0.05;
2878 }
2879 
2880 
2881 
2882 
2883 void G4OpenGLQtViewer::addPVSceneTreeElement(const G4String& model, G4PhysicalVolumeModel* pPVModel, int currentPOIndex) {
2884 
2885   const QString& modelShortName = getModelShortName(model);
2886 
2887   if (modelShortName == "") {
2888     return ;
2889   }
2890   // try to init it
2891   if (fSceneTreeComponentTreeWidget == NULL) {
2892     createSceneTreeComponent();
2893   }
2894 
2895   // if no UI
2896   if (fSceneTreeComponentTreeWidget == NULL) {
2897     return;
2898   }
2899 
2900   fSceneTreeComponentTreeWidget->blockSignals(true);
2901 
2902   // Create the "volume" node if not
2903   //  if (fSceneTreeComponentTreeWidget->topLevelItemCount () == 0) {
2904   if (!fPVRootNodeCreate) {
2905     const G4Colour& color = fSceneHandler.GetColour();
2906 
2907     fModelShortNameItem = createTreeWidgetItem(pPVModel->GetFullPVPath(),
2908                                                modelShortName,
2909                                                0, // currentPVCopyNb
2910                                                -1, // currentPVPOIndex
2911                                                "",
2912                                                Qt::Checked,
2913                                                NULL,
2914                                                color);
2915     fPVRootNodeCreate = true;
2916   }
2917 
2918   bool added = parseAndInsertInSceneTree(fModelShortNameItem,pPVModel,0,modelShortName,0,currentPOIndex);
2919   if (!added) {
2920   }
2921 
2922   fSceneTreeComponentTreeWidget->blockSignals(false);
2923 
2924 }
2925 
2926 
2927 /**
2928    if treeNode is NULL, then add this treeNode to the TreeWidget
2929    @return the inserted item
2930 */
2931 QTreeWidgetItem* G4OpenGLQtViewer::createTreeWidgetItem(
2932  const PVPath& fullPath
2933  ,const QString& name
2934  ,int copyNb
2935  ,int POIndex
2936  ,const QString& logicalName
2937  ,Qt::CheckState state
2938  ,QTreeWidgetItem * parentTreeNode
2939  ,const G4Colour& color
2940 ) {
2941 
2942   // Set depth
2943   if (fullPath.size() > fSceneTreeDepth) {
2944     fSceneTreeDepth = (unsigned int)fullPath.size();
2945     // Change slider value
2946     if (fSceneTreeDepthSlider) {
2947       fSceneTreeDepthSlider->setTickInterval(1000/(fSceneTreeDepth+1));
2948     }
2949   }
2950   QTreeWidgetItem * newItem = NULL;
2951   if (parentTreeNode == NULL) {
2952     newItem = new QTreeWidgetItem();
2953     fSceneTreeComponentTreeWidget->addTopLevelItem(newItem);
2954   } else {
2955     newItem = new QTreeWidgetItem(parentTreeNode);
2956     fSceneTreeComponentTreeWidget->addTopLevelItem(parentTreeNode);
2957   }
2958 
2959 
2960   newItem->setText(0,name);
2961   newItem->setData(1,Qt::UserRole,copyNb);
2962   newItem->setText(2,QString::number(POIndex));
2963   newItem->setData(0, Qt::UserRole, POIndex);
2964   newItem->setText(3,logicalName);
2965   newItem->setFlags(newItem->flags()|Qt::ItemIsUserCheckable);
2966   newItem->setCheckState(0,state);
2967   newItem->setExpanded(true);
2968   updatePositivePoIndexSceneTreeWidgetQuickMap(POIndex,newItem);
2969 
2970   changeQColorForTreeWidgetItem(newItem,QColor((int)(color.GetRed()*255),
2971                                                (int)(color.GetGreen()*255),
2972                                                (int)(color.GetBlue()*255),
2973                                                (int)(color.GetAlpha()*255)));
2974 
2975   // If invisible
2976   if ((state == Qt::Unchecked) && (POIndex == -1)) {
2977     newItem->setForeground (0, QBrush( Qt::gray) );
2978 
2979     // Set a tootip
2980     newItem->setToolTip (0,QString(
2981                                    "This node exists in the geometry but has not been\n")+
2982                          "drawn, perhaps because it has been set invisible. It \n"+
2983                          "cannot be made visible with a click on the button.\n"+
2984                          "To see it, change the visibility, for example, with \n"+
2985                          "/vis/geometry/set/visibility " + logicalName + " 0 true\n"+
2986                          "and rebuild the view with /vis/viewer/rebuild.\n"+
2987                          "Click here will only show/hide all child components");
2988   } else {
2989     // Set a tootip
2990     newItem->setToolTip (0,QString("double-click to change the color"));
2991   }
2992 
2993   // special case: if alpha=0, it is a totally transparent objet,
2994   // then, do not redraw it
2995   if (color.GetAlpha() == 0) {
2996     state = Qt::Unchecked;
2997     newItem->setCheckState(0,state);
2998     updatePositivePoIndexSceneTreeWidgetQuickMap(POIndex,newItem);
2999   }
3000 
3001   fTreeItemModels.insert(std::pair <int, PVPath > (POIndex,fullPath) );
3002 
3003   // Check last status of this item and change if necessary
3004   // open/close/hidden/visible/selected
3005   changeOpenCloseVisibleHiddenSelectedColorSceneTreeElement(newItem);
3006   return newItem;
3007 }
3008 
3009 
3010 //
3011 //   Recursive function.
3012 //   Try to insert the given item :
3013 //   - If not present and last item of the path: insert it and mark it CHECK
3014 //   - If not present and NOT last item of the path: insert it and mark it UNCHECKED
3015 //   - If already present and name/PO/Transformation identical, then it is a transparent
3016 //     object : Change the PO number and transparency
3017 //   - If already present and PO different, then it is an unvisible item : Have to
3018 //     set it visible
3019 //   - else : Create a new element
3020 //   @return true if inserted, false if already present
3021 //
3022 bool G4OpenGLQtViewer::parseAndInsertInSceneTree(
3023  QTreeWidgetItem * parentItem
3024  ,G4PhysicalVolumeModel* pPVModel
3025  ,unsigned int fullPathIndex
3026  ,const QString& parentRoot
3027  ,unsigned int currentIndexInTreeSceneHandler
3028  ,int currentPVPOIndex
3029 ) {
3030 
3031   if (parentItem == NULL) {
3032     return false;
3033   }
3034 
3035   const PVPath& fullPath = pPVModel->GetFullPVPath();
3036 
3037   std::ostringstream oss;
3038   oss << fullPath.at(fullPathIndex).GetCopyNo();
3039   std::string currentPVName = G4String(fullPath.at(fullPathIndex).GetPhysicalVolume()->GetName()+" ["+oss.str()+"]").data();
3040 
3041   int currentPVCopyNb = fullPath.at(fullPathIndex).GetCopyNo();
3042 
3043   const G4Colour& color = fSceneHandler.GetColour();
3044 
3045   // look in all children in order to get if their is already a
3046   // child corresponding:
3047   // - if so, go into this child
3048   // - if not : create it as invisible
3049 
3050   // Realy quick check if the POindex is already there
3051   QTreeWidgetItem* subItem = NULL;
3052   QList<QTreeWidgetItem *> parentItemList;
3053 
3054 
3055   // first of all, very quick check if it was not the same as last one
3056 
3057   // Check only if it is a transparent object
3058   // If it is the last item and it is not transparent -> nothing to look for,
3059   // simply add it.
3060   if ((currentIndexInTreeSceneHandler == (fullPath.size()-1)) && ((color.GetAlpha() == 1.))) {
3061   } else {
3062     QString lookForString = QString(currentPVName.c_str());
3063     for (int i = 0;i < parentItem->childCount(); i++ ) {
3064       if (parentItem->child(i)->text(0) == lookForString) {
3065         parentItemList.push_back(parentItem->child(i));
3066       }
3067     }
3068   }
3069 
3070   for (int i = 0; i < parentItemList.size(); ++i) {
3071     const std::string& parentItemName = parentItemList.at(i)->text(0).toStdString();
3072     int parentItemCopyNb = parentItemList.at(i)->data(1,Qt::UserRole).toInt();
3073     int parentItemPOIndex = parentItemList.at(i)->data(0,Qt::UserRole).toInt();
3074 
3075     // if already inside
3076     // -> return true
3077     // special case, do not have to deal with hierarchy except for PhysicalVolume
3078 
3079 
3080     /* Physical Volume AND copy number equal AND  name equal */
3081     if (((parentRoot == fTouchableVolumes) &&  (currentPVCopyNb == parentItemCopyNb)
3082          && (currentPVName == parentItemName))        ||
3083         /* NOT a Physical Volume AND  copy number equal */
3084         ((parentRoot != fTouchableVolumes) && (currentPVCopyNb == parentItemCopyNb)
3085          /*AND  name equal AND  PO index equal*/
3086          && (currentPVName == parentItemName) && (currentPVPOIndex == parentItemPOIndex) )) {
3087 
3088       // then check for the Transform3D
3089       bool sameTransform = true;
3090       if (parentItemPOIndex >= 0) {
3091         const PVPath& fullPathTmp = fTreeItemModels[parentItemPOIndex];
3092         if (fullPathTmp.size() > 0) {
3093           if (fullPathTmp.at(fullPathTmp.size()-1).GetTransform () == pPVModel->GetTransformation ()) {
3094             sameTransform = true;
3095           } else {
3096             sameTransform = false;
3097           }
3098         }
3099       }
3100 
3101       // Same transformation, then try to change the PO index
3102       if (sameTransform == true) {
3103         // already exist in the tree, is it a transparent object ?
3104         // If so, then have to change the PO index ONLY if it is the last
3105         // and then change the state ONLY if POIndex has change
3106         // If not, then go deaper
3107 
3108         // last element
3109         if (currentIndexInTreeSceneHandler == (fullPath.size()-1)) {
3110 
3111           parentItemList.at(i)->setText(2,QString::number(currentPVPOIndex));
3112           parentItemList.at(i)->setData(0, Qt::UserRole,currentPVPOIndex);
3113 
3114           fTreeItemModels.insert(std::pair <int, PVPath >(currentPVPOIndex,fullPath) );
3115 
3116           // Then remove tooltip and special font
3117           QFont f = QFont();
3118           parentItemList.at(i)->setFont (0,f);
3119 
3120           // set foreground
3121           parentItemList.at(i)->setForeground (0,QBrush());
3122 
3123           // Set a tootip
3124           parentItemList.at(i)->setToolTip (0,"");
3125 
3126           changeQColorForTreeWidgetItem(parentItemList.at(i),QColor((int)(color.GetRed()*255),
3127                                                                     (int)(color.GetGreen()*255),
3128                                                                     (int)(color.GetBlue()*255),
3129                                                                     (int)(color.GetAlpha()*255)));
3130 
3131           // set check only if there is something to display
3132           if (color.GetAlpha() > 0) {
3133             parentItemList.at(i)->setCheckState(0,Qt::Checked);
3134             updatePositivePoIndexSceneTreeWidgetQuickMap(currentPVPOIndex,parentItemList.at(i));
3135           }
3136           return false;
3137         } else {
3138           subItem = parentItemList.at(i);
3139         }
3140 
3141         // Exists but not the end of path, then forget get it
3142       } else if (currentIndexInTreeSceneHandler < (fullPath.size()-1))  {
3143         subItem = parentItemList.at(i);
3144       }
3145     }
3146 
3147   } // end for
3148 
3149   // if it the last, then add it and set it checked
3150   if (currentIndexInTreeSceneHandler == (fullPath.size()-1)) {
3151     /* subItem =*/ createTreeWidgetItem(fullPath,
3152                                    QString(currentPVName.c_str()),
3153                                    currentPVCopyNb,
3154                                    currentPVPOIndex,
3155                                    QString(fullPath.at(fullPathIndex).GetPhysicalVolume()->GetLogicalVolume()->GetName().data()),
3156                                    Qt::Checked,
3157                                    parentItem,
3158                                    color);
3159 
3160     if (currentPVPOIndex > fMaxPOindexInserted) {
3161       fMaxPOindexInserted = currentPVPOIndex;
3162     }
3163 
3164   } else {
3165 
3166     // if no child found, then this child is create and marked as invisible, then go inside
3167     if (subItem == NULL) {
3168 
3169       if (currentIndexInTreeSceneHandler < (fullPath.size()-1))  {
3170         subItem = createTreeWidgetItem(fullPath,
3171                                        QString(currentPVName.c_str()),
3172                                        currentPVCopyNb,
3173                                        -1,
3174                                        QString(fullPath.at(fullPathIndex).GetPhysicalVolume()->GetLogicalVolume()->GetName().data()),
3175                                        Qt::Unchecked,
3176                                        parentItem,
3177                                        color);
3178       }
3179     }
3180 
3181     return parseAndInsertInSceneTree(subItem,pPVModel,fullPathIndex+1,parentRoot,currentIndexInTreeSceneHandler+1,currentPVPOIndex);
3182   }
3183   return true;
3184 }
3185 
3186 
3187 void G4OpenGLQtViewer::changeOpenCloseVisibleHiddenSelectedColorSceneTreeElement(
3188  QTreeWidgetItem* subItem
3189 )
3190 {
3191   // Check if object with the same POIndex is the same in old tree
3192   QTreeWidgetItem* oldItem = NULL;
3193 
3194   QTreeWidgetItem* foundItem = getOldTreeWidgetItem(subItem->data(0,Qt::UserRole).toInt());
3195 
3196   if (foundItem != NULL) {
3197     if (isSameSceneTreeElement(foundItem,subItem)) {
3198       oldItem = foundItem;
3199     }
3200   }
3201   if (foundItem == NULL) {  // PO should have change, parse all
3202 
3203     // POindex > 0
3204     std::map <int, QTreeWidgetItem*>::const_iterator i;
3205     i = fOldPositivePoIndexSceneTreeWidgetQuickMap.cbegin();
3206     while (i != fOldPositivePoIndexSceneTreeWidgetQuickMap.cend()) {
3207       if (isSameSceneTreeElement(i->second,subItem)) {
3208         oldItem = i->second;
3209         i = fOldPositivePoIndexSceneTreeWidgetQuickMap.cend();
3210       } else {
3211         ++i;
3212       }
3213     }
3214     // POindex == 0 ?
3215     if (oldItem == NULL) {
3216       std::size_t a = 0;
3217       while (a < fOldNullPoIndexSceneTreeWidgetQuickVector.size()) {
3218         if (isSameSceneTreeElement(fOldNullPoIndexSceneTreeWidgetQuickVector[a],subItem)) {
3219           oldItem = fOldNullPoIndexSceneTreeWidgetQuickVector[a];
3220           a = fOldNullPoIndexSceneTreeWidgetQuickVector.size();
3221         } else {
3222           ++a;
3223         }
3224       }
3225     }
3226   }
3227 
3228   // if found : retore old state
3229   if (oldItem != NULL) {
3230     subItem->setFlags(oldItem->flags());   // flags
3231     subItem->setCheckState(0,oldItem->checkState(0)); // check state
3232     subItem->setSelected(oldItem->isSelected());  // selected
3233     subItem->setExpanded(oldItem->isExpanded ());  // expand
3234 
3235     // change color
3236     // when we call this function, the color in the item is the one of vis Attr
3237 
3238     std::map <int, QTreeWidgetItem* >::iterator it;
3239 
3240     // getOldPO
3241     int oldPOIndex = oldItem->data(0,Qt::UserRole).toInt();
3242     it = fOldPositivePoIndexSceneTreeWidgetQuickMap.find(oldPOIndex);
3243     QColor color;
3244 
3245     // get old Vis Attr Color
3246     std::map <int, QColor >::iterator itVis;
3247     itVis = fOldVisAttrColorMap.find(oldPOIndex);
3248 
3249     QColor oldVisAttrColor;
3250     const QColor& newVisAttrColor = subItem->data(2,Qt::UserRole).value<QColor>();
3251 
3252     bool visAttrChange = false;
3253     // if old vis attr color found
3254     if (itVis != fOldVisAttrColorMap.end()) {
3255       oldVisAttrColor = itVis->second;
3256       if (oldVisAttrColor != newVisAttrColor) {
3257         visAttrChange = true;
3258       }
3259     } else {
3260       visAttrChange = true;
3261     }
3262 
3263     if (visAttrChange) {
3264       fOldVisAttrColorMap.insert(std::pair <int, QColor > (subItem->data(0,Qt::UserRole).toInt(),newVisAttrColor) );
3265 
3266     } else { // if no changes, get old PO value
3267       // if old PO found
3268       if (it != fOldPositivePoIndexSceneTreeWidgetQuickMap.end()) {
3269         color = (it->second)->data(2,Qt::UserRole).value<QColor>();
3270       } else {
3271         color = oldItem->data(2,Qt::UserRole).value<QColor>();
3272       }
3273       changeQColorForTreeWidgetItem(subItem,color);
3274     }
3275   }
3276 
3277   return;
3278 }
3279 
3280 
3281 
3282 // Check if both items are identical.
3283 // For that, check name, copy number, transformation
3284 // special case for "non Touchables", do not check the PO index, check only the name
3285 bool G4OpenGLQtViewer::isSameSceneTreeElement(
3286  QTreeWidgetItem* parentOldItem
3287  ,QTreeWidgetItem* parentNewItem
3288 ) {
3289 
3290   int newPO = -1;
3291   int oldPO = -1;
3292 
3293   int newCpNumber = -1;
3294   int oldCpNumber = -1;
3295 
3296   bool firstWhile = true;
3297 
3298   while ((parentOldItem != NULL) && (parentNewItem != NULL)) {
3299 
3300     // check transform, optimize getting data(..,..) that consume lot of time
3301     if (!firstWhile) {
3302       oldPO = parentOldItem->data(0,Qt::UserRole).toInt();
3303       newPO = parentNewItem->data(0,Qt::UserRole).toInt();
3304     }
3305     firstWhile = false;
3306 
3307     if ((oldPO >= 0) &&
3308         (newPO >= 0)) {
3309       const PVPath& oldFullPath = fOldTreeItemModels[oldPO];
3310       const PVPath& newFullPath = fTreeItemModels[newPO];
3311       if ((oldFullPath.size() > 0) &&
3312           (newFullPath.size() > 0)) {
3313         if (oldFullPath.size() != newFullPath.size()) {
3314           return false;
3315         }
3316         if (oldFullPath.at(oldFullPath.size()-1).GetTransform () == newFullPath.at(newFullPath.size()-1).GetTransform ()) {
3317           newCpNumber = newFullPath.at(newFullPath.size()-1).GetCopyNo();
3318           oldCpNumber = oldFullPath.at(oldFullPath.size()-1).GetCopyNo();
3319           // ok
3320         } else {
3321           return false;
3322         }
3323       }
3324     }
3325 
3326     // Check copy Number
3327     if (oldCpNumber == -1) {
3328       oldCpNumber = parentOldItem->data(1,Qt::UserRole).toInt();
3329     }
3330     if (newCpNumber == -1) {
3331       newCpNumber = parentNewItem->data(1,Qt::UserRole).toInt();
3332     }
3333     if ((oldCpNumber != newCpNumber) ||
3334         // Check name
3335         (parentOldItem->text(0) != parentNewItem->text(0)) ) {
3336       // try to optimize
3337       return false;
3338     } else if ((parentOldItem->text(0) != parentNewItem->text(0)) || // Logical Name
3339                (parentOldItem->text(3) != parentNewItem->text(3))) {   // Check logical name
3340       return false;
3341     } else {
3342       parentOldItem = parentOldItem->parent();
3343       parentNewItem = parentNewItem->parent();
3344     }
3345   } // end while
3346 
3347   return true;
3348 }
3349 
3350 
3351 void G4OpenGLQtViewer::addNonPVSceneTreeElement(
3352  const G4String& model
3353  ,int currentPOIndex
3354  ,const std::string& modelDescription
3355  ,const G4Visible& visible
3356 ) {
3357 
3358   QString modelShortName = getModelShortName(model);
3359   G4Colour color;
3360 
3361   // Special case for text
3362   try {
3363     const G4Text& g4Text = dynamic_cast<const G4Text&>(visible);
3364     color = fSceneHandler.GetTextColour(g4Text);
3365   }
3366   catch (const std::bad_cast&) {
3367     color = fSceneHandler.GetColour();
3368   }
3369 
3370   // Special case for marker
3371   try {
3372     const G4VMarker& g4Marker = dynamic_cast<const G4VMarker&>(visible);
3373     if (g4Marker.GetInfo() != "") {
3374       modelShortName = g4Marker.GetInfo();
3375     }
3376   }
3377   catch (const std::bad_cast&) {}
3378 
3379   if (modelShortName == "") {
3380     return ;
3381   }
3382   // try to init it
3383   if (fSceneTreeComponentTreeWidget == NULL) {
3384     createSceneTreeComponent();
3385   }
3386 
3387   // if no UI
3388   if (fSceneTreeComponentTreeWidget == NULL) {
3389     return;
3390   }
3391 
3392   fSceneTreeComponentTreeWidget->blockSignals(true);
3393 
3394   // Create the "Model" node if not
3395 
3396   QList<QTreeWidgetItem *> resItem;
3397   resItem =  fSceneTreeComponentTreeWidget->findItems (modelShortName, Qt::MatchExactly, 0 );
3398   QTreeWidgetItem * currentItem = NULL;
3399   const PVPath tmpFullPath;
3400 
3401   if (resItem.empty()) {
3402     currentItem = createTreeWidgetItem(tmpFullPath,
3403                                        modelShortName,
3404                                        0, // currentPVCopyNb
3405                                        -1, // currentPVPOIndex
3406                                        "",
3407                                        Qt::Checked,
3408                                        NULL,
3409                                        color);
3410   } else {
3411     currentItem = resItem.first();
3412   }
3413 
3414   // Is this volume already in the tree AND PO is not the same?
3415   const QList<QTreeWidgetItem *>&
3416   resItems =  fSceneTreeComponentTreeWidget->findItems (QString(modelDescription.c_str()), Qt::MatchFixedString| Qt::MatchCaseSensitive|Qt::MatchRecursive, 0 );
3417 
3418   bool alreadyPresent = false;
3419   for (int i = 0; i < resItems.size(); ++i) {
3420     if (currentPOIndex == resItems.at(i)->data(0,Qt::UserRole).toInt()) {
3421       alreadyPresent = true;
3422     }
3423   }
3424   if (!alreadyPresent) {
3425     createTreeWidgetItem(tmpFullPath,
3426                          modelShortName,
3427                          0, // currentPVCopyNb
3428                          currentPOIndex,
3429                          "",
3430                          Qt::Checked,
3431                          currentItem,
3432                          color);
3433   }
3434   fSceneTreeComponentTreeWidget->blockSignals(false);
3435 
3436 }
3437 
3438 
3439 /**
3440    Get the short name for a given label
3441 */
3442 QString G4OpenGLQtViewer::getModelShortName(const G4String& model) {
3443 
3444   QString modelShortName = model.data();
3445   if (modelShortName.mid(0,modelShortName.indexOf(" ")) == "G4PhysicalVolumeModel") {
3446     modelShortName = fTouchableVolumes;
3447   } else {
3448     if (modelShortName.mid(0,2) == "G4") {
3449       modelShortName = modelShortName.mid(2);
3450     }
3451     if (modelShortName.indexOf("Model") != -1) {
3452       modelShortName = modelShortName.mid(0,modelShortName.indexOf("Model"));
3453     }
3454   }
3455   return modelShortName;
3456 }
3457 
3458 
3459 
3460 bool G4OpenGLQtViewer::isTouchableVisible(int POindex){
3461 
3462   // If no scene tree (Immediate viewer)
3463   if (fSceneTreeComponentTreeWidget == NULL) {
3464     return false;
3465   }
3466 
3467   // should be the next one
3468   // Prevent to get out the std::map
3469   if (fLastSceneTreeWidgetAskForIterator != fLastSceneTreeWidgetAskForIteratorEnd) {
3470     fLastSceneTreeWidgetAskForIterator++;
3471   }
3472   QTreeWidgetItem* item = getTreeWidgetItem(POindex);
3473 
3474   if (item != NULL) {
3475     if ( item->checkState(0) == Qt::Checked) {
3476       return true;
3477     }
3478   }
3479   return false;
3480 }
3481 
3482 
3483 bool G4OpenGLQtViewer::parseAndCheckVisibility(QTreeWidgetItem * treeNode,int POindex){
3484   bool isFound = false;
3485   for (int i = 0; i < treeNode->childCount() ; ++i) {
3486 
3487     if (treeNode->child(i)->data(0,Qt::UserRole).toInt() == POindex) {
3488       if (treeNode->child(i)->checkState(0) == Qt::Checked) {
3489         return true;
3490       }
3491     }
3492     isFound = parseAndCheckVisibility(treeNode->child(i),POindex);
3493     if (isFound) {
3494       return true;
3495     }
3496   } // end for
3497   return false;
3498 }
3499 
3500 
3501 std::string G4OpenGLQtViewer::parseSceneTreeAndSaveState(){
3502   std::string commandLine = "";
3503   for (int b=0;b<fSceneTreeComponentTreeWidget->topLevelItemCount();b++) {
3504     commandLine += parseSceneTreeElementAndSaveState(fSceneTreeComponentTreeWidget->topLevelItem(b),1)+"\n";
3505   }
3506   if (commandLine != "") {
3507     commandLine = std::string("# Disable auto refresh and quieten vis messages whilst scene and\n") +
3508     "# trajectories are established:\n" +
3509     "/vis/viewer/set/autoRefresh false\n" +
3510     "/vis/verbose errors" +
3511     commandLine +
3512     "# Re-establish auto refreshing and verbosity:\n" +
3513     "/vis/viewer/set/autoRefresh true\n" +
3514     "/vis/verbose confirmations\n";
3515   }
3516   return commandLine;
3517 }
3518 
3519 
3520 std::string G4OpenGLQtViewer::parseSceneTreeElementAndSaveState(QTreeWidgetItem* item, unsigned int level){
3521   // parse current item
3522   std::string str( level, ' ' );
3523   std::string commandLine = "\n#"+ str + "PV Name: " + item->text(0).toStdString();
3524 
3525   if (item->text(3) != "") {
3526     commandLine += " LV Name: "+item->text(3).toStdString()+"\n";
3527     // save check state
3528     commandLine += "/vis/geometry/set/visibility " + item->text(3).toStdString() + " ! "; // let default value for depth
3529     if (item->checkState(0) == Qt::Checked) {
3530       commandLine += "1";
3531     }
3532     if (item->checkState(0) == Qt::Unchecked) {
3533       commandLine += "0";
3534     }
3535     commandLine +="\n";
3536 
3537     // save color
3538     const QColor& c = item->data(2,Qt::UserRole).value<QColor>();
3539     std::stringstream red;
3540     red << ((double)c.red())/255;
3541     std::stringstream green;
3542     green << (double)c.green()/255;
3543     std::stringstream blue;
3544     blue << ((double)c.blue())/255;
3545     std::stringstream alpha;
3546     alpha << ((double)c.alpha())/255;
3547 
3548     commandLine += "/vis/geometry/set/colour " + item->text(3).toStdString() + " ! " + red.str() + " " + green.str() + " " + blue.str() + " " + alpha.str()+"\n";
3549 
3550   } else {
3551     commandLine += "\n";
3552   }
3553 
3554   // parse childs
3555   for (int b=0;b< item->childCount();b++) {
3556     commandLine += parseSceneTreeElementAndSaveState(item->child(b),level+1);
3557   }
3558 
3559   return commandLine;
3560 }
3561 
3562 
3563 void G4OpenGLQtViewer::sceneTreeComponentItemChanged(QTreeWidgetItem* item, int) {
3564 
3565   if (fCheckSceneTreeComponentSignalLock == false) {
3566     fCheckSceneTreeComponentSignalLock = true;
3567     G4bool checked = false;
3568     if (item->checkState(0) == Qt::Checked) {
3569       checked = true;
3570     }
3571     setCheckComponent(item,checked);
3572     updateQWidget();
3573 
3574     fCheckSceneTreeComponentSignalLock = false;
3575   }
3576 }
3577 
3578 
3579 void G4OpenGLQtViewer::sceneTreeComponentSelected() {
3580 }
3581 
3582 void G4OpenGLQtViewer::changeDepthInSceneTree (int val){
3583 
3584   // If no scene tree (Immediate viewer)
3585   if (fSceneTreeComponentTreeWidget == NULL) {
3586     return;
3587   }
3588 
3589   // max depth :   fSceneTreeDepth
3590   // val is between 0 and 1
3591   // 0  .1  .2  .3  .4  .5  .6  .7  .8  .9  1
3592   // 1      1.4          2
3593   // 1         2         3         4
3594 
3595   // Get the depth :
3596   double depth = 1 + ((double)val)/1000 * ((double)fSceneTreeDepth+1);
3597 
3598   // lock update on scene tree items
3599   fCheckSceneTreeComponentSignalLock = true;
3600 
3601   // Disable redraw each time !
3602   G4bool currentAutoRefresh = fVP.IsAutoRefresh();
3603   fVP.SetAutoRefresh(false);
3604 
3605   for (int b=0;b<fSceneTreeComponentTreeWidget->topLevelItemCount();b++) {
3606     changeDepthOnSceneTreeItem(depth,1.,fSceneTreeComponentTreeWidget->topLevelItem(b));
3607   }
3608 
3609   // Enable redraw !
3610   fVP.SetAutoRefresh(currentAutoRefresh);
3611   updateQWidget();
3612 
3613   // unlock update on scene tree items
3614   fCheckSceneTreeComponentSignalLock = false;
3615 
3616 }
3617 
3618 
3619 void G4OpenGLQtViewer::changeColorAndTransparency(QTreeWidgetItem* item,int) {
3620 
3621   if (item == NULL) {
3622     return;
3623   }
3624   const QColor& old = QColor(item->data(2,Qt::UserRole).value<QColor>());
3625 
3626   const QColor& color = QColorDialog::getColor(old,
3627                                         fSceneTreeComponentTreeWidget,
3628                                         " Get color and transparency",
3629                                         QColorDialog::ShowAlphaChannel);
3630 
3631   if (color.isValid()) {
3632 
3633     changeColorAndTransparency(item->data(0,Qt::UserRole).toInt(),
3634                                G4Colour (((G4double)color.red())/255,
3635                                          ((G4double)color.green())/255,
3636                                          ((G4double)color.blue())/255,
3637                                          ((G4double)color.alpha())/255));
3638 
3639     // set scene tree parameters
3640     changeQColorForTreeWidgetItem(item,color);
3641   }
3642 }
3643 
3644 
3645 void G4OpenGLQtViewer::changeColorAndTransparency(GLuint index, G4Color color) {
3646 
3647   // change vis attributes to set new colour
3648   G4int iPO = index;
3649   if (iPO >= 0 && fTreeItemModels.find(iPO) != fTreeItemModels.end()) {
3650     const PVPath& fullPath = fTreeItemModels[iPO];
3651     // If a physical volume
3652     if (fullPath.size()) {
3653       SetTouchable(fullPath);
3654       TouchableSetColour(fullPath, color);
3655       fMouseOnSceneTree = true;
3656     }
3657   }
3658 }
3659 
3660 
3661 G4Colour G4OpenGLQtViewer::getColorForPoIndex(int poIndex) {
3662   // FIXME 09/2014 : Could be optimize by searching in a tab instead of item->data
3663   QTreeWidgetItem* item = getTreeWidgetItem(poIndex);
3664 
3665   if (item != NULL) {
3666 
3667     const QColor& color = item->data(2,Qt::UserRole).value<QColor>();
3668     G4Colour g4c(((G4double)color.red())/255,
3669                  ((G4double)color.green())/255,
3670                  ((G4double)color.blue())/255,
3671                  ((G4double)color.alpha())/255);
3672 
3673     return g4c;
3674   }
3675   return G4Colour();
3676 }
3677 
3678 
3679 const std::vector<G4ModelingParameters::VisAttributesModifier>*
3680 G4OpenGLQtViewer::GetPrivateVisAttributesModifiers() const
3681 {
3682   static std::vector<G4ModelingParameters::VisAttributesModifier>
3683   privateVisAttributesModifiers;
3684 
3685   privateVisAttributesModifiers.clear();
3686 
3687 // I don't think we need this.  (JA Sep 2016).
3688 //  // For each modified touchable...
3689 //  std::map<int,PVPath>::const_iterator i;
3690 //  for (i = fTreeItemModels.begin();
3691 //       i != fTreeItemModels.end();
3692 //       ++i) {
3693 //
3694 //    // How do I know if it's been modified or not?
3695 //
3696 //    int iPO = i->first;
3697 //    const PVPath& fullPath = i->second;
3698 //
3699 //    // If a physical volume
3700 //    if (fullPath.size()) {
3701 //
3702 //      //    const G4bool& visibilityChanged = ???
3703 //      //    const G4bool& visibility = ???
3704 //      //    const G4bool& colourChanged = ???
3705 //      //    const QColor& colour = ???
3706 //      //    G4Colour g4colour(((G4double)colour.red())/255,
3707 //      //                      ((G4double)colour.green())/255,
3708 //      //                      ((G4double)colour.blue())/255,
3709 //      //                      ((G4double)colour.alpha())/255);
3710 //      // Next 4 lines are for testing, to be replaced by the above...
3711 //      G4bool visibilityChanged = true;
3712 //      G4bool visibility = true;
3713 //      G4bool colourChanged = true;
3714 //      G4Colour g4colour(G4Colour::Red());
3715 //
3716 //      // Instantiate a working copy of a G4VisAttributes object...
3717 //      G4VisAttributes workingVisAtts;
3718 //      // ...and use it to create vis attribute modifiers...
3719 //      if (visibilityChanged) {
3720 //        workingVisAtts.SetVisibility(visibility);
3721 //        privateVisAttributesModifiers.push_back
3722 //        (G4ModelingParameters::VisAttributesModifier
3723 //         (workingVisAtts,
3724 //          G4ModelingParameters::VASVisibility,
3725 //          fullPath));
3726 //      }
3727 //      if (colourChanged) {
3728 //        workingVisAtts.SetColour(g4colour);
3729 //        privateVisAttributesModifiers.push_back
3730 //        (G4ModelingParameters::VisAttributesModifier
3731 //         (workingVisAtts,
3732 //          G4ModelingParameters::VASColour,
3733 //          fullPath));
3734 //      }
3735 //    }
3736 //  }
3737 
3738   return &privateVisAttributesModifiers;
3739 }
3740 
3741 
3742 void G4OpenGLQtViewer::changeSearchSelection()
3743 {
3744   const QString& searchText = fFilterOutput->text();
3745   if (fSceneTreeComponentTreeWidget == NULL) {
3746     return;
3747   }
3748 
3749   // unselect all
3750   for (int a=0; a<fSceneTreeComponentTreeWidget->topLevelItemCount(); a++) {
3751     fSceneTreeComponentTreeWidget->topLevelItem(a)->setExpanded(false);
3752     fSceneTreeComponentTreeWidget->topLevelItem(a)->setSelected(false);
3753     clearSceneTreeSelection(fSceneTreeComponentTreeWidget->topLevelItem(a));
3754   }
3755 
3756   QList<QTreeWidgetItem *> itemList = fSceneTreeComponentTreeWidget->findItems (searchText,Qt::MatchContains | Qt::MatchRecursive,0);
3757 
3758   for (int i = 0; i < itemList.size(); ++i) {
3759     QTreeWidgetItem* expandParentItem = itemList.at(i);
3760     while (expandParentItem->parent() != NULL) {
3761       expandParentItem->parent()->setExpanded(true);
3762       expandParentItem = expandParentItem->parent();
3763     }
3764     itemList.at(i)->setSelected(true);
3765   }
3766 
3767 }
3768 
3769 
3770 void G4OpenGLQtViewer::clearSceneTreeSelection(QTreeWidgetItem* item) {
3771   for (int a=0; a<item->childCount(); a++) {
3772     item->child(a)->setSelected(false);
3773     item->child(a)->setExpanded(false);
3774     clearSceneTreeSelection(item->child(a));
3775   }
3776 
3777 }
3778 
3779 
3780 bool G4OpenGLQtViewer::isPVVolume(QTreeWidgetItem* item) {
3781   QTreeWidgetItem* sParent = item;
3782   while (sParent->parent() != NULL) {
3783     sParent = sParent->parent();
3784   }
3785   if (sParent->text(0) != fTouchableVolumes) {
3786     return false;
3787   }
3788   // item is the "Touchable" node
3789   if (item->text(0) == fTouchableVolumes) {
3790     return false;
3791   }
3792   return true;
3793 }
3794 
3795 
3796 void G4OpenGLQtViewer::changeDepthOnSceneTreeItem(
3797  double lookForDepth
3798  ,double currentDepth
3799  ,QTreeWidgetItem* item
3800 ) {
3801   double transparencyLevel = 0.;
3802 
3803   // look for a 2.2 depth and we are at level 3
3804   // -> Set all theses items to Opaque
3805   // ONLY if it is a PV volume !
3806   if (isPVVolume(item)) {
3807     if ((lookForDepth-currentDepth) < 0) {
3808       item->setCheckState(0,Qt::Checked);
3809       updatePositivePoIndexSceneTreeWidgetQuickMap(item->data(0,Qt::UserRole).toInt(),item);
3810       transparencyLevel = 1;
3811     } else if ((lookForDepth-currentDepth) > 1 ){
3812       item->setCheckState(0,Qt::Unchecked);
3813       updatePositivePoIndexSceneTreeWidgetQuickMap(item->data(0,Qt::UserRole).toInt(),item);
3814       transparencyLevel = 0;
3815     } else {
3816       item->setCheckState(0,Qt::Checked);
3817       updatePositivePoIndexSceneTreeWidgetQuickMap(item->data(0,Qt::UserRole).toInt(),item);
3818       transparencyLevel = 1-(lookForDepth-currentDepth);
3819     }
3820   }
3821 
3822   if (item->data(0,Qt::UserRole).toInt() >= 0) {
3823     const G4Colour& color = getColorForPoIndex(item->data(0,Qt::UserRole).toInt());
3824 
3825     // We are less depper (ex:tree depth:2) than lookForDepth (ex:3.1)
3826     // -> Have to hide this level ONLY if it was not hidden before
3827 
3828     // Not on a top level item case
3829     // Do not set if it was already set
3830 
3831     // Should add them all the time in case of an older command has change transparency
3832     // before. Should be checked in changeDepthInSceneTree for duplicated commands
3833     // Do not change transparency if not visible by humain (and avoid precision value
3834     // problems..)
3835     if (((color.GetAlpha()-transparencyLevel) >  0.000001) ||
3836         ((color.GetAlpha()-transparencyLevel) < -0.000001))  {
3837       if ((item->text(3) != "")) {
3838         // FIXME : Should not test this here because of transparent
3839         // volume that will came after and with a different alpha level
3840         // Good thing to do is to check and suppress doubles in changeDepthInSceneTree
3841         // and then check if last (transparents volumes) has to change alpha
3842 
3843         changeQColorForTreeWidgetItem(item,QColor((int)(color.GetRed()*255),
3844                                                   (int)(color.GetGreen()*255),
3845                                                   (int)(color.GetBlue()*255),
3846                                                   (int)(transparencyLevel*255)));
3847       }
3848     }
3849   }
3850 
3851   for (int b=0;b< item->childCount();b++) {
3852     changeDepthOnSceneTreeItem(lookForDepth,currentDepth+1,item->child(b));
3853   }
3854 }
3855 
3856 
3857 void G4OpenGLQtViewer::clearTreeWidget(){
3858   // be careful about calling this twice
3859 
3860   if (fSceneTreeComponentTreeWidget) {
3861 
3862     if (fSceneTreeComponentTreeWidget->topLevelItemCount () > 0) {
3863 
3864       fPVRootNodeCreate = false;
3865 
3866       // reset all old
3867       fOldPositivePoIndexSceneTreeWidgetQuickMap.clear();
3868       fOldNullPoIndexSceneTreeWidgetQuickVector.clear();
3869       fOldTreeItemModels.clear();
3870 
3871       // Clone everything
3872       for (int b =0; b <fSceneTreeComponentTreeWidget->topLevelItemCount();b++) {
3873         // All tree widgets are in :
3874         // then we could get the old POindex and get
3875         // .visible/Hidden
3876         // .Check/Uncheck
3877         // .selected
3878         // .colour status from std::map
3879 
3880         // clone top level items
3881         int poIndex = fSceneTreeComponentTreeWidget->topLevelItem(b)->data(0,Qt::UserRole).toInt();
3882         if (poIndex != -1) {
3883           fOldPositivePoIndexSceneTreeWidgetQuickMap.insert(std::pair <int, QTreeWidgetItem*> (poIndex,cloneWidgetItem(fSceneTreeComponentTreeWidget->topLevelItem(b))));
3884         } else {
3885           fOldNullPoIndexSceneTreeWidgetQuickVector.push_back(cloneWidgetItem(fSceneTreeComponentTreeWidget->topLevelItem(b)));
3886         }
3887 
3888         // clone leaves
3889         cloneSceneTree(fSceneTreeComponentTreeWidget->topLevelItem(b));
3890       }
3891       // delete all elements
3892 
3893       fOldTreeItemModels.insert(fTreeItemModels.begin(), fTreeItemModels.end());
3894 
3895       // all is copy, then clear scene tree
3896       int tmp2 = fSceneTreeComponentTreeWidget->topLevelItemCount();
3897       while (tmp2 > 0) {
3898         delete fSceneTreeComponentTreeWidget->takeTopLevelItem (0);
3899         tmp2 = fSceneTreeComponentTreeWidget->topLevelItemCount();
3900       }
3901       fPositivePoIndexSceneTreeWidgetQuickMap.clear();
3902 
3903       // put correct value in paramaters
3904       fOldLastSceneTreeWidgetAskForIterator = fOldPositivePoIndexSceneTreeWidgetQuickMap.begin();
3905       fOldLastSceneTreeWidgetAskForIteratorEnd = fOldPositivePoIndexSceneTreeWidgetQuickMap.end();
3906       fSceneTreeDepth = 1;
3907       fModelShortNameItem = NULL;
3908       fMaxPOindexInserted = -1;
3909 
3910     }
3911   }
3912 }
3913 
3914 
3915 /**
3916    Clone :
3917    - Open/close
3918    - Visible/hidden
3919    - Selected
3920 */
3921 QTreeWidgetItem * G4OpenGLQtViewer::cloneWidgetItem(QTreeWidgetItem* item) {
3922 
3923   QTreeWidgetItem* cloneItem = new QTreeWidgetItem();
3924 
3925   // Clone what is create createTreeWidgetItem step
3926 
3927   cloneItem->setText(0,item->text(0));
3928   cloneItem->setData(1,Qt::UserRole,item->data(1,Qt::UserRole).toInt());
3929   cloneItem->setText(2,item->text(2));
3930   cloneItem->setData(0, Qt::UserRole,item->data(0,Qt::UserRole).toInt());
3931   cloneItem->setText(3,item->text(3));
3932   cloneItem->setFlags(item->flags());
3933   cloneItem->setToolTip(0,item->toolTip(0));
3934   cloneItem->setCheckState(0,item->checkState(0));
3935   cloneItem->setSelected(item->isSelected());
3936   cloneItem->setExpanded(item->isExpanded ());
3937 
3938   cloneItem->setData(2,Qt::UserRole,item->data(2,Qt::UserRole).value<QColor>());
3939 
3940   return cloneItem;
3941 }
3942 
3943 
3944 /**
3945    Clone the current tree in order to get a snapshot of old version
3946 */
3947 void G4OpenGLQtViewer::cloneSceneTree(
3948  QTreeWidgetItem* rootItem
3949 ) {
3950 
3951   for (int b=0;b< rootItem->childCount();b++) {
3952 
3953     QTreeWidgetItem *child = rootItem->child(b);
3954 
3955     // clone top level items
3956     int poIndex = child->data(0,Qt::UserRole).toInt();
3957     if (poIndex != -1) {
3958       fOldPositivePoIndexSceneTreeWidgetQuickMap.insert(std::pair <int, QTreeWidgetItem*> (poIndex,cloneWidgetItem(child)));
3959     } else {
3960       fOldNullPoIndexSceneTreeWidgetQuickVector.push_back(cloneWidgetItem(child));
3961     }
3962     cloneSceneTree(child);
3963   }
3964 }
3965 
3966 
3967 /**
3968    Update the quick scene tree visibility map (used by parseAndCheckVisibility)
3969 */
3970  void G4OpenGLQtViewer::updatePositivePoIndexSceneTreeWidgetQuickMap(int POindex,QTreeWidgetItem* item) {
3971 
3972    // Check state
3973    std::map <int, QTreeWidgetItem*>::iterator i;
3974    i = fPositivePoIndexSceneTreeWidgetQuickMap.find(POindex);
3975 
3976    if (i == fPositivePoIndexSceneTreeWidgetQuickMap.end()) {
3977      fPositivePoIndexSceneTreeWidgetQuickMap.insert(std::pair <int, QTreeWidgetItem*> (POindex,item) );
3978      fLastSceneTreeWidgetAskForIterator = fPositivePoIndexSceneTreeWidgetQuickMap.end();
3979      fLastSceneTreeWidgetAskForIteratorEnd = fPositivePoIndexSceneTreeWidgetQuickMap.end();
3980    } else {
3981      i->second = item;
3982    }
3983  }
3984 
3985 
3986 
3987 void G4OpenGLQtViewer::changeQColorForTreeWidgetItem(QTreeWidgetItem* item,const QColor& qc) {
3988 
3989   int POIndex = item->data(0,Qt::UserRole).toInt();
3990   updatePositivePoIndexSceneTreeWidgetQuickMap(POIndex,item );
3991 
3992   QPixmap pixmap = QPixmap(QSize(16, 16));
3993   if (item->data(0,Qt::UserRole).toInt() != -1) {
3994     pixmap.fill (qc);
3995   } else {
3996     pixmap.fill (QColor(255,255,255,255));
3997   }
3998   QPainter painter(&pixmap);
3999   painter.setPen(Qt::black);
4000   painter.drawRect(0,0,15,15); // Draw contour
4001 
4002   item->setIcon(0,pixmap);
4003   item->setData(2,Qt::UserRole,qc);
4004 }
4005 
4006 
4007 
4008 /**
4009    @return the corresponding item if existing.
4010    Look into fPositivePoIndexSceneTreeWidgetQuickMap
4011  */
4012 QTreeWidgetItem* G4OpenGLQtViewer::getTreeWidgetItem(int POindex){
4013 
4014   // -1 is not a visible item
4015   if (POindex == -1) {
4016     return NULL;
4017   }
4018 
4019   if (fPositivePoIndexSceneTreeWidgetQuickMap.size() == 0){
4020     return NULL;
4021   }
4022 
4023   if (fLastSceneTreeWidgetAskForIterator != fLastSceneTreeWidgetAskForIteratorEnd) {
4024     if (POindex == fLastSceneTreeWidgetAskForIterator->first) {
4025       if (fLastSceneTreeWidgetAskForIterator->second != NULL) {
4026         return fLastSceneTreeWidgetAskForIterator->second;
4027       }
4028     }
4029   }
4030 
4031   // if not, use the "find" algorithm
4032   fLastSceneTreeWidgetAskForIterator = fPositivePoIndexSceneTreeWidgetQuickMap.find(POindex);
4033   fLastSceneTreeWidgetAskForIteratorEnd = fPositivePoIndexSceneTreeWidgetQuickMap.end();
4034 
4035   if (fLastSceneTreeWidgetAskForIterator != fPositivePoIndexSceneTreeWidgetQuickMap.end()) {
4036     return fLastSceneTreeWidgetAskForIterator->second;
4037   }
4038   return NULL;
4039 }
4040 
4041 /**
4042    @return the corresponding item if existing in the old tree
4043    Look into fOldPositivePoIndexSceneTreeWidgetQuickMap
4044  */
4045 QTreeWidgetItem* G4OpenGLQtViewer::getOldTreeWidgetItem(int POindex){
4046 
4047 
4048   // -1 is not a visible item
4049   if (POindex == -1) {
4050     return NULL;
4051   }
4052 
4053   if (fOldPositivePoIndexSceneTreeWidgetQuickMap.size() == 0){
4054     return NULL;
4055   }
4056 
4057   // Should be call only once by item addition
4058   // Prevent to get out the std::map
4059   if (fOldLastSceneTreeWidgetAskForIterator != fOldLastSceneTreeWidgetAskForIteratorEnd) {
4060     fOldLastSceneTreeWidgetAskForIterator++;
4061   }
4062 
4063   if (fOldLastSceneTreeWidgetAskForIterator != fOldPositivePoIndexSceneTreeWidgetQuickMap.end()) {
4064     if (POindex == fOldLastSceneTreeWidgetAskForIterator->first) {
4065       if (fOldLastSceneTreeWidgetAskForIterator->second != NULL) {
4066         return fOldLastSceneTreeWidgetAskForIterator->second;
4067       }
4068     }
4069   }
4070 
4071   // if not, use the "find" algorithm
4072   fOldLastSceneTreeWidgetAskForIterator = fOldPositivePoIndexSceneTreeWidgetQuickMap.find(POindex);
4073   fOldLastSceneTreeWidgetAskForIteratorEnd = fOldPositivePoIndexSceneTreeWidgetQuickMap.end();
4074 
4075   if (fOldLastSceneTreeWidgetAskForIterator != fOldPositivePoIndexSceneTreeWidgetQuickMap.end()) {
4076     return fOldLastSceneTreeWidgetAskForIterator->second;
4077   }
4078   return NULL;
4079 }
4080 
4081 
4082 
4083 /**
4084    Should replace actual tree by the one in this class
4085    and update tree
4086 */
4087 void G4OpenGLQtViewer::displaySceneTreeComponent() {
4088   // no UI
4089   if (fUISceneTreeWidget == NULL) {
4090     return;
4091   }
4092   if (fSceneTreeComponentTreeWidget == NULL) {
4093     return;
4094   }
4095 
4096   // sort tree items
4097   fSceneTreeComponentTreeWidget->sortItems (0, Qt::AscendingOrder );
4098 
4099   return;
4100 }
4101 
4102 
4103 /**
4104    Update the toolbar Icons/Mouse context menu
4105    - Change ortho/perspective
4106    - Change surface style
4107    - Change cursor style
4108  */
4109 void G4OpenGLQtViewer::updateToolbarAndMouseContextMenu(){
4110    if (fBatchMode) {
4111      return;
4112    }
4113 
4114   G4ViewParameters::DrawingStyle
4115   d_style = fVP.GetDrawingStyle();
4116 
4117   // Surface style
4118   if (d_style == G4ViewParameters::wireframe) {
4119     if (fUiQt) fUiQt->SetIconWireframeSelected();
4120     if (fContextMenu) {
4121       fDrawingWireframe->setChecked(true);
4122       fDrawingLineRemoval->setChecked(false);
4123       fDrawingSurfaceRemoval->setChecked(false);
4124       fDrawingLineSurfaceRemoval->setChecked(false);
4125     }
4126   } else if (d_style == G4ViewParameters::hlr) {
4127     if (fUiQt) fUiQt->SetIconHLRSelected();
4128     if (fContextMenu) {
4129       fDrawingLineRemoval->setChecked(true);
4130       fDrawingWireframe->setChecked(false);
4131       fDrawingSurfaceRemoval->setChecked(false);
4132       fDrawingLineSurfaceRemoval->setChecked(false);
4133     }
4134   } else if (d_style == G4ViewParameters::hsr) {
4135     if (fUiQt) fUiQt->SetIconSolidSelected();
4136     if (fContextMenu) {
4137       fDrawingSurfaceRemoval->setChecked(true);
4138       fDrawingWireframe->setChecked(false);
4139       fDrawingLineRemoval->setChecked(false);
4140       fDrawingLineSurfaceRemoval->setChecked(false);
4141     }
4142   } else if (d_style == G4ViewParameters::hlhsr) {
4143     if (fUiQt) fUiQt->SetIconHLHSRSelected();
4144     if (fContextMenu) {
4145       fDrawingLineSurfaceRemoval->setChecked(true);
4146       fDrawingWireframe->setChecked(false);
4147       fDrawingLineRemoval->setChecked(false);
4148       fDrawingSurfaceRemoval->setChecked(false);
4149       fDrawingLineSurfaceRemoval->setChecked(false);
4150     }
4151   }
4152 
4153 
4154   // projection style
4155   G4double d_proj = fVP.GetFieldHalfAngle () ;
4156   if (d_proj == 0.) { // ortho
4157     if (fUiQt) fUiQt->SetIconOrthoSelected();
4158     if (fContextMenu) {
4159       fProjectionOrtho->setChecked(true);
4160       fProjectionPerspective->setChecked(false);
4161     }
4162   } else {
4163     if (fUiQt) fUiQt->SetIconPerspectiveSelected();
4164       if (fContextMenu) {
4165         fProjectionPerspective->setChecked(true);
4166         fProjectionOrtho->setChecked(false);
4167       }
4168   }
4169 
4170 
4171   // mouse style : They are controlled by UI !
4172   if (fUiQt && fContextMenu) {
4173     if (fUiQt->IsIconPickSelected()) {
4174       fMousePickAction->setChecked(true);
4175       fMouseZoomOutAction->setChecked(false);
4176       fMouseZoomInAction->setChecked(false);
4177       fMouseRotateAction->setChecked(false);
4178       fMouseMoveAction->setChecked(false);
4179     } else if (fUiQt->IsIconZoomOutSelected()) {
4180       fMouseZoomOutAction->setChecked(true);
4181       fMousePickAction->setChecked(false);
4182       fMouseZoomInAction->setChecked(false);
4183       fMouseRotateAction->setChecked(false);
4184       fMouseMoveAction->setChecked(false);
4185     } else if (fUiQt->IsIconZoomInSelected()) {
4186       fMouseZoomInAction->setChecked(true);
4187       fMousePickAction->setChecked(false);
4188       fMouseZoomOutAction->setChecked(false);
4189       fMouseRotateAction->setChecked(false);
4190       fMouseMoveAction->setChecked(false);
4191     } else if (fUiQt->IsIconRotateSelected()) {
4192       fMouseRotateAction->setChecked(true);
4193       fMousePickAction->setChecked(false);
4194       fMouseZoomOutAction->setChecked(false);
4195       fMouseZoomInAction->setChecked(false);
4196       fMouseMoveAction->setChecked(false);
4197     } else if (fUiQt->IsIconMoveSelected()) {
4198       fMouseMoveAction->setChecked(true);
4199       fMousePickAction->setChecked(false);
4200       fMouseZoomOutAction->setChecked(false);
4201       fMouseZoomInAction->setChecked(false);
4202       fMouseRotateAction->setChecked(false);
4203     }
4204   }
4205 }
4206 
4207 
4208 /**
4209  Update the scene tree widget
4210  */
4211 void G4OpenGLQtViewer::updateSceneTreeWidget() {
4212   // Ensure case where closing a UI tab close the widget
4213   if (!fSceneTreeWidget) {
4214     createSceneTreeWidget();
4215   }
4216 }
4217 
4218 
4219   /**
4220  Update the viewer properties component widget
4221  Clear it only if the number of command is less than the previous table widget row count
4222  */
4223 void G4OpenGLQtViewer::updateViewerPropertiesTableWidget() {
4224 
4225   if (!isCurrentWidget()) {
4226     return;
4227   }
4228 
4229   // Ensure case where closing a UI tab close the widget
4230   if (!fViewerPropertiesTableWidget) {
4231     createViewerPropertiesWidget();
4232   }
4233   int treeWidgetInfosIgnoredCommands = 0;
4234   G4UImanager* UI = G4UImanager::GetUIpointer();
4235   G4UIcommandTree * commandTreeTop = UI->GetTree();
4236   G4UIcommandTree* path = commandTreeTop->FindCommandTree("/vis/viewer/set/");
4237 
4238   if (!path) {
4239     return;
4240   }
4241 
4242   // clear old table
4243   if ((path->GetCommandEntry()-fTreeWidgetInfosIgnoredCommands) != fViewerPropertiesTableWidget->rowCount()) {
4244     fViewerPropertiesTableWidget->clear();
4245   }
4246 
4247   fViewerPropertiesTableWidget->blockSignals(true);
4248   // TODO : Could be optimized by comparing current command to old commands. That should not change so much
4249 
4250   fViewerPropertiesTableWidget->setColumnCount (2);
4251   fViewerPropertiesTableWidget->setRowCount (path->GetCommandEntry()-fTreeWidgetInfosIgnoredCommands);
4252   fViewerPropertiesTableWidget->setHorizontalHeaderLabels(QStringList() << tr("Property")
4253                                                         << tr("Value"));
4254   fViewerPropertiesTableWidget->verticalHeader()->setVisible(false);
4255   fViewerPropertiesTableWidget->setAlternatingRowColors (true);
4256 
4257   // For the moment, we do only command that have a "set" command in UI
4258 
4259   for (int a=0;a<path->GetCommandEntry();a++) {
4260     G4UIcommand* commandTmp = path->GetCommand(a+1);
4261 
4262     // get current parameters
4263     QString params = "";
4264 
4265     if(commandTmp->GetCommandName() == "autoRefresh") {
4266       if (fVP.IsAutoRefresh()) {
4267         params = "True";
4268       } else {
4269         params = "False";
4270       }
4271     } else if(commandTmp->GetCommandName() == "auxiliaryEdge") {
4272       if (fVP.IsAuxEdgeVisible()) {
4273         params = "True";
4274       } else {
4275         params = "False";
4276       }
4277     } else if(commandTmp->GetCommandName() == "background") {
4278       params = QString().number(fVP.GetBackgroundColour().GetRed()) + "  "+
4279       QString().number(fVP.GetBackgroundColour().GetGreen()) + "  "+
4280       QString().number(fVP.GetBackgroundColour().GetBlue()) + "  "+
4281       QString().number(fVP.GetBackgroundColour().GetAlpha());
4282 
4283     } else if(commandTmp->GetCommandName() == "culling") {
4284       params = QString().number(fVP. IsCulling ());
4285     } else if(commandTmp->GetCommandName() == "cutawayMode") {
4286       if (fVP.GetCutawayMode() == G4ViewParameters::cutawayUnion) {
4287         params = "union";
4288       } else {
4289         params = "intersection";
4290       }
4291 
4292     } else if(commandTmp->GetCommandName() == "defaultColour") {
4293       params = QString().number(fVP.GetDefaultVisAttributes()->GetColor().GetRed()) + "  "+
4294       QString().number(fVP.GetDefaultVisAttributes()->GetColor().GetGreen()) + "  "+
4295       QString().number(fVP.GetDefaultVisAttributes()->GetColor().GetBlue()) + "  "+
4296       QString().number(fVP.GetDefaultVisAttributes()->GetColor().GetAlpha());
4297 
4298     } else if(commandTmp->GetCommandName() == "defaultTextColour") {
4299       params = QString().number(fVP.GetDefaultTextVisAttributes()->GetColor().GetRed()) + "  "+
4300       QString().number(fVP.GetDefaultTextVisAttributes()->GetColor().GetGreen()) + "  "+
4301       QString().number(fVP.GetDefaultTextVisAttributes()->GetColor().GetBlue()) + "  "+
4302       QString().number(fVP.GetDefaultTextVisAttributes()->GetColor().GetAlpha());
4303 
4304     } else if(commandTmp->GetCommandName() == "edge") {
4305       G4ViewParameters::DrawingStyle existingStyle = fVP.GetDrawingStyle();
4306       params = "False";
4307       if (existingStyle == G4ViewParameters::hsr) {
4308         params = "True";
4309       }
4310 
4311     } else if(commandTmp->GetCommandName() == "explodeFactor") {
4312       params = QString().number(fVP.GetExplodeFactor()) +  "  " + QString(G4String(G4BestUnit(fVP.GetExplodeFactor(),"Length")).data());
4313 
4314     } else if(commandTmp->GetCommandName() == "globalLineWidthScale") {
4315       params = QString().number(fVP.GetGlobalLineWidthScale());
4316 
4317     } else if(commandTmp->GetCommandName() == "globalMarkerScale") {
4318       params = QString().number(fVP.GetGlobalMarkerScale());
4319 
4320     } else if(commandTmp->GetCommandName() == "hiddenEdge") {
4321       G4ViewParameters::DrawingStyle style = fVP.GetDrawingStyle();
4322       if ((style == G4ViewParameters::hlr) ||
4323           (style == G4ViewParameters::hlhsr)) {
4324         params = "True";
4325       } else {
4326         params = "False";
4327       }
4328 
4329     } else if(commandTmp->GetCommandName() == "hiddenMarker") {
4330       if (fVP.IsMarkerNotHidden()) {
4331         params = "False";
4332       } else {
4333         params = "True";
4334       }
4335 
4336     } else if(commandTmp->GetCommandName() == "lightsMove") {
4337       if (fVP.GetLightsMoveWithCamera()) {
4338         params = "camera";
4339       } else {
4340         params = "object";
4341       }
4342     } else if(commandTmp->GetCommandName() == "lightsThetaPhi") {
4343       G4Vector3D direction = fVP.GetLightpointDirection();
4344       // degree
4345       params = QString().number(direction.theta()/CLHEP::degree)+ "  "+ QString().number(direction.phi()/CLHEP::degree)+"  deg";
4346       if (commandTmp->GetParameterEntries() == 3) {
4347         if (commandTmp->GetParameter(2)->GetDefaultValue() != "deg") {
4348           params = QString().number(direction.theta())+ "  "+ QString().number(direction.phi())+" "+commandTmp->GetParameter(2)->GetDefaultValue().data();
4349         }
4350       }
4351     } else if(commandTmp->GetCommandName() == "lightsVector") {
4352      params = QString().number(fVP.GetLightpointDirection().x()) + "  "+
4353       QString().number(fVP.GetLightpointDirection().y()) + "  "+
4354       QString().number(fVP.GetLightpointDirection().z());
4355 
4356     } else if(commandTmp->GetCommandName() == "lineSegmentsPerCircle") {
4357       params = QString().number(fVP.GetNoOfSides());
4358 
4359     } else if(commandTmp->GetCommandName() == "picking") {
4360       if (fVP.IsPicking()) {
4361         params = "True";
4362       } else {
4363         params = "False";
4364       }
4365 
4366     } else if(commandTmp->GetCommandName() == "projection") {
4367       if (fVP.GetFieldHalfAngle() == 0.) {
4368         params = "orthogonal";
4369       } else {
4370         params = QString("perspective ") + QString().number(fVP.GetFieldHalfAngle()/CLHEP::degree) + " deg";
4371       }
4372 
4373     } else if(commandTmp->GetCommandName() == "rotationStyle") {
4374       if (fVP.GetRotationStyle() == G4ViewParameters::constrainUpDirection) {
4375         params = "constrainUpDirection";
4376       } else {
4377         params = "freeRotation";
4378       }
4379 
4380     } else if(commandTmp->GetCommandName() == "sectionPlane") {
4381       if (fVP.IsSection()) {
4382         params =  QString("on ") +
4383         G4String(G4BestUnit(fVP.GetSectionPlane().point(),"Length")).data()+
4384         QString().number(fVP.GetSectionPlane().normal().x())
4385         + " " + QString().number(fVP.GetSectionPlane().normal().y())
4386         + " " + QString().number(fVP.GetSectionPlane().normal().z());
4387       } else {
4388         params = "off";
4389       }
4390 
4391     } else if(commandTmp->GetCommandName() == "style") {
4392       if (fVP.GetDrawingStyle() == G4ViewParameters::wireframe || fVP.GetDrawingStyle() == G4ViewParameters::hlr) {
4393          params = "wireframe";
4394       } else {
4395         params = "surface";
4396       }
4397 
4398 
4399     } else if(commandTmp->GetCommandName() == "targetPoint") {
4400       G4Point3D point = fVP.GetCurrentTargetPoint();
4401       if (fSceneHandler.GetScene()) {
4402         G4String b = G4BestUnit(fSceneHandler.GetScene()->GetStandardTargetPoint() + fVP.GetCurrentTargetPoint(),"Length");
4403         params = b.data();
4404       }
4405     } else if(commandTmp->GetCommandName() == "upThetaPhi") {
4406       G4Vector3D up = fVP.GetUpVector();
4407       // degree
4408       params = QString().number(up.theta()/CLHEP::degree)+ "  "+ QString().number(up.phi()/CLHEP::degree)+"  deg";
4409       if (commandTmp->GetParameterEntries() == 3) {
4410         if (commandTmp->GetParameter(2)->GetDefaultValue() != "deg") {
4411           params = QString().number(up.theta())+ "  "+ QString().number(up.phi())+" "+commandTmp->GetParameter(2)->GetDefaultValue().data();
4412         }
4413       }
4414     } else if(commandTmp->GetCommandName() == "upVector") {
4415       G4Vector3D up = fVP.GetUpVector();
4416       params = QString().number(up.x())+ "  "+ QString().number(up.y())+"  "+QString().number(up.z())+ "  ";
4417 
4418     } else if(commandTmp->GetCommandName() == "viewpointThetaPhi") {
4419       G4Vector3D direction = fVP.GetViewpointDirection();
4420       // degree
4421       params = QString().number(direction.theta()/CLHEP::degree)+ "  "+ QString().number(direction.phi()/CLHEP::degree)+"  deg";
4422       if (commandTmp->GetParameterEntries() == 3) {
4423         if (commandTmp->GetParameter(2)->GetDefaultValue() != "deg") {
4424           params = QString().number(direction.theta())+ "  "+ QString().number(direction.phi())+" "+commandTmp->GetParameter(2)->GetDefaultValue().data();
4425         }
4426       }
4427     } else if(commandTmp->GetCommandName() == "viewpointVector") {
4428       G4Vector3D direction = fVP.GetViewpointDirection();
4429       params = QString().number(direction.x())+ "  "+ QString().number(direction.y())+" "+QString().number(direction.z());
4430     } else {
4431       // No help
4432     }
4433 
4434     /* DO NOT DISPLAY COMMANDS WITHOUT ANY PARAMETERS SET
4435     if (params == "") {
4436       // TODO : display default parameters // should not be editable ?
4437 
4438       for( G4int i_thParameter=0; i_thParameter<commandTmp->GetParameterEntries(); i_thParameter++ ) {
4439         commandParam = commandTmp->GetParameter(i_thParameter);
4440 
4441         if (QString(QChar(commandParam->GetParameterType())) == "b") {
4442           if (commandParam->GetDefaultValue().data()) {
4443             params += "True";
4444           } else {
4445             params += "False";
4446           }
4447         } else {
4448           params += QString((char*)(commandParam->GetDefaultValue()).data());
4449         }
4450         if (i_thParameter<commandTmp->GetParameterEntries()-1) {
4451           params += " ";
4452         }
4453       }
4454     }
4455     */
4456 
4457     if (params != "") {
4458 
4459       QTableWidgetItem *nameItem;
4460       QTableWidgetItem *paramItem;
4461 
4462       // already present ?
4463       QList<QTableWidgetItem *> list = fViewerPropertiesTableWidget->findItems (commandTmp->GetCommandName().data(),Qt::MatchExactly);
4464       if (list.size() == 1) {
4465         nameItem = list.first();
4466         paramItem = fViewerPropertiesTableWidget->item(nameItem->row(),1);
4467 
4468       } else {
4469         nameItem = new QTableWidgetItem();
4470         paramItem = new QTableWidgetItem();
4471         fViewerPropertiesTableWidget->setItem(a-treeWidgetInfosIgnoredCommands, 0, nameItem);
4472         fViewerPropertiesTableWidget->setItem(a-treeWidgetInfosIgnoredCommands, 1, paramItem);
4473 
4474         // Set Guidance
4475         QString guidance;
4476         G4int n_guidanceEntry = (G4int)commandTmp->GetGuidanceEntries();
4477         for( G4int i_thGuidance=0; i_thGuidance < n_guidanceEntry; i_thGuidance++ ) {
4478           guidance += QString((char*)(commandTmp->GetGuidanceLine(i_thGuidance)).data()) + "\n";
4479         }
4480 
4481         nameItem->setToolTip(guidance);
4482         paramItem->setToolTip(GetCommandParameterList(commandTmp));
4483 
4484         fViewerPropertiesTableWidget->setRowHeight(a-treeWidgetInfosIgnoredCommands,15);
4485       }
4486 
4487       // set current name and parameters
4488       nameItem->setText(commandTmp->GetCommandName().data());
4489       paramItem->setText(params);
4490 
4491       nameItem->setFlags(Qt::NoItemFlags);
4492       nameItem->setForeground(QBrush());
4493 
4494     } else {
4495       treeWidgetInfosIgnoredCommands++;
4496     }
4497   }
4498   // remove empty content row
4499   for (int i=0; i<treeWidgetInfosIgnoredCommands; i++) {
4500     fViewerPropertiesTableWidget->removeRow (fViewerPropertiesTableWidget->rowCount() - 1);
4501   }
4502 
4503   // The resize should done only at creation
4504   if (!fViewerPropertiesTableWidgetIsInit) {
4505     fViewerPropertiesTableWidgetIsInit = true;
4506 
4507     fViewerPropertiesTableWidget->resizeColumnsToContents();
4508 
4509     int x = fViewerPropertiesTableWidget->horizontalHeader()->length();
4510     int y = fViewerPropertiesTableWidget->verticalHeader()->length()+ fViewerPropertiesTableWidget->horizontalHeader()->sizeHint().height() + 2;
4511 
4512     //  fViewerPropertiesTableWidget->verticalHeader()->setResizeMode(QHeaderView::ResizeToContents);
4513     //  fViewerPropertiesTableWidget->horizontalHeader()->setResizeMode(QHeaderView::ResizeToContents);
4514 
4515     // resize to fit content
4516     QDialog* dial = static_cast<QDialog*> (fUIViewerPropertiesWidget->parent());
4517     if (dial) {
4518       dial->resize(x+56,y+46); // more or less (margins) ...
4519     }
4520   }
4521   fViewerPropertiesTableWidget->blockSignals(false);
4522 
4523   fTreeWidgetInfosIgnoredCommands = treeWidgetInfosIgnoredCommands;
4524 }
4525 
4526 
4527 /**
4528  Update the pick infos component widget
4529  */
4530 void G4OpenGLQtViewer::updatePickInfosWidget(int aX, int aY) {
4531   fLastPickPoint = QPoint(aX,aY);
4532 
4533   if (!isCurrentWidget()) {
4534     return;
4535   }
4536   // Ensure case where closing a UI tab close the widget
4537   if (!fPickInfosWidget) {
4538     createPickInfosWidget();
4539   }
4540 
4541 #if QT_VERSION < 0x060000
4542 #else
4543  {auto* qGLW = dynamic_cast<G4QGLWidgetType*> (fGLWidget) ;
4544   if (qGLW) qGLW->makeCurrent();}
4545   ResizeGLView();
4546 #endif
4547   const std::vector < G4OpenGLViewerPickMap* > & pickMapVector = GetPickDetails(aX,aY);
4548 
4549   // remove all previous widgets
4550   if (fPickInfosWidget) {
4551     QLayoutItem * wItem;
4552     if (fPickInfosWidget->layout()->count()) {
4553       while ((wItem = fPickInfosWidget->layout()->takeAt(0)) != 0) {
4554         delete wItem->widget();
4555         delete wItem;
4556       }
4557     }
4558   } else {
4559     // Ensure case where closing a UI tab close the widget
4560     if (!fPickInfosWidget) {
4561       createPickInfosWidget();
4562     }
4563   }
4564 
4565   // parse all pick results
4566   G4int nPickedObjectsWithAttributes = 0;
4567   for (unsigned int a=0; a< pickMapVector.size(); a++) {
4568     const auto& pickMap = pickMapVector[a];
4569     // Add a box inside the pick viewer box
4570     std::ostringstream label;
4571     std::ostringstream content;
4572     std::string txt = pickMap->getAttributes()[0].data();
4573     if (pickMapVector[a]->getAttributes().size()) {
4574       ++nPickedObjectsWithAttributes;
4575 
4576       std::size_t pos1 = txt.find(':');
4577       std::string storeKey = txt.substr(0,pos1);
4578 
4579       if (storeKey == "G4PhysicalVolumeModel") {
4580 
4581         label << "Volume:";
4582         std::size_t pos2 = txt.find(':',pos1+1);
4583         std::size_t pos3 = txt.find('\n',pos2+1);
4584         label << txt.substr(pos2+1,pos3-pos2-1);
4585 
4586       } else if (storeKey == "G4TrajectoriesModel") {
4587 
4588         label << "Trajectory:";
4589         std::size_t pos2 = txt.find(':',pos1+1);
4590         std::size_t pos3 = txt.find('\n',pos2+1);
4591         label << " Run:" << txt.substr(pos2+1,pos3-pos2-1);
4592         std::size_t pos4 = txt.find(':',pos3+1);
4593         std::size_t pos5 = txt.find('\n',pos4+1);
4594         label << ", Event:" << txt.substr(pos4+1,pos5-pos4-1);
4595 
4596       } else {
4597 
4598         label << "Hit number:" << a << ", PickName: " << pickMap->getPickName();
4599 
4600       }
4601 
4602       // Accumulate all content with the same pickname
4603       content << pickMap->print().data();
4604       G4int thisPickName = pickMap->getPickName();
4605       while (++a < pickMapVector.size()) {
4606         const auto& a_pickMap = pickMapVector[a];
4607         if (a_pickMap->getPickName() == thisPickName) {
4608           content << a_pickMap->print().data();
4609         } else {
4610           a--;
4611           break;
4612         }
4613       }
4614 
4615       QPushButton* pickCoutButton = new QPushButton(label.str().c_str());
4616       pickCoutButton->setStyleSheet ("text-align: left; padding: 1px; border: 0px;");
4617       pickCoutButton->setIcon(*fTreeIconClosed);
4618       fPickInfosWidget->layout()->addWidget(pickCoutButton);
4619 
4620       QStringList newStr;
4621 
4622       // Add to stringList
4623       newStr = QStringList(QString(content.str().c_str()).trimmed());
4624 
4625       QTextEdit* ed = new QTextEdit();
4626       ed->setReadOnly(true);
4627       fPickInfosWidget->layout()->addWidget(ed);
4628       ed->setVisible((false));
4629       ed->append(newStr.join(""));
4630 
4631       std::cout << pickCoutButton->text().toStdString() << " "<< fPickInfosWidget->layout()->count()-1<< std::endl;
4632       int tmp = fPickInfosWidget->layout()->count()-1;
4633       connect(pickCoutButton, &QPushButton::clicked , [this, tmp](){ this->toggleSceneTreeComponentPickingCout(tmp);});
4634     }
4635   }
4636 
4637   // add a label to push everything up!
4638   QLabel * pushUp = new QLabel("");
4639   QSizePolicy vPolicy = QSizePolicy(QSizePolicy::Minimum,QSizePolicy::Minimum);
4640   vPolicy.setVerticalStretch(10);
4641   pushUp->setSizePolicy(vPolicy);
4642   fPickInfosWidget->layout()->addWidget(pushUp);
4643 
4644   // highlight the first one :
4645 
4646   // first un-highlight the last selected
4647   changeColorAndTransparency(fLastHighlightName,fLastHighlightColor);
4648 
4649   if (pickMapVector.size() > 0 ) {
4650     // get the new one
4651     fLastHighlightName = pickMapVector[0]->getPickName();
4652     fLastHighlightColor = getColorForPoIndex(fLastHighlightName);
4653     // set the new one
4654     changeColorAndTransparency(fLastHighlightName,G4Color(1,1,1,1));
4655 
4656     updateQWidget();
4657   }
4658   QDialog* dial = static_cast<QDialog*> (fUIPickInfosWidget->parent());
4659   if (dial) {
4660     // change name
4661     std::ostringstream oss;
4662     if (nPickedObjectsWithAttributes == 0) {
4663       oss << "No object";
4664     } else if (nPickedObjectsWithAttributes == 1) {
4665       oss << "1 object";
4666     } else {
4667       oss << nPickedObjectsWithAttributes << " objects";
4668     }
4669     oss << " selected - " << GetName();
4670     dial->setWindowTitle(oss.str().c_str());
4671   }
4672   // set picking cout visible
4673   fPickInfosScrollArea->setVisible(true);
4674 }
4675 
4676 
4677 void G4OpenGLQtViewer::toggleSceneTreeComponentPickingCout(int pickItem) {
4678 
4679   QWidget* w;
4680   // close other items, it could take too much space
4681 
4682   for (int a=0; a<fPickInfosWidget->layout()->count(); a++) {
4683     w = fPickInfosWidget->layout()->itemAt(a)->widget();
4684     QTextEdit* ed = dynamic_cast<QTextEdit*>(w);
4685     QPushButton* button;
4686     if (ed) {
4687       if (a == pickItem) {
4688         w->setVisible(!w->isVisible());
4689       } else {
4690         w->setVisible(false);
4691       }
4692       if (a >= 1) {
4693         button = dynamic_cast<QPushButton*>(fPickInfosWidget->layout()->itemAt(a-1)->widget());
4694         if (button) {
4695           if (button->isVisible()) {
4696             button->setIcon(*fTreeIconOpen);
4697           } else {
4698             button->setIcon(*fTreeIconClosed);
4699           }
4700         }
4701       }
4702     }
4703   }
4704 }
4705 
4706 
4707 void G4OpenGLQtViewer::currentTabActivated(int currentTab) {
4708   if (fUiQt->GetViewerTabWidget()->tabText(currentTab) == GetName().data())  {
4709     createViewerPropertiesWidget();
4710 //    createPickInfosWidget();  // Causes a /vis/set/touchable command to do with...
4711 //    createSceneTreeWidget();  // ...this old scene tree widget (no longer used)
4712   }
4713 }
4714 
4715 
4716 void G4OpenGLQtViewer::tableWidgetViewerSetItemChanged(QTableWidgetItem * item) {
4717   G4UImanager* UI = G4UImanager::GetUIpointer();
4718   if(UI != NULL)  {
4719     QTableWidgetItem* previous = fViewerPropertiesTableWidget->item(fViewerPropertiesTableWidget->row(item),0);
4720     if (previous) {
4721       fViewerPropertiesTableWidget->blockSignals(true);
4722       UI->ApplyCommand((std::string("/vis/viewer/set/")
4723                         + previous->text().toStdString()
4724                         + " "
4725                         + item->text().toStdString()).c_str());
4726       fViewerPropertiesTableWidget->blockSignals(false);
4727     }
4728   }
4729 }
4730 
4731 bool G4OpenGLQtViewer::isCurrentWidget(){
4732   G4Qt* interactorManager = G4Qt::getInstance ();
4733   if (!interactorManager->IsExternalApp()) {
4734 
4735     // Prevent from repainting a hidden tab (the current tab name has to be the one of th GL viewer)
4736     if ( GetName() != fUiQt->GetViewerTabWidget()->tabText(fUiQt->GetViewerTabWidget()->currentIndex()).toStdString().c_str()) {
4737       return false;
4738     }
4739   }
4740   return true;
4741 }
4742 
4743 /**   Build the parameter list parameters in a QString<br>
4744  Reimplement partialy the G4UIparameter.cc
4745  @param aCommand : command to list parameters
4746  @see G4UIparameter::List()
4747  @see G4UIcommand::List()
4748  @return the command list parameters, or "" if nothing
4749  */
4750 QString G4OpenGLQtViewer::GetCommandParameterList (
4751                                   const G4UIcommand *aCommand
4752                                   )
4753 {
4754   G4int n_parameterEntry = (G4int)aCommand->GetParameterEntries();
4755   QString txt;
4756 
4757   if( n_parameterEntry > 0 ) {
4758     G4UIparameter *param;
4759 
4760     // Re-implementation of G4UIparameter.cc
4761 
4762     for( G4int i_thParameter=0; i_thParameter<n_parameterEntry; i_thParameter++ ) {
4763       param = aCommand->GetParameter(i_thParameter);
4764       txt += "\nParameter : " + QString((char*)(param->GetParameterName()).data()) + "\n";
4765       if( ! param->GetParameterGuidance().empty() )
4766         txt += QString((char*)(param->GetParameterGuidance()).data())+ "\n" ;
4767       txt += " Parameter type  : " + QString(QChar(param->GetParameterType())) + "\n";
4768       if(param->IsOmittable()){
4769         txt += " Omittable       : True\n";
4770       } else {
4771         txt += " Omittable       : False\n";
4772       }
4773       if( param->GetCurrentAsDefault() ) {
4774         txt += " Default value   : taken from the current value\n";
4775       } else if( ! param->GetDefaultValue().empty() ) {
4776         txt += " Default value   : " + QString((char*)(param->GetDefaultValue()).data())+ "\n";
4777       }
4778       if( ! param->GetParameterRange().empty() ) {
4779         txt += " Parameter range : " + QString((char*)(param->GetParameterRange()).data())+ "\n";
4780       }
4781       if( ! param->GetParameterCandidates().empty() ) {
4782         txt += " Candidates      : " + QString((char*)(param->GetParameterCandidates()).data())+ "\n";
4783       }
4784     }
4785   }
4786   return txt;
4787 }
4788 
4789 #ifdef G4MULTITHREADED
4790 
4791 namespace {
4792   G4Mutex visSubThreadMutex = G4MUTEX_INITIALIZER;
4793   G4Condition waitForVisSubThreadInitialized = G4CONDITION_INITIALIZER;
4794   G4bool visSubThreadEstablished = false;
4795   G4bool qObjectsSwitched = false;
4796   G4QGLWidgetType* qGLWidget = nullptr;
4797 }
4798 
4799 void G4OpenGLQtViewer::DoneWithMasterThread()
4800 {
4801   // Called by Main Thread !
4802 
4803   // Initialise and check qGLWidget - no need to check in subsequent functions
4804   qGLWidget = dynamic_cast<G4QGLWidgetType*>(fGLWidget);
4805   if (qGLWidget == nullptr) return;
4806 
4807   // Done with master thread
4808   qGLWidget->doneCurrent();
4809 
4810   // Set current QThread for the way back
4811   fQGLContextMainThread = QThread::currentThread();
4812 }
4813 
4814 void G4OpenGLQtViewer::MovingToVisSubThread()
4815 {
4816   // Still on master thread but vis thread has been launched
4817 
4818   if (qGLWidget == nullptr) return;
4819 
4820   // Wait until SwitchToVisSubThread has found vis sub-thread QThread
4821   {
4822   G4AutoLock lock(&visSubThreadMutex);
4823   G4CONDITIONWAITLAMBDA(&waitForVisSubThreadInitialized, &lock, []{return visSubThreadEstablished;})
4824   }
4825 
4826   // Move stuff to sub-thread
4827   if(qGLWidget->context()) qGLWidget->context()->moveToThread(fQGLContextVisSubThread);
4828 
4829   // Inform sub-thread
4830   G4AutoLock lock(&visSubThreadMutex);
4831   qObjectsSwitched = true;
4832   lock.unlock();
4833   G4CONDITIONBROADCAST(&waitForVisSubThreadInitialized);
4834 }
4835 
4836 void G4OpenGLQtViewer::SwitchToVisSubThread()
4837 {
4838   // Called by VisSub Thread !
4839 
4840   if (qGLWidget == nullptr) return;
4841 
4842   // Set the current QThread to its static variable
4843   fQGLContextVisSubThread = QThread::currentThread();
4844 
4845   // Let MovingToVisSubThread know we have the QThread
4846   {
4847   G4AutoLock lock(&visSubThreadMutex);
4848   visSubThreadEstablished = true;
4849   G4CONDITIONBROADCAST(&waitForVisSubThreadInitialized);
4850   }
4851 
4852   // Wait until MovingToVisSubThread has moved stuff
4853   {
4854   G4AutoLock lock(&visSubThreadMutex);
4855   G4CONDITIONWAITLAMBDA(&waitForVisSubThreadInitialized, &lock, []{return qObjectsSwitched;})
4856   }
4857 
4858   // make context current
4859   qGLWidget->makeCurrent();
4860 }
4861 
4862 void G4OpenGLQtViewer::DoneWithVisSubThread()
4863 {
4864   // Called by vis sub thread
4865 
4866   if (qGLWidget == nullptr) return;
4867 
4868   // finish with this vis sub thread context
4869   qGLWidget->doneCurrent();
4870 
4871   // and move stuff back to the main thread
4872   if(qGLWidget->context()) qGLWidget->context()->moveToThread(fQGLContextMainThread);
4873 }
4874 
4875 void G4OpenGLQtViewer::SwitchToMasterThread()
4876 {
4877   // Called by master Thread !
4878 
4879   if (qGLWidget == nullptr) return;
4880 
4881   qGLWidget->makeCurrent();
4882 
4883   visSubThreadEstablished = false;
4884   qObjectsSwitched = false;
4885 }
4886 
4887 #endif
4888 
4889 
4890 /*
4891 
4892 void MultiLayer::exportToSVG(const QString& fname)
4893 {
4894 QPicture picture;
4895 QPainter p(&picture);
4896 for (int i=0;i<(int)graphsList->count();i++)
4897 {
4898 Graph *gr=(Graph *)graphsList->at(i);
4899 Plot *myPlot= (Plot *)gr->plotWidget();
4900 
4901 QPoint pos=gr->pos();
4902 
4903 int width=int(myPlot->frameGeometry().width());
4904 int height=int(myPlot->frameGeometry().height());
4905 
4906 myPlot->print(&p, QRect(pos,QSize(width,height)));
4907 }
4908 
4909 p.end();
4910 picture.save(fname, "svg");
4911 }
4912 */
4913