From 67c2f20c00d541e26739a71b1a34a436be014750 Mon Sep 17 00:00:00 2001 From: Paul Baksic Date: Fri, 20 Dec 2024 11:51:03 +0100 Subject: [PATCH 1/5] Add possibility to fetch and add by default for external plugins and set SofaGLFW as ON by default --- Sofa/framework/Config/cmake/SofaMacrosConfigure.cmake | 8 +++++++- applications/projects/CMakeLists.txt | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Sofa/framework/Config/cmake/SofaMacrosConfigure.cmake b/Sofa/framework/Config/cmake/SofaMacrosConfigure.cmake index 47574524727..0fa3ac323ae 100644 --- a/Sofa/framework/Config/cmake/SofaMacrosConfigure.cmake +++ b/Sofa/framework/Config/cmake/SofaMacrosConfigure.cmake @@ -294,7 +294,13 @@ macro(sofa_add_subdirectory type directory name) endif() set(default_value OFF) - if(${ARGV3}) + if(ARG_EXTERNAL) + set(input_value ${ARGV6}) + else() + set(input_value ${ARGV3}) + endif () + + if(${input_value}) set(default_value ON) endif() diff --git a/applications/projects/CMakeLists.txt b/applications/projects/CMakeLists.txt index 0c85f91879a..82a9f133288 100644 --- a/applications/projects/CMakeLists.txt +++ b/applications/projects/CMakeLists.txt @@ -14,6 +14,6 @@ sofa_add_subdirectory(application runSofa runSofa ON) sofa_add_subdirectory(application sofaOPENCL sofaOPENCL OFF) sofa_add_subdirectory(directory Regression Regression EXTERNAL GIT_REF master) -sofa_add_subdirectory(directory SofaGLFW SofaGLFW EXTERNAL GIT_REF master) +sofa_add_subdirectory(directory SofaGLFW SofaGLFW EXTERNAL GIT_REF master ON) sofa_add_subdirectory(application sofaProjectExample sofaProjectExample) sofa_add_subdirectory(application sofaInfo sofaInfo) From 52369f895a345a9007f36f1bbb0dc464f2d5b3cb Mon Sep 17 00:00:00 2001 From: Paul Baksic Date: Fri, 20 Dec 2024 11:56:13 +0100 Subject: [PATCH 2/5] Remove Qt-based GUI from sources --- Sofa/GUI/CMakeLists.txt | 2 +- Sofa/GUI/Qt/CMakeLists.txt | 406 --- Sofa/GUI/Qt/LICENSE.GPL.txt | 339 --- Sofa/GUI/Qt/Sofa.GUI.QtConfig.cmake.in | 62 - Sofa/GUI/Qt/etc/Sofa.GUI.Qt.ini.in | 2 - Sofa/GUI/Qt/src/sofa/gui/qt/AboutDialog.ui | 209 -- .../Qt/src/sofa/gui/qt/AboutSOFADialog.cpp | 55 - Sofa/GUI/Qt/src/sofa/gui/qt/AboutSOFADialog.h | 41 - .../Qt/src/sofa/gui/qt/BaseGenGraphForm.ui | 1065 -------- .../Qt/src/sofa/gui/qt/DataFilenameWidget.cpp | 99 - .../Qt/src/sofa/gui/qt/DataFilenameWidget.h | 59 - Sofa/GUI/Qt/src/sofa/gui/qt/DataWidget.cpp | 308 --- Sofa/GUI/Qt/src/sofa/gui/qt/DataWidget.h | 311 --- .../GUI/Qt/src/sofa/gui/qt/DialogAddObject.ui | 235 -- .../sofa/gui/qt/DisplayFlagsDataWidget.cpp | 220 -- .../src/sofa/gui/qt/DisplayFlagsDataWidget.h | 112 - .../Qt/src/sofa/gui/qt/Doc_Viewer/index.html | 284 -- .../gui/qt/Doc_Viewer/index_fichiers/GUI.png | Bin 8483 -> 0 bytes .../index_fichiers/GUI_caption0.png | Bin 69466 -> 0 bytes .../index_fichiers/GUI_caption1.png | Bin 154800 -> 0 bytes .../Doc_Viewer/index_fichiers/Tab_graph.png | Bin 7561 -> 0 bytes .../qt/Doc_Viewer/index_fichiers/Tab_show.png | Bin 9293 -> 0 bytes .../Doc_Viewer/index_fichiers/Tab_stats.png | Bin 6197 -> 0 bytes .../Doc_Viewer/index_fichiers/VIewerOGRE.png | Bin 320324 -> 0 bytes .../qt/Doc_Viewer/index_fichiers/Viewer.png | Bin 10961 -> 0 bytes .../GUI/Qt/src/sofa/gui/qt/FileManagement.cpp | 120 - Sofa/GUI/Qt/src/sofa/gui/qt/FileManagement.h | 42 - Sofa/GUI/Qt/src/sofa/gui/qt/GLPickHandler.cpp | 138 - Sofa/GUI/Qt/src/sofa/gui/qt/GLPickHandler.h | 68 - Sofa/GUI/Qt/src/sofa/gui/qt/GUI.ui | 1008 ------- Sofa/GUI/Qt/src/sofa/gui/qt/GenGraphForm.cpp | 526 ---- Sofa/GUI/Qt/src/sofa/gui/qt/GenGraphForm.h | 70 - Sofa/GUI/Qt/src/sofa/gui/qt/GenericWidget.h | 84 - .../Qt/src/sofa/gui/qt/GraphDataWidget.cpp | 151 -- Sofa/GUI/Qt/src/sofa/gui/qt/GraphDataWidget.h | 385 --- .../sofa/gui/qt/GraphListenerQListView.cpp | 766 ------ .../src/sofa/gui/qt/GraphListenerQListView.h | 96 - Sofa/GUI/Qt/src/sofa/gui/qt/GraphVisitor.cpp | 362 --- Sofa/GUI/Qt/src/sofa/gui/qt/GraphVisitor.h | 88 - Sofa/GUI/Qt/src/sofa/gui/qt/LinkWidget.cpp | 119 - Sofa/GUI/Qt/src/sofa/gui/qt/LinkWidget.h | 189 -- .../Qt/src/sofa/gui/qt/MaterialDataWidget.cpp | 245 -- .../Qt/src/sofa/gui/qt/MaterialDataWidget.h | 123 - Sofa/GUI/Qt/src/sofa/gui/qt/ModifyObject.cpp | 568 ---- Sofa/GUI/Qt/src/sofa/gui/qt/ModifyObject.h | 188 -- Sofa/GUI/Qt/src/sofa/gui/qt/MouseManager.ui | 104 - .../src/sofa/gui/qt/PickHandlerCallBacks.cpp | 107 - .../Qt/src/sofa/gui/qt/PickHandlerCallBacks.h | 64 - Sofa/GUI/Qt/src/sofa/gui/qt/PieWidget.cpp | 173 -- Sofa/GUI/Qt/src/sofa/gui/qt/PieWidget.h | 88 - Sofa/GUI/Qt/src/sofa/gui/qt/PluginManager.ui | 186 -- .../sofa/gui/qt/QDataDescriptionWidget.cpp | 193 -- .../src/sofa/gui/qt/QDataDescriptionWidget.h | 51 - .../Qt/src/sofa/gui/qt/QDisplayDataWidget.cpp | 351 --- .../Qt/src/sofa/gui/qt/QDisplayDataWidget.h | 131 - .../Qt/src/sofa/gui/qt/QDisplayLinkWidget.cpp | 204 -- .../Qt/src/sofa/gui/qt/QDisplayLinkWidget.h | 108 - .../sofa/gui/qt/QDisplayPropertyWidget.cpp | 668 ----- .../src/sofa/gui/qt/QDisplayPropertyWidget.h | 155 -- .../Qt/src/sofa/gui/qt/QEnergyStatWidget.cpp | 81 - .../Qt/src/sofa/gui/qt/QEnergyStatWidget.h | 53 - .../Qt/src/sofa/gui/qt/QGraphStatWidget.cpp | 163 -- .../GUI/Qt/src/sofa/gui/qt/QGraphStatWidget.h | 99 - .../sofa/gui/qt/QMenuFilesRecentlyOpened.cpp | 61 - .../sofa/gui/qt/QMenuFilesRecentlyOpened.h | 49 - .../gui/qt/QModelViewTableDataContainer.h | 751 ------ .../sofa/gui/qt/QModelViewTableUpdater.cpp | 127 - .../src/sofa/gui/qt/QModelViewTableUpdater.h | 65 - .../src/sofa/gui/qt/QMomentumStatWidget.cpp | 81 - .../Qt/src/sofa/gui/qt/QMomentumStatWidget.h | 52 - .../Qt/src/sofa/gui/qt/QMouseOperations.cpp | 426 --- .../GUI/Qt/src/sofa/gui/qt/QMouseOperations.h | 190 -- .../gui/qt/QMouseWheelAdjustementGuard.cpp | 45 - .../sofa/gui/qt/QMouseWheelAdjustementGuard.h | 46 - .../Qt/src/sofa/gui/qt/QRGBAColorPicker.cpp | 168 -- .../GUI/Qt/src/sofa/gui/qt/QRGBAColorPicker.h | 85 - Sofa/GUI/Qt/src/sofa/gui/qt/QSofaListView.cpp | 744 ------ Sofa/GUI/Qt/src/sofa/gui/qt/QSofaListView.h | 174 -- .../Qt/src/sofa/gui/qt/QSofaStatWidget.cpp | 147 -- Sofa/GUI/Qt/src/sofa/gui/qt/QSofaStatWidget.h | 66 - .../Qt/src/sofa/gui/qt/QTableDataContainer.h | 736 ------ Sofa/GUI/Qt/src/sofa/gui/qt/QTableUpdater.h | 80 - .../sofa/gui/qt/QTabulationModifyObject.cpp | 159 -- .../src/sofa/gui/qt/QTabulationModifyObject.h | 93 - .../src/sofa/gui/qt/QTransformationWidget.cpp | 144 - .../src/sofa/gui/qt/QTransformationWidget.h | 57 - .../src/sofa/gui/qt/QVisitorControlPanel.cpp | 142 - .../Qt/src/sofa/gui/qt/QVisitorControlPanel.h | 54 - .../src/sofa/gui/qt/QtMessageRedirection.cpp | 50 - .../Qt/src/sofa/gui/qt/QtMessageRedirection.h | 29 - .../src/sofa/gui/qt/RGBAColorDataWidget.cpp | 61 - .../Qt/src/sofa/gui/qt/RGBAColorDataWidget.h | 75 - Sofa/GUI/Qt/src/sofa/gui/qt/RealGUI.cpp | 2335 ----------------- Sofa/GUI/Qt/src/sofa/gui/qt/RealGUI.h | 437 --- .../Qt/src/sofa/gui/qt/SimpleDataWidget.cpp | 352 --- .../GUI/Qt/src/sofa/gui/qt/SimpleDataWidget.h | 1031 -------- .../Qt/src/sofa/gui/qt/SofaMouseManager.cpp | 157 -- .../GUI/Qt/src/sofa/gui/qt/SofaMouseManager.h | 67 - .../Qt/src/sofa/gui/qt/SofaPluginManager.cpp | 336 --- .../Qt/src/sofa/gui/qt/SofaPluginManager.h | 65 - .../src/sofa/gui/qt/SofaSceneGraphWidget.cpp | 71 - .../Qt/src/sofa/gui/qt/SofaSceneGraphWidget.h | 118 - .../sofa/gui/qt/SofaVideoRecorderManager.cpp | 167 -- .../sofa/gui/qt/SofaVideoRecorderManager.h | 110 - .../src/sofa/gui/qt/SofaWindowDataGraph.cpp | 270 -- .../Qt/src/sofa/gui/qt/SofaWindowDataGraph.h | 87 - .../Qt/src/sofa/gui/qt/SofaWindowProfiler.cpp | 763 ------ .../Qt/src/sofa/gui/qt/SofaWindowProfiler.h | 265 -- .../Qt/src/sofa/gui/qt/StructDataWidget.cpp | 43 - .../GUI/Qt/src/sofa/gui/qt/StructDataWidget.h | 564 ---- .../Qt/src/sofa/gui/qt/TableDataWidget.cpp | 128 - Sofa/GUI/Qt/src/sofa/gui/qt/TableDataWidget.h | 47 - .../src/sofa/gui/qt/VideoRecorderManager.ui | 188 -- Sofa/GUI/Qt/src/sofa/gui/qt/VisitorGUI.ui | 89 - .../Qt/src/sofa/gui/qt/WDoubleLineEdit.cpp | 158 -- Sofa/GUI/Qt/src/sofa/gui/qt/WDoubleLineEdit.h | 101 - Sofa/GUI/Qt/src/sofa/gui/qt/WindowProfiler.ui | 233 -- Sofa/GUI/Qt/src/sofa/gui/qt/WindowVisitor.cpp | 276 -- Sofa/GUI/Qt/src/sofa/gui/qt/WindowVisitor.h | 125 - Sofa/GUI/Qt/src/sofa/gui/qt/config.h.in | 52 - .../qt/dataGraph/SofaComponentNodeModel.cpp | 213 -- .../gui/qt/dataGraph/SofaComponentNodeModel.h | 119 - Sofa/GUI/Qt/src/sofa/gui/qt/fwd.h | 30 - Sofa/GUI/Qt/src/sofa/gui/qt/init.cpp | 78 - Sofa/GUI/Qt/src/sofa/gui/qt/init.h | 28 - .../Qt/src/sofa/gui/qt/panels/QDocBrowser.cpp | 318 --- .../Qt/src/sofa/gui/qt/panels/QDocBrowser.h | 90 - Sofa/GUI/Qt/src/sofa/gui/qt/qt.conf.h | 137 - .../Qt/src/sofa/gui/qt/resources/RealGUI.qrc | 9 - .../src/sofa/gui/qt/resources/icons/back.png | Bin 519 -> 0 bytes .../src/sofa/gui/qt/resources/icons/back.xpm | 55 - .../gui/qt/resources/icons/collapseAll.svg | 63 - .../sofa/gui/qt/resources/icons/expandAll.svg | 70 - .../sofa/gui/qt/resources/icons/graphicon.ico | Bin 1406 -> 0 bytes .../src/sofa/gui/qt/resources/icons/home.png | Bin 1279 -> 0 bytes .../src/sofa/gui/qt/resources/icons/home.xpm | 150 -- .../sofa/gui/qt/resources/icons/icondata.xpm | 32 - .../sofa/gui/qt/resources/icons/iconerror.xpm | 27 - .../sofa/gui/qt/resources/icons/iconinfo.xpm | 27 - .../gui/qt/resources/icons/iconmultinode.xpm | 27 - .../sofa/gui/qt/resources/icons/iconnode.xpm | 26 - .../sofa/gui/qt/resources/icons/iconsleep.xpm | 26 - .../gui/qt/resources/icons/iconwarning.xpm | 27 - .../sofa/gui/qt/resources/icons/openFile.png | Bin 1247 -> 0 bytes .../icons/sceneGraphRefresh-dirty.svg | 205 -- .../icons/sceneGraphRefresh-locked.svg | 199 -- .../icons/sceneGraphRefresh-unlocked.svg | 201 -- .../src/sofa/gui/qt/sofa-logo-alpha-text.png | Bin 34666 -> 0 bytes .../Qt/src/sofa/gui/qt/viewer/EngineBackend.h | 53 - .../Qt/src/sofa/gui/qt/viewer/GLBackend.cpp | 154 -- .../GUI/Qt/src/sofa/gui/qt/viewer/GLBackend.h | 60 - .../src/sofa/gui/qt/viewer/OglModelPolicy.cpp | 48 - .../src/sofa/gui/qt/viewer/OglModelPolicy.h | 57 - .../Qt/src/sofa/gui/qt/viewer/SofaViewer.cpp | 554 ---- .../Qt/src/sofa/gui/qt/viewer/SofaViewer.h | 103 - .../sofa/gui/qt/viewer/VisualModelPolicy.cpp | 38 - .../sofa/gui/qt/viewer/VisualModelPolicy.h | 41 - .../src/sofa/gui/qt/viewer/qgl/QtGLViewer.cpp | 1122 -------- .../src/sofa/gui/qt/viewer/qgl/QtGLViewer.h | 232 -- .../Qt/src/sofa/gui/qt/viewer/qt/QtViewer.cpp | 1558 ----------- .../Qt/src/sofa/gui/qt/viewer/qt/QtViewer.h | 267 -- .../src/sofa/gui/qt/viewer/qt/graphicon.ico | Bin 1406 -> 0 bytes .../Qt/src/sofa/gui/qt/viewer/qt/iconnode.xpm | 26 - Sofa/GUI/Sofa.GUIConfig.cmake.in | 1 - 164 files changed, 1 insertion(+), 32186 deletions(-) delete mode 100644 Sofa/GUI/Qt/CMakeLists.txt delete mode 100644 Sofa/GUI/Qt/LICENSE.GPL.txt delete mode 100644 Sofa/GUI/Qt/Sofa.GUI.QtConfig.cmake.in delete mode 100644 Sofa/GUI/Qt/etc/Sofa.GUI.Qt.ini.in delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/AboutDialog.ui delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/AboutSOFADialog.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/AboutSOFADialog.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/BaseGenGraphForm.ui delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/DataFilenameWidget.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/DataFilenameWidget.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/DataWidget.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/DataWidget.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/DialogAddObject.ui delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/DisplayFlagsDataWidget.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/DisplayFlagsDataWidget.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/Doc_Viewer/index.html delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/Doc_Viewer/index_fichiers/GUI.png delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/Doc_Viewer/index_fichiers/GUI_caption0.png delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/Doc_Viewer/index_fichiers/GUI_caption1.png delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/Doc_Viewer/index_fichiers/Tab_graph.png delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/Doc_Viewer/index_fichiers/Tab_show.png delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/Doc_Viewer/index_fichiers/Tab_stats.png delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/Doc_Viewer/index_fichiers/VIewerOGRE.png delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/Doc_Viewer/index_fichiers/Viewer.png delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/FileManagement.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/FileManagement.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/GLPickHandler.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/GLPickHandler.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/GUI.ui delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/GenGraphForm.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/GenGraphForm.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/GenericWidget.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/GraphDataWidget.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/GraphDataWidget.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/GraphListenerQListView.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/GraphListenerQListView.h delete mode 100755 Sofa/GUI/Qt/src/sofa/gui/qt/GraphVisitor.cpp delete mode 100755 Sofa/GUI/Qt/src/sofa/gui/qt/GraphVisitor.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/LinkWidget.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/LinkWidget.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/MaterialDataWidget.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/MaterialDataWidget.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/ModifyObject.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/ModifyObject.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/MouseManager.ui delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/PickHandlerCallBacks.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/PickHandlerCallBacks.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/PieWidget.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/PieWidget.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/PluginManager.ui delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/QDataDescriptionWidget.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/QDataDescriptionWidget.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/QDisplayDataWidget.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/QDisplayDataWidget.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/QDisplayLinkWidget.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/QDisplayLinkWidget.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/QDisplayPropertyWidget.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/QDisplayPropertyWidget.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/QEnergyStatWidget.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/QEnergyStatWidget.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/QGraphStatWidget.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/QGraphStatWidget.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/QMenuFilesRecentlyOpened.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/QMenuFilesRecentlyOpened.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/QModelViewTableDataContainer.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/QModelViewTableUpdater.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/QModelViewTableUpdater.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/QMomentumStatWidget.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/QMomentumStatWidget.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/QMouseOperations.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/QMouseOperations.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/QMouseWheelAdjustementGuard.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/QMouseWheelAdjustementGuard.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/QRGBAColorPicker.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/QRGBAColorPicker.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/QSofaListView.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/QSofaListView.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/QSofaStatWidget.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/QSofaStatWidget.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/QTableDataContainer.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/QTableUpdater.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/QTabulationModifyObject.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/QTabulationModifyObject.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/QTransformationWidget.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/QTransformationWidget.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/QVisitorControlPanel.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/QVisitorControlPanel.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/QtMessageRedirection.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/QtMessageRedirection.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/RGBAColorDataWidget.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/RGBAColorDataWidget.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/RealGUI.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/RealGUI.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/SimpleDataWidget.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/SimpleDataWidget.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/SofaMouseManager.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/SofaMouseManager.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/SofaPluginManager.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/SofaPluginManager.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/SofaSceneGraphWidget.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/SofaSceneGraphWidget.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/SofaVideoRecorderManager.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/SofaVideoRecorderManager.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/SofaWindowDataGraph.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/SofaWindowDataGraph.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/SofaWindowProfiler.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/SofaWindowProfiler.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/StructDataWidget.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/StructDataWidget.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/TableDataWidget.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/TableDataWidget.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/VideoRecorderManager.ui delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/VisitorGUI.ui delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/WDoubleLineEdit.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/WDoubleLineEdit.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/WindowProfiler.ui delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/WindowVisitor.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/WindowVisitor.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/config.h.in delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/dataGraph/SofaComponentNodeModel.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/dataGraph/SofaComponentNodeModel.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/fwd.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/init.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/init.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/panels/QDocBrowser.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/panels/QDocBrowser.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/qt.conf.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/resources/RealGUI.qrc delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/back.png delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/back.xpm delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/collapseAll.svg delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/expandAll.svg delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/graphicon.ico delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/home.png delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/home.xpm delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/icondata.xpm delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/iconerror.xpm delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/iconinfo.xpm delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/iconmultinode.xpm delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/iconnode.xpm delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/iconsleep.xpm delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/iconwarning.xpm delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/openFile.png delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/sceneGraphRefresh-dirty.svg delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/sceneGraphRefresh-locked.svg delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/sceneGraphRefresh-unlocked.svg delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/sofa-logo-alpha-text.png delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/viewer/EngineBackend.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/viewer/GLBackend.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/viewer/GLBackend.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/viewer/OglModelPolicy.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/viewer/OglModelPolicy.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/viewer/SofaViewer.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/viewer/SofaViewer.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/viewer/VisualModelPolicy.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/viewer/VisualModelPolicy.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/viewer/qgl/QtGLViewer.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/viewer/qgl/QtGLViewer.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/viewer/qt/QtViewer.cpp delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/viewer/qt/QtViewer.h delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/viewer/qt/graphicon.ico delete mode 100644 Sofa/GUI/Qt/src/sofa/gui/qt/viewer/qt/iconnode.xpm diff --git a/Sofa/GUI/CMakeLists.txt b/Sofa/GUI/CMakeLists.txt index 2e983351bd6..e24b621396d 100644 --- a/Sofa/GUI/CMakeLists.txt +++ b/Sofa/GUI/CMakeLists.txt @@ -20,7 +20,7 @@ else() list(APPEND SOFAGUI_MISSINGTARGETS ${PROJECT_NAME}.Common) endif() -set(SOFAGUI_DIRS Batch Qt) +set(SOFAGUI_DIRS Batch) foreach(dir ${SOFAGUI_DIRS}) sofa_add_subdirectory(plugin ${dir} ${PROJECT_NAME}.${dir} ON) if(TARGET ${PROJECT_NAME}.${dir}) diff --git a/Sofa/GUI/Qt/CMakeLists.txt b/Sofa/GUI/Qt/CMakeLists.txt deleted file mode 100644 index 85ebe4e49ee..00000000000 --- a/Sofa/GUI/Qt/CMakeLists.txt +++ /dev/null @@ -1,406 +0,0 @@ -cmake_minimum_required(VERSION 3.22) -project(Sofa.GUI.Qt LANGUAGES CXX) - -# Detect if component is compiled outside SOFA -if ("${CMAKE_PROJECT_NAME}" STREQUAL "${PROJECT_NAME}") - find_package(Sofa.Config REQUIRED) - sofa_find_package(Sofa.GUI.Common REQUIRED) -endif() - -# Qt dependencies -set(SOFA_GUI_QT_TARGETS "") -set(QT_USE_IMPORTED_TARGETS 1) -set(QT5_NO_LINK_QTMAIN 1) - - - - -# Will only use Qt5 if Qt5 is found and Qt6 is not found -# if Qt5 and Qt6 are both found, Qt6 will take priority -find_package(Qt6 COMPONENTS Core CoreTools QUIET) -if (NOT Qt6Core_FOUND) - find_package(Qt5 COMPONENTS Core QUIET) -endif() - -if (Qt6Core_FOUND) - message("${PROJECT_NAME}: will use Qt6") - find_package(Qt6 COMPONENTS Gui GuiTools Widgets WidgetsTools OpenGLWidgets REQUIRED) - # GuiTools & WidgetsTools does not define a Qt6::component CMake target - # So we can't look for those target to know how we should define SOFA_GUI_QT_HAVE_QT6 - sofa_find_package(Qt6 COMPONENTS Gui Widgets OpenGLWidgets REQUIRED) - set(SOFA_GUI_QT_TARGETS ${SOFA_GUI_QT_TARGETS} Qt::Core Qt::Gui Qt::Widgets Qt::OpenGLWidgets ) -elseif (Qt5Core_FOUND) - message("${PROJECT_NAME}: will use Qt5 (deprecated)") - sofa_find_package(Qt5 COMPONENTS Core Gui OpenGL REQUIRED) - set(SOFA_GUI_QT_TARGETS ${SOFA_GUI_QT_TARGETS} Qt5::Core Qt5::Gui Qt5::OpenGL) -else() - message(SEND_ERROR "${PROJECT_NAME}: Could not find either Qt5 or Qt6.") -endif() - -if (Qt5Core_FOUND) - # Profiling - sofa_find_package(Qt5 COMPONENTS Charts QUIET BOTH_SCOPES) - if(Qt5Charts_FOUND) - set(SOFA_GUI_QT_TARGETS ${SOFA_GUI_QT_TARGETS} Qt5::Charts) - else() - message(STATUS "${PROJECT_NAME}: Qt5Charts not found. No chart nor Profile will be compiled") - endif() - - # QDocBrowser - find_package(Qt5 COMPONENTS WebEngine QUIET) # if found, then QDocBrowser will be ON by default - option(SOFA_GUI_QT_ENABLE_QDOCBROWSER "Build the QDocBrowser. QtWebEngine is needed." ${Qt5WebEngine_FOUND}) - - if(SOFA_GUI_QT_ENABLE_QDOCBROWSER) - sofa_find_package(Qt5 COMPONENTS WebEngine WebEngineWidgets REQUIRED BOTH_SCOPES) - set(SOFA_GUI_QT_TARGETS ${SOFA_GUI_QT_TARGETS} Qt5::WebEngine Qt5::WebEngineWidgets) - endif() -elseif (Qt6Core_FOUND) - - # Profiling - sofa_find_package(Qt6 COMPONENTS Charts QUIET BOTH_SCOPES) - if(Qt6Charts_FOUND) - set(SOFA_GUI_QT_TARGETS ${SOFA_GUI_QT_TARGETS} Qt::Charts) - else() - message(STATUS "${PROJECT_NAME}: Qt6 Charts not found. No chart nor Profile will be compiled") - endif() - - # # QDocBrowser - find_package(Qt6 COMPONENTS WebEngineCore QUIET) # if found, then QDocBrowser will be ON by default - option(SOFA_GUI_QT_ENABLE_QDOCBROWSER "Build the QDocBrowser. QtWebEngine is needed." ${Qt6WebEngineCore_FOUND}) - if(SOFA_GUI_QT_ENABLE_QDOCBROWSER) - # WebEngineCore needs Positioning WebChannel at cmake configure step (?) - sofa_find_package(Qt6 COMPONENTS Positioning WebChannel WebEngineCore WebEngineWidgets REQUIRED BOTH_SCOPES) - set(SOFA_GUI_QT_TARGETS ${SOFA_GUI_QT_TARGETS} Qt::WebEngineCore Qt::WebEngineWidgets) - endif() -endif() - - -sofa_find_package(Sofa.GL QUIET) - -# QtViewer and QGLViewer -if(Sofa.GL_FOUND) - # QtViewer - option(SOFA_GUI_QT_ENABLE_QTVIEWER "Compile the QtViewer for the Qt GUI" ON) - - # QGLViewer - option(SOFA_GUI_QT_ENABLE_QGLVIEWER "Compile the QGLViewer for the Qt GUI" ON) - - if(SOFA_GUI_QT_ENABLE_QGLVIEWER) - - find_package(QGLViewer QUIET) - if(NOT QGLViewer_FOUND AND SOFA_ALLOW_FETCH_DEPENDENCIES) - message("Sofa.GUI.Qt: DEPENDENCY QGLViewer NOT FOUND. SOFA_ALLOW_FETCH_DEPENDENCIES is ON, fetching QGLViewer...") - include(FetchContent) - FetchContent_Declare(QGLViewer - GIT_REPOSITORY https://github.com/sofa-framework/libQGLViewer/ - GIT_TAG v2.8.1_SOFA - ) - FetchContent_MakeAvailable(QGLViewer) - elseif (NOT QGLViewer_FOUND) - message(FATAL_ERROR "Sofa.GUI.Qt: DEPENDENCY QGLViewer NOT FOUND. SOFA_ALLOW_FETCH_DEPENDENCIES is OFF and thus cannot be fetched. Install QGLViewer (version=2.8.0), or enable SOFA_ALLOW_FETCH_DEPENDENCIES to fix this issue.") - endif() - endif() -else() - message(STATUS "Sofa.GUI.Qt: Sofa.GL has not been activated, QtViewer/QGLViewer will be disabled.") - set(SOFA_GUI_QT_ENABLE_QTVIEWER OFF) - set(SOFA_GUI_QT_ENABLE_QGLVIEWER OFF) -endif() - -# VSync -option(SOFA_GUI_QT_ENABLE_VSYNC "Enable vertical refresh sync" OFF) - -# Qt native menu -set(DEFAULT_SOFA_GUI_QT_ENABLE_NATIVE_MENU ON) -if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - set(DEFAULT_SOFA_GUI_QT_ENABLE_NATIVE_MENU OFF) -endif() -option(SOFA_GUI_QT_ENABLE_NATIVE_MENU "Enable Qt to use the operating system's native menu for the Qt GUI" ${DEFAULT_SOFA_GUI_QT_ENABLE_NATIVE_MENU}) - -# NodeEditor -option(SOFA_GUI_QT_ENABLE_NODEGRAPH "Build the Node graph representation. NodeEditor library is needed." OFF) -if(SOFA_GUI_QT_ENABLE_NODEGRAPH) - sofa_find_package(NodeEditor REQUIRED BOTH_SCOPES) -endif() - -# FFMPEG_exec -sofa_find_package(FFMPEG_exec BOTH_SCOPES) - -# TinyXML -if(SOFA_DUMP_VISITOR_INFO) - sofa_find_package(TinyXML2 REQUIRED BOTH_SCOPES) -endif() - - - -set(SRC_ROOT src/sofa/gui/qt) - -set(MOC_HEADER_FILES - ${SRC_ROOT}/AboutSOFADialog.h - ${SRC_ROOT}/DataFilenameWidget.h - ${SRC_ROOT}/DataWidget.h - ${SRC_ROOT}/DisplayFlagsDataWidget.h - ${SRC_ROOT}/GenGraphForm.h - ${SRC_ROOT}/LinkWidget.h - ${SRC_ROOT}/MaterialDataWidget.h - ${SRC_ROOT}/RGBAColorDataWidget.h - ${SRC_ROOT}/ModifyObject.h - ${SRC_ROOT}/QDataDescriptionWidget.h - ${SRC_ROOT}/QDisplayDataWidget.h - ${SRC_ROOT}/QDisplayLinkWidget.h - ${SRC_ROOT}/QDisplayPropertyWidget.h - ${SRC_ROOT}/QRGBAColorPicker.h - ${SRC_ROOT}/QModelViewTableUpdater.h - ${SRC_ROOT}/QMouseOperations.h - ${SRC_ROOT}/QMouseWheelAdjustementGuard.h - ${SRC_ROOT}/QSofaListView.h - ${SRC_ROOT}/QSofaStatWidget.h - ${SRC_ROOT}/QTabulationModifyObject.h - ${SRC_ROOT}/QtMessageRedirection.h - ${SRC_ROOT}/QTransformationWidget.h - ${SRC_ROOT}/RealGUI.h - ${SRC_ROOT}/SimpleDataWidget.h - ${SRC_ROOT}/SofaMouseManager.h - ${SRC_ROOT}/SofaVideoRecorderManager.h - ${SRC_ROOT}/SofaPluginManager.h - ${SRC_ROOT}/SofaSceneGraphWidget.h - ${SRC_ROOT}/WDoubleLineEdit.h - ) -set(HEADER_FILES - ${SRC_ROOT}/config.h.in - ${SRC_ROOT}/fwd.h - ${SRC_ROOT}/init.h - ${SRC_ROOT}/qt.conf.h - ${SRC_ROOT}/FileManagement.h - ${SRC_ROOT}/GraphListenerQListView.h - ${SRC_ROOT}/PickHandlerCallBacks.h - ${SRC_ROOT}/QMenuFilesRecentlyOpened.h - ${SRC_ROOT}/QModelViewTableDataContainer.h - ${SRC_ROOT}/StructDataWidget.h - ${SRC_ROOT}/TableDataWidget.h - ${SRC_ROOT}/viewer/SofaViewer.h - ${SRC_ROOT}/viewer/EngineBackend.h - ${SRC_ROOT}/viewer/VisualModelPolicy.h - ${SRC_ROOT}/GenericWidget.h - ) -set(SOURCE_FILES - ${SRC_ROOT}/init.cpp - ${SRC_ROOT}/AboutSOFADialog.cpp - ${SRC_ROOT}/DataFilenameWidget.cpp - ${SRC_ROOT}/DataWidget.cpp - ${SRC_ROOT}/DisplayFlagsDataWidget.cpp - ${SRC_ROOT}/FileManagement.cpp - ${SRC_ROOT}/GenGraphForm.cpp - ${SRC_ROOT}/GraphListenerQListView.cpp - ${SRC_ROOT}/LinkWidget.cpp - ${SRC_ROOT}/MaterialDataWidget.cpp - ${SRC_ROOT}/RGBAColorDataWidget.cpp - ${SRC_ROOT}/ModifyObject.cpp - ${SRC_ROOT}/PickHandlerCallBacks.cpp - ${SRC_ROOT}/QDataDescriptionWidget.cpp - ${SRC_ROOT}/QDisplayDataWidget.cpp - ${SRC_ROOT}/QDisplayLinkWidget.cpp - ${SRC_ROOT}/QDisplayPropertyWidget.cpp - ${SRC_ROOT}/QRGBAColorPicker.cpp - ${SRC_ROOT}/QMenuFilesRecentlyOpened.cpp - ${SRC_ROOT}/QModelViewTableUpdater.cpp - ${SRC_ROOT}/QMouseOperations.cpp - ${SRC_ROOT}/QMouseWheelAdjustementGuard.cpp - ${SRC_ROOT}/QSofaListView.cpp - ${SRC_ROOT}/QSofaStatWidget.cpp - ${SRC_ROOT}/QTabulationModifyObject.cpp - ${SRC_ROOT}/QtMessageRedirection.cpp - ${SRC_ROOT}/QTransformationWidget.cpp - ${SRC_ROOT}/RealGUI.cpp - ${SRC_ROOT}/SimpleDataWidget.cpp - ${SRC_ROOT}/SofaMouseManager.cpp - ${SRC_ROOT}/SofaPluginManager.cpp - ${SRC_ROOT}/SofaVideoRecorderManager.cpp - ${SRC_ROOT}/StructDataWidget.cpp - ${SRC_ROOT}/TableDataWidget.cpp - ${SRC_ROOT}/WDoubleLineEdit.cpp - ${SRC_ROOT}/viewer/SofaViewer.cpp - ${SRC_ROOT}/SofaSceneGraphWidget.cpp - ${SRC_ROOT}/viewer/VisualModelPolicy.cpp -) -set(UI_FILES - ${SRC_ROOT}/AboutDialog.ui - ${SRC_ROOT}/BaseGenGraphForm.ui - ${SRC_ROOT}/DialogAddObject.ui - ${SRC_ROOT}/GUI.ui - ${SRC_ROOT}/MouseManager.ui - ${SRC_ROOT}/PluginManager.ui - ${SRC_ROOT}/VideoRecorderManager.ui -) -set(QRC_FILES - ${SRC_ROOT}/resources/RealGUI.qrc -) - - -if(SOFA_DUMP_VISITOR_INFO) - list(APPEND MOC_HEADER_FILES - ${SRC_ROOT}/WindowVisitor.h - ${SRC_ROOT}/QVisitorControlPanel.h - ${SRC_ROOT}/PieWidget.h - ) - list(APPEND HEADER_FILES - ${SRC_ROOT}/GraphVisitor.h - ) - list(APPEND SOURCE_FILES - ${SRC_ROOT}/GraphVisitor.cpp - ${SRC_ROOT}/WindowVisitor.cpp - ${SRC_ROOT}/QVisitorControlPanel.cpp - ${SRC_ROOT}/PieWidget.cpp - ) - list(APPEND UI_FILES - ${SRC_ROOT}/VisitorGUI.ui - ) -endif() - -if(Sofa.GL_FOUND) - list(APPEND HEADER_FILES - ${SRC_ROOT}/GLPickHandler.h - ${SRC_ROOT}/viewer/GLBackend.h - ${SRC_ROOT}/viewer/OglModelPolicy.h - ) - list(APPEND SOURCE_FILES - ${SRC_ROOT}/GLPickHandler.cpp - ${SRC_ROOT}/viewer/GLBackend.cpp - ${SRC_ROOT}/viewer/OglModelPolicy.cpp - ) - - # QtViewer - if(SOFA_GUI_QT_ENABLE_QTVIEWER) - list(APPEND MOC_HEADER_FILES ${SRC_ROOT}/viewer/qt/QtViewer.h) - list(APPEND SOURCE_FILES ${SRC_ROOT}/viewer/qt/QtViewer.cpp) - endif() - - if(SOFA_GUI_QT_ENABLE_QGLVIEWER) - list(APPEND MOC_HEADER_FILES ${SRC_ROOT}/viewer/qgl/QtGLViewer.h) - list(APPEND SOURCE_FILES ${SRC_ROOT}/viewer/qgl/QtGLViewer.cpp) - endif() - -endif() - -# QtCharts -if(Qt5Charts_FOUND OR Qt6Charts_FOUND) - list(APPEND MOC_HEADER_FILES - ${SRC_ROOT}/SofaWindowProfiler.h - ${SRC_ROOT}/QGraphStatWidget.h - ${SRC_ROOT}/QEnergyStatWidget.h - ${SRC_ROOT}/QMomentumStatWidget.h - ${SRC_ROOT}/GraphDataWidget.h - ) - list(APPEND SOURCE_FILES - ${SRC_ROOT}/SofaWindowProfiler.cpp - ${SRC_ROOT}/QGraphStatWidget.cpp - ${SRC_ROOT}/QEnergyStatWidget.cpp - ${SRC_ROOT}/QMomentumStatWidget.cpp - ${SRC_ROOT}/GraphDataWidget.cpp - ) - list(APPEND UI_FILES ${SRC_ROOT}/WindowProfiler.ui) -else() - message(STATUS "${PROJECT_NAME}: QtCharts not found, SofaWindowProfiler will not be built.") -endif() - -if(SOFA_GUI_QT_ENABLE_QDOCBROWSER) - list(APPEND MOC_HEADER_FILES ${SRC_ROOT}/panels/QDocBrowser.h) - list(APPEND SOURCE_FILES ${SRC_ROOT}/panels/QDocBrowser.cpp) -else() - message(STATUS "${PROJECT_NAME}: QtWebEngine not found, QDocBrowser will not be built.") -endif() - - - - -#NodeEditor -if (SOFA_GUI_QT_ENABLE_NODEGRAPH) - list(APPEND MOC_HEADER_FILES - ${SRC_ROOT}/SofaWindowDataGraph.h - ${SRC_ROOT}/dataGraph/SofaComponentNodeModel.h - ) - - list(APPEND SOURCE_FILES - ${SRC_ROOT}/SofaWindowDataGraph.cpp - ${SRC_ROOT}/dataGraph/SofaComponentNodeModel.cpp - ) -endif() - -if (Qt5Core_FOUND) - qt5_wrap_cpp(MOC_FILES ${MOC_HEADER_FILES}) - qt5_wrap_ui(FORM_FILES ${UI_FILES}) -elseif (Qt6Core_FOUND) - qt6_wrap_cpp(MOC_FILES ${MOC_HEADER_FILES}) - qt6_wrap_ui(FORM_FILES ${UI_FILES}) -endif() -set(MOC_FILES_GROUP "Generated") -set(FORM_FILES_GROUP "Generated") - -sofa_find_package(Sofa.Component.Visual REQUIRED) -sofa_find_package(Sofa.Component.SceneUtility REQUIRED) - -set(CMAKE_AUTORCC ON) -add_library(${PROJECT_NAME} SHARED ${MOC_HEADER_FILES} ${HEADER_FILES} ${MOC_FILES} ${FORM_FILES} ${SOURCE_FILES} ${QRC_FILES}) - - -# For files generated by the moc -target_include_directories(${PROJECT_NAME} PUBLIC "$") - -target_link_libraries(${PROJECT_NAME} PUBLIC Sofa.GUI.Common) -target_link_libraries(${PROJECT_NAME} PUBLIC Sofa.Component.Visual) -target_link_libraries(${PROJECT_NAME} PUBLIC Sofa.Component.SceneUtility) -target_link_libraries(${PROJECT_NAME} PUBLIC ${SOFA_GUI_QT_TARGETS}) - - - -if(SOFA_DUMP_VISITOR_INFO) - target_link_libraries(${PROJECT_NAME} PRIVATE tinyxml2::tinyxml2) -endif() - -if(Sofa.GL_FOUND) - target_link_libraries(${PROJECT_NAME} PUBLIC Sofa.GL) - if(SOFA_GUI_QT_ENABLE_QGLVIEWER) - target_link_libraries(${PROJECT_NAME} PUBLIC QGLViewer) - include_directories(${PROJECT_NAME} PUBLIC ${qglviewer_SOURCE_DIR}) - endif() -endif() - -if(SOFA_GUI_QT_ENABLE_NODEGRAPH) - target_link_libraries(${PROJECT_NAME} PUBLIC NodeEditor::nodes) - if (SOFA_BUILD_RELEASE_PACKAGE OR CMAKE_SYSTEM_NAME STREQUAL Windows) - sofa_install_libraries(TARGETS NodeEditor::nodes) - endif() -endif() - -# FFMPEG -if(FFMPEG_EXEC_FOUND) - install(PROGRAMS "${FFMPEG_EXEC_FILE}" DESTINATION bin COMPONENT applications) -endif() - -# Create build and install versions of .ini file for resources finding -set(RESOURCES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/${SRC_ROOT}/resources") -set(FFMPEG_EXEC_PATH "${FFMPEG_EXEC_FILE}") # absolute path for build dir, see .ini file -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/etc/${PROJECT_NAME}.ini.in "${CMAKE_BINARY_DIR}/etc/${PROJECT_NAME}.ini") -set(RESOURCES_DIR "../share/sofa/gui/qt") -get_filename_component(FFMPEG_EXEC_FILENAME "${FFMPEG_EXEC_FILE}" NAME) -set(FFMPEG_EXEC_PATH "../bin/${FFMPEG_EXEC_FILENAME}") # relative path for install dir, see .ini file -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/etc/${PROJECT_NAME}.ini.in "${CMAKE_BINARY_DIR}/etc/installed${PROJECT_NAME}.ini") -install(FILES "${CMAKE_BINARY_DIR}/etc/installed${PROJECT_NAME}.ini" DESTINATION etc RENAME ${PROJECT_NAME}.ini COMPONENT applications) - -install(DIRECTORY "${SRC_ROOT}/resources/" DESTINATION "share/sofa/gui/qt" COMPONENT resources) - - - - -sofa_create_package_with_targets( - PACKAGE_NAME ${PROJECT_NAME} - PACKAGE_VERSION ${Sofa_VERSION} - TARGETS ${PROJECT_NAME} AUTO_SET_TARGET_PROPERTIES - INCLUDE_SOURCE_DIR "src" - INCLUDE_INSTALL_DIR ${PROJECT_NAME} - ) - -# Qt deployment -if(CMAKE_SYSTEM_NAME STREQUAL Windows) - include(windeployqt) - windeployqt(${PROJECT_NAME} ${CMAKE_BINARY_DIR}/bin bin) -endif() diff --git a/Sofa/GUI/Qt/LICENSE.GPL.txt b/Sofa/GUI/Qt/LICENSE.GPL.txt deleted file mode 100644 index 23cb790338e..00000000000 --- a/Sofa/GUI/Qt/LICENSE.GPL.txt +++ /dev/null @@ -1,339 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - {description} - Copyright (C) {year} {fullname} - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - {signature of Ty Coon}, 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. diff --git a/Sofa/GUI/Qt/Sofa.GUI.QtConfig.cmake.in b/Sofa/GUI/Qt/Sofa.GUI.QtConfig.cmake.in deleted file mode 100644 index f9e7ce28642..00000000000 --- a/Sofa/GUI/Qt/Sofa.GUI.QtConfig.cmake.in +++ /dev/null @@ -1,62 +0,0 @@ -# CMake package configuration file for the @PROJECT_NAME@ module - -@PACKAGE_GUARD@ -@PACKAGE_INIT@ - -set(SOFA_GUI_QT_HAVE_QT5 @SOFA_GUI_QT_HAVE_QT5@) -set(SOFA_GUI_QT_HAVE_QT6 @SOFA_GUI_QT_HAVE_QT6@) - -set(SOFA_GUI_QT_ENABLE_QTVIEWER @SOFA_GUI_QT_ENABLE_QTVIEWER@) -set(SOFA_GUI_QT_ENABLE_QGLVIEWER @SOFA_GUI_QT_ENABLE_QGLVIEWER@) -set(SOFA_GUI_QT_ENABLE_NATIVE_MENU @SOFA_GUI_QT_ENABLE_NATIVE_MENU@) -set(SOFA_GUI_QT_ENABLE_VSYNC @SOFA_GUI_QT_ENABLE_VSYNC@) - -set(SOFA_GUI_QT_HAVE_QT5_CHARTS @SOFA_GUI_QT_HAVE_QT5_CHARTS@) -set(SOFA_GUI_QT_HAVE_QT5_WEBENGINE @SOFA_GUI_QT_HAVE_QT5_WEBENGINE@) -set(SOFA_GUI_QT_HAVE_NODEEDITOR @SOFA_GUI_QT_HAVE_NODEEDITOR@) -set(SOFA_GUI_QT_HAVE_TINYXML @SOFA_GUI_QT_HAVE_TINYXML@) - -set(SOFA_GUI_QT_HAVE_QT6_CHARTS @SOFA_GUI_QT_HAVE_QT6_CHARTS@) -set(SOFA_GUI_QT_HAVE_QT6_WEBENGINECORE @SOFA_GUI_QT_HAVE_QT6_WEBENGINECORE@) - -# This is needed to be able to find plugins/SofaGuiQt/lib/cmake/QGLViewer -list(APPEND CMAKE_PREFIX_PATH "${CMAKE_CURRENT_LIST_DIR}/..") - -find_package(Sofa.GUI.Common QUIET REQUIRED) -find_package(Sofa.Component.Visual QUIET REQUIRED) -find_package(Sofa.Component.SceneUtility QUIET REQUIRED) - -if(SOFA_GUI_QT_HAVE_QT5) - find_package(Qt5 QUIET REQUIRED Core Gui OpenGL) - if(SOFA_GUI_QT_HAVE_QT5_CHARTS) - find_package(Qt5 QUIET REQUIRED Charts) - endif() - if(SOFA_GUI_QT_HAVE_QT5_WEBENGINE) - find_package(Qt5 QUIET REQUIRED WebEngine WebEngineWidgets) - endif() - if(SOFA_GUI_QT_ENABLE_QGLVIEWER) - find_package(QGLViewer QUIET REQUIRED) - endif() -elseif(SOFA_GUI_QT_HAVE_QT6) - find_package(Qt6 COMPONENTS Widgets OpenGLWidgets QUIET REQUIRED) - if(SOFA_GUI_QT_HAVE_QT6_CHARTS) - find_package(Qt6 QUIET REQUIRED Charts) - endif() - if(SOFA_GUI_QT_HAVE_QT6_WEBENGINECORE) - find_package(Qt6 QUIET REQUIRED Positioning WebChannel WebEngineCore WebEngineWidgets) - endif() - if(SOFA_GUI_QT_ENABLE_QGLVIEWER) - find_package(QGLViewer QUIET REQUIRED) - endif() -endif() - -if(SOFA_GUI_QT_HAVE_NODEEDITOR) - find_package(NodeEditor QUIET REQUIRED) -endif() - - -if(NOT TARGET @PROJECT_NAME@) - include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") -endif() - -check_required_components(@PROJECT_NAME@) diff --git a/Sofa/GUI/Qt/etc/Sofa.GUI.Qt.ini.in b/Sofa/GUI/Qt/etc/Sofa.GUI.Qt.ini.in deleted file mode 100644 index 27a0653ccb6..00000000000 --- a/Sofa/GUI/Qt/etc/Sofa.GUI.Qt.ini.in +++ /dev/null @@ -1,2 +0,0 @@ -RESOURCES_DIR=@RESOURCES_DIR@ -FFMPEG_EXEC_PATH=@FFMPEG_EXEC_PATH@ diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/AboutDialog.ui b/Sofa/GUI/Qt/src/sofa/gui/qt/AboutDialog.ui deleted file mode 100644 index 83fa0b0cb74..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/AboutDialog.ui +++ /dev/null @@ -1,209 +0,0 @@ - - - AboutDialog - - - true - - - - 0 - 0 - 464 - 543 - - - - - 0 - 0 - - - - About - - - false - - - false - - - - QLayout::SetFixedSize - - - - - - - - Qt::AlignCenter - - - false - - - - - - - 0 - 0 - - - - - - - SOFA is an open-source framework for interactive physics simulation, with an emphasis on soft body dynamics. After years of research and development, the project remains open-source under the LGPL v2.1 license, fostering both research and development. - - - false - - - true - - - - - - - Open-source is kept thriving thanks to partners supporting us: be part of the community and get involved your own way! - - - Qt::AutoText - - - true - - - - - - - - 0 - 0 - - - - - - - Qt::LeftToRight - - - - - - Qt::PlainText - - - Qt::AlignCenter - - - false - - - 0 - - - - - - - - - - - - Qt::Horizontal - - - QSizePolicy::Expanding - - - - 20 - 20 - - - - - - - - - 150 - 40 - - - - Support us - - - false - - - true - - - true - - - false - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - SOFA, Simulation Open-Framework Architecture (c) 2006 INRIA, USTL, UJF, CNRS, MGH - - - Qt::AlignCenter - - - true - - - - - - - - - - buttonOk - clicked() - AboutDialog - accept() - - - 223 - 226 - - - 20 - 20 - - - - - diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/AboutSOFADialog.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/AboutSOFADialog.cpp deleted file mode 100644 index e6d2edd9521..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/AboutSOFADialog.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#include "AboutSOFADialog.h" -#include - -#include -#include -#include - -#include -using sofa::helper::system::DataRepository; - -namespace sofa::gui::qt -{ - -AboutSOFADialog::AboutSOFADialog(QWidget *parent) - : QDialog(parent) -{ - setupUi(this); - connect(buttonOk, &QPushButton::clicked, this, &AboutSOFADialog::clickSupportUs); - - std::string file = "icons/AboutSOFA.png"; - if (DataRepository.findFile(file)) - { - const QPixmap pix(QPixmap::fromImage(QImage(DataRepository.getFile ( file ).c_str()))); - label_2->setPixmap(pix); - } -} - -void AboutSOFADialog::clickSupportUs() -{ - QDesktopServices::openUrl(QUrl("https://www.sofa-framework.org/consortium/support-us/")); -} - - -} // namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/AboutSOFADialog.h b/Sofa/GUI/Qt/src/sofa/gui/qt/AboutSOFADialog.h deleted file mode 100644 index 122bc5738da..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/AboutSOFADialog.h +++ /dev/null @@ -1,41 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include -#include - - -namespace sofa::gui::qt -{ - -class SOFA_GUI_QT_API AboutSOFADialog: public QDialog, public Ui_AboutDialog -{ - Q_OBJECT -public: - AboutSOFADialog(QWidget *parent); - -public slots: - void clickSupportUs(); -}; - - -} // namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/BaseGenGraphForm.ui b/Sofa/GUI/Qt/src/sofa/gui/qt/BaseGenGraphForm.ui deleted file mode 100644 index ac73925ed81..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/BaseGenGraphForm.ui +++ /dev/null @@ -1,1065 +0,0 @@ - - - BaseGenGraphForm - - - - 0 - 0 - 382 - 654 - - - - Sofa Graph Export - - - - - - - - - 1 - 0 - - - - Output DOT &File - - - false - - - filename - - - - - - - Browse... - - - - - - - untitled - - - - - - - - - - - Layout - - - - - - Directed Vertically - - - true - - - - - - - Directed Horizontally - - - - - - - Undirected Spring - - - - - - - Undirected Radial - - - - - - - - - - Outputs - - - - - - Generate PNG - - - true - - - - - - - Generate PS - - - - - - - Generate FIG - - - - - - - Generate SVG - - - - - - - - - - - - - - Presets - - - false - - - - - - - - 1 - 0 - - - - true - - - QComboBox::InsertAtTop - - - false - - - true - - - - Full Graph - - - - - All Objects - - - - - All Nodes - - - - - Mechanical Graph - - - - - Mechanical Objects - - - - - Visual Graph - - - - - Visual Objects - - - - - Collision Graph - - - - - Collision Objects - - - - - Collision Response Graph - - - - - Collision Response Objects - - - - - - - - - - Filter - - - - - - Solvers - - - true - - - - - - - Mechanical Models - - - true - - - - - - - Force Fields - - - true - - - - - - - Interaction Force Fields - - - true - - - - - - - Constraints - - - true - - - - - - - Mass - - - true - - - - - - - Mechanical Mappings - - - true - - - - - - - Topology - - - true - - - - - - - Mappings - - - true - - - - - - - Collision Models - - - true - - - - - - - Visual Models - - - true - - - - - - - Collision Pipeline - - - true - - - - - - - Behavior Models - - - true - - - - - - - Context - - - true - - - - - - - Objects - - - true - - - - - - - Nodes - - - true - - - - - - - - - - - - Nodes Labels - - - - - - Name - - - true - - - - - - - Class - - - - - - - - - - Objects Labels - - - - - - Name - - - true - - - - - - - Class - - - true - - - - - - - - - - - - - - Qt::Horizontal - - - QSizePolicy::Expanding - - - - 16 - 20 - - - - - - - - false - - - &Display - - - Alt+D - - - - - - - &Export - - - Alt+E - - - true - - - - - - - &Close - - - Alt+C - - - - - - - - - - filename - browseButton - layoutDirV - layoutDirH - layoutSpring - layoutRadial - genPNG - genPS - genFIG - genSVG - showSolvers - showMechanicalStates - showForceFields - showInteractionForceFields - showConstraints - showMass - showMechanicalMappings - showTopology - showMappings - showCollisionModels - showVisualModels - showCollisionPipeline - showBehaviorModels - showContext - showObjects - showNodes - labelNodeName - labelNodeClass - labelObjectName - labelObjectClass - displayButton - exportButton - - - - - showObjects - toggled(bool) - showBehaviorModels - setEnabled(bool) - - - 20 - 20 - - - 20 - 20 - - - - - showObjects - toggled(bool) - showCollisionModels - setEnabled(bool) - - - 20 - 20 - - - 20 - 20 - - - - - showObjects - toggled(bool) - showVisualModels - setEnabled(bool) - - - 20 - 20 - - - 20 - 20 - - - - - showObjects - toggled(bool) - showMappings - setEnabled(bool) - - - 20 - 20 - - - 20 - 20 - - - - - showObjects - toggled(bool) - showContext - setEnabled(bool) - - - 20 - 20 - - - 20 - 20 - - - - - showObjects - toggled(bool) - showCollisionPipeline - setEnabled(bool) - - - 20 - 20 - - - 20 - 20 - - - - - showObjects - toggled(bool) - showSolvers - setEnabled(bool) - - - 20 - 20 - - - 20 - 20 - - - - - showObjects - toggled(bool) - showMechanicalStates - setEnabled(bool) - - - 20 - 20 - - - 20 - 20 - - - - - showObjects - toggled(bool) - showForceFields - setEnabled(bool) - - - 20 - 20 - - - 20 - 20 - - - - - showObjects - toggled(bool) - showInteractionForceFields - setEnabled(bool) - - - 20 - 20 - - - 20 - 20 - - - - - showObjects - toggled(bool) - showConstraints - setEnabled(bool) - - - 20 - 20 - - - 20 - 20 - - - - - showObjects - toggled(bool) - showMass - setEnabled(bool) - - - 20 - 20 - - - 20 - 20 - - - - - showObjects - toggled(bool) - showTopology - setEnabled(bool) - - - 20 - 20 - - - 20 - 20 - - - - - showObjects - toggled(bool) - showMechanicalMappings - setEnabled(bool) - - - 20 - 20 - - - 20 - 20 - - - - - showObjects - toggled(bool) - groupObjectLabel - setEnabled(bool) - - - 20 - 20 - - - 20 - 20 - - - - - showNodes - toggled(bool) - groupNodeLabel - setEnabled(bool) - - - 20 - 20 - - - 20 - 20 - - - - - showNodes - toggled(bool) - presetFilter - clearEditText() - - - 20 - 20 - - - 20 - 20 - - - - - showObjects - toggled(bool) - presetFilter - clearEditText() - - - 20 - 20 - - - 20 - 20 - - - - - showBehaviorModels - toggled(bool) - presetFilter - clearEditText() - - - 20 - 20 - - - 20 - 20 - - - - - showCollisionModels - toggled(bool) - presetFilter - clearEditText() - - - 20 - 20 - - - 20 - 20 - - - - - showVisualModels - toggled(bool) - presetFilter - clearEditText() - - - 20 - 20 - - - 20 - 20 - - - - - showMappings - toggled(bool) - presetFilter - clearEditText() - - - 20 - 20 - - - 20 - 20 - - - - - showContext - toggled(bool) - presetFilter - clearEditText() - - - 20 - 20 - - - 20 - 20 - - - - - showCollisionPipeline - toggled(bool) - presetFilter - clearEditText() - - - 20 - 20 - - - 20 - 20 - - - - - showSolvers - toggled(bool) - presetFilter - clearEditText() - - - 20 - 20 - - - 20 - 20 - - - - - showMechanicalStates - toggled(bool) - presetFilter - clearEditText() - - - 20 - 20 - - - 20 - 20 - - - - - showForceFields - toggled(bool) - presetFilter - clearEditText() - - - 20 - 20 - - - 20 - 20 - - - - - showInteractionForceFields - toggled(bool) - presetFilter - clearEditText() - - - 20 - 20 - - - 20 - 20 - - - - - showConstraints - toggled(bool) - presetFilter - clearEditText() - - - 20 - 20 - - - 20 - 20 - - - - - showMass - toggled(bool) - presetFilter - clearEditText() - - - 20 - 20 - - - 20 - 20 - - - - - showTopology - toggled(bool) - presetFilter - clearEditText() - - - 20 - 20 - - - 20 - 20 - - - - - showMechanicalMappings - toggled(bool) - presetFilter - clearEditText() - - - 20 - 20 - - - 20 - 20 - - - - - diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/DataFilenameWidget.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/DataFilenameWidget.cpp deleted file mode 100644 index fa1575132ec..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/DataFilenameWidget.cpp +++ /dev/null @@ -1,99 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#include "DataFilenameWidget.h" -#include - -#include "FileManagement.h" //static functions to manage opening/ saving of files -#include -#include - - -#include - -namespace sofa::gui::qt -{ - -helper::Creator DW_Datafilename("widget_filename",false); - - - -bool DataFileNameWidget::createWidgets() -{ - QHBoxLayout* layout = new QHBoxLayout(this); - - openFilePath = new QLineEdit(this); - const std::string& filepath = this->getData()->getValue(); - openFilePath->setText( QString(filepath.c_str()) ); - - openFileButton = new QPushButton(this); - openFileButton->setText("..."); - - layout->addWidget(openFilePath); - layout->addWidget(openFileButton); - connect( openFileButton, &QPushButton::clicked, this, &DataFileNameWidget::raiseDialog ); - connect( openFilePath, &QLineEdit::textChanged, [=](const QString &){ setWidgetDirty(); } ); - return true; -} - -void DataFileNameWidget::setDataReadOnly(bool readOnly) -{ - openFilePath->setReadOnly(readOnly); - openFileButton->setEnabled(!readOnly); -} - -void DataFileNameWidget::readFromData() -{ - const std::string& filepath = this->getData()->getValue(); - if (openFilePath->text().toStdString() != filepath) - openFilePath->setText(QString(filepath.c_str()) ); -} - -void DataFileNameWidget::writeToData() -{ - const std::string fileName( openFilePath->text().toStdString() ); - if (this->getData()->getValueString() != fileName) - this->getData()->setValue(fileName); - -} - - -void DataFileNameWidget::raiseDialog() -{ - std::string fileName( openFilePath->text().toStdString() ); - - if (sofa::helper::system::DataRepository.findFile(fileName)) - fileName=sofa::helper::system::DataRepository.getFile(fileName); - else - fileName=sofa::helper::system::DataRepository.getFirstPath(); - - const QString s = getOpenFileName(this, QString(fileName.c_str()), "All (*)", "open file dialog", "Choose a file to open" ); - const std::string SofaPath = sofa::helper::system::DataRepository.getFirstPath(); - - if (s.isNull() ) return; - fileName=std::string (s.toStdString()); - fileName = sofa::helper::system::FileRepository::relativeToPath(fileName,SofaPath); - - openFilePath->setText( QString( fileName.c_str() ) ); -} - - -} // namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/DataFilenameWidget.h b/Sofa/GUI/Qt/src/sofa/gui/qt/DataFilenameWidget.h deleted file mode 100644 index f471ddc431b..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/DataFilenameWidget.h +++ /dev/null @@ -1,59 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include -#include -#include - -#include "DataWidget.h" - - -namespace sofa::gui::qt -{ - -class DataFileNameWidget : public TDataWidget -{ - Q_OBJECT -public: - - DataFileNameWidget( - QWidget* parent, - const char* name, - core::objectmodel::Data* data): - TDataWidget(parent,name,data) {} - - virtual bool createWidgets(); - virtual void setDataReadOnly(bool readOnly); -protected: - ///Implements how update the widgets knowing the data value. - virtual void readFromData(); - ///Implements how to update the data, knowing the widget value. - virtual void writeToData(); - - QLineEdit* openFilePath; - QPushButton* openFileButton; - -protected slots : - virtual void raiseDialog(); -}; - -} // namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/DataWidget.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/DataWidget.cpp deleted file mode 100644 index c51dbb3040f..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/DataWidget.cpp +++ /dev/null @@ -1,308 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#define SOFA_GUI_QT_DATAWIDGET_CPP - -#include "DataWidget.h" -#include "ModifyObject.h" -#include -#include -#include -#include -#include - -#include - -#define SIZE_TEXT 60 - -namespace sofa::helper -{ -template class SOFA_GUI_QT_API Factory; -} // namespace sofa::helper - -namespace sofa::gui::qt -{ - -using namespace core::objectmodel; - -DataWidget::DataWidget(QWidget* parent,const char* name, MyData* d) - : QWidget(parent /*,name */) - , baseData(d) - , dirty(false) - , counter(-1) - , m_isFilled(true) - , m_toFill(false) -{ - this->setObjectName(name); -} - -DataWidget::~DataWidget() -{ -} - -void -DataWidget::setData( MyData* d) -{ - baseData = d; - readFromData(); -} - -void -DataWidget::updateVisibility() -{ - parentWidget()->setVisible(baseData->isDisplayed()); -} - -void -DataWidget::updateDataValue() -{ - if (dirty) - { - const bool hasOwner = baseData->getOwner(); - std::string previousName; - if ( hasOwner ) previousName = baseData->getOwner()->getName(); - writeToData(); - - if (hasOwner) - { - std::string path; - const BaseNode* ownerAsNode = dynamic_cast(baseData->getOwner() ); - BaseObject* ownerAsObject = dynamic_cast(baseData->getOwner() ); - - if (ownerAsNode) - { - path = ownerAsNode->getPathName() + "." + baseData->getName(); - } - else if (ownerAsObject) - { - std::string objectPath = ownerAsObject->getName(); - sofa::core::objectmodel::BaseObject* master = ownerAsObject->getMaster(); - while (master) - { - objectPath = master->getName() + "/" + objectPath; - master = master->getMaster(); - } - const BaseNode* n = dynamic_cast(ownerAsObject->getContext()); - if (n) - { - path = n->getPathName() + std::string("/") + objectPath + std::string(".") + baseData->getName(); // TODO: compute relative path - } - else - { - path = objectPath + "." + baseData->getName(); - } - } - else - { - msg_error("DataWidget") << "updateDataValue: " << baseData->getName() << " has an owner that is neither a Node nor an Object. Something went wrong..."; - } - - const QString dataString = (path + " = " + baseData->getValueString()).c_str(); - Q_EMIT dataValueChanged(dataString); - - } - - updateVisibility(); - if(hasOwner && baseData->getOwner()->getName() != previousName) - { - Q_EMIT DataOwnerDirty(true); - } - } - - dirty = false; - counter = baseData->getCounter(); - -} - -void DataWidget::fillFromData() -{ - if (!m_isFilled) // only activate toFill if only is not already filled - { - m_toFill = true; - } - else - { - m_toFill = false; - } -} - -void -DataWidget::updateWidgetValue() -{ - if(!dirty) - { - if(m_toFill || counter != baseData->getCounter()) - { - readFromData(); - this->update(); - - if (m_toFill) // update parameters to avoid other force fill - { - m_toFill = false; - m_isFilled = true; - } - } - } -} - -void -DataWidget::setWidgetDirty(bool b) -{ - dirty = b; - Q_EMIT WidgetDirty(b); -} - -typedef sofa::helper::Factory DataWidgetFactory; - -DataWidget *DataWidget::CreateDataWidget(const DataWidget::CreatorArgument &dwarg) -{ - DataWidget *datawidget_ = nullptr; - const std::string &widgetName=dwarg.data->getWidget(); - if (widgetName.empty()) - datawidget_ = DataWidgetFactory::CreateAnyObject(dwarg); - else - datawidget_ = DataWidgetFactory::CreateObject(widgetName, dwarg); - - return datawidget_; -} - - -/*QDisplayDataInfoWidget definitions */ - -QDisplayDataInfoWidget::QDisplayDataInfoWidget(QWidget* parent, const std::string& helper, - core::objectmodel::BaseData* d, bool modifiable, const ModifyObjectFlags& modifyObjectFlags):QWidget(parent), data(d), numLines_(1) -{ - setMinimumHeight(25); - - QHBoxLayout* layout = new QHBoxLayout(this); - layout->setContentsMargins(0,0,0,0); - layout->setSpacing(0); - - std::string final_str; - formatHelperString(helper,final_str); - const std::string ownerClass=data->getOwner()->getName(); - if (modifiable) - { - QPushButton *helper_button = new QPushButton(this); - helper_button->setIcon(LinkIcon()); - helper_button->setFixedSize(QSize(16, 16)); - helper_button->setToolTip(QString(final_str.c_str())); - helper_button->setAutoDefault(false); - layout->addWidget(helper_button, 0, Qt::AlignLeft); - connect(helper_button, &QPushButton::clicked, this, &QDisplayDataInfoWidget::linkModification); - if (!ownerClass.empty()) - helper_button->setToolTip( ("Data from "+ownerClass).c_str()); - } - if(data->getParent()) - { - const std::string linkvalue = data->getParent()->getLinkPath(); - linkpath_edit = new QLineEdit(this); - linkpath_edit->setContentsMargins(2, 0, 0, 0); - linkpath_edit->setText(QString(linkvalue.c_str())); - linkpath_edit->setReadOnly(!modifiable); - layout->addWidget(linkpath_edit); - linkpath_edit->setVisible(!linkvalue.empty()); - if(modifyObjectFlags.PROPERTY_WIDGET_FLAG) - connect(linkpath_edit, &QLineEdit::textChanged, [=](){ WidgetDirty(); }); - else - connect(linkpath_edit, &QLineEdit::editingFinished, this, &QDisplayDataInfoWidget::linkEdited); - } - else - { - linkpath_edit=nullptr; - } -} - -void QDisplayDataInfoWidget::linkModification() -{ - if (linkpath_edit->isVisible() && linkpath_edit->text().isEmpty()) - linkpath_edit->setVisible(false); - else - { - linkpath_edit->setVisible(true); - //Open a dialog window to let the user select the data he wants to link - } -} -void QDisplayDataInfoWidget::linkEdited() -{ - data->setParent(linkpath_edit->text().toStdString() ); -} - -void QDisplayDataInfoWidget::formatHelperString(const std::string& helper, std::string& final_text) -{ - std::string label_text=helper; - numLines_ = 0; - while (!label_text.empty()) - { - const std::string::size_type pos = label_text.find('\n'); - std::string current_sentence; - if (pos != std::string::npos) - current_sentence = label_text.substr(0,pos+1); - else - current_sentence = label_text; - if (current_sentence.size() > SIZE_TEXT) - { - const std::size_t cut = current_sentence.size()/SIZE_TEXT; - for (std::size_t index_cut=1; index_cut<=cut; index_cut++) - { - const std::string::size_type numero_char=current_sentence.rfind(' ',SIZE_TEXT*index_cut); - current_sentence = current_sentence.insert(numero_char+1,1,'\n'); - numLines_++; - } - } - if (pos != std::string::npos) label_text = label_text.substr(pos+1); - else label_text = ""; - final_text += current_sentence; - numLines_++; - } -} - -unsigned int QDisplayDataInfoWidget::numLines(const std::string& str) -{ - std::string::size_type newline_pos; - unsigned int numlines = 1; - newline_pos = str.find('\n',0); - while( newline_pos != std::string::npos ) - { - numlines++; - newline_pos = str.find('\n',newline_pos+1); - } - return numlines; -} - - -/* QPushButtonUpdater definitions */ - -void QPushButtonUpdater::setDisplayed(bool b) -{ - - if (b) - { - this->setText(QString("Hide the values")); - } - else - { - this->setText(QString("Display the values")); - } - -} - -} // namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/DataWidget.h b/Sofa/GUI/Qt/src/sofa/gui/qt/DataWidget.h deleted file mode 100644 index 7b068fedf58..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/DataWidget.h +++ /dev/null @@ -1,311 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ - -#pragma once -#include -#include "ModifyObject.h" -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - - -//If a table has higher than MAX_NUM_ELEM, its data won't be loaded at the creation of the window -//user has to click on the button update to see the content -#define MAX_NUM_ELEM 100 - - -namespace sofa::gui::qt -{ - -/** -*\brief Abstract Interface of a qwidget which allows to edit a data. -*/ -class SOFA_GUI_QT_API DataWidget : public QWidget -{ - Q_OBJECT -public: - // - // Factory related code - // - - struct CreatorArgument - { - std::string name; - core::objectmodel::BaseData* data; - QWidget* parent; - bool readOnly; - }; - - template - static T* create(T*, const CreatorArgument& arg) - { - typename T::MyData* data = dynamic_cast(arg.data); - if(!data) return nullptr; - T* instance = new T(arg.parent, arg.name.c_str(), data); - if ( !instance->createWidgets() ) - { - delete instance; - instance = nullptr; - } - if (instance) - { - instance->setDataReadOnly(arg.readOnly); - } - return instance; - } - - static DataWidget *CreateDataWidget(const DataWidget::CreatorArgument &dwarg); - - -public slots: - /// Checks that widget has been edited - /// emit DataOwnerDirty in case the name field has been modified - void updateDataValue(); - - /// First checks that the widget is not currently being edited - /// checks that the data has changed since the last time the widget - /// has read the data value. - /// ultimately read the data value. - void updateWidgetValue(); - - /// You call this slot anytime you want to specify that the widget - /// value is out of sync with the underlying data value. - void setWidgetDirty(bool b=true); - - /// slot to be called if DataWidget has not been filled at constructor and need to be filled - /// at first call. Will turn toFill to true only if isFilled == false - void fillFromData(); - -signals: - /// Emitted each time setWidgetDirty is called. You can also emit - /// it if you want to tell the widget value is out of sync with - /// the underlying data value. - void WidgetDirty(bool ); - /// Currently this signal is used to reflect the changes of the - /// component name in the sofaListview. - void DataOwnerDirty(bool ); - - void dataValueChanged(QString dataValueString ); -public: - typedef core::objectmodel::BaseData MyData; - - DataWidget(QWidget* parent,const char* name, MyData* d); - - ~DataWidget() override; - - virtual void setData( MyData* d); - - /// BaseData pointer accessor function. - inline const core::objectmodel::BaseData* getBaseData() const { return baseData; } - inline core::objectmodel::BaseData* getBaseData() { return baseData; } - - void updateVisibility(); - - inline bool isDirty() { return dirty; } - - /// return if DataWidget as been filled - bool isFilled() { return m_isFilled; } - /// method to warn if Data has not been filled at constructor. - void setFilled(bool value) { m_isFilled = value; } - - /// The implementation of this method holds the widget creation and the signal / slot - /// connections. - virtual bool createWidgets() = 0; - /// This method is called after createWidgets to configure whether the created widgets should be read-only - virtual void setDataReadOnly(bool readOnly) = 0; - /// Helper method to give a size. - virtual unsigned int sizeWidget() {return 1;} - /// Helper method for column. - virtual unsigned int numColumnWidget() {return 3;} - -protected: - /// The implementation of this method tells how the widget reads the value of the data. - virtual void readFromData() = 0; - /// The implementation of this methods needs to tell how the widget can write its value - /// in the data - virtual void writeToData() = 0; - - core::objectmodel::BaseData* baseData; - bool dirty; - int counter; - bool m_isFilled; ///< tell if DataWidget has been filled from Data true by default - bool m_toFill; ///< bool to warn action is needed to fill Data, false by default -}; - - - -/** -*\brief This class is basically the same as DataWidget, except that it -* takes a template parameter so the actual type of Data can be retrieved -* through the getData() accessor. In most cases you will need to derive -* from this class to implement the edition of your data in the GUI. -**/ -template -class TDataWidget : public DataWidget -{ - -public: - typedef sofa::core::objectmodel::Data MyTData; - - template - static RealObject* create( RealObject*, CreatorArgument& arg) - { - RealObject* obj = nullptr; - - typename RealObject::MyTData* realData = dynamic_cast< typename RealObject::MyTData* >(arg.data); - if (!realData) - { - if constexpr (std::is_constructible_v) - { - if (arg.data) - { - const void* rawPtr = arg.data->getValueVoidPtr(); - - //note that this cast is not type-safe. You need to check - //later in createWidget that the baseData is the expected - //type. You can use getValueTypeString for example - if (const T* castedPtr = static_cast(rawPtr)) - { - obj = new RealObject(arg.parent, arg.name.c_str(), arg.data, castedPtr); - } - } - } - } - else - { - if constexpr (std::is_constructible_v) - { - obj = new RealObject(arg.parent, arg.name.c_str(), realData); - } - } - - if (obj) - { - if( !obj->createWidgets() ) - { - delete obj; - obj = nullptr; - } - if (obj) - { - obj->setDataReadOnly(arg.readOnly); - } - } - return obj; - } - - TDataWidget(QWidget* parent,const char* name, core::objectmodel::BaseData* d, const T* object): - DataWidget(parent, name, d), - Tdata(nullptr) - { - SOFA_UNUSED(object); - } - - TDataWidget(QWidget* parent,const char* name, MyTData* d): - DataWidget(parent,name,d),Tdata(d) {} - - /// Accessor function. Gives you the actual data instead - /// of a BaseData pointer of it like in getBaseData(). - sofa::core::objectmodel::Data* getData() {return Tdata;} - const sofa::core::objectmodel::Data* getData() const {return Tdata;} - - using DataWidget::setData; - inline virtual void setData(MyTData* d) - { - Tdata = d; - } -protected: - MyTData* Tdata { nullptr }; -}; - - - - - -class SOFA_GUI_QT_API QPushButtonUpdater: public QPushButton -{ - Q_OBJECT -public: - - QPushButtonUpdater( const QString & text, QWidget * parent = nullptr ): QPushButton(text,parent) {}; - -public Q_SLOTS: - void setDisplayed(bool b); -}; - -//Widget used to display the name of a Data and if needed the link to another Data -class SOFA_GUI_QT_API QDisplayDataInfoWidget: public QWidget -{ - Q_OBJECT -public: - QDisplayDataInfoWidget(QWidget* parent, const std::string& helper, core::objectmodel::BaseData* d, bool modifiable, const ModifyObjectFlags& modifyObjectFlags); -public Q_SLOTS: - void linkModification(); - void linkEdited(); - unsigned int getNumLines() const { return numLines_;} - -signals: - void WidgetDirty(); - -protected: - static QIcon& LinkIcon() - { - static QIcon icon; - if(icon.isNull()) - { - std::string filename = "textures/link.png"; - sofa::helper::system::DataRepository.findFile(filename); - icon = QIcon(filename.c_str()); - } - return icon; - } - -protected: - void formatHelperString(const std::string& helper, std::string& final_text); - static unsigned int numLines(const std::string& str); - core::objectmodel::BaseData* data; - unsigned int numLines_; - QLineEdit *linkpath_edit; -}; - -typedef sofa::helper::Factory DataWidgetFactory; - -} //namespace sofa::gui::qt - -//MOC_SKIP_BEGIN -#if !defined(SOFA_BUILD_SOFA_GUI_QT) -namespace sofa::helper -{ -//delay load of the specialized Factory class. unique definition reside in the cpp file -extern template class SOFA_GUI_QT_API Factory; -} // namespace sofa::helper - -#endif -//MOC_SKIP_END diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/DialogAddObject.ui b/Sofa/GUI/Qt/src/sofa/gui/qt/DialogAddObject.ui deleted file mode 100644 index 97b4a58d5ef..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/DialogAddObject.ui +++ /dev/null @@ -1,235 +0,0 @@ - - - DialogAddObject - - - - 0 - 0 - 556 - 264 - - - - - 0 - 0 - - - - Add a scene or an object - - - true - - - - - - - - Scale - - - false - - - - - - - - - - - - - - Open File - - - false - - - - - - - - - - - - - - - - - - - - - Qt::Horizontal - - - QSizePolicy::Expanding - - - - 20 - 20 - - - - - - - - &OK - - - - - - true - - - true - - - - - - - &Cancel - - - - - - true - - - - - - - - - - - Initial Position - - - false - - - - - - - - - - - - - - - - - - - - Initial Rotation - - - false - - - - - - - - - - - - - - - - - - Objects - - - - - - custom - - - - - - - - - - - - - openFileButton - clicked() - DialogAddObject - fileOpen() - - - 20 - 20 - - - 20 - 20 - - - - - buttonOk - clicked() - DialogAddObject - accept() - - - 20 - 20 - - - 20 - 20 - - - - - buttonCancel - clicked() - DialogAddObject - reject() - - - 20 - 20 - - - 20 - 20 - - - - - - fileOpen() - - diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/DisplayFlagsDataWidget.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/DisplayFlagsDataWidget.cpp deleted file mode 100644 index e09f283ef12..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/DisplayFlagsDataWidget.cpp +++ /dev/null @@ -1,220 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#include - -#include -#include -#include - -namespace sofa::gui::qt -{ -using namespace sofa::core::objectmodel; -using namespace sofa::core::visual; - -helper::Creator DWClass_DisplayFlags("widget_displayFlags",true); - - - -DisplayFlagWidget::DisplayFlagWidget(QWidget* parent, const char* name, Qt::WindowFlags f ): - QTreeWidget(parent /*,name ,f*/) -{ - this->setWindowFlags(f); - this->setObjectName(name); - - //addColumn(QString::null); - setRootIsDecorated( true ); - //setTreeStepSize( 12 ); - header()->hide(); - clear(); - - - setMouseTracking(false); - - //setFocusPolicy(Qt::NoFocus); - - setFrameShadow(QFrame::Plain); - setFrameShape(QFrame::NoFrame ); - - this->setSortingEnabled(false); - //setSortColumn(-1); - QTreeWidgetItem* itemShowAll = new QTreeWidgetItem(this); - this->setTreeWidgetNodeCheckable(itemShowAll, "All"); - - QTreeWidgetItem* itemShowVisual = new QTreeWidgetItem(itemShowAll); - this->setTreeWidgetNodeCheckable(itemShowVisual, "Visual"); - - itemShowFlag[VISUALMODELS] = new QTreeWidgetItem(itemShowVisual); - this->setTreeWidgetCheckable(itemShowFlag[VISUALMODELS], "Visual Models"); - - QTreeWidgetItem* itemShowBehavior = new QTreeWidgetItem(itemShowAll); - this->setTreeWidgetNodeCheckable(itemShowBehavior, "Behavior"); - - itemShowFlag[BEHAVIORMODELS] = new QTreeWidgetItem(itemShowBehavior); - this->setTreeWidgetCheckable(itemShowFlag[BEHAVIORMODELS], "Behavior Models"); - itemShowFlag[FORCEFIELDS] = new QTreeWidgetItem(itemShowBehavior, itemShowFlag[BEHAVIORMODELS]); - this->setTreeWidgetCheckable(itemShowFlag[FORCEFIELDS], "Force Fields"); - itemShowFlag[INTERACTIONFORCEFIELDS] = new QTreeWidgetItem(itemShowBehavior, itemShowFlag[FORCEFIELDS]); - this->setTreeWidgetCheckable(itemShowFlag[INTERACTIONFORCEFIELDS], "Interactions"); - - QTreeWidgetItem* itemShowCollision = new QTreeWidgetItem(itemShowAll, itemShowBehavior); - this->setTreeWidgetNodeCheckable(itemShowCollision, "Collision"); - itemShowFlag[COLLISIONMODELS] = new QTreeWidgetItem(itemShowCollision); - this->setTreeWidgetCheckable(itemShowFlag[COLLISIONMODELS], "Collision Models"); - itemShowFlag[BOUNDINGCOLLISIONMODELS] = new QTreeWidgetItem(itemShowCollision, itemShowFlag[COLLISIONMODELS]); - this->setTreeWidgetCheckable(itemShowFlag[BOUNDINGCOLLISIONMODELS], "Bounding Collision Models"); - itemShowFlag[DETECTIONOUTPUTS] = new QTreeWidgetItem(itemShowCollision, itemShowFlag[COLLISIONMODELS]); - this->setTreeWidgetCheckable(itemShowFlag[DETECTIONOUTPUTS], "Detection Outputs"); - QTreeWidgetItem* itemShowMapping = new QTreeWidgetItem(itemShowAll, itemShowCollision); - this->setTreeWidgetNodeCheckable(itemShowMapping, "Mapping"); - itemShowFlag[MAPPINGS] = new QTreeWidgetItem(itemShowMapping); - this->setTreeWidgetCheckable(itemShowFlag[MAPPINGS], "Visual Mappings"); - itemShowFlag[MECHANICALMAPPINGS] = new QTreeWidgetItem(itemShowMapping, itemShowFlag[MAPPINGS]); - this->setTreeWidgetCheckable(itemShowFlag[MECHANICALMAPPINGS], "Mechanical Mappings"); - QTreeWidgetItem* itemShowOptions = new QTreeWidgetItem(this, itemShowAll); - this->setTreeWidgetNodeCheckable(itemShowOptions, "Options"); - itemShowFlag[RENDERING] = new QTreeWidgetItem(itemShowOptions); - this->setTreeWidgetCheckable(itemShowFlag[RENDERING], "Advanced Rendering"); - itemShowFlag[WIREFRAME] = new QTreeWidgetItem(itemShowOptions); - this->setTreeWidgetCheckable(itemShowFlag[WIREFRAME], "Wire Frame"); - itemShowFlag[NORMALS] = new QTreeWidgetItem(itemShowOptions, itemShowFlag[WIREFRAME]); - this->setTreeWidgetCheckable(itemShowFlag[NORMALS], "Normals"); - this->addTopLevelItem(itemShowAll); - itemShowAll->setExpanded(true); - itemShowAll->addChild(itemShowVisual); - itemShowVisual->addChild(itemShowFlag[VISUALMODELS]); - itemShowAll->addChild(itemShowBehavior); - itemShowBehavior->addChild(itemShowFlag[BEHAVIORMODELS]); - itemShowBehavior->addChild(itemShowFlag[FORCEFIELDS]); - itemShowBehavior->addChild(itemShowFlag[INTERACTIONFORCEFIELDS]); - itemShowAll->addChild(itemShowCollision); - itemShowCollision->addChild(itemShowFlag[COLLISIONMODELS]); - itemShowCollision->addChild(itemShowFlag[BOUNDINGCOLLISIONMODELS]); - itemShowCollision->addChild(itemShowFlag[DETECTIONOUTPUTS]); - itemShowAll->addChild(itemShowMapping); - itemShowMapping->addChild(itemShowFlag[MAPPINGS]); - itemShowMapping->addChild(itemShowFlag[MECHANICALMAPPINGS]); - - this->addTopLevelItem(itemShowOptions); - itemShowOptions->setExpanded(true); - itemShowOptions->addChild(itemShowFlag[RENDERING]); - itemShowOptions->addChild(itemShowFlag[WIREFRAME]); - itemShowOptions->addChild(itemShowFlag[NORMALS]); - for (int i=0; isetText(0, name); - w->setExpanded(true); - w->setFlags(w->flags() | Qt::ItemIsUserCheckable); - -} - -void DisplayFlagWidget::setTreeWidgetNodeCheckable(QTreeWidgetItem* w, const char* name) -{ - w->setText(0, name); - w->setExpanded(true); - w->setFlags(w->flags() | Qt::ItemIsUserCheckable | Qt::ItemIsAutoTristate); - -} - -void DisplayFlagWidget::findChildren(QTreeWidgetItem *item, std::vector &children) -{ - for(int i=0; ichildCount() ; i++) - { - QTreeWidgetItem * child = (QTreeWidgetItem * )item->child(i); - children.push_back(child); - findChildren(child,children); - } -} - -void DisplayFlagWidget::mouseReleaseEvent ( QMouseEvent * e ) -{ - //if ( QTreeWidgetItem *item = dynamic_cast(itemAt(contentsToViewport(e->pos()))) ) - QTreeWidgetItem *item = this->itemAt(e->pos()); - - if ( e->button() == Qt::LeftButton && item ) - { - const bool value = !(item->checkState(0) == Qt::Checked); - item->setCheckState(0, ( (value) ? Qt::Checked : Qt::Unchecked) ); - - emit clicked(); - } -} - -bool DisplayFlagsDataWidget::createWidgets() -{ - flags = new DisplayFlagWidget(this); - QVBoxLayout* layout = new QVBoxLayout(this); - layout->addWidget(flags); - connect(flags, &DisplayFlagWidget::clicked, [=](){ setWidgetDirty(true); }); - setMinimumSize(QSize(50,400)); - layout->setContentsMargins(2,2,4,4); - return true; -} - -void DisplayFlagsDataWidget::setDataReadOnly(bool readOnly) -{ - flags->setEnabled(!readOnly); -} - -void DisplayFlagsDataWidget::readFromData() -{ - const DisplayFlags& displayFlags = this->getData()->getValue(); - if (isRoot) - flags->setFlag(DisplayFlagWidget::VISUALMODELS, sofa::core::visual::merge_tristate(true,displayFlags.getShowVisualModels())); - else - flags->setFlag(DisplayFlagWidget::VISUALMODELS, displayFlags.getShowVisualModels()); - flags->setFlag(DisplayFlagWidget::BEHAVIORMODELS, displayFlags.getShowBehaviorModels()); - flags->setFlag(DisplayFlagWidget::COLLISIONMODELS, displayFlags.getShowCollisionModels()); - flags->setFlag(DisplayFlagWidget::BOUNDINGCOLLISIONMODELS, displayFlags.getShowBoundingCollisionModels()); - flags->setFlag(DisplayFlagWidget::DETECTIONOUTPUTS, displayFlags.getShowDetectionOutputs()); - flags->setFlag(DisplayFlagWidget::MAPPINGS, displayFlags.getShowMappings()); - flags->setFlag(DisplayFlagWidget::MECHANICALMAPPINGS, displayFlags.getShowMechanicalMappings()); - flags->setFlag(DisplayFlagWidget::FORCEFIELDS, displayFlags.getShowForceFields()); - flags->setFlag(DisplayFlagWidget::INTERACTIONFORCEFIELDS, displayFlags.getShowInteractionForceFields()); - flags->setFlag(DisplayFlagWidget::RENDERING, displayFlags.getShowAdvancedRendering()); - flags->setFlag(DisplayFlagWidget::WIREFRAME, displayFlags.getShowWireFrame()); - flags->setFlag(DisplayFlagWidget::NORMALS, displayFlags.getShowNormals()); -} - -void DisplayFlagsDataWidget::writeToData() -{ - DisplayFlags& displayFlags = *this->getData()->beginEdit(); - - displayFlags.setShowVisualModels(flags->getFlag(DisplayFlagWidget::VISUALMODELS)); - displayFlags.setShowBehaviorModels(flags->getFlag(DisplayFlagWidget::BEHAVIORMODELS)); - displayFlags.setShowCollisionModels(flags->getFlag(DisplayFlagWidget::COLLISIONMODELS)); - displayFlags.setShowBoundingCollisionModels(flags->getFlag(DisplayFlagWidget::BOUNDINGCOLLISIONMODELS)); - displayFlags.setShowDetectionOutputs(flags->getFlag(DisplayFlagWidget::DETECTIONOUTPUTS)); - displayFlags.setShowMappings(flags->getFlag(DisplayFlagWidget::MAPPINGS)); - displayFlags.setShowMechanicalMappings(flags->getFlag(DisplayFlagWidget::MECHANICALMAPPINGS)); - displayFlags.setShowForceFields(flags->getFlag(DisplayFlagWidget::FORCEFIELDS)); - displayFlags.setShowInteractionForceFields(flags->getFlag(DisplayFlagWidget::INTERACTIONFORCEFIELDS)); - displayFlags.setShowAdvancedRendering(flags->getFlag(DisplayFlagWidget::RENDERING)); - displayFlags.setShowWireFrame(flags->getFlag(DisplayFlagWidget::WIREFRAME)); - displayFlags.setShowNormals(flags->getFlag(DisplayFlagWidget::NORMALS)); - this->getData()->endEdit(); - -} - -} // namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/DisplayFlagsDataWidget.h b/Sofa/GUI/Qt/src/sofa/gui/qt/DisplayFlagsDataWidget.h deleted file mode 100644 index 0f007836d8b..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/DisplayFlagsDataWidget.h +++ /dev/null @@ -1,112 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include -#include - -#include -#include -#include -#include -#include -#include - -namespace sofa::gui::qt -{ - - -class SOFA_GUI_QT_API DisplayFlagWidget : public QTreeWidget -{ - Q_OBJECT; -public: - - enum VISUAL_FLAG - { - VISUALMODELS, - BEHAVIORMODELS, - COLLISIONMODELS, - BOUNDINGCOLLISIONMODELS, - DETECTIONOUTPUTS, - MAPPINGS,MECHANICALMAPPINGS, - FORCEFIELDS, - INTERACTIONFORCEFIELDS, - RENDERING, - WIREFRAME, - NORMALS, - ALLFLAGS - }; - - - DisplayFlagWidget(QWidget* parent, const char* name= nullptr, Qt::WindowFlags f = Qt::WindowType::Widget ); - - bool getFlag(int idx) {return itemShowFlag[idx]->checkState(0) == Qt::Checked;} - void setFlag(int idx, bool value) - { - itemShowFlag[idx]->setCheckState(0, (value) ? Qt::Checked : Qt::Unchecked) ; - } - -Q_SIGNALS: - void change(int,bool); - void clicked(); - - -protected: - void setTreeWidgetNodeCheckable(QTreeWidgetItem* w, const char* name); - void setTreeWidgetCheckable(QTreeWidgetItem* w, const char* name); - - void mouseReleaseEvent ( QMouseEvent * e ) override; - - void findChildren(QTreeWidgetItem *, std::vector &children); - - - QTreeWidgetItem* itemShowFlag[ALLFLAGS]; - std::map< QTreeWidgetItem*, int > mapFlag; -}; - - -class SOFA_GUI_QT_API DisplayFlagsDataWidget : public TDataWidget< sofa::core::visual::DisplayFlags > -{ - Q_OBJECT; -public: - typedef sofa::core::visual::DisplayFlags DisplayFlags; - DisplayFlagsDataWidget(QWidget* parent, const char* name, core::objectmodel::Data* data, bool root = false) - :TDataWidget(parent,name,data), isRoot(root) - { - } - - virtual bool createWidgets(); - virtual void setDataReadOnly(bool readOnly); - -protected: - - virtual void readFromData(); - virtual void writeToData(); - virtual unsigned int sizeWidget() {return 8;} - virtual unsigned int numColumnWidget() {return 1;} - - DisplayFlagWidget* flags; - bool isRoot; - - -}; - -} // namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/Doc_Viewer/index.html b/Sofa/GUI/Qt/src/sofa/gui/qt/Doc_Viewer/index.html deleted file mode 100644 index cb83d966dcd..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/Doc_Viewer/index.html +++ /dev/null @@ -1,284 +0,0 @@ - - - - - - - - - - - -

The Graphic User -Interface in SOFA

- -

I) An overview of the GUI
-II) Structure
-III) Viewers
-IV) QtOgreViewer
-

-
-

-
- -


-
-

-
    -
  1. An Overview of the GUI
    -
    -
    The main - program is divided in two main parts, a rendering window and a control - panel. A slider between them sets their - dimension.

    -
  2. -
-
    -

    -
    -
    -

    - -

    Common Interactions:

    -
      -
    1. ANIM: launch the Simulation. - The text field, DTsets the step of time used during the - simulation.

      -
    2. -
    3. STEP: the simulation will - compute its state at the time: t+DT.

      -
    4. -
    5. Reset Scene: reset the - Simulation to the initial time, t=0.

      -
    6. -
    7. Reset View: set your camera at - initial configuration found in a view file, or default - configuration.

      -
    8. -
    9. Save View: keep in memory the - position and orientation of the camera, for the next time you will load - the scene.

      -
    10. -
    11. Save Screenshot: take a picture - of the current scene.

      -
    12. -
    13. Direct interaction with the scene is - possible. To do so, maintain SHIFT button pressed and click on a - collision model present in the scene; A “Pick Interactor” will be - created, and attached to the picked collision model, if any, with a - spring.

      -

      If - you release the mouse button first, the spring is removed.

      -

      If - you release the SHIFT button first, the spring is fixed and exists - until you pick another point in the scene.

      -
    14. -
    15. Several tabs give more information - about the current scene:
      - This images were taken for the scene shown above.

      -

      - - -
      -

      -
      -
      -
      -
      -

      -

      -


      -
      -
      - - depending on the viewer, you can control what is displayed, and the - way it is.
      - - statistical outputs during the simulation (position and velocity for - each elements, time passed at each branch of the graph, gnuplot files) -
      - - the graph of the scene, with all the nodes. You can export this graph - into a nice png file.
      - - the last tab, “
      Viewergives the name and information about - the possibilities of the current viewer, and the shortcuts.
      -
      - Double clicking on a node allows you to modify several values and - change dynamically the simulation (here, the TetrahedronFEMForceField - node). In some cases, you need to press the update button - to apply the changes.

      -

      -
      -

      -
    16. -
    -
-
    -
  1. Structure
    -
    The main interface uses a Qt - environment (Qt3.3 for Linux and Qt4 for Windows). The Main window, - providing all the interactions possible with the simulation,was created - using QtDesigner. The RealGUI contains the viewer which can be selected - directly in the main window. The possible choices depends on the - configuration file, sofa-default.cfg.

    -
      -

      -
      - The RealGUI gets a pointer to the root of the scene graph, and controls - the application by calling class - sofa::simulation::tree:Simulation.

      -

      The RealGUI uses an internal timer to repeatedly call - method sofa::simulation::tree:Simulation::animate(GNode* root, double - dt) and then update the viewer.

      -


      - To select the - viewers:

      -
    -
      -

      sofa-default.cfg located in - your Sofa main directory allows to define what kind of viewer you are - willing to have.
      - By commenting (adding a“#” in front of the line) or uncommenting - (removing the “#”) lines, you can allow multiple kind of viewer.
      - For example, if you want to have the choice to render the scene with - QtGLViewer and QtOgreViewer, just modify sofa-default.cfg as below:
      -
      - # DEFINES += SOFA_GUI_QT
      - DEFINES += SOFA_GUI_QTOGREVIEWER
      - DEFINES += SOFA_GUI_QGLVIEWER

      -
      -
      Then, in the main window, you select the kind of viewer you - want. Qt will appear in gray, whereas QGLViewer and Ogre will be - available.
      - Warning, you must reload the scene when this change is made!

      -
      -
    -
  2. -
  3. Viewers

    -
  4. -
-
    -

    -
    -
    - The Viewers available now are:

    -
      -
    1. QtViewer: uses openGL, derives a - QGLWidget in order to be embedded within Qt. This class will probably - not be maintained. Use QtGLViewer instead.

      -
    2. -
    3. QtGLViewer: uses the library - QGLViewer. Provides lots of functions to navigate through the scene.
      - More information at QGLViewer - Website.

      -
    4. -
    5. QtOgreViewer: Embed Ogre within a - Qwidget. Allows to get great visuals, through an intensive use of - shaders, materials.
      - Shadows are provided, but all the controls (show the mechanical - objects, force fields...) are disabled.
      - Warning: Ogre is not provided by Sofa, you have to previously install - Ogre on your computer.
      - The support works with Windows, Linux.
      - - For Windows, the easiest way to proceed is to download and install - the SDK.
      - - For Linux, you have to compile from the source. If Ogre's library - aren't installed in the default directory “
      /usr/local/lib/Ogre” - then, you have to set the - correct path in “YourSofaDirectory/share/config/plugins_unix.cfg
      - To get it: Ogre - Website

      -
    6. -
    -
-
    -
  1. QtOgreViewer

    -

    To - use this viewer, you must have Ogre installed on your computer.
    - The configuration files are located in: “
    YourSofaDirectory/share/config/. plugins.cfgdescribes the plugins used by Ogre for - Windows, and “plugins_unix.cfgfor... Linux.
    - By creating a .scene file with the same name as the simulation, you can - provide additional models, lights, visuals to the simulation( see Ogre - documentation for mode details ). This won't interact with Sofa, but - allows you to get a better render.

    -

    Additional textures, materials, - shaders... in Ogre, have to be put in “ - YourSofaDirectory/share/textures” or “ - YourSofaDirectory/share/materials” or - “YourSofaDirectory/share/shaders” ... They are loaded at the - start-up of the viewer.

    -

    -
    -
    -

    -
    -

    -
  2. -
- -


-

- -


-

- -


-

- -


-

- -


-

- -


-

- -


-

- -


-

- - diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/Doc_Viewer/index_fichiers/GUI.png b/Sofa/GUI/Qt/src/sofa/gui/qt/Doc_Viewer/index_fichiers/GUI.png deleted file mode 100644 index ad4d140c97d691db53939ecfc92bd86de967fe02..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8483 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 Y85OcVyhZ;U+cki)g2wZTXXfwz4N35EApigX diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/Doc_Viewer/index_fichiers/GUI_caption0.png b/Sofa/GUI/Qt/src/sofa/gui/qt/Doc_Viewer/index_fichiers/GUI_caption0.png deleted file mode 100644 index 940789a539b370a462ebd632a567a6ab11c7a6b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 69466 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μufF#BfqZ6DQiT^ny$Ew#0LG}OXp`zY~xCvbIfWb8y_weI^W zq4WUD&OjhMvts{?>MeMdhn+!lD-)uytBn{Fe{}P)NK~^yml;zB$kbzOx#YHMFxjX` z(1%;Stt6}=uD_GrTO2i0qSajOS#jucFD5bQSy5g6eX9&)B9N*~`ZG#f`){(A7b z_MzD(xjha??zoBZ325t;a!dWUF5VUIEdb;84)$2Ki$go}=PQEKD&biIv&b?~@4rxu z{ZDlDdbRBY)rAJZX7&QTmDZYwH8nUnW`;#2pY^mGNR_0x^Y?c)vb>l&&2HG1?W^Kj z>7#rxCcs)@2^LGEFjR*=oL~KM@MnSA?zz{#--{WSzj}JT+fxxj;H6x?J6}u1pzWZE zN2ThNasPfe=LO5toe5Q^T!UE&(&X|)>j>}Gihz+0U9MF54tS7fUjL}u<`2B;;6BMD z(seX_nDu?a_Qn)Lt=2EuU+r8z$3WSajrsNgCANt1ueFlEh~w0L0kq0Wu=i5=RtSod z40`Q@ycHD=dBQktS&#sO%2{^3fh0xazm1$cr=BeeC%<83Lr-X%0`T#--I@p#+}=E@ z$h@z{OP8^tkkj>&sjGEYvyaP zJL4TLOLpC(mNU`a+JJhZ;gx5(??gqxBRUz6N;iLhZ*}_$x|Glqc+rsN*7NgbC?pgF zL}HJ)^83%{?s~Qdb=)yWUV0KO0MTnEc_YS`c+l*y1idS zrNt|8Kpr>9WJUgCGZ`ar6>74=^cJKGmCpl9ov=v9hAVIbV};q`WwIWDxq=Ci|gvh){* zwVTgOOHBM#=NaESCrD3kF5k6w=LkZpQ^_Am1_*vX@Bz%G?83_cwrQ&44hmN~4!kv! z1OqxhnnyotulTJS!LJAHCU<2dVPOV#6g}fgeA=h3cCq(cG^ZskGv&IX)d7K>&-Zly zhLHBYOzf(2TDDW&4v4JPGx~ODpF26flac#VNW!&za5jj#DLwd2IW06(X^M4d`}{vF z!oy7!)h-2N0s2Y}ouJx6{oqvxWh?EKrjtDQ|BA>-40{!KMVFV*89%O3u6M*c;h_L3 zR<2xvbUZ2_KUGm6_*-;7_sJ}?@ZK8_xDeJF?{j)t=cOED3nb8N3IkGh9M;v36S-c5 zwJiNGJkrcrcT&Wj0*K?!J>%02f2SLB-|YE#4oqp&tFvQa?T4^5#UvRa^*YWpC*11P znr14J7)o4+DrpQO3AuadtK)$PoAS!*}NT2qI%NmQ2|nz8!!EL8qAzla6GOopeZON0yv~n z^NMYr`%C%ZA>7ih=Kan|^s-M7BdkNto_+BxC*hutYsmIF2>>dV$Lq_|zclvjWKIX} z*j62G_l+!P!7YEE@S};VsLlPd(&e^d+rHB*Spoq81bUVtm>~bcL@Tk; zzGW+XY`0`Zz&~GjYj^WQT&<}+z+v$##j$L3a6!2p74HRr(p<#z-7vRtpl6tx{_!Qd zzUJZx)822HiVpm7yXI}N@xDTG96UDXPU9>34vp&np9NUPX|@m65c6>1M(xKDX<2~7jt zHwY=|wxZnn$1+{Q2|O z_U@nMCHb6Q$GSi%nZF`m%C^dO0>%od^eXg@CJyWWU8UqLl=z&+D+FX5#8^@bRQw=W zJyz?^5v#=HBi7+D#8}Q)z?-W1DB>}Z3WvPZ7kWRIZrRdO0jjpjh-~%>GdT4B>FS-w z`X1^|YhJOWL1+nXye^;U_l?5ufF)^me2TBSe zP#ss;O}ltlky!a#k?3~l8h{T$L zkE!wcW@vEdfiO`@2``5HYVv$v_qx(-C#Sz1$yo;aO`(h~Fjfg-ky=D!6Wshq>d0eC zyJ)51^TanIFWg!Pek)PW`1su7;@$t=5k5Q427;}TnL%Jpl7eYOo_yo*UE1f!gUHMdL)6_qx#cna zKHyOD9!W2JS2QdGqwxm91MAvWfc@7~R7~Y1^a!@c9ba1%5GPc_Vbx>%U2@F=eHoGC ztzBJh0UY{nnBiq?k0sWQ3bOpP+H>VAK@S;M_y1Osq3PT#wo)B=n+%ZRoWC=$zvq0a z>``GcbG~=-rW0w&0r%{SM?1kb>b7A;t_Q5ipxF$C;U-jiU*&4gA}Z&HZKgI{I2XW& z;OJrL&GO=CG6+wMU#!GoSo6zE`%j5>qBC+`fTo6pDTZ2qPt&gyvBs=}Rpf+&XwT3l zRztBVO>H)lU7YVoHmnu0Tb80nujvcT4+Vf7{TJe;4qo34vInB!ASs>}eO)^j|+?EFy z50RFY+)~ATn!49$g{B(F-yP`GO08oRz<%@$1 zuU?B+iQx5?s7te;HX}}V(;gIPCEZOPIf9uR)-tiCG!W7Olc~mxrdAWWQ z;dL){;S~(Tfh3ajqZ5FuG+D(^C1=28@y!x!%%1m`wr}})gAEY`@!(=9$Tl+z%IE08 z$s_E(nO`72i6ump@TqSns`CEyeJ$# zra>txP)$eqbW|^zDV`HzaxMc(>V^nSY%V23dMO&2MCZc)qv@=pqUydrP9suEN{e)h zbj*;_Ff`Ii4=pJ(q!JO+&FAivA$7wFhFa+3H#qR;U-t@-jokaE@I zmc|l#=kjV-g2;#v;!Fbr=CeR`wtAYs9LEfJUYY;unYOJiZ_I2~CY+TNrml&^7C>uW zmt!@+&4vgz+9h-P)NWE2DK_``fK60R3Iz*YW6u@upLh zu59LSICqaFO60w?8XIbqzFBEMk8NOw9@KmU9aYpH^l|#NqoKrDGd|BFG1M!n@S@C}OBK16Jy-S{5vP1UNXjce?UA10A_7Y9nfy%$iK@ugUPu)4SS zQW{Oqkg=J_;0C`lF9Qu5GD682p1Z3k_=L14{w&UIr}htNopOZP~~wZ*0F! zo*I-Bxzt$;`GSZL4^IEQ(_$=ijn2(P-}mxYH-iqiG-`^-IWz-w@NvwxSg$4eBK-VD zB#`YF>3dAaAfBYeaniikaD{}jvf#W4tiR)GN1-IF6f_dTWFFR0LuujB zad+MPHkl{75VDyYQlq@ZZ<(~>2WKOMHxI&t?(DmBeW)7Z`YcLLuFkg#JS9J$vHsZ5 zV2-F!dBbTDl*?(yzJe8fIs5yNFK|~(MplA0&#t`!VSJu8xoH+MWIz4g&#MjR*vZME z7P;vPELk5zJ(PkjI6fATjmC&3u+OY?-X540!!cWH zoTLnv4v9%YYzcVU?)7gn8h1{!{+`|4Jk0?7)jTi){K$JJ?xzl9yFBGZoDKLJwO;1M z+er`9Sb}X8+8ghSa-82x;BMLt)&}LxGiBcOc!{~Z6cu1c+aOx|3+Z~wd7y4-$jfY) znfc@hp|f-05h+XJe))xPEIv}I^{Ali6)ab~emDaQP-CuMtWg|jp^HUwd^;C;+R`lY zOV#rjR*4eJK@&;%1!yE4k~(xj7EY9DDvB}N<|FKY3{;0hCd40Vjy3!KiJm8s#0ZgS zMm1W>w8SI<=bFJ$;$L())zggk^K3`K%#pXyn+<{P^>}D4;hevM}G3wCKNGn62oD8X(>C*)BiynaIJ&MJ)uCr zQzQinG>EQSxYDwT&&3m{c%ZKR<0ZgFm0CWgkf&p$LC4`32RfXm@u5-)i(=5Q7Trm~ zwt(FJDS1ZZ$1|vOTnPfr+1b^vZF_Vi)FH!cM58DB2VAU=omn&Wy)MaN(P!TZO^T5% z8W|h!Z{s`|XhPQtK;1`Z1=TYiM;6HEe?@+3kFuH)1OP_rg@t<{E3-A;M~3cfPcwY| z+U}5bUq^3J=YA8|{;K*lbS#;ozIOd7DW0LqZYG(32Dt1PPw_l=&w+X@&e{0_;mx=! zDB1ieb32K22aI{Hu*c4r>>`itbI553$<#cTf42Xe91Nu4qzprla40s_;cy)1$ATGH z2Q&QVDtlvD?|TJ=TZVu#h9ct<1c`I1@d)!g-F|DwOp zCO=gg@uS<@9ES6=|GWfHyUIYDl^eZ{0$p~VBVw(X-afrhEXMozu3YVv9ClOa>bRpf z-!!Q0b%K-GhbYNjJBf4-J}2pc<&!5(5cPhl(qa*-Ftxv2g*w%H z@7Lv}xQmq3U#P+i>f@OqdNXU9*w#9XT?Z=abj3-Q6t-8IE*u|c_nC9hr5j@%wRayM~T(i1k6LPkk?{MpxHz+!^M?{AA`bgR!^{_?1u8 z?(i9%*jf*A{w=}$a|M}*GFcnrWD1i1Ca8y)=nwGLkP?xbpAZB?^faePn@CijosDbq zhWHZeXZy|fwGMef(6yDXnjAiQQJ8*Gf$YUbst|FeDe3l-?R zpVf>xFqw4O6P~3heqR1dNMea>;%JK^$pbwvDZ(K!&OLJ8E~ znLKMW@s3X(e&VsRhG_ZdbwJCS&8?9tL6$VKlt|g7q1#2UvtNzouoeYvj4O?W9%(-UggEZ-SCk!`U`W_MPxh5tg23?T zP&hWyo=^CrJ%y*$Z?GIr#?^dNGar6q55Pek|B#pXCjlsIBj27Ihs`HkE}Ww6OFqzW#JC(A|un1&Pp5V}MAPy`Cf_ zJfsN*sutUp4i|{<=&kg~Y;NlDqDW6}I-8q~aZJ%x#_FYXc248RNO3?qi$%IB=Yq6N z2IvEn@;cm<0ua98?@1eeiF=vR>nlo)?fx+ThyICTv9#;7(^JJ);MhvS_<|YB{=~W% zn>zcxuu%Xm$C1W=DnE4z3T!`;x}P?`#|=0)Sb1vl2F>Z`R)Gm!ywy#-LX;gt&g-&I zuM872*&Y+0fpWZOOgg^X+^8;!4PGOr73C7jeU%6%z1a*=v8pzG_lA$bYf!V%YZ| z8S94`F(*A`Ilcj>a0bhG7Ak5Se|9Yk?bg4+4Hh zq-z1BZ)zM{PYnjI^F~{0gt3eBgiAnH zOs^zTs8A-nN%)xQft@2u&aAh^K-=YeZpgL6-_My^Q^gwNy&3+PWK{%;;lQhy1e{7;N>2}VL-$!c3y(Vjf z)o4#p7bKSD&ygju5e(_+8GSusN9Lz+yQCvy%*3p7lwiCx`Pz=B)oV-_c|PTZs+u%V>tNPw96q0>zx_F(j5&Vd(rOypw)g#oB=bw|398X1 zYX9eie9%ugsohDfq5<-Xu{;F|-!yJqH)rhG_}u3u4yNtvfRw8Npxfu%u;d&$oNPAG zH{*b|GKl6u{wdqDl;veY%#Yc45~f?WNIFoq-dw`7i7@Gr={JF)y6G3Kis?tVQ&$Ar zI&&&Y?0<+7o%1Z7c#=)Ko2wzn4Nh7Qp4~w=^qX$}YAFCj@PIGV6XiqpIJTTT5aaAQ zGo>-%FzFu`>6C6c| zUs88d0lsMUYR`{ZKX1!F<=p>=;>aHt8$f2S8VOt^Aw&1|PD?K=T(^Pp-lX7DRCxWu zflbZYp(iJ}7u;^v(ci2ueBYg=No^?>>L&7M!@Y24zhtN&Q$-rst!zQ!JzL>a z2r8VvH(??H%wb@sy!jcNC&={V47$+LQQ{zM?xz@+Ety;BP&ZxD~`*3F6@iF87svn#w^F=AGEQMoBD z@#&cc@_KooXDi0K>5d9plMYkUlX+TaK!UN%yUVjtr~0y}nmTA?q>l8%3&8>FdTF;vV`k4cY_nJU3s)s>a~-`F9GVY?HA9m295JNCeVP3%3PGq(dmOSp8tx zeQ-0k-htmrx+Pxyj6)~iQu4*5Bqr76q#G6ho*-V{EAbB?6M@#A?VSG=U${M$e+xc^)%8|vsz1#w_fvHm%pK1*J0phk!E$t z>9&Z|ciH_+@|{fD);U6G#=R>MBERMHmj&A8ToD6gT%ttVT3;;*qBWPdG&Ri~cCFwzUgb(-4Jr)01nna1$mH`I(Na}R)qodD*->&x!-mo+VZdXQ5>p5sRr z&O*)#i%y!5V%z3gTa?$)oO|7E6heT1wMYJFjXPo{z?QvsZ3wv zc=qu!@YZ{>^b+G+4LE@6qUKhOCCbp)VblTJ8b(fcq1pS^+Vb)SC`)j@rMnaL3j`)m zw?5|DPXmr^^#0P~Lu1Vm+*oD@wFo`ndsroYNI1XYc+W5^y&W4&*=-Q}Yk`2e*ucHO z-9Telbx%DQ$?Q95EEUM`ayRhcCF*bydb(;nQI)jj+b-QF-;#biVJto0RfstcFNq!q z8!t{KDl#K{X^ITbfA{tCu)I8DjUk_M6{q z;kk(1J0*c?Ihi^tpoqAHBt5m>x;+E9J1tkx=QB~psmvMuVy|z-1<>b?TaFD2raugH z->E}O@6XR~FrDDSFO6QI#ecEUot)?-Pm%jcvVkO7t4UJ5ICi?49K3+HSU0k=zO%m& zeR(D@CD9E98J=(KQj@QmqU@!PUHOw0>XRi_abZNBAgF62n~@#KkiAIs5#FU75q}Z) ziqb&ckNB}$P9ERNu1=a#A->gW z3}&jC)mNI<_#u-tlqUn}PXCbls<{U9HfPUs)^p{0E7h1%I*A_@uc1kY73jr{Lnc$D zToh{eBoq1}dXXCre(=e*B$Y0(sq)4n;mLyZZ0+GyAj7ft=6zw>elTmp-INuU@}3m4hfrC2HYoxSZGR=$9VvauYmf!Kim?a+~e z>DFn7(#JQGkCmC?B9#)QVoajrp}Ifaasg>Yw3R>g{~2O1iru5}zpuF2MX+LW+O;oq~=vQcRA}uHL0w|KK|LK(*F{cjQmq z!ol~Fr5yq_k6GXlRDfZ%@3`A7?E-6QVt;r{JTCXiYo6+hpxIICurRW96c`(7pYXJt z@KnB_PXn`H$N#nALv9?xHL7Cq&9(>oxfmt2`o;wKWs7sL?!$m+pyp`Y^R~}RRR&)j zFUqI%;QW&dJXGRKdkTxVb#rCw;PqZnX1liVSsa6D-S6%Dw-C51OCx|p%Bl;Ji1ej~ zMK|#2p;IkA=Nd1~$f!#6w?^8tS=(?=8hM)nR}i@#ZsJxkimZnufb6V2@KT!OC;X8Z zw2=OVb>L~C&FCe2pf-_o4M)OXjW8sm4{S zyDf%MxB?WU+LDx%Es?n0s8*rJvOJ@ZCBx`{iShvgZxSuR9)Xfn*bSP)m12>MU?(N zTt%x*2Va`XNu=U8Q3oRoR&2YHb5vA12CCrs6`jbGT|ixnls(L~`mB|$t0Vq{j2{Ue zRFU)5S@6%lSv(SfeOu(nEU=p~aQ09+jfG$~HqWd6q@;YH4fo>ZHG4C(=+Asyv*Lc> z>NS2Y*wJiMS9NJu45iM~UAbgIL8T{OZ7n63y#cZ&)W5vzowhkMRr;mZH>KbP6m|5! zxdSgw4ATqQ<1-<9NB*R!prjXmyg@8nUZmAFvgaE0P1vH_XX;>gCN8Ng5O-GcY{iC50I zKIz_EF)`rvj#pjG&hELm7@HZolM~GO?Wb^FujHWZ*8N}@s%coNA2CKRtyA2 z5&?)O&|cq(|G<5A>()M}$pohq@}zvV$6RsJNrf<8K(@ zYRkfmy=e*h?79+NPWN&i1T#~ToWrq~N>7B;7R<6I`k5a^=qe=t+!bwFZn>%_6Yn`7 zGaHZ6p`0v8WKm<0XNel*R}ov6JHu*qg>dxs3-J2*yEG^D$)Ax$p_Bc{t~F3q;ZC?( z9O4=_Xz@lBU7(WJHS#<#KoeBp@lmWrk|NYZ%G=piPUbw0x1n1sl~fS|dSUQ79c>UH zWyxsO9yf`=eSR8_!pD#wxh@k(^0`L#SVW>~#AlYH| zb8Ganv7>~IIk-Kq+m>jBWumlnU7~PcURe!GH)E<}0{HnhAwYT3O@xiJFLO@F%I^?* zFiHPj2zd|AF1UW2Tx^m`iSDQ06?UCOEH=1T?~6GOl*OfEHJzi-MEHD0Ia2heYv#kV zY(>s^2eTP3`)N>s#6T$D$Wte!!Wh|xg)*&olo7p!Kd)xZ1L%JJ?TbG6FSZ4AlUBN@ zH7+pAmD#eQ$nSTS1S8V90u*T8#x8c*W>ZNT;XHV2WM~lpxAVW8nJFXxp6RcXSV$1^ zA2KJ}OyOJOW-~#d6s;w&!&E$MF$I?5ZC<;j@=3by#3T~<$y_?k&8Es;d$$&J12-sxa? zvhRLYMAc%44$>v^777>>9S6R}no3WJ0Wk$42iPQ|A%%27+lKWHDf-LAtHql0 z4aVW&H|&2v%I8fha%X~0QYwv;ku3&j$NRg=pDZL>U|mwblK5RXw(1jU57&Gp6pJqn zjTHQi3^IyfN50v%gy*Ff(#(*dkQd1WR_hXkhUNMt)Ar=kZ_4n^9NOM-f5)(=W=(ES z-*Ksh6(kux)&Ti<7pH{^w6m9XACwmVJ%y0OwXowT(+8gT=DXpjZ!^ny35A$l&0=EL zlD^PYQJn}HHq^*y#qv`U{xM`k=T3Xkdf?X)K(C9G-}6#Fxr1O3sjehiFYev7V5R4o zpz~Zdu|1HHkWI3C%ktKq5jx!p=v}o+p`Z`rKzGHw-J#grFPUtP^%bklk0cKxiJQ8{ zjRq1t({VU7G@H;s&nA<38zq*?7NatPdN0$yI;34_-1(8>%Kg>C^2Lbx&4dAVX9_;! zAR+dSB7Txk8y+(YGWI$1@LxRBU*_0oS4}|@26FO2L@#Ow!ZRb&cIopVkdw1!1{vu@ z$9yqz4D+uD*1mr6MclO-&GW@K(>mnYw&K;3E#gmod|2=l%=T3K!I#}LK!a{iW__6! zIru1bx5)pn;GyR&j_oVY z12vS?C+~)5{SO#!^2~3F-xa9yjNu^rFVc=&gWixy#BiG;UdVcm$>LNg>@NC?NdH=~ z{*cKP8teBAjapCo_GdwR%ubvf8}|JQqlW_)pba)1C7alGU`h5C^4Ez$7c%WR<%!r0 z{|r|6Dn#b#s$-*>7oYnv5&y^Bx=Of_YDl~16RxnAAqq@jhw5=ZIV9Cxtq--7cPwC$C$7;nQT5sU!7!S7Zg z^zp^JN=_?bLRIEblIfbg{!a_|x=2A-v73v8ME`q>O!b27T+22repMAXYpx3Qk!!)9 zB}yPVpkAeOy1m|Y?+`(WeMu2=F}={yH-V1wRRBrc;jWb)A*@S3CRn>enUBm^us*d4 zg7tmoUURgR|6Tj6`B6}X6`JkI3MH>M>`C5c& usCE{;zKB=(+w-i+9&*_?_h^9~ z185#pftC!X^@^KFuy7ou<@#B&w0)%!`cCC6=3$t#`}fkC)Xf(mRfwgXZ zQ?hJwSYuQZwt{&(D>$%KmnQT~Ki(1cG*WVz+jjyG7A81@N91mVDtAJ50p3|Hk$0ly zR38T8v&?O0$IJrU9QD6qV^cpf1oY9>3z|zXR$#)D>;T(np~Ve*SgXGWl1arcg=-X1pQ%#e04j?cq2m{L!0`*+IjG*{7~f9Jqxu(6@Zu@Ot>Wz4WVcs- zFnsA5iI{G6valddG1KoE971y_`mAkCj~wtHibFw`30=LzGOt&ciNnABg?H^6bnSNp zn!l$*Rr#opxAt|hf>W-nE+CgJU%-~<*E{`m>kfhNb>Pb0Szw6g0AfFUlc)j1kul@$ zMgf(K`uu$KvB&3ssu0;9Op(j!MHpv}FS><3Gl6%rr!jd8(8@M)3^xZoW7ZWKiRvr1SwNip}dRSrA{Mz6*sK(g4w6_-*L zv!&~%SL4^J0MnNsKa_kqI60Q<^x#}2#LX1VFOv~&rsC`5HGhS|tCZ?&JqNH3kWD3n^95Xf5wsn*I zG!?)=&3RFcN%G0e0$nvkl+i|F#fY=}&+Id@cLdQmqS2<#bp&6v(A0-Ae$307Vueu|{o)1YV-Neutzf7F=v zSj@R8!kd-6?SC(I&=?`zZ|BZ$(w$md(FG;T64cVhRRl?3oai7$!WXg9f0D)vEU1)> zR~`EUTudsOdm8Bj?E$V>)!V6ttj<2AN-lzkIxv#B*O1X`cAr zpUCGi!lXLzt*nTq^9@sda1@ceC30~jhXA1L2rrx70B z+L5wKyz^N)xP3QVXi$k4v|EI4axh1Y@d{>7nD?~^(KcrOZ6Oi#TDV~;PLl8oX97>T z{6Ii`k5u`_#ok2mL>F7^{URf--;Dp^rRuqwd%;ZDogoJ&oq(@77mzX`ow zovI2@sUFeYP1h3yt#xTm7bm2=ZK#q-PLn6ZrcW#P7>mq0sh6q+c={vvozqvD?8@4M zCwZ~B)ShFXa!qCk!ppp&#HDBX>6Svp0hm`Afq&2vEU zhVP5i_EKRtNE1}kyYX19y6saYQ}o1}sOT`~8y90g3uggRe$f`gHpX#^<6sw<$zm{l zYL;c2A`znSn`M&~#g3L2g)gI1?PNCtyAUYJpDs@SZiovL=K*zA*hpi&zdKUI)pS`P zsk{Z61&uw>#ouF^5F#!k-W-sg2n=TGr~^eG_WDBo-&O zPcmtDWY^+Mf_SKBIAqvZmU@;mE53g6r&5GK&efC<^#NOr(oY~KR$y~Bmj3p+M)9DP{sA7=0!~{C4ZIf- zZ66Y;mSNrggQ@QSP@A@pmCBRt0}LeMy=#xTS$pqjlN?5>n7+G$S7|43Q|@+*{V~%_ zlj2C0n98Ch&t8T-92p3b< z+~&aY8wC(MVs}Sg>nG#J<%sqV{S#^?AV18^2t_{gCE1OKGF#;Gi{p0GNd_eswBt2Lx4qdOXMY=a1|sEa7Y=G?@=IDc z8hj`pyh~t-V+a2=K?;l&_?&gWs|Z&3yu*&H8cU|@m2@io0nW4j%0iyJ9YFX8?eS7N zgDcSuOXhrvJ1iVO1Mzh4gEgPGXEaV_Xnb;iz{>XJncp8NAb|9Cc9nhTcx^z7oTj&6 zSM|J1Kf1sv3RGdakzaLBQsHKSb;hA}h9eZlEgM7O%Ye|SyDb^0YW(+>)hSoA)_$zw8#s}iAjKGug`8m9M60#!Drl;|*H~pB zU@)o5V6bU9T|fO+<@op2n}APabNHsx;pfZpnN3}uEnHbh3a*}%02w&Z+;8owSOuv)vqWMKs5dpA^Ivk+qbFZE?L4E|x z{i{*kUM%Fb@m33PPbu!QuCwh#2ehqtTLSDQiLBqJ=rofoyaB3{L%>;gUl0Zvrt8p+H+?{UXGq zOXum0Zq<$(al7}*=5%7k8p7Lt^W#g7!|&0nAC?x56L~)Eot$jCp*8!Sx!vO*MHKyu zwx8b82ZfLgbccJ@%)Owzb88Z`{#j}ywp0G&Jb>@6pSWxmaoiLK)mHyITE$k&`9iAp zw$xIqSp4gUI_tGm#vwq9Ti_R=E8!xVu?SHo*cSMw(!W05%z8|o%K`)#A+=E12i6=@-cLbJ#@kDv+C=L3+N?}Sg<9u(uV2JS(Ad{ z+HUuf$>}8stpgq0xkLKe`pPVU_nhta5t*Q6Dz1-K zTCx1rR;&RAMxqjI)%7pOCh=}sF)@5AjS$7lH}(agh@s7suT?+Igk%48&;-P2+go7f zF37%ODiICoD)72(vLzoW?Uz@~L)ta5j67}12?VF2%|E#+`$cg@l7kFU&3HeSJYy{} zBT&TL_Z2HkntZ?f^$)Tt&-tVPZ4RA{3KY9C^NSE?@5EiRhm6p(@b$z)D4QV$|MLjK zkigV&olG0*CH!fD2{r+~rfFFzK;$RRrViJ=lz3pLwP)gwkZ8ik&(){E$8FjiezN<{ zrKL`)#_aK0`pWThe2!aK%b?cOyBmTq1ggM8X0afBuF>~*(}4(j$7s99bE}q9(@to# z#HBYTY<@ilx>jS$nKHGI-qj`(T#dzK3rIwqo-uCb*k;#mA8R`a1D-F7K!mZuBdPeM zR;)~SE(^&wE|#bU>O(Ou*k}L4xE1*2s+(F=^1Eg2k%rAvo5qss3!gfB3vJq}!5!PqfL(8Poc*Q|e+Eun6xGy7evvTyDga9D@RZ9h zcHtKtrulqQ2i_G)YR&saJdwSAuJRsBHSf|Ycy_oJSwi-QbbSGX&R8vUAuUAFSx5hj z?zc8w(=cX>Pyymh$P+l h3?PS+DMWfgq;1wm>T%1R(7Q2nT6jQx#$hY**J1dd#$u`Y`=5;SKO`*8jMv{K@s`{I?~mS@t-c)&ypW>UoDcB&cv1x zYVdX&lK62Q@0DKwT<6*#xqGAWXDQK1J99dLo1^M#m3TFc_1j$uV2Lxy``K8bUg-x^ z>e!2hN*Msj)COYU!KD}x9Au?oOyV=|25rkDzkJy6SekM7uSvNKDR=pCCHudhXB zcZh^`n?>`pK^q-C~n-+PpxOAVhdnOvs#SGFFZq6#1^Wh107& zr!-7rb8sk!I~jzfLo7mCT(IF@0oy*6LmEz%8h#NfP!8D5?UhP#$N(JkUDjk;SH4Qg52@k$dr-E&>Jw-7xbzm>0Dok^m6QjoUG z%lPtXj|Q17HKTzseRVe|b;Hcb|4edm4IH!@FK#&Pu74eEbLR{4D@lLpeK=iO&gEF_ z!!6JYR=2&-N(F_3`S>1(Yi!NCgCP;zMun|kEzm}q=Xab@qyvuLLo@Huwmqi=NsTXS zp3%1aUT~&f-Vb~uCP~`11%*zxOY|fNPo1437w*LZTCyN$iD1(G zq0I>n^ZV0ZEXN7MYvu3GtQhU+(`@4NAshp3KY)=P&mk{!UyvCwgK%LA8S2iWQ<#Cf znVtEGTz;>%e4_)##}>a@fV~?<@J3$$i`+cNed^GU>+HRLgj1jve%h7>u1QuXR=d*W zPj9T;aVV?sfT6@joIn+?-Yg_e`Wk~1B)RH=cCg>+l70OODT#}25K3#QId37VcWBkW0M(ohh%0^=Yq*neFjtBNh-3!3zEhv;OK z-3mV5iLMtp9_g6QT@>ge{PlU`9)SB)})<32Wf?OL;#Q%;1&eD2_~40OHfM zleBs~{Yu>GAcPXk>NTmlKa=C@qDyB1rH4n^ zABb!W6D~4t0JznXS<4j+1VqeH-3Lo0>1=Ys`w=%mV5b*(g8c_r2JoMw78$88zNkoN zu$RcA8?!xdd~`WoS6S|M*hdteHO)ys8R2fFPoT6J+)dwfnJ$^($Yi6F93Ab~tkDuE zG8DP*d<6&SJWTmXx24*b;;;PNHtIIhC?a}CMk2Ey#M<2o>4viEckC{se3;RJE3ZU6 zVvhOw|HQ)=hG<+=2;sg_7VVxE2yebEzNcQkn4>rc?%SV>1Q&~~-n4`vIDZIC3PJ{W zQ@)vYkslwi?Fdxwef90BeN`wTW%$9&bLqouB*~gU8V@iK7S+diK{ebZz!0CfX^9%4X`#(bmWOi&o? zM-m#kd;yf+Y+hPFyvYh}!mS8rl>@#t`08v>(q>7fVjQ4Kp3U~TC#lDoxdD&Vg8tg( z`hV|eer^=Gt0n)Y z=KOk|X677jbsixl0#IS?^_#E%CHx{?eIv#_&wP3C;tUI)S?_bW22N2 zo8(E%*OMi#MTd@+Yh>AmV{98>xr;LQz(DoGJul}fWbFdzInT2;0_7mY-E8tnc86j< zGJ9#W*AK(OmPuzk&hAn4%*~M3H~Zy?ow&2UQJXhP4E-)I>&SS=3Oec13u!mQKV&Xu65M+&+o+- zqobc`3_r-hIXp{w-I*$g|9XlhjXoA1i>A7Y8M>y|e6a⋘}lJCrJ?o(x?4n$|^7w z$Usu@Q==K#DZCRR-}mtc1$I=KJyz8_1+JuVTQSA344!GI{g6y}4QAPuR+h_FUv`Nd zP%%0Z~qC#OR~7Y<63Dqb|z_>3H>k{#-Qy}us@a>7dx|bI}G0` z*pDC7$&HbndSYA&%amtWDDU=5CYHUMCW!B`-JOU-%6P5n?2cDPhCqKEpdJ3+Flx%E zLcgT_=zoEOyhl8Sn($&AWg2lesI&MD-SK22WTl3|sG_66iDtFLf~|%^aHtziP$r}A z4j9)nh3Q}+-R#^i_ids;`BMO9EXQqWY5AUwaIy&JU?isa3;v_`(JL*>+94!)*@d@z9Bcz zcy71U3Xn0ab{!mMN6FZNz!ZeO7cSfLSH(cmmVZ$4ouVL`J%%))-!2rdgMytDv{=Tv zJHm%+3BHdw@MEl*|cX66c1AyuI&(*!CMFJvreSWMj1XGO! z-(JNxJI889ZE9gzNz^zprCNO*k7lVpeYSDb*Go<_Wp7Obl3QCx0zN+LwbIK zWwOV)S}YgJYNVpGoTEZUQ5eN)y^{L*T5a*8k%;b>39A}qH0@Sf{a!qy`MQ2%bS zCpo*%jmzfc_;Xk<`~u=<6kD&fo_u9hM!oi#E*+UFhv4TWf>$U?+RaEVlM^JL?|CK< zM2k%nYLqM4TvyXD|6WdL7o3z=&&f%zJTXOgw!xJ4*+z~|TXeRb3YxT1zOha`RB%t; zLN1hjD%rANrz4-5AV@iG`kQHC?9KU7Z)gQdIT4T|5jH*+{Y^MK-!w)f{h7RNq8zJa zv0ll=7;2Ird{Wils6yPrEgyRl(=B!QM1AtOLTPLp*l`T6&K!4Tb`V-<^4|~m3@Yuz z7V7K_#kusq83^Goz6-p~KRZ5NHbt`y#PPx;Giqq;>{;qp6p{2M5QP6uPuJ9@cf4JJ znJks>RnEO{>2+2dU=k_`O$=yJc_;{n6!8xk47M8R&)boP2L3+_&<%(ned)gc!Bgq% zO^}_>GR2)bn31i>=hu6DGGueJlWI;6_Po^xQI`Onp=r0D%G4$E{&g?}R?b=9w2RP1 z6Gc#bO>~V#oqlmD3G5^2bqu*?of+49de03JIGHNanzyZuw&*$_GPp8%y%tyF`FG|@ zQ0&O1k}N}TshHny+2>JvDE$da2XE(KXT2z|e(A6+$&aCH(>>}+exEp@nW~7%x^boV zsMzZn>`-x<%zM}JY^oXy`sd@%2e^Q6kf{Rs)%oAB**cam>~pMSuSwptrnldKPhm9jBn!|o{a@^ zP~s6fd<=lQP%zDT zuUy`bHmT})UfvxIo9nVai+`?)!7WGh1MSuKmw^m~m_Gf^Z2XRzvINF67I?WoHJF9CjU3OT0Gv(q(QuH!g%UAFr?8_d2N?#Y|bRnwrW} z3rSHrUZEtaBHM?uKV@e-VDF?1hZJHHvj>jsYfaEd1TOxZnIi^jBVearZ#-VM>c`YX zrG#Rid9ve80KA!C(RBJ3uFsZ#P7ynS! zcD-L*5T8rNL9SlC;+7k z&N-dC?u{$zq-<%u*+0#?baJzROC7b1V&blP{skK@t&3-O*6d!)7Qx*Jrk4-soJ06+ z)~lKN)J>-QQnnypg|if>I##}}m%ZKaq>dC%WG55&qv8GScTu}dl7+Qqz?5Xwd_Tit#HsY`-UN|vA^5U)mJmnb_-I7)+@55Nq z1IJMjPGlKX{Iv5Y2VrBR8RZ?d_4J^AWYOsxBzIPE`@7N3h(IF)`IwP;o39O@&-sbr zag%?CK{kkzpUx}inZ&9S=CSykFsDz!F7%%7KGMj+xTt9%UE~6aBb&T^JYHk%8ZiO< z4HsZ9Y_ll8zahKI)%+>&&)Ri{f3yPN-y^4q9<&q@wM8)W~Yh zhf&<V_vOT`y)&1Sv3~JAFJ9PuZXVh zZ|^$!@9I9sb*dnC2;DAIk9_JHJPE6UuL>6@Q!HKu9X=$9IZ>B=4%^SLY`^@germwW z_gKt6{m{JRdVhY~IiR;iebFOBd~rej zQ*Qi3>$NwfrL$+tZ?Mzol74=E#Rk6m=^irU(en4;2YJosdIRXnRpF}f(yFk9gV#_Uvc!S=q{qLsv>9 z{eTx0!%$?~ynafQE1)B1x!m-)Cpy1CT!BbIMN6G}dsQO*OL@NJC9e1IseFp+`ZznZYClA~Wx=9a7G~A~M$EzVNEl^l;lGFCT zQE!K~!zJNm!=4BSZ=YB70tv5nlPi8UoRj>H75HiKPlKZAPd%TJxn0UOGJ|ydZ{N=_ z_m}63*9OpzjRQt<=MHV;eLK8E$|SOq1#Lls{e0xFwc&j4Pvg8*b@aUS7tLjUHn4)u zk*1Yz0k#)^-A6liQrpAb&Hj(D_W*~p`5MMIosftig5c3>B1+U)iRi)VLV|>_(M4NU zOM>V`kFqxEDnaz63yb^OKqdrt|U% zYkQgXhwFN_w|Ba?*GWM?uI>iqXtSKtTwE7>GC$;oD1p#C>oStDR<1}<;iM*KtMp;H zTWP})9Sa-pt|mMKQ@4pR#dNXZXPC2(uUMJH8tI=5R{1VZ9KlY|5pWv<9hdBUtbAa~ z(_tPi6Y(Kh)&hUdV*jAP4q4?sAfaDTZk^b%crBF9XI?>8IdU#4bGk_$vdT4ARSALR z=sc6X1leW?6LXAqT_m*0I0_^Lco5zWxIq0!i_JH`P<5`65X|LxCDtzwS=R4-U;MDZ zB~ifLS^7D@L*?UZtwzdzX(N4oE8rqVW3s7)=ko<9ufSNpc@tbWfvCP+{#s*`aj4s# z!BBi@1#MEQ|AqU9O~n&|?700BSp71G*+~sT;3c0(8Yj0Q?gKq3)U6h(B%*}NXOdqpTDxA6o+0PwSzU9FE_l&~g=vj9M&`fpi@|5pKCZ1@V!%7= zQLfk72W+y+&P71YV~e9(WK3q%^9kwG52S~iZ#=HOq`^%nemu3eakJkAnmvRvCA#9n zua25GAYIpWU(~`p?|@nZskPVwN`yI*kFjB^*RIREnp8cCioSijTWI2CxSNR~&P|(E zJ{aG(QM4Rztx|tNGMCO_U}k^$q*qLit`xKNt~T|~d}`4rd!!Ffy{=bu+w(ST&RDl~ z*+py3t-k8bb|8me4+2&%uHEzT$}*7UA-Q}xw5u&nQ*h6Pew`LQ$aV~(&r1CORp-SL z<3;dM1|DlN7q>_2yMviOLsD7n#`m1Yj2K;Hzsu5T(GbeEBiA_Hz1}h#hmEFunGqKq zz+O}%owdzW>bI}Zk|s3Mz$oqdTRkvwaZt7gEju+PgQ;N$l{Y8Da@;B`VH4}KGS?4n8$Y_3WHP9GU1Xtp^?b#N$$=lLJu(wkSZnhZW@0 zUiRD63N@4Zk-yKdIr`K*3&tYo$P{kAw7AlDE016hCI%}W4m%8YeFdp!IcB%4;kJys z^X6P0>QR?W)47LY)V+I|JUAibFj2{DZ7*$cx;M69qpDl_(WrwBH3=sR^G2K(b>qwueeYj$=RZHVX#u*jqfGLf;q zyXyRfevxc|jT1un>kaPw6AtNQ!=194ITToX5rpJ5ZkXiQ0ND6a#4~QcYB*|vi zhP_|nP)muWwYZR&%SIJ<1C+12AwL)o!Vj7(UDolK`H zmE3ZVqx^^rT|d#IAItdyJ8nJxISO0RRn@bxXkm$DW;;-Nv{N&&|NK(q2R_hKa%71T zhYSAw{jpVLc{1IN>Vf@iX5dWmjuTUC-Q0E^W-|?bUAw*6IkU?*Gc^OUnI-xHIl9bV zx@1IS|6U^feTlkMohIIq6MmrgXuW(p^I@4@dh{j`gECB{X1}l7Ziw+1kv*||j#(9v z-pgJ`9CIDT!Fy!fSc@(O8DdgkZmR3yoTHAuYlcD-)n7fFw>J^Jd-PG`<@8_7OzXW{ zrA~%_X7bD{oZN=2_*FN)&J1I|>saqtGSBwY4f9m#8{L~jQm(O@HDh>JtW5(v`DT{P z%YOAYo2@^WaYILh@lkBf-mf08k#{?A>KsqKa&+m_G#X6w;y2XJtvD(ZEE2I;#3<6Q zPzbA7s}c}8v>w1ZY|ci~?U~Lg?|!U0r%L6u>KL=0Q!-ZfY&Aa(woyx0P_3?9VNvZN z8}9;3{GN=x4G4j{tnN62cVBpf$2}f2YrW@KXRfS0rn&*Hq(GpDDDiYbNw-?g(&I%- z^Qndwy05xpNOh!-%j6HhR#?T9W9HF=|(hEj;FJ02Va@F$c>7zuJPO%M!mOL zT5Y(BK&#j4j@x-U3*JWbS&rZL^eZA1B({yOswq40Lv^AAWJ^|)FKFWmP7&bY0*zN$mB=vcBw9wt9fMQ`Kpf)n-zK^Y8lRRcZ+3P?%4>}^_zqEKfS!~+i za=f5HGe+5;@mlG@^DDndfg+1t-^_# z3b+HST_mu({P^KQUDzI#re+lN&Q0cMc(TJrIZUL5Z`{zq7g22X2Me`3EYHu7!`-I! zwPF_zr$Y`jaE6%C6WQlKXVL}|ye?^w>O9?pTPfcJ%^|GTqkHe^=6F6ew5`08jZu80pAt>6Ld)tb*ax`^IcpbdX zLs^9f_lu5Y@z|s@Gi)qC{xaUYwWu8%jj#nf@iR<E0Zh3Ho z$6cDdbJSISBT07MUHN`(cKX-(edU7)Q^*Qd>FHPs*0{?4;2~NrKkfi7M>I$J_Rbo- zg;aLOzGDBiSO9gpp#7yC0blG-XNc{>GR{@h;Kex}>Rl(#;OAIM0?!6m)t6#^zRM5I zw~^RZhlr?o=atbTTUt(#|nzY{yY2j{kqZzQudR&@8#2MJZdxB)z+S5N8|Z~bRn5t zUODicF~y`8ng~kMeUd1`3E6K{KPL#0SlaVi5SC`&ei`^;$ z?pVaMG_0Er!$S+AlG{{H^HC=C%_AfX8Ky`r^NRDk< z&0XvjPiMl95~%&X)MbV>F_Gf3aAEdBTBZT`MbQ;axr0j2ox=>w5Qb|v_DTbkj1fBW zkPaRmsVQ4=JOerDA>W-Y1V^RiZi~3*w_f>X+oXQ4^emj-*eas=BXA;wRR9llk_Xf% zp~@@FH!q6m*!3O^k4TcD7pE7ERa=ct_CPU>nt1u%rBJ-oPVPN!-loeV$J6gpx+bc0 zH)m{{57=;?!?Z20)f3l+?4u z#=+#^hPI)~1JUl8D3(kPT};!f{&Vr49^v5A$`@JAO9-oEuUvd=_<-ct-K$_#^`V-L z=4|-}`Y8$h@han@nDnds$K8FIKH=gX4B2sUeUkMdZ${dRp6dOgN}2a>`iA08mf4WH zLGqY*2rJm_qSdXE6d77wOM5ha1Z|pol&QLIF8Q&2O=7zK+}>LVI2>MMlfuw(1;$tn z8)^nCFAbkuUrBn3iyq=sT;rUJ_sW6ZyLfKsJD1XvbkBgL_ML;LVAVjG{-g2Q$JUrD zoBLW(lAz*)>SDpbYdUJN@#0tEGULMqAFF+Rbnjq(PpfS3iyH@cl#~t*tU-TxjM1xv zfA`_EkCFArI{9J^?h%qMjoKWgxMm|HqO7~aP?i*6)&jB2d+u@(1q0UhVcGuk^3xDZFzQ*_s)YU2G` zKB_x7qh6--jNr{;Qx>0X_oLuWRN6^YJl(q~XHnaN-=FB~8+5K{sb^Zs^eZRdMa65< zoB-+i*7Ll3K8oL8tiWDpDQbVLJ5hN6lj-V>PCgN#`sArMHiGTd@cwM=JG<#Td#6kV zK7y1gu1Wr2p5dGAvj`Hl7kn-9%`lehQ|QfrwG;+l*hbbR6(WT`1ORIZG@ z8e=@vC6tSl{F4TqgM41UmS*kthK6!fE!^vMsePzrm5Pao6XvBJFiqsnSU@$wDciq2 zbKHe_?hMtfH&}YD6b9ChNe7Nw;*eOxgo9q!>I=2UP&@J-M4f2=NSCunSR9ZW6J!*SIv@vmR;qGeUPS3M-u^@)e>}}PCjs0SBcf)C@-Lph&CTCyGp%4npAWS-&3tmu!lqRw zmUBZzaa)WqvJ3a5J(hiQ2X@jSEb|(#>=y=ihU}$%tEMzg46)uPbJ!I*hNh?_lO|uE=N>6QEoY3&N~$FFv)nU2?oZwxlG(96gr>kG-!g4w zJ)jG)P~9&N46eAWElN5FCft`|j*Suc5gUHwC>i6-rfLD=B2Q{blKG7HXUY1ZPet6v;~-^q+J1WUE@k9j$tQhx0(=6 zY`*K6%L4+vX?nVS#_$*Lyy}@>f@NTlGZ{E*OSiNfU%LYa~2{h2wM2eSWQFa?n@89ZRo1Si?o)Dy$dj zV9TWpRAVi~M7I5ZX&15rThSghS*FYf@>6qf6x{cpxy>gtRWu{f{NA=l*|1aI za2i${HP1?y{>GsP{8(8!nEw3TqK!nZio@86 zv)H-`72zB4c*Jf#aocO0LOE*GXKm-TR?~afW|6oo4%>kL0!)Ah+qxUE;+e91#XX`3 z7ZfG5JFip)2WOup-#!m|Ba_7&svLEvyicgu)ScbdL=6Szfj+(_Xyy<%!@tv3jt^{;5}HvHf?Q@R}l4gqe5fg5TZ< zZrrswgIy=mqqnDgv?75P_pl@FF6*tN+3|#(md;qxZ4Uc^bhPUjiHX6V7F~7l79urD zT+2s!+0!O3FKTY-ZM+59@gl@3qGNIfbFe#7B!xcY^g09Yu@d`}pnrtbkDWdZ(tv(} z#$VE3fY$MzBVsJNtjhMhxwF33G}P2I^r>N>#QagxYR$GLddM402)}LESx_Lr*6b}V zP}O*hkS!_VFN~|U#)VTIW-hTJC+w0>9&EW~EH})x`39?WR;uNgV`XmAaL>Z1OWH^} zSrfb_Yl}C0HtRw$&#MWQvPvPEzsg;8+9dgm*wmiRkJ9ArY~2BMFcoBdN(=IlZ+Qd) zeI?TtYUrC@>9_Shgi62p%5;r z8QvxS&yqx?3%Wb>9~!V&j;({d$|84#r#5ZfQ28pnO!#4ISQtg^x%Si^V*$Q+h7)wP zUWc89L`Awp?K}h4n1ZjH`ME;A8AQ(s9rlj8-Pf$Q;8K8#xdcJ9#Y2vFe6&5KHoXGa zz5jE#z;n}>Pa~Q5E_k+^+8XVcIxeU@eF54W7o+gm&%<%8Feb(YeGspmFUKw#JyBDv zdiG=|#nGm)kuY2)5i-u1OeLXeY{+zh!{}Kn_FTNPx$|c#36WNduNDc?ae=t>A)e*+ zx}z>cQ?KU8SOwfqjN*18zQ*Pi{VZ2Hcl|zhPPZ5krAC z(!N`mSTF!D^Q;JRxmiLh|5<{GE1M5wB6y?4+^}FE$G)@lSr?&mK#fcr=Vp|!JdmGn ztIMBX(NNQHC(Id)e2osg21J(0gpn9RnbgeZdo@Jiv_ zOo+$Q3?&6~+ZvJyE`{6hdQ(U7GT=yvk=(2Nwh1Yt}Mj{EQI@ZFTuenRT)@g zp&Jy1+g4>#W~uR(d;z>+n=32oYDTm_3sQ3Pe|nFTzc@&Z(=lEw6)ec_%L&FXjy6gNF`SWM!fsP)zf;VY;hSp04#&0`Ew^gb2=ZGyq-lLvoY!3O0uk( z#vbC@z+(omdDR%EUBUWLh%DvT$Vvw776tp#oExR#jW;$nPd{nxL#`JwcI`j~>{<@}=Z zK0v1VA1m3_8x`YhTdD`)6~@K50wR(4)BL+6PfOe&t}}aZzxSsiPYsX|KOT3<$#gJ& zaDRW_m~Wu2H%_0c2K{oC0?nYUeIvCbgEvc0Utic9Q1eC0;~rZ|`udgoJ4^e(;MJ!C zqF6sO1Uu$z2HX(B+Y{D|>~KG!PjuROXNm zXnc4OxA6_dbgUQ6O$x}4kWZ^@KgvPB;QbUQQNk;_l*D@6wW!}g<;0zl_S;2diK#+j zV<{SdT;w=Cb=yKcRRwuK0ByLl9b~rWP8wzDv}^s8&woSHDy9Pr(djUf6$oFxn-elp zL?cheRXuMS7Z#DhUL;J`>js2`c&re>1!Wv;7c9c#tQ0J@KTda`Z)V3R8ZEq_*s}ow zb(rO0Z$JT*RgMW77L*Fc1Nj+BD%IBbnzW5~B!3C7+694g>hF!ov}prBWY8c!I9D6L zZb75bUEefyZyM{#+TUMNoyF9I>Y8KS(c9tDJ zf-%y2f7Nj>;bcd^On66%BOiY0_WkYIjWg=Bo*Ekw#Tz)BSIx^d*U`b3pjh`7H5W8rv3i*!wMKNuuA zPLOQLFP%=sx!IfBNkoei8Q~VDZ$sqwmE>AQ@(;*zUyIDR&*KT(E7OPG;z&_+w^6u? zfbO8DE`D+R!yvrc*rdVxwVNAP_OmYfvKn#4gyNV@W1^wvr&Zz)t%vPLMSzMjE$(Jc z!4QDQ&4j-0CNl@Lt1?#9yk`B84xpj{nGVd`EkEv}@w0TEUW>R zuIa=yJyN+Xb9jF3_DfWrxMw4NECOCSMfgFqVun>Sl!xs*3a zG(5z`A@#8}p`!4F#;O^(vZfD?t>hMyB%{CL*rfO5h9X<`vqGF9&QM3qqMepygSh0g zG+7-TG1s!C$zU&jT`+sJoQZE>!Ke+~^d#?g*mQ)lV=4j>uV~$jtH)N}Q>)##U3)z7 z&xAWdM~&Z_1Ke6X8^BKPMeEp2$54u4@sX?c9GrBQS^p1V>^g=|rtVzP$-tgY%qKLQTY*{)6FC_`Z)21U4O*?}>Zd$i`h4%@! z1kaZZdxIs;k~V*aK{3cikG)NJwb(;to)4X_toVx8!^3th6b;6OKvxzB1Y3J3OHz>f zL^J;ZsjGbavssyVCV7Z_hemnlURq%BSd9`d>q05C8i=Rs;I#>V6E*b7~t^mZ? zO7viNA|Rn+u)h6-D0gIU#sv?0C0dTT!*W2$h|fI}IK3fd#yS?FB6dqKpQA=KS{T{I zL|kk@Tw~Z-g!RpO5T5KF4xu;@QsYVD{e?e}<+hcG={)zj`{p2R44yo4&67)RyO&Ay z;9Hb;8*qWEU)-_{5U09@P?^9#hacrelD+YIV3g3Dy9yjZnTMVY$G5SE9WJW!;fn-1 z%nb%oi5Zi`;c($`w;W)zxW3U1fX4dzHrX{r7UA*wmW+f6kFDIIWo>(|D~)2pKT||N zfMS?UwJr?2mb#HI=&9ZB)-4&U#@Hm_@f~pDLQ|gYMI1#78=BHuYTr-~Raf>pP=jXp8mh!TgJOiYc zE|r4qW;FMp8zQ7qjW2!>L94HwU}@xJimQ+kTVIt}mhN>oF~R8@T^*;}vZRtHGyUba zpGNYIU$D>M(#GQnR+4@V%eOT=`Vv^$St#oEb{5h7dhMm!a;utbXKu?L=%xQt6nqIx zoI$*u8GzGT59%)WS4F^cj+{^IylQ%|x4@TWsrJgf`HiLE2Xjalfy1FF-`rU4^#`oG z(Nu+;W7jMhh}K0EjE-rYiz*FJM7s@3Wf&R zN&EwqtDlSL!vJTQzhK)+HeaNax^f$CV(6wnuzJ_mZn|8ajBVB%eBw`PfC;A@WefT3 zk@r@|YTgdAU$0yLvB44fu=i#3q-7Vl0_!X`VwFU8_p6Drx8!=+&7Er9x@jz;v{{xa zv*)GTjU;}@^_T-<+Wlyn?XE<0sxw4wiCcrNE(7qRt21*W(8Hn++HOL9j>BE*sf}$* z6LyM3k0WfmRm)x$&kl6L)a!^#b23Rmv63woz~pD`Eu24F{U!SHM9ykB8E}S#Sc8gn ztNjS+Es)JRxct+*;UgwQ<#P42JF7)5d3r{1#=3xU!@J9WmXv!6Oy()Fw{0p!&PmGj zCN1Q|^^G!+AimY6)S16mDA_eqx^tGLb@x318e54LmPf0w+u*xDr|Ii+#na03yC4p8 z%xmqRiXI>m>z13X%7kC{c*+V8vbCJ+O*_-!?(+Ox-6g@AA2ZDy#Vw71^KMmA2EAu8 zkEYpe6nV1x6_kQ21_3{hH#9##1eiuGceiYfRoS;vN@{HMkw=#X!}rG-m;@nsu`EYR zWXP=7ipc&BuuZIVRSQ!a9KQCCy|F-HId@@50AuTgY@Ouje!aT!n%M)G0;uj7RtQW3 z8!!Kej&rWA8&oo{NqH~{FSEuQcsiaK6vQvoWbg(Hvjx!pXt)bQe2KI?>{@A>O=S|+ zQFD_IE)XcobGC3W=kzln>2&1hcinENkz3yG)>pO0$0u}J;wxY7w^k&m6~>z%xh1zJ z+vAh1f6d=Xauhy@7*$+-KeuUYYtJ5MxYXetc5Qlpza!2{jgKL^Z-mlBMB?Q{4OYwQv`{Y}#oe5P_X&^eAD2%Zs^~n|KdNfvu517=#8hF#b8)by8H^ z0hlu0h!pJWcjl;_!j9A22!-e1taY4=cml;=TFsBTT$Xj)IqtHTQwmJ;nDd8^t3Gj+ z80}EOM-wgN_R~|>>piw|LSQ2_2;!uD$oR$)RgZK*f#8tE2daub!A2G+{$HW=zH;+jMw`eQ`dn<)Fsam8_)#n4|j z%KaP`Q_n31{DaXolZ7As;Bi@=caJcOOq&ZgNKWvZdzPMLyp;20bp75y(N5(k`AO!< zfC3X~;pTxk_FAu5ZJk&<%;ILY=#q%!W~|~fo2<01u&&ixyPO!AZOylsXrCMDqtcK& zPOP_O&7*J5Ufofxogd}nz!y~kGf5$Qq&&vI2{Qe&NqlYymES8>73fwPb#xe!?K45; zO9r?BvO#ku&p)%JcQ7!BI4lP1t)kS|WD>%lD0b84sGJcyvEtikg=Hn^-< zI<0HZKo+iw&vIQl;s=XXS*{6wDw#`at9&F|0Vo)^|;m% znK`BKkn74m14SNm{uSf~b1GMBj8j1d_oEhr}Z>faSRc+Yf^pYDHIU)e#l=dN$?3iL^V0#<*}FjAPvmucw}F zvRAc~vurnwS8oCH1i|w?@{@CH?)aB_Je4A)YVocbFG!U_K->54pX^n|*VyV;C79TP z8%`XLKCKuJxT#l`+ENU}tQ@Z(`CkK44LNsVI7(H%6nl(Fnt&)gQclOJj-9w-0Sm6k zNi2FV9xH_4UlWF>!0tW00A9V&r<-7&u7NONS7qqSgB9+ob7HMK6_sV6c~Hodycc9H(o6OW&P^vXceLkT$K3iZqN&Ej=XVgqqksI9ATnPHqyIq<0f?Q zNzi5Kyt&Vsvm{tm3b7tbv23*Pi)LM6Yex#EhQ6eMB#Y$x`GhN%V81y9IsPu|^?7}Q z`V0{3Al7O;QaHiS&Bcx@cBF$_l1G(|a~>gmUY92tHYNQE5k@W=z-V@9+xZ>`>Q;5g z_Ga0?7tq#;c6;^!;NSs|gDgJs#ika9R&GCW0)}B(_^n4lPV(Lv)(xc1O;sV*wSIM$ z(t>ExmszMEf35qWW6(DqrpvE-F`A(oMvN5u_*@WqWL zyEr>GRCtlN_@UF&vDK4{5xQ4vcZn$xyx@A?RR{}t> zTx-OMDQr~<31Nc8v{+46(nNhy9)RaITCEW3r|VI7(a-Zu4eOiE z55?A@H-XR^)cBSKn~qGsNstY%(M!qPr=)hoCheQIwNTo^L zRpX7k8DM)sTi$Ngs!W8d^DXPUB`g%~YFmHNo0+B2AYSdgr;S&>;U6~Q&#Xv$T~|%B zC(DDg5>OpG1Rw%tu3y@3B59R}Md1erC;jS%lw8J|b=bPdIYJ=D_Fk)Az zeNN`)GS(NTn#k22_FYkRFZr-kWj@k*xAmU#!3QP6UFe`rFKVpN>Su>+QWX;)!X4E! zg1L1tDo$u9gw^0aPb(IDH%imOaGd`m3zKw>qhf{%x3vj3$6qZ{|FlS=)#1A5w)cEQ zc5KPPtZ__*kZYu-aZgbL-Jdnv%hiyzlD8=~hk)CBj@i_F{4a{SQ@G@LuHNKajwSZc zN9~l`v#%f%OHXu^jtpQd#k+c@Ek-}@xmJNms%N@cXY)ksM0=lS#XJu0QMm$yjzA#I zH&GJZ`f{#$OlwMzuA}r1UhN+!7waBA&J6ebGOY_K!~(H({g>DB&_l0~e4nJf(!6tE z`90FuAu+D4O4i%Cf1sT8URl&--`}VjPo}X}-hT4zhJRqpNM7A+^g!~CnmL8|-Y=J7 zL+?m&QaISdYw05;u>MFH~rsu~t#k=1u$YN(b4R?b;JQz9n{+5xP1K$fI z?RB-e2-yxc;a4G4t)0srsOa%N8JZSV-4<1bUfmJ!xD>i2jhq|`xrAsVy=&sC9mLoz(7gE+fa7+B+^P-|r$d)i~ zKe|(^A}Y?0yaM4&p0JL}CCsAL5vg6sA&Y?5X{X#V-FONZC%=QuQui9~XEIqmyO8;> z)UYk#GfbvsSMM+OmnJ|XSI)t*>cRii?iWvDmrJAZJx z1hCqD$}riAW`b@O)o(BQA(-ArbDC=RmcBQC@wDiZwY*KW+kECo{2liR_rQ7H@x1AK zr5$*DIp~}JBre73o+~#)DOab-c`yafuSGmmdWt2yH^)CQYf}ahZ)45j%W!5()vfPIBdV<%EH)`P(&h->K+UCRcax8Z0d4 z;NoFt*)SaMqX$pvths`9^n3kNSQ0L}nq00isoUzZCJkQHox8HDJ~7ah%d=`Y$`B4W z4?k1|{Ta&+K~Yqzx>re+1?kEo_m=t7;5)0>Bj<9%GS?lOB@eW0f)hsd9ac*T772Hl zON>n{Y2D>3M9TR^PCWXpxk10Qb`|7aw^u;?YBR5RUlv+TYoYRv1U%bss7(iUgp(nj zXVia%h`{Fu)R4V!jmg@Nz9dy+dEX+|6?jR2~?Of;~@`(y(@M zq9cf$_i%@J_Xz{(3W*A}_i%jg^Yec&WhwV@S3THz+RFpAxp=R?3i5PARd+5e>1iY{ z`zP{B{eeQI)&u>tQ=5C=6}&h^P?626g7^`xX(-;}W7!pa{}!^^5AWEubC=b(%SYwk zcM~zFAp|v+wn=CQ-2cOD&?m^f2&WZY%G$9cXpq>PpWQ1Yt=S?v2q!dsG*dFC1kay# z=`(QrDd|>f1uFYn=_VfKevzg6J;BgBCne(D6ohepbnnZw=$M_@B0^?Gcto^-1&?9D zr;>mAEm0tkpK=b#=*9Tgt)8V*NzR=WN=0ujJ{@b<#ZQX3k~%*c#P9&RTk!BIeb}lE zYaWmM=_mw+?{();E702TGhP4_Gs9W28?t;Vc2Moha0^zPE|X?_9-^xz$Y zlmwf3=P;>z^&Ha1riSNdeXOT8=6P8!?@-<}G5#IQ!kKX)&C?z6dGlG5%qihM&WieA zYHg8X`Ww}SZ=4C4|ClS7cxPHUA*?wU`M> z^jcaOTgy~?Q@B6M!xTPxl<~v!iMRqI8|2S`0J#I(yZTVK+-rG8y8JoMBIi?|piR8| z6I_*cfo%b`+jQ2gQQ26=0V(7Z;uMb+2c*r-dDly9*}CJg;b)Pv-ENyRh>m5kWJ;)m zEs~#Mg>orJs^oidXI|5vu6;}2+v)+2eM8Bu$i6_XX=Xl|jFqR$=9jYZ#gnU*`>bGB zP3hBoMzAUs6I7=)rhrlY)MnPHz{__9Yl!)gbQ= zs?g$jgPo+08;hDoF&pKz2xnwl^`%E?tKQ(fl#5Z)(4Z)zu!1T9)pL zV8O}=B{=U_{F<;D+YlubrV^jZe`fO3uD)Jcg9#)x&BHP#8f4{fP)hHNf@D^O`` zi4JoMy!bV4Qn%E}92T`^vQO_=Z&`d&iv%AvRgD9%pf>$mAkft$RfUIoe5FjHA8o3J zcc^SM6y&~tTt@s?@2hefu(>NH-HzfJ;YFFxCv~?aghRo}SVi8C`(~6H{Z;Crm~}nV z7!yTC{1pdS^gGzN@~QcX48!awB4eM9(Ik;vt5Gb*lexq9HQ*f4p?+<_>x?+>j-iwG zkBb5}UO;e`O5u-JkeSKBAADYW_<&*AK3t}#{XO6AC%AFwF?{z+{CE{cbz5%(!yf}R zjBasXq=QxZ1PaKfk*@!(J44jq2$62Z^eqeczms(C0ZzbtiHu!h=tS|HMT(@^!<6~# zC;v)foi7+GW-fWb`su+-!2NP>j=9P(l?A`ptph>KT#}h{@<6qhWmb@#GNkFlRqq=6 zdEueEL~tlPU!@wFd^n9(fJHCW=6!Z`2p;LF(L6OX-*?z9ZU$3y-aX;9F^t+$v-8;#^8cW8V3T7yqV-BnUos@Lb*wtQ6K z`sfzv*7)yasNZtB`;2wBT#BSU#^br6$;>Du$4{C?e}qraVsm@G?iQA=id!oqed_q5pB{dXzTbs*Jyfm@ zYyMTnw64dy;jMD&souKRI3SktV$!7`6mo-A2$KFWn^Ges(e#TbszWrvzG_2LMlIxn zjo;nBpUc>@3JJ>ltaVX%MmA8+kJ=kSe=v^OPLAtjw@<10e$kcQK)cEPsq$|NGC&LV zbpvCF(m3AzTlI&;aX}XwF@FukJzLSS^4bX4Nd0Q#rFCzzFDYh9r#z;4t|-QlAFS8- zST{&Ibd*cU;DJTiTp0LQ(pAsRhH|kn%~{L`Dq#Q8Ap4J^ouEq@e;gC?UyxJ=AF)+TlCM(2-!VPFWz{bL}5;&|S49H27Q}-~098kH;l>MSW7#U`^_>-_nOSk>JZb zFl3~8{t91OiJsTva&}|shr<)L$*obpllyMU4PUx&+PU^GocRa2Su!*YHw`@Z?bjb3 z%xZReXAH&y`%)2v-yM9M5${6sr7u*ZqC4$h>{MC$Hy)|>*Q@sZ>ZtOt-8YzC1x))r z#SUAms@toE3bw$h6Nmq>-VOfaO`w`b$h~ba_lAEdu+J`vGcEd3Ld_A}o%p218!*Q8 zI(LI|8ttIP4JxKn@Ve6y2j5W4Wb4HuebHc&YeDUfm1jrzJj_qg3+H!p-Hw?Q2s`_i zA(we6#@2)8{_1sx>GZ90*L&y zhq=bUtbi>jIETo0pL{h?OKvV(uLsri8 zN~l6EvYrWz41&G;=Pw^RT2qu1&LR_@_v^N{dY6ua^>dTPqFjwH=?BB!QT|2cV?yq& zB3IOlFR^vGD8>%nZ9*-z*6QciRcExR_r5H8jFDK_Kx#!(;Do*q@C{~ zb+WqK=(R{?6&USwRBcD@vOnS#|JkHdps`R@JsNUo$92!|=uhw#k@dACs~>eY9qj0R zEKvYV`v)o1fUa`d^e(a)s2STwKOk)97rkPQk z7}r!Vd*k{J4GX-3|qA?O_i}8J1CyKJ!O4fnk%Oh<%~MKEBCuJGr@0N1mpZrn~O=45I! zh6W*}4XHx3rG6NCeEzRr$SQhpf}67x>~73(zUKe;0ozodeJ58Yu|$7a-`APM}j;4EnF)zdie3!JPubWv3;J zv5@^KI2pdu2&ev|q5|7i6Me46dg`~RZP|Ax)~z4`wE?)Q`x zc>Y2msmtDLnmPSxjndsB^;89c-r&yv2OfWm|8noY;j;YyA^ywN|6t?4#edQKU%{RB zfiXiIPr5qSJ(RgVY;OD@xo&>cAbf%201zD6Fr(WHOSKu zUfO*|4_s{{)_Zj96in*H@I&qduNWt}iw+!Qm;W&Jo6ltJ1gYUUam$CL$n{diN9<0B z*}XqK15(EDC!0);C!M`XJWShq%Jlz~Z|BrLEtWq;9u@j@!gJk5Ha@vd-HLM4{Zafu zfU(Qf%fBm6dc0niS=TK@eK400QV&B(OE4?f%bzwKwAeU`@_l#+mh6fPxyO{@N?Gk<-vZvEBUy8Ff6{+b)TgE8`L zHShP#*_OL|wJ&Zj*8TomF!7hhyJHjoEZVf#a@ybbC*RsEd1Fy`wf}i#kL=NnGN&gi z*6F!d>iEbXUw?e9&8p*{C)Jwxx4jk3bX}vcXwsAYk@b&{{*r$7@9uK@6N`V&KC<~v zVU>{etow$wGX6K+Kk<~h)a(EHV1M&*<--HQl6_BAE_~P6Qn8_-K7+0ad&qK?h>?U(c*5!-Q8OpfNDW!@0Qb16#qG2`upZd1bndamH%tfrkzTp9OgH2X@G;s9tRW;2Q3JMSa^dr>Zh@8|aczx9Ef{F5Zw<@glRb#Ro z7Ha$?@J(rZM9mr-#nCZ|IzVU~BI>sE2TpM#R0BuqXi+t#OWtDJODFQHI0V0Y+1rXK zzEbg*z3QYcTL{3gkJT{h-rc@BOCZ#<`M6B$eYOEyV=*h7j?3zkY+T{`xQFe%M_Ek+ z0vn1b8^fTubexuRAlGtz`r67Az^xl^=;^8A`8|vx{=~6YFe~v7ulI}i^toMkJ^##6z+zJ&lSiiU{3j; zLG7$>^)5cBFyF8D3L|!>Q~Xpf@WI0PJ*4wmrObB# zdUc!0fwIR-YmMzaeegVBwP?HP0Kx{N$a}&&_Q-yy3gNn}bLwn5oWuxX28C@Yi4bJB z+~$8|K==i;S^KtyQPimKGx+nWGQ>a_V$<4Inyx@h0d-LB5_iPl?AK!ju3L1+5B zibtJbbi%$)dqHp%P2hj!VdShss?>vNoqjD3LA&m3qW+1Pw@TiTv%qNQZOT{7c0#|y%PaaV z=$PF-wCf=WrJg$0InMcy%@i?kffXGQ-L^ZV-pHV?Xw|=#Pw+Y>8V7S_^*82}xO=U& z7;(2-I+xkgrG_<7U5;sEV_~Rgej+U@fNfOP5a~`^{O-V$GSnFolkMR!@?L&~;d(HQ z%k8TP6H@$Y3CqqeByi}9l24Cl;_jy1XR)+@$2XyRp22W$ict|+p|5MvLOi(re5KdS zj2XH#W3gA_zNF|n8xd|D#09T&nlda884=v0+4mS)0dlfI()DgOdd=oLhzD*cI}Px{&3X#6#P58oIbsWch}0b zK5vQDO?+(`gS)e^^=aVAV^m#$3_ZuHwsfNiO)ehAgiH6?G$Fb*ftVh_$K2c#M4s;wPUs{3tB+?%^%t$@15LZzo zun9Qy3c@pS6X(H=qK!DJ-G^jyF8lLK$twRREquVQ1^5W2Qc4iB9Imp$aX{mkid zW(?S$3)iuQIqhlNFJ7A~C$%H~>wM=D>M$+6c1q@9UdQp$M6%p!1meynBG{yLtQ~3% z++&aMP>h-s9#6^N;aeDvXh52V;t8-(8)Vkz@Oi(HiT@;ZysLBNcI(3y?OlmPr{wsUpW=Wm zuqBtv;&vlSd1#i2<*~x5Uv|5$?Q(_-7i{mwh1%G`7kyhIpHjgiKv)*b z#a^{m$1^hG?Y;7lW1l4}Xg51x5+2fAi|hZ~llQS6k9`(*NmIwD0ud6Sc$Na?Tw36y zZAC7I6%G+Q4=brDYCzb_890M$Ts(q}UCJJL_`_hYUBaN)9iH}`@lct;` zV~aiy1O$jF4cpOAkL%ebitn%tRVIUU@BjsjxN{)zwkekCs6zayjP`oGLzz@yfrJ)aBkhRj((^7MZ4~dzAv<}c}br+vfx{~`43@J zMUWVi*FWUAA*S(%)3B>WeqUI&!M>b8Sv&-p>x0onzlWqY_;@Gk>~KDF!S#5 zeo}-eZA|$^=-i$Sqb}2b1LILQp<~b1NIv{QX>G)QtWWp zxKFsROo%)n<8eK)6Wl}FX|KsqAHdBTDdopMi_3=)3{S4T62p~+cy6FJk$cVV0^W2T zwWG2$ABANbBqSeO=)WEy3*cP0@XkxAF$Tnqu( zur9YDzvz(t1H7)~WsmgzJ!YC(w}$P*wjF|FS%CwV%Ymy3R0WXq(UAzejph1VP|+1# zorwEqmEO?;&pC88$w(yO5f2qr8RWbzi97eFmiMDDa93W$CcbsE`Wg-$Pg-udTEVZ{ zsL~^&?zDuh-E?BrmN2TP*%xm%FcY%?lL^B`13B;CzYILtH8LHCYQEpU?h%Y7o1(C9 z`(3_3S7i97cL8dDd@={k-2M2Jc29)mdXq8iIQ|jvl=^f<(CM>)pKj+drIl=7GyM}W z=F|DNc84s^vDuF_Ve8KH1Aun@Bl?4R`8YM#^VOXKsozyCFeWa8Y9OnHy{2c;IIWD< z<6dFX(0YH=_up%Po62MA_~Av~Fw@Pr()%U%fe zyi%l};W0(#FxtsZ*RM8R)bETS`{I|nTYG%zbqU$nTAL1y2y>gU@dmoQ>i zbHFkh!idH#v3Jy!p!6v>gDJ6BLO{ZEj=lg-^F0W^xV5&S!ULixM)yX;N4kn<*o!$` zvqWiD2wgM2)>C>~CS9GlqZWY$!{l&=uo9t72Mg)Xv}hdA#2Rr>xd$aL#n$A{r=Qb` z^mB>|(s2b}N|-TsNxQDI^cxp5oO-eSKwAZ7Wn;MpkmWHmgo7G`dGb3~SH|xm7i}d{ znTx`ntu) z^4BD`W^Ea4a}(3qE31Z-lg#1+_gnaAY!+fAl58)3S5J8({lvKM1$g>a3Lu~g#1f-%~G;{(K}XR(#Y$eZcM$i@6{vZ!pFqQlz~+0 zzf@ySZrGft0Q7e8df5~~G&*r|NP!ZjlO**6>G2{8(n7)Fjb~eZW5``>tTm?(@A36K z*Cm^TtH&^&)SlXCma7;HaG{Sd$(1{HDI3pNK++PDblke6 z=P_He)XtLK;QoI~C)59Ch%==+Pa^LyTzTE1mlwYbjMw3lx_1DG7O{!sugzKM8Qg{W-2MN72S{Qos`aH z?O07t0uuKL=Xr)}b#&_%{iy|9PWdX5A8ve zp_`cfMg2wPGwwB`)7#;*T6sYA-tn(S^puyZAyI`sLa8sycTQ}^U)gvsfLTBrOJzCT z)Lo47C&^(6W<=bkW%nkA=jW^8xY&$H)3$?DFS469z9pMyH)_iv;Dwwc06Gg68k6s) z?FaQ`hod9XPI$hF2+w*RXi%dJAH*oQZUqsB19yF6MFvR^RK6-8?HV<%$(L%W;%$~c zI(4TdH$-r^bM{pa&@5msp;+^3dqD<2OPu>h(GTmEreD=C12 zsV_Tgemm21DXK}x0dg3Q2#1^w!B+UJ`ocMQ;GMo}5gXQbj{-DZPx5`Ea&_K*AGKgP z2-o^6>US+bYW#yxpW~CQS8_;(pKvlkiuY;W-rq}$TxUu<2k%D>_zReBFCk2T@q4q{ zF3%cU+l`9)@fMWf2%V^Dr*UhPgn~%!==N*f zuhy2k-_H8la+*)7*>B1GcMv4=s?F{u^$!~N&XY@u_haL23X1|jH27u!H#Mxi@GciD z9ZLUEJ=R$DZiK7C)~au$m2Bnuvka=kXfh4yU1IobA0BzdvMpx~Pz>?PFi}w4%Gbk6 zyw$yt9}nP$85=8H4IZZC_o zBo+nOR|NXGgc0lcF$FijqlzV|5e^H#^^;|M-xDLJ&G9>KL_6ku@{S_c)85w9^p+z1 z31M4A9;%*)ZuNatW+?9MtL6iDL`IxxSZTqa;P4@VtkC7H;gOAu5skI}SR*@=(WYK> zNoz3vk-&IHQ@7#`n+JQOs87%i4}l!xnDfSL*&r4! z2R1s13ZW+E5jK-!pkY9YB3#+`)_WBRfSV_eWeK9HMzN`Wf%7s{-`7hqKx+FjFpRuo zr&YZ2LBBlrF_whI?;wK$Fgl7P&Pqu&LiO4q2Z6yn|5$!*6%|2Q@h9V64Uc}!^yF(} zfnDAEtV8>|EgIN#nL1=FYAC9yWgz1O*$aKT=(hlYctFCiLA7N_ut_H`LbuRUWOU63s8qXe>+TC^(&%cpU>GXE#H8AQc-py_s zd%3Q7z6zwABI01EnK}`Hg5%I5s!EeZO9IzcLhk$UsWQMRU_+w`v0!KHfFO$2M>*L``{yR-PedN9q z)2H!_Pvms387%i)NtWVLT6=4>eM*}WF3WGVORf5lcbR|!DBa%n^TY$&;Fci>eI6_0 zsq3e*e&qz&O2(M)0J|k#Iaz%{#U{GB#ePn_QKI5?OJ?hX;EVS|8uY^Pi3eM{a|~)L zEMs6$?QMP)1sbJZ!OO#K+Xnf>OBKByn@lGvb$0{7I%%zf@%-nhy6kR-e^Vt>P2;qu z64l2}yyC5VZO6R^M;yuxikf*>ZC)=LhhZ!NiJ}ACa>XJ%9;VNmD?Ro-b0N6o9B9b7 zfNFADc5ZQ0J7yws|B;r^2D5N|_4IYscM)OG>+kgfh&02EXS=lPDW8 zA7s{BtQ`q}{NBut{$cYp?=LZki}>J2dIU7FMb zr@gw8o?YG0Hws_p?G{XD!KOT52&hr^{zAbHl(ELvh?hX+07GLTo}e6iiSGjEsy9EZ zaU}bxuJ3kMzD-~j+VfRsk#Nzyt-liLCw+-gZDI0II* zRybi6+@nDBfLvM;keQ|Y0#z-5sxPEo>N9$*Qckb-F)$3H#)$i;++#pZ2u7Vbl8n%n zj)_=YpxjG~$*Y77i?hQE2eQ`a8MN|ys-aMsPRhGS;^Y3lK8Z7UhDbgCg@JUd=R1fuTwea zj(A5xlqBVdGn+C0vyna$T0}x>lLaK1ueWLS(^Cs@*`hy=YgRmykw9V;Q>)k)FMIBZ z%&V0Qt3!+kF-#02Cmjf91j&&!{ohm_Hb(|pguJ8T?x!sl#L^L8tR#g^6YD>GKTxZ~OQ8nBakWJ#{eX=6Y^aP{I!Gs&2m z`i1jj<>vioV)4;Y^gg7ykQS@^(V(u=t6rdieW}y=YLI{y0&O}VmaII#kGl1-!3c4$ zYX0t(JN9uPt99&mTQRI}u0gG@RN#x~-nP^@KTr)~0EPbmfkxD3=JpXs{<`mp z$eN{M08TpE1Dguo$COYgB?!6%(t>Ki6^~YU8&5qn^NO-+d1U z%ISQt;N~!YYELQUDF%F`ADHvXpH16a81V5F@81H;x5-cRnGFbpwOR-v%=|s56ZV=0 z|J7c;#ceG@Z8h0U5ZF=vSBA-dt#tCQ0R&Q_{X6cShu;6u(7*PGoL|&sf5H>VRF;fo zgNgoKp#7Uhsh@xaAAl|-Zj)MjT|Y+2q+67r4U-WF1ue9$|F$vOS>W1&!e2X=wv+NB z@{?Y@o&TZm?_Xk?G`K$4e{Z1r7?MBIR~#;bT8+3Zc3X|By20T>BFC~wv{INDm8+}$ zkH4dQ*_X3gwlh-PN>)Er@iSMZ;67*eu+ty~r-+z8HWADA|Egp2;n6(|uR{N2k}{I&bI(6ztLO9oS3$%2KiY&n6R~G^?0>DxZE%^) z7ZiH@9=hb?0k8h|C=%Nd$ar`@lWhMf18a`Ul>9$CqYMiq_}7w<#QtY{lrP;9O%^R# zwk_8=&KIHskNHiiZPs0T-e6gM~T=)Y*1_yJ0Q8S$#-G$zScr z$WLg!VWC)g($#@Z{ru&az+S-Rc%4;O#p_EmM?Cx^i>yzG;jfZ=Z$-I~?KNTd*LSvu zNA}FZZsTw2K5FJCJTdMy-$!??Tnx5tlh#yR94s7Z{lod3J2K|#)^j@@ zSzDbnRdw0B=!*lw|GTroXFxr7-ic(x=(+B^v$IjCG&uTC)<2pw0%@Ee^jl7P=4)=U zksR8l@wV z#UjptEEw&tsH!^|t^<2X5Q7?Y^9H?#$u?_mq-?R8tD{P?^BOfxkQumK0%i(Cg^{|75g@qsTG&tdoyDqi$MiO^_qgb+Wyz4FT-{FFv;ep+TzxX9UbZ|FD)%X+gfQmbo-?2vHG_JJpjSc;y-alu6U`W zT6EVI_W#qXJ+cUj`6MTg0}8M&H!b_ zK~zHjD&Y!zh8*(Zfe?Zyk=LRoT|ubK2?|Y@Br=~UBUv}3(Z9tdiK;&-NGu;0iySk}ukGmo=~&c$+^t>t3eL z%rKvKxhZym&b4HSeuk^Qejbd*iRRXR$`Q2kL}y9vETpUD?P z^fyEFFD;*nii?po+E9`ClBJri-6Xk70$DrL0yUCc7q%@7;6=&N5bwG1I-N~bLJ%4` z`d08^N0;x~!+SVHR*P*HqAcv}i_SF78-KBfN6M?OagwFRmY#qS0bzq0HEJSM;|Ywq zeg*{oqC^%Y!?wYy!6bUZMKS^nG&?0nv-TGA5+%1pGpVQD`r_r4Q^yRQ@D|8+UbC9a9-9AXvf^y-1vS*V3SD=MP}_?6lRy}Ca9{3 zNp84=RXTMljn``&Uxd%ZuYhqE!u~5dr0H$=s|@)|y%cCf6x*-{8*Ew;kbu#R%1G&< zgF1PEVCHjH(aGRhv00Au%o~Bi+ZRT^9CKo`kFy9w`w;QnPHPv313GuU`HIkB(V6`Qwyo5ze~yUQ zR*K2RBxVps84z*tAc&)S!C}FXx#(VCk^jrx4|$p!@KFhtp-}ogn2H>r713wqLXRJA z55whS4-qOmHrB(EByWktMG~WR7Y{MAL`kO#c;kO3%=_MpH};?t6Cm7&`zvwegdK2l zD#divVpDQU(f*`-vDwfBr5tv3W4S3VTO<^$o9xU7Ny+=OgxCAry?<2#|==#IwI{*$ERxIWBQUqytX zYv;gI>^w(?BF}RMtT*r4Ubl(^aAotpt)Cm?$#U8zBGGmGJbmNtDs+Da;D6iz?Xw8F z^j<#gyIx)Il5XpI2)79*1nwh+7?3 zglfBj zzkxYl;SC)B(iX3f0(-XHCp@5zI{z-f+`wxtMGX9U!nUC9K!DGNKq)zT4l-BgzCjlt z1Yy<|ukpEkXtIB`5BMA7+c3ivpjAhHM}B`cd4w6ylZIW7vvOv|@q1*>^!ZEcb zC07L;)B^xY@}MFk?HvAjiT_`;w|OWeeFM&m9yz|=my2ol9><1Ca}HZ&5~`Boiw*qI z4%1;pLn}{Zd{ON^G7zI}aq)}VcFvbTboiXmG+~zKROs^-_~rgfr;l`lUz@2(uUc-; zI~RR!Fk+tb@$z=6VQLHRi65`b%w;&ay4oR0G^+3?s13`KS(gezBy8d@>|&kgKdj78 zcDuMh%EkDKulGI^Jx+XxS@$OL)sEr(* zeLO@I>XELxN{=b_=1v;nRv}mb{@A(c+QRWZoRdpld52l~9L5upEIBBTRQbX$&Z`*9 z6MBCB2@~FBgtzw* z#iN?wy-mZk)LGt+9LgBxFKgv6I~RN!?lpKg+9i=+EQ$zp2e}kMl)r4p4)r)f9RJ)N zv^w})`S1#pgxDJb%&UzgaO+Bb#P2JVmFb^nj>{*z_GZ~AFDv3(Ov@Pvw)<{(K)bvd z1?N4O@7@T;fKdB^*8>D9ety;ajb#v4Ite%6GM`mhl|-MdO#B_N>s6f*3MG}VYVT;9 zEn9=Fm9iF_mR}*^Y=j58Fn|OCQA0*c=jg2=uh`W5Fv_48=*T`5YcQQe`GUO>rv0+>d zts>!F8o8^!%!4?TYP)Yi%7!?qf#AMjxnB&`uX#l*$ho=m+h&;2!-cm)tS|!+$}Y`Q zt$?|gpEFDlpq-wz0;JyF%JAK{@zy&R%ARg%$1066N*=3_D=K*cT@QJ0iFEYf52+pE zP&|S555T5Z{MB{2HP<1uMTXGCt>WyxZ;)2ypc*u-_viG&ZOZpb8rviGm&@kQi5Jb@ z`g$4(qYT3J80h@X-EcI`VSMv`_NH3r_WR5rWd}VEeP3sxW?@~-$`BHH3sh`T%5UIE z_y{i%7EDou286b@vEGZbQVNI@h>U$?2o{XFMW@h zeuE~5>4?q0MX1Rro=o?#QSbp~bgTjAQ`ym4E4&YT{kt05sCtD4WWez4Lr4CJa3Ae;~PKbm}a` zCj1rtS&Bs`-`RBsy(VgQ)5PjTI$LR= zE2-L3D?;1(p7TuCeN{4VQ+^#{wOPUd{shc(1Mz8ZU6f;*3n==Pc3$&b@Kvy=5hrnlcfY+ld zMKfn(8ZwQp@zPu@aun66+1gkUXnFa`7|Fol1CaD86EzGw-K`ixT!uCNg`-bxPN_elb6sW?~9mcUSN{wdZ>7^(ibv=aPK?9M^UFe7@Q* zYMlRg5#`Low_PehGQO8C`@<1?RA}fU-wERC0o3|IO|V)TzrUf3@bRp^)wD3}227#1 z&bEI3CYC|JmPF$#{juo~Uv=hT};vJ&qXJv9&td}Gp$k|ak;6uNyq$oyHQEpVWf zat$YZ%NNFzy3(-Ww;&W#9cW=ITN9Yg>00LCr#9eiHvRoglyxtRf_`NZJ0?F`)Ovc< z&zK)d?5ysM0k2v(Y}aCOk$)_e-X2fA*gx8Zj{ENO$F>^ATQIYUcuzdu?owo-1FE~x zr-HxoPG&b62fwQsies5ioJ@Guo&;TiXKzC6-(S0l>{8Qx+OwMYPgd+?9qfXts-c#M zHZn1@kG0Am>vFe(W;{IAK|c3x|Im=lHAoc&hj+GqqaVYigy~CXJ2Ba8;jovgd~w#6 z+Ff&aS`y#@?Im@7Bb4R-9|&8%iRii*9=EgClC)`A)Lq}y-IeOB5*+1X8uEvq#cZZrk_g7SrYnTY<$`WfyQr?3^zwy`G2 z`c7t7bI;f9d{=G$>#Wq8uay^&B`qG2og){=Rg0+3-iY$jbX2Kh;jYJTBu)ieRUpuWuU9+e*E26)l!0qMmbdw`*yrC z!lVf_MdO0E#p0yXSk78AJS(pXoL%D>%|32>Y(T?QBkE@+x!r=Q{TP(`Zn!Z_RS>@~-JUA-oAPP@H)UxEzE)|U1p8P_*P7pgU1jryfcw*KIOgJ5 zDh+wU*iV4ZWeOgupULkZnEQU7v;Er!9hQAsEgvba#tL3pO!6)l=CgjMZj}QTW@jq% z)TBypce@nv&D-)*)maX2y=tt|%fzT#2Kj>GJYAvEI+lt1+0bA zY8eQdK%`OPH1zx#J?(YEg-$T@3OkHevmZj$7{)}1Tro&<_8QZQe*Xh*iCHG#qx^x_ zLJy+8pqtZ)t|bMqTXuA0oR^Z(48kKU6A`W%r!?_PxF*I>QNaAYq~;~hSOC7TL)bmQdZL*{oVs|s>{P;?5Odu6wuSr zt&HLCE~Di%h$r~Gf}*c72ydGDO*Epq>Ao{DC=~*S-moNE*wKA$qKaVERG44$rY=YZ zV+=$MYX$E%;9^&(DQV4);((WwxjXF7T!d>3yp)WUguVx=tXcbSC8(!I@6xAe)K#c% zBLpepHsyJQT>kNhZK zDtB~rK!9_uLMF0=eZJW=4{cM^4xnd5asV3u0qv(K{vg%DXKq6rs439$HTj^)wJb>Z zP5_~l4?#KuAW91G&N|iOPK%=E{_Niysgs4S5KaW|tiVk$rV^hp6|{^AB*LJ6Q;G=S zdE@=7e-$-L0;d$}YAXK)k}M~;9+atCDv+jp!Jam?u<-UsBh^4@$T?+dhEq6kkG#4W zJ90tvg|8vC+_Lp(sN4OCWo~UI%NrzuR&+DF-)3s2xgNz`zIYM`>vnATmkY ze1o#!i)uWh<-mt$AK~I-uN6<6uBd%zDV?aOT6(T%{L(-A7=w5OUXRaJQSX<3mum#m zw0bA*Xhz@Uej=p*gGM`BEW1b7{>#1m~=a)M* zg?Eo2XI7m;$J^Ogl+O(9yK43lg}^y4UypQdtNFUW;pMSExvQvP5~4%xey!r3GI@IZ zJaZ7(%Qlx>aPW<$w+7*rX{d{r&X1VY0ak9X=0!H1_PSX3?$$_RG6aqi-d}at9GQ^- zg+cpdna;k~r=`))NG|AVgCCb|2WaoazEYag8jzLVblD+W+Qhtod2oQ9^AWM0)imvd-aw|B+gE#%w(YU#~lQOi29=h1$qxhz8iZmpZuH&n9Ts3 zgx~VjtoN+XLYx;|HMLBk=*C;SJVM0R_9C{~R!u^vhI@Kd<-Crl%A?xx2se_*CMT!7 z3kZ9YJAJ6CnY7mduM*k(pV#+u)W^muo>%ydLY_}p3wF#>-g04f1dsQPstHQz;T97 zgamx7h96+4S{pVJ-}ni)rcPBOv?eJW7|0@yk~>Gq9mJT_8wiw4CWBb3vh*+TvaScB z64SU7tgEc*L)k+1HX>U%*bJwK7eji6$Q=;WibvCeGi?@*;YLP@Zt9&c)W*Iu4B?X{ zf4x7y{W~q)ne#KNc)c*~^bq5%t6Dou|A>rihbSVEg<4Oxilx(6XYh^^aE%$hlWU1I zH(gDuCd{a&Xzl;2=SbwfRM--Bc=R;T2FP04qr|Jy>Ac`_((U#&>eI{0c#wFlczU{< zu)Q%AFcbtXg54Cuw_$fzB=(&*bEOKME~*T%GVoFzKmN^S9vmI}?2g+c2@W1ts<%rR zz=mwuBJEEi2vT~9m5*GT@VYcrQ}WFy1Mt$HQNxCTufdtco?KA+b&r6=V;Zt(9Sphf zDEAT9_Stw{Q>|gbd~I_s*&AT&We%vx_gYP_vkowCF%V>n@Hs|zPBG1>jbfwwm!V4Y zkQ&L^8CXoCKpv}{yDuY~P4*@EZ$e%K!{RO+05^i+bB1RGMM3yk__UVW?jzzX;x8b- zY`b^JqGY@@b0?zfc#U2iF!z(|F1X>UjxXPlVG};{cnNChk9g3JUmSyEa~Yw z`FPKj9Wv6x=fg}ptvCc=U9-~cn6kFh<$kC$@IE}q20F2frOZ`()zo%>Mt?mB+Zgw? zy1483&5o)&=-!VoF=UqrRMS^qG{~na;1)q5n}wD}J#|Z*h+}U+97mrL?1e z6tS^2$+-~d{oQt*>p&{}=k$=>sz|YD0nm&=Yhg&|Mrls0vIMHWAu5)i;nZoDK`s$`iHs3`Fm~;{D{3++k zq$2g%7;c$kSMS9fZz_j^V7 z6*&(_F4mHjhKVC)r9OekQce6f%eDWaj(nSfn$=)PIgVS~0pR4V_SJ=DzY{`@+l*Hs zU=#I%-?IoWaL@+1 zSST4kADqv>v378ZU*ZQ#$<(R2<0a{rjORO|`ySv-7uO-)REBA4d3X4bGroAn;1I>P zkuUzN1erB*&BpoL>Ybgk9mm$hNmno{l_T;6<$`aJ9x?GC67AteY*07J#(p5|vaQtuQg3ZL0I>x!& zR3IKG;kg?aHaat1YPVFx8Dg9j(imm#W(A$a3Hlq2(jS8?6B;ciY#B?{Vm~DY!Qd+3 zc#(<=$zt5Hz2h|c8-~nJ60Xb@eJSX#Gs|z}89IFInsF+8w)|z#Y9D_{Q;dew_@(i! z{-8P!ykNN^3OlS#?K6;K*z7u2^`0!e}AKrtyqOKO9@`H7pBa&;{lzs|hQf1qWKimc!J&1eQ1F(}WM z4Bp<6*5B8&AcR0@@{Kr2I?fixXr5}A;4{aDoOi;4*&wnkEPq5j%qF{^2{KG*oyW)g zIjbXG=P`FIO;PD?OS<927fT^UTX>?Y_$y4l0W`t!CPjToPfC|mO~4*sVQQ;#JQp* z;H>R;!RPlQ^X%wJ!1N)Bw|P~>*nM2q+Ly$0EBARkQu+}RQ~3mj4c~|hn20}-FerVb zzSY3xHo_v)5ORiGhtl{&B~tVm85#4BeIUP6dXY2TneA~P1qwPfxbwYCnXn46R{>z< zubRyWeqoVD2hIIx;e2`&X}-vQzaDuJ1i#To zp%e$c3|Yl)p^W*%=0nk`uxMc*ZnGuoL$LH_=a*44`n*)-Ot>vF&ISYy86zH19@T4l z`neyS?T>*hGgXJW-V4v^zzYTYr<)kmU1{y#dsvQ!Y`-szMK(77dGVUez29cKs6i12 z!DsT-LkK3v?n+dbrvcc2Sxc&aRY#lpV|>|gC(tSt_W-v*6^wPM&aHq4Hj-B2>iwmx z@tzCi*gHCXWQ=z!=|~EE)uCIw*e6(U*70RJMl#%Qdi^_#h}lyoPREniNzd&MfMpO5 zs|{5@PYxvSmR|yw-ebf(@OK0Ni%s=upv4L>@lnAWrMvTf?Omkt3r~Wb%hlfZvd`uC zh!CVTC)`=BUiQGm_Qx;$o*M2)6=2xi{8Iw?7>K=V^Ys%;TeWRif$8ERHMxhPmWY~* z`c+V4xB(w6$HLTKJl}M^m8a`t)Psog<*qmE#_2u~wOP8Uk+joGH{ZInsFe`-JuF9z z(h@m)H9$ASW^mR4)XeN%$=xBe`56$9-+-Y~YC&tQYL!Kw)t8WG{VlwpQu0erdTpI$ zDZ^P_zKP+t&Z+ydH6?uWO&d+ON1`(PK?ev0;?N5l%~4Nmdw94N!TSD$e65HPrs4xK%34adMEyhz% znJ$CbI+2_5{I;(Q{z5vR$$i5%g~Q2;&eAn!>7YAcq;<4^BrfrPSb#`JA%=KE0{PKe zJe(RMV~L)8O)f2RaSqQAklZ3_o3PVRYP`on#;=#;EIPJRIOY!X(jLIyWSZQfhT^|| zg=p$bOpp_eExw0Y7~GN52u)n1=EjG)I7n{TD(44Mc-{#6-Y1~d`Ud+@VNUq6u8mrN_34&U1-JTK$Ax*q!5vKb$L;`k30@@}SzEu&~t6^`7MoNXH2n^JG?_9O{^ zJ};)2&dh$f!d__vTW;jNkjWPMGogp?x)uh#j^jaP?K(5sUTMMUWP?`}kaf6~M-S2x z_EFY~{!HaeKp|3>w6QfbtT`Ub*b%y)24^~2;1^Tm#ZUXCzsk@(6+NO6X@mcE?u^sz z&&AMp3Zq5C?g4nY8XW9>fLXc(PgN0K9f9tz?p3i$=f( z1)TEb`-nO5+rS|O>?@D%&tilof*17UUWp2~f@H0I{d0uBlV$E_VF(c8oZ|Yk6;XbmajiX5Cu8r>j$UqWyZ+Nyg5F;9D*snoR(AND>1G z8CGl;`Y03dcUCV)mK`9}5>+sGUR(e=9bJK8?c8KxJc?Q8v#OGMIHMO zgF%aXC$R-N1T?=`&;p`d!{{$K;?&j?CBqi-S#uv=BPEH7cAE5>AC%)Zu-htd>&tQk zXJ;eevrz_A$ERZ>mus1N$4O-en=1ULl5rOcNy5EHRZpt{DXSAoJ+klh;^{KC0WwgC1Ap zTtXkQq@Pzmg>!tuZoL<3Qgd#>W{fWZHz>5AgfEDUya#Xax53PbMbEIlkliMfBwl!jLKF z!H&StI!Q9BtJaJkja_51D=tSpbpUy8IK>`5$Zy$M5aFWY=x+PL*KP8a4#@s2Le3mh z;8z}1D^(aakf`sHJpXx2wMO@Ka1U14llN+Z$gbom(=FXjl0^F5zr?}^=|4>M{&!NJ zA*x;w%hTxB!fAk-_B9Tl#`gOy+mqdqu=cC_!NN{OSxrL~dX!(VEBZ@_;#EoZ!oJ{@3+m-gl3jE8K#4?O zFyG>%8>0WcubcEx(0}Dknbi`^4~zfLv?(XW+FyFvH;&xdxGD=1+vF|2uXsrYOo~?j zEuC2O#o+mWDi-C;`hHL{wGf{RF}<@>Rj{)B8!sw}=$pegqN%ZK*NV%Wk?7j$6ulUh zEDwW~5T_ZJ@yNY>%^TA1*9X`ljif9BR)iZ)hUxBG=L02C{}4g^%_9Lfs+hTR+O;N5XVe7c>o~JtZ!~@9SFe%L$H*IsB=UxfsIg+N{ThYR z2|0SCG7-X5#A3m0#3`I^*BpN>>bgVy`h(5%x$4QJmFq~sH~x1|X~UVn{iEWQF@*i_ zNh6k>n~W~S(`#83c@#dJ>dlQdEAF9~evIo5X7);ZMXoxgAs0#Ep_@`NG~;rfU$J~u)(sn)igdBG z9GV!P=fmHxd&wW&Gjj-|N^`@%cNh7E{qX}$x2tfmXS-t!b=OD>)g~Ag2qdP3UC%=& zxbBlZNT4Jk4KWFBNnCMK2}+RW?Zx-rfvxo`koqQ2FChi7SSk3Yo8J9sCl_*_Y?PHNtgoIex`3foTrg;%x4sJDY zB2ijhUu2C8MfO@Rs>A!k%1GoJ+zzAVEsMf;ikS;Vo%(TSD)DhKL7>JuA1gJhHS=TI z8M(}}(-CEaPM#aDYxuEY3Edi_Jz2178@^N~7Ft0lVQnr_DO+-bTUFx>8FX@Tm>~GY z#jZurz|nWp$_#gPk&^_a=@+CU969o<*y*WR%ugYw)n>G_5im=&=o8u6BFD7{H*Y`k za>bl+)aFK^BC4C$*4mX#y|O5{-f$}%49FgkifUv;jrU_&sShP3y`4r23;R~{QS*v| z3Jb;u_w!4BP&A~)+!0`<(L(>J=PS?SpHQyvEWT(g2HQ7>AyA6uw^@g{SCIty!Opl`Y z*;@sBO?Wgi= z7mntEiLeeQW{n!LYo`n`Y~u5=AmYxnlqLp)T1R_y)4I2~g!|UZs~_(jGvGC>^X3Ke zZ1Y5e7-oK&YZiHN9N%mh3_Yd-%?7v2s-R=p+C4yc=T1WMX2od%fcypvXKU!q9d%}Y z#cc`>7mTEv#3M{)4T3pdI~>5aXdg=#PlXwnpgOpyCmoNr171$hOgcC5ZEMunFYfpI zCD-4@;lBd{?Y={*b{K@1*saU2qlx)#E~rW*vr98wjN%c>9hthBnuftrSu^#PH6>I> zOn#<255Ax4sut=5SMC5qs8!y58u9&nfvxszck(g zMX?7?!(x;GG6od;pFa5C-&JT=Y#-AIT`%qI2%;UaLWbavPTD*d=5;PRj$+zvK~BSs zJZQh0`YFDIZtTqW-|3)7(aG91kZ^seoexv|f4r?%v?39a=p(78C~lVIgl5>#6Oj#r z!<)ByfmSIo3r!G;42)4P;&YlBVZ!-5kN*L7ItavSoODN7aslJ!GpJ8;bZnLg!ax!j zsBSVQ;pv6L_$qcx8dJmjS z+!O|=O`*Bv4c7NB?HAlNGfYokhit&Di{)iX>GaE|Z)P~BV$1S&1|iJ=YP zfYURU3jRG}kuv)b0!f(#ZaGkl*)V&b&0jvF7A5LnkLfJ8jf3=#{<15>d;hXij?z7c z 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%0Af!}UG>bagmbG~u~B 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 diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/Doc_Viewer/index_fichiers/Tab_graph.png b/Sofa/GUI/Qt/src/sofa/gui/qt/Doc_Viewer/index_fichiers/Tab_graph.png deleted file mode 100644 index 7978131a0fdb6ed088b27107862ee4e8b4be7405..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7561 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 h5|vt@ir0Rm=I)0xRca2={{{XhHthfa diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/Doc_Viewer/index_fichiers/Tab_show.png b/Sofa/GUI/Qt/src/sofa/gui/qt/Doc_Viewer/index_fichiers/Tab_show.png deleted file mode 100644 index 3a127dff5c2adec33b8fe2526111d23ef2fc6901..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9293 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
  • !v{aq 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 z zn^k<*5qQZ;z@=H+N=vG2v-S-|XG^EJ7Eijb^X0<;{o z$^{A0lk`#oPvIWd8It;26?N6|e|5i5zGZWTYL$zO{;n=M{<;dN^(s<+fA83L0W$DIq z==gapn?Ex7vz?3_CO)Dc27!z_Uf@Q@5^{yal0-Hny{8NRO8{<3ebKg;qJW73$>7H? zT;;<6yhFDZ(h`)L&?k0Y=B#y_C{xXp&#kbZ;?kMhA`#^pPgEgI`7l1q{oh&S6g+LJ zq^;v0UY`tsDP}mN&!%U9`sG&bAzDarie3xL#SWl zG9)ClWG+$UxMe(s8o(g^sl|37?N|!J>U+hv_{bo0BkHWy;U4Fpdbeoi z<@z_o429YJWzJUBBvkAtb84`ND$`FurTibHF@f6~VaD{|J?cDRO*H$+{yIgImpZht zYDLh0D0$OBs7_QHLudQ{STyKAqcedbKeA_{o5B}wN(v}O_17AMQ~OK6^5Az+Qm{u0 zx_kU~#Z>�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
    Collision Elements present:
      "); - std::map< std::string, int >::const_iterator it; - - for (it=mapElement.begin(); it!=mapElement.end(); ++it) - { - if (it->second) - { - char buf[100]; - sprintf ( buf, "
    • %s: %d
    • ", it->first.c_str(), it->second ); - textStats += buf; - } - } - textStats += "

    "; - statsLabel->setText( textStats.c_str()); - statsLabel->update(); -} - -} // namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/QSofaStatWidget.h b/Sofa/GUI/Qt/src/sofa/gui/qt/QSofaStatWidget.h deleted file mode 100644 index 7030d999841..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/QSofaStatWidget.h +++ /dev/null @@ -1,66 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include -#include - -#include -#include -#include -#include -#include -#include - -namespace sofa::core -{ - class CollisionModel; -} // namespace sofa::core - -namespace sofa::core::objectmodel -{ - class Base; -} // namespace sofa::core::objectmodel - -namespace sofa::simulation -{ - class Node; -} // namespace sofa::core - -namespace sofa::gui::qt -{ - -class SOFA_GUI_QT_API QSofaStatWidget : public QWidget -{ - Q_OBJECT -public: - QSofaStatWidget(QWidget* parent); - void CreateStats(sofa::simulation::Node* root); -protected: - QLabel* statsLabel; - QTreeWidget* statsCounter; - void addSummary(); - void addCollisionModelsStat(const sofa::type::vector< sofa::core::CollisionModel* >& v); - std::vector > items_stats; - -}; - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/QTableDataContainer.h b/Sofa/GUI/Qt/src/sofa/gui/qt/QTableDataContainer.h deleted file mode 100644 index cfb2fe62159..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/QTableDataContainer.h +++ /dev/null @@ -1,736 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include "SimpleDataWidget.h" -#include "StructDataWidget.h" - -#include "QTableUpdater.h" -#include - -namespace sofa::gui::qt -{ - -enum -{ - TYPE_SINGLE = 0, - TYPE_VECTOR = 1, - TYPE_STRUCT = 2, -}; - -template -class flat_data_trait; - -template -class default_flat_data_trait : public flat_data_trait< T, ( (struct_data_trait::NVAR >1 ) ? TYPE_STRUCT : ( (vector_data_trait::NDIM > 0) ? TYPE_VECTOR : TYPE_SINGLE ) ) > {}; - - - -template inline std::string toString(const T& v) -{ - std::ostringstream o; - o << v; - return o.str(); -} - -inline std::string toString(const std::string& s) -{ - return s; -} - -template inline void fromString(const std::string& s, T& v) -{ - std::istringstream i(s); - i >> v; -} - -inline void fromString(const std::string& s, std::string& v) -{ - v = s; -} - -template -class flat_data_trait -{ -public: - enum { is_struct = 0 }; - enum { is_vector = 0 }; - enum { is_single = 1 }; - typedef T data_type; - typedef T value_type; - static int size() { return 1; } - static int size(const data_type&) { return size(); } - static const char* header(const data_type& /*d*/, int /*i*/ = 0) - { - return NULL; - } - static const value_type* get(const data_type& d, int /*i*/ = 0) { return &d; } - static void set(const value_type& v, data_type& d, int /*i*/ = 0) { d = v; } - static void setS(const std::string& v, data_type& d, int /*i*/ = 0) - { - fromString(v, d); - } -}; - -template::NVAR> -class flat_struct_data_trait -{ -public: - enum { is_struct = 1 }; - enum { is_vector = 0 }; - enum { is_single = 0 }; - enum { is_default = ((struct_data_trait::NVAR > 1) ? 1 : 0) }; - typedef T data_type; - typedef std::string value_type; - typedef struct_data_trait shelper; - typedef struct_data_trait_var vhelper; - typedef typename vhelper::value_type vtype; - typedef default_flat_data_trait vtrait; - typedef typename vtrait::value_type iotype; - typedef flat_struct_data_trait prev; - static int size() { return prev::size() + vtrait::size(); } - static int size(const data_type&) { return size(); } - static const char* header(const data_type& d, int i = 0) - { - int s = prev::size(); - if (i < s) - return prev::header(d, i); - else - { - const char* h1 = vhelper::shortname(); - const char* h2 = vtrait::header(*vhelper::get(d), i-s); - if (h2 == NULL) return h1; - else if (h1 == NULL) return h2; - else - { - static std::string t; - t = h1; - t += " "; - t += h2; - return t.c_str(); - } - } - } - static value_type* get(const data_type& d, int i = 0) - { - int s = prev::size(); - if (i < s) - return prev::get(d, i); - else - { - static std::string t; - t = toString(*vtrait::get(*vhelper::get(d), i-s)); - return &t; - } - } - static void setS(const std::string& v, data_type& d, int i = 0) - { - int s = prev::size(); - if (i < s) - prev::setS(v, d, i); - else - { - vtype var = *vhelper::get(d); - vtrait::setS(v, var,i-s); - vhelper::set(var, d); - } - } - static void set(const value_type& v, data_type& d, int i = 0) - { - setS(v, d, i); - } -}; - -template -class flat_struct_data_trait -{ -public: - enum { is_struct = 1 }; - enum { is_vector = 0 }; - enum { is_single = 0 }; - enum { is_default = ((struct_data_trait::NVAR > 1) ? 1 : 0) }; - typedef T data_type; - typedef std::string value_type; - typedef struct_data_trait shelper; - static int size() { return 0; } - static int size(const data_type&) { return size(); } - static const char* header(const data_type& /*d*/, int /*i*/ = 0) - { - return NULL; - } - static value_type* get(const data_type& /*d*/, int /*i*/ = 0) - { - return NULL; - } - static void setS(const std::string& /*v*/, data_type& /*d*/, int /*i*/ = 0) - { - } - static void set(const value_type& /*v*/, data_type& /*d*/, int /*i*/ = 0) - { - } -}; - -template -class flat_data_trait : public flat_struct_data_trait -{ -}; - -template -class flat_vector_data_trait -{ -public: - enum { is_struct = 0 }; - enum { is_vector = 1 }; - enum { is_single = 0 }; - enum { is_default = ((struct_data_trait::NVAR == 1 && vector_data_trait::NDIM >= 1) ? 1 : 0) }; - typedef T data_type; - typedef vector_data_trait vhelper; - typedef typename vhelper::value_type vtype; - typedef default_flat_data_trait vtrait; - typedef typename vtrait::value_type value_type; - static int size() { return vhelper::SIZE * vtrait::size(); } - static int size(const data_type&) { return size(); } - static const char* header(const data_type& d, int i = 0) - { - int s = vtrait::size(); - int j = i / s; - i = i % s; - const char* h1 = vhelper::header(d, j); - const char* h2 = vtrait::header(*vhelper::get(d, j), i); - if (h2 == NULL) return h1; - else if (h1 == NULL) return h2; - else - { - static std::string t; - t = h1; - t += " "; - t += h2; - return t.c_str(); - } - } - static const value_type* get(const data_type& d, int i = 0) - { - int s = vtrait::size(); - int j = i / s; - i = i % s; - return vtrait::get(*vhelper::get(d, j), i); - } - static void set(const value_type& v, data_type& d, int i = 0) - { - int s = vtrait::size(); - int j = i / s; - i = i % s; - vtype t = *vhelper::get(d, j); - vtrait::set(v, t, i); - vhelper::set(t, d, j); - } - static void setS(const std::string& v, data_type& d, int i = 0) - { - int s = vtrait::size(); - int j = i / s; - i = i % s; - vtype t = *vhelper::get(d, j); - vtrait::setS(v, t, i); - vhelper::set(t, d, j); - } -}; - -template -class flat_data_trait : public flat_vector_data_trait -{ -}; - -enum -{ - TABLE_NORMAL = 0, - TABLE_HORIZONTAL = 1 << 0, - TABLE_FIXEDSIZE = 1 << 1, -}; - -template -class table_data_widget_container -{ -public: - typedef T data_type; - typedef vector_data_trait rhelper; - typedef typename rhelper::value_type row_type; - //typedef vector_data_trait vhelper; - typedef default_flat_data_trait vhelper; - typedef typename vhelper::value_type value_type; - typedef QVBoxLayout Layout; - - QSpinBox* wSize; - QTableUpdater* wTable; - QPushButtonUpdater* wDisplay; - DataWidget * widget; - Layout* container_layout; - - int rows; - int cols; - - table_data_widget_container() : wSize(NULL), wTable(NULL), wDisplay(NULL), widget(NULL), container_layout(NULL) {} - - - bool createLayout( DataWidget* parent ) - { - if( parent->layout() != NULL || container_layout != NULL) return false; - container_layout = new Layout(parent); - return true; - } - - bool createLayout( QLayout* layout) - { - if ( container_layout != NULL ) return false; - container_layout = new Layout(layout); - return true; - } - - - void setRowHeader(int r, const std::string& s) - { - if (FLAGS & TABLE_HORIZONTAL) - wTable->horizontalHeader()->setLabel(r, QString(s.c_str())); - else - wTable->verticalHeader()->setLabel(r, QString(s.c_str())); - } - void setColHeader(int c, const std::string& s) - { - if (FLAGS & TABLE_HORIZONTAL) - wTable->verticalHeader()->setLabel(c, QString(s.c_str())); - else - wTable->horizontalHeader()->setLabel(c, QString(s.c_str())); - } - void setCellText(int r, int c, const std::string& s) - { - if (FLAGS & TABLE_HORIZONTAL) - wTable->setText(c, r, QString(s.c_str())); - else - wTable->setText(r, c, QString(s.c_str())); - } - std::string getCellText(int r, int c) - { - QString text; - if (FLAGS & TABLE_HORIZONTAL) - text=wTable->text(c, r); - else - text=wTable->text(r, c); - if (!text.isNull()) - return std::string(text.ascii()); - else - return std::string(""); - } - template - void setCell(int r, int c, const V& v) - { - std::ostringstream o; - o << v; - setCellText(r,c, o.str()); - } - void setCell(int r, int c, const std::string& s) - { - setCellText(r, c, s); - } - template - void getCell(int r, int c, V& v) - { - std::istringstream i(getCellText(r,c)); - i >> v; - } - void getCell(int r, int c, const std::string& s) - { - s = getCellText(r,c); - } - - bool createWidgets(DataWidget* parent, const data_type& d, bool readOnly) - { - rows = 0; - int dataRows = rhelper::size(d); - - wSize = new QSpinBox(0, INT_MAX, 1, parent); - wDisplay = new QPushButtonUpdater( QString("Display the values"), parent); - - if (dataRows > 0) - cols = vhelper::size(*rhelper::get(d,0)); - else - cols = vhelper::size(row_type()); - - if (FLAGS & TABLE_HORIZONTAL) - wTable = new QTableUpdater(cols, dataRows, parent); - else - wTable = new QTableUpdater(dataRows, cols, parent); - - widget=parent; - - - - - wDisplay->setToggleButton(true); - wDisplay->setOn(dataRows < MAX_NUM_ELEM && dataRows != 0 ); - wDisplay->setAutoDefault(false); - - wSize->setValue(dataRows); - - if (isDisplayed()) - { - processTableModifications(d); - fillTable(d); - rows = dataRows; - } - - - wTable->setDisplayed(isDisplayed()); - - - - if (readOnly) - { - wSize->setEnabled(false); - wTable->setEnabled(false); - } - else - { - if (!(FLAGS & TABLE_FIXEDSIZE)) - { - parent->connect(wSize, SIGNAL( valueChanged(int) ), parent, SLOT( setWidgetDirty() )); - //_widget->connect(wSize, SIGNAL( valueChanged(int) ), _widget, SLOT(updateDataValue()) ); - - - if( FLAGS & TABLE_HORIZONTAL) - parent->connect(wSize, SIGNAL( valueChanged(int) ), wTable, SLOT(resizeTableH(int) ) ); - else - parent->connect(wSize, SIGNAL( valueChanged(int) ), wTable, SLOT(resizeTableV(int) ) ); - } - else - { - wSize->setEnabled(false); - } - parent->connect(wTable, SIGNAL( valueChanged(int,int) ), parent, SLOT(setWidgetDirty()) ); - } - parent->connect(wDisplay, SIGNAL( toggled(bool) ), wTable, SLOT(setDisplayed(bool))); - parent->connect(wDisplay, SIGNAL( toggled(bool) ), wDisplay, SLOT(setDisplayed(bool))); - parent->connect(wDisplay, SIGNAL( toggled(bool) ), parent, SLOT( updateWidgetValue() )); - return true; - } - - - void setReadOnly(bool readOnly) - { - wSize->setEnabled(!readOnly); - wTable->setEnabled(!readOnly); - } - - bool isDisplayed() - { - return (wDisplay->isOn()); - } - - - void readFromData(const data_type& d) - { - if (isDisplayed()) - { - processTableModifications(d); - fillTable(d); - } - } - void writeToData(data_type& d) - { - rows = wSize->value(); - if (!(FLAGS & TABLE_FIXEDSIZE)) - { - int oldrows = rhelper::size(d); - if( rows != oldrows) - { - rhelper::resize(rows,d); - } - int newrows = rhelper::size(d); - if( rows != newrows) - { - wSize->setValue(newrows); - rows = newrows; - if (FLAGS & TABLE_HORIZONTAL) - wTable->setNumCols(newrows); - else - wTable->setNumRows(newrows); - } - } - processTableModifications(d); - - if (isDisplayed()) - { - for (int y=0; ynumCols(); - else - currentTableNumRows=wTable->numRows(); - - int rows = wSize->value(); - - - for (int x=0; x 0) ? vhelper::header(*rhelper::get(d,0),x) : vhelper::header(row_type(),x); - if (h && *h) - setColHeader(x,h); - else - { - std::ostringstream o; - o << x; - setColHeader(x,o.str()); - } - } - for (int y=0; ysetNumCols(rows); - else - wTable->setNumRows(rows); - - for (int y=currentTableNumRows; ynumCols() > dsize ? dsize : wTable->numCols(); - else - currentNum=wTable->numRows() > dsize ? dsize : wTable->numRows(); - - for (int y=0; yadd(wSize); - container_layout->add(wTable); - container_layout->add(wDisplay); - } - -}; - - -//////////////////////////////////////////////////////////////// -/// variable-sized vectors support -//////////////////////////////////////////////////////////////// - -template -class vector_data_trait < std::vector > -{ -public: - typedef std::vector data_type; - typedef T value_type; - enum { NDIM = 1 }; - static int size(const data_type& d) { return d.size(); } - static const char* header(const data_type& /*d*/, int /*i*/ = 0) - { - return NULL; - } - static const value_type* get(const data_type& d, int i = 0) - { - return ((unsigned)i < (unsigned)size(d)) ? &(d[i]) : NULL; - } - static void set( const value_type& v, data_type& d, int i = 0) - { - if ((unsigned)i < (unsigned)size(d)) - d[i] = v; - } - static void resize( int s, data_type& d) - { - d.resize(s); - } -}; - - - - -template -class vector_data_trait < sofa::type::vector > : public vector_data_trait< std::vector > -{ -}; - - -// template -// class vector_data_trait < sofa::component::topology::PointData > //: public vector_data_trait < sofa::type::vector > -// { -// public: -// typedef sofa::component::topology::PointData data_type; -// typedef T value_type; -// enum { NDIM = 1 }; -// -// static int size(const data_type& d) { return d.getValue().size(); } -// static const char* header(const data_type& /*d*/, int /*i*/ = 0) -// { -// return NULL; -// } -// static const value_type* get(const data_type& d, int i = 0) -// { -// return ((unsigned)i < (unsigned)size(d.getValue())) ? &(d.getValue()[i]) : NULL; -// } -// static void set( const value_type& v, data_type& d, int i = 0) -// { -// if ((unsigned)i < (unsigned)size(d.getValue())) -// { -// sofa::type::vector& d_data = *(d.beginEdit()); -// d_data[i] = v; -// d.endEdit(); -// } -// } -// static void resize( int s, data_type& d) -// { -// sofa::type::vector& d_data = *(d.beginEdit()); -// d_data.resize(s); -// d.endEdit(); -// } -// }; - - -//////////////////////////////////////////////////////////////// -/// PointSubset support -//////////////////////////////////////////////////////////////// - -template<> -class vector_data_trait < sofa::component::topology::PointSubset > -{ -public: - typedef sofa::component::topology::PointSubset data_type; - typedef unsigned int value_type; - enum { NDIM = 1 }; - static int size(const data_type& d) { return d.size(); } - static const char* header(const data_type& /*d*/, int /*i*/ = 0) - { - return NULL; - } - static const value_type* get(const data_type& d, int i = 0) - { - return ((unsigned)i < (unsigned)size(d)) ? &(d[i]) : NULL; - } - static void set( const value_type& v, data_type& d, int i = 0) - { - if ((unsigned)i < (unsigned)size(d)) - d[i] = v; - } - static void resize( int s, data_type& d) - { - d.resize(s); - } -}; - -//////////////////////////////////////////////////////////////// -/// std::map from strings support -//////////////////////////////////////////////////////////////// - -template -class vector_data_trait < std::map > -{ -public: - typedef std::map data_type; - typedef T value_type; - enum { NDIM = 1 }; - static int size(const data_type& d) { return d.size(); } - static const char* header(const data_type& d, int i = 0) - { - typename data_type::const_iterator it = d.begin(); - while (i > 0 && it != d.end()) - { - ++it; - --i; - } - if (i == 0) return it->first.c_str(); - else return NULL; - } - static const value_type* get(const data_type& d, int i = 0) - { - typename data_type::const_iterator it = d.begin(); - while (i > 0 && it != d.end()) - { - ++it; - --i; - } - if (i == 0) return &(it->second); - else return NULL; - } - static void set( const value_type& v, data_type& d, int i = 0) - { - typename data_type::iterator it = d.begin(); - while (i > 0 && it != d.end()) - { - ++it; - --i; - } - if (i == 0) it->second = v; - } - static void resize( int /*s*/, data_type& /*d*/) - { - //d.resize(s); - } -}; - - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/QTableUpdater.h b/Sofa/GUI/Qt/src/sofa/gui/qt/QTableUpdater.h deleted file mode 100644 index 608f4b7a22d..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/QTableUpdater.h +++ /dev/null @@ -1,80 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ - -#pragma once -#include - - -#include -#include - - - -namespace sofa::gui::qt -{ - - -class QTableUpdater : virtual public QTable -{ - Q_OBJECT -public: - QTableUpdater ( int numRows, int numCols, QWidget * parent = 0, const char * name = 0 ): - QTable(numRows, numCols, parent, name) - {}; -public slots: - void setDisplayed(bool b) {this->setShown(b);} - void resizeTableV( int number ) - { - QSpinBox *spinBox = (QSpinBox *) sender(); - QString header; - if( spinBox == NULL) - { - return; - } - if (number != numRows()) - { - setNumRows(number); - } - } - - void resizeTableH( int number ) - { - QSpinBox *spinBox = (QSpinBox *) sender(); - QString header; - if( spinBox == NULL) - { - return; - } - if (number != numCols()) - { - setNumCols(number); - - } - } - -}; - -} -} -} - -#endif diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/QTabulationModifyObject.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/QTabulationModifyObject.cpp deleted file mode 100644 index da863e35ab2..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/QTabulationModifyObject.cpp +++ /dev/null @@ -1,159 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#include "QTabulationModifyObject.h" - -#include "QDisplayDataWidget.h" -#include "QDisplayLinkWidget.h" - -#include "ModifyObject.h" -#include -#include -#include -#include - -namespace sofa::gui::qt -{ - -QTabulationModifyObject::QTabulationModifyObject(QWidget* parent, - core::objectmodel::Base *o, QTreeWidgetItem* i, - unsigned int idx): - QWidget(parent), object(o), item(i), index(idx), size(0), dirty(false), pixelSize(0), pixelMaxSize(600) -{ - const int screenHeight = QGuiApplication::primaryScreen()->availableGeometry().height(); - - QVBoxLayout* vbox = new QVBoxLayout(); - vbox->setObjectName("tabVisualizationLayout"); - vbox->setContentsMargins(0, 0, 0, 0); - vbox->setSpacing(0); - - this->setLayout(vbox); - - //find correct maxPixelSize according to the current screen resolution - pixelMaxSize = screenHeight - 300; -} - -void QTabulationModifyObject::addData(sofa::core::objectmodel::BaseData *data, const ModifyObjectFlags& flags) -{ - - if ( (!data->isDisplayed()) && flags.HIDE_FLAG ) - { - return; - } - - data->setDisplayed(true); - - const std::string name=data->getName(); - QDisplayDataWidget* displaydatawidget = new QDisplayDataWidget(this,data,flags); - this->layout()->addWidget(displaydatawidget); - - size += displaydatawidget->getNumWidgets(); - pixelSize += displaydatawidget->sizeHint().height(); - displaydatawidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); - - connect(displaydatawidget, SIGNAL( WidgetDirty(bool) ), this, SLOT( setTabDirty(bool) ) ); - connect(this, SIGNAL(UpdateDatas()), displaydatawidget, SLOT( UpdateData())); - connect(this, SIGNAL(UpdateDataWidgets()), displaydatawidget, SLOT( UpdateWidgets())); - connect(displaydatawidget, SIGNAL( dataValueChanged(QString) ), SLOT(dataValueChanged(QString) ) ); -} - - -void QTabulationModifyObject::addLink(sofa::core::objectmodel::BaseLink *link, const ModifyObjectFlags& flags) -{ - const std::string name=link->getName(); - QDisplayLinkWidget* displaylinkwidget = new QDisplayLinkWidget(this,link,flags); - this->layout()->addWidget(displaylinkwidget); - - size += displaylinkwidget->getNumWidgets(); - pixelSize += displaylinkwidget->sizeHint().height(); - - connect(displaylinkwidget, SIGNAL( WidgetDirty(bool) ), this, SLOT( setTabDirty(bool) ) ); - connect(this, SIGNAL(UpdateDatas()), displaylinkwidget, SLOT( UpdateLink())); - connect(this, SIGNAL(UpdateDataWidgets()), displaylinkwidget, SLOT( UpdateWidgets())); -} - -void QTabulationModifyObject::dataValueChanged(QString dataValue) -{ - m_dataValueModified[sender()] = dataValue; -} - -QString QTabulationModifyObject::getDataModifiedString() const -{ - if (m_dataValueModified.empty()) - { - return QString(); - } - - QString dataModifiedString; - std::map< QObject*, QString>::const_iterator it_map; - std::map< QObject*, QString>::const_iterator it_last = m_dataValueModified.end(); - --it_last; - - for (it_map = m_dataValueModified.begin(); it_map != m_dataValueModified.end(); ++it_map) - { - const QString& lastDataValue = it_map->second; - dataModifiedString += lastDataValue; - if (it_map != it_last) - { - dataModifiedString += "\n"; - } - } - - return dataModifiedString; -} - -void QTabulationModifyObject::setTabDirty(bool b) -{ - dirty=b; - emit TabDirty(b); -} - -bool QTabulationModifyObject::isDirty() const -{ - return dirty; -} - -bool QTabulationModifyObject::isFull() const -{ - return pixelSize >= pixelMaxSize; -} - -bool QTabulationModifyObject::isEmpty() const -{ - return size==0; -} - -void QTabulationModifyObject::updateDataValue() -{ - emit UpdateDatas(); -} - -void QTabulationModifyObject::updateWidgetValue() -{ - emit UpdateDataWidgets(); -} - -void QTabulationModifyObject::addStretch() -{ - dynamic_cast(this->layout())->addStretch(); -} - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/QTabulationModifyObject.h b/Sofa/GUI/Qt/src/sofa/gui/qt/QTabulationModifyObject.h deleted file mode 100644 index 79312be505d..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/QTabulationModifyObject.h +++ /dev/null @@ -1,93 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include -#include -#include -#include - -#include -#include -#include -#include -#include - - -namespace sofa::gui::qt -{ - -struct ModifyObjectFlags; -class DataWidget; - -class QTabulationModifyObject : public QWidget -{ - Q_OBJECT -public: - QTabulationModifyObject(QWidget* parent, - core::objectmodel::Base *object, QTreeWidgetItem* item, - unsigned int idx=1); - - void externalWidgetAddition(int num) {size+=num;} - void addData(sofa::core::objectmodel::BaseData *data, const ModifyObjectFlags& flags); - void addLink(sofa::core::objectmodel::BaseLink *link, const ModifyObjectFlags& flags); - void addStretch(); - - unsigned int getIndex() const {return index;} - bool isFull() const; - void setFull() {pixelSize=pixelMaxSize;} - bool isEmpty() const; - bool isDirty() const; - - QString getDataModifiedString() const; - -public slots: - void setTabDirty(bool=true); - void updateDataValue(); - void updateWidgetValue(); - void dataValueChanged(QString dataValue); - -signals: - void UpdateDatas(); - void UpdateDataWidgets(); - void TabDirty(bool); - void nodeNameModification(simulation::Node *); - - - -protected: - core::objectmodel::Base *object; - QTreeWidgetItem* item; - - - const unsigned int index; - unsigned int size; - - bool dirty; - std::map< QObject*, QString> m_dataValueModified; - - unsigned int pixelSize; - unsigned int pixelMaxSize; - -}; - - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/QTransformationWidget.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/QTransformationWidget.cpp deleted file mode 100644 index 1103da46d4c..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/QTransformationWidget.cpp +++ /dev/null @@ -1,144 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#include "QTransformationWidget.h" -#include -#include -#include -#include -#include -#include - -#if !defined(INFINITY) -#define INFINITY 9.0e10 -#endif - - -namespace sofa::gui::qt -{ -QTransformationWidget::QTransformationWidget(QWidget* parent, QString name):QGroupBox(parent), numWidgets_(2) - -{ - //this->setColumns(4); - - this->setTitle(name); - //******************************************************************************** - //Translation - new QLabel(QString("Translation"), this); - translation[0] = new WDoubleLineEdit( this, "translation[0]" ); - translation[0]->setMinValue( (double)-INFINITY ); - translation[0]->setMaxValue( (double)INFINITY ); - - translation[1] = new WDoubleLineEdit( this, "translation[1]" ); - translation[1]->setMinValue( (double)-INFINITY ); - translation[1]->setMaxValue( (double)INFINITY ); - - translation[2] = new WDoubleLineEdit( this, "translation[2]" ); - translation[2]->setMinValue( (double)-INFINITY ); - translation[2]->setMaxValue( (double)INFINITY ); - - - //******************************************************************************** - //Rotation - new QLabel(QString("Rotation"), this); - rotation[0] = new WDoubleLineEdit( this, "rotation[0]" ); - rotation[0]->setMinValue( (double)-INFINITY ); - rotation[0]->setMaxValue( (double)INFINITY ); - - rotation[1] = new WDoubleLineEdit( this, "rotation[1]" ); - rotation[1]->setMinValue( (double)-INFINITY ); - rotation[1]->setMaxValue( (double)INFINITY ); - - rotation[2] = new WDoubleLineEdit( this, "rotation[2]" ); - rotation[2]->setMinValue( (double)-INFINITY ); - rotation[2]->setMaxValue( (double)INFINITY ); - - - //******************************************************************************** - //Scale - QLabel *textScale = new QLabel(QString("Scale"), this); - scale[0] = new WDoubleLineEdit( this, "scale[0]" ); - scale[0]->setMinValue( (double)-INFINITY ); - scale[0]->setMaxValue( (double)INFINITY ); - - scale[1] = new WDoubleLineEdit( this, "scale[1]" ); - scale[1]->setMinValue( (double)-INFINITY ); - scale[1]->setMaxValue( (double)INFINITY ); - - scale[2] = new WDoubleLineEdit( this, "scale[2]" ); - scale[2]->setMinValue( (double)-INFINITY ); - scale[2]->setMaxValue( (double)INFINITY ); - - setDefaultValues(); - - connect( translation[0], SIGNAL( textChanged(const QString&) ), this, SLOT( changeValue() ) ); - connect( translation[1], SIGNAL( textChanged(const QString&) ), this, SLOT( changeValue() ) ); - connect( translation[2], SIGNAL( textChanged(const QString&) ), this, SLOT( changeValue() ) ); - connect( rotation[0], SIGNAL( textChanged(const QString&) ), this, SLOT( changeValue() ) ); - connect( rotation[1], SIGNAL( textChanged(const QString&) ), this, SLOT( changeValue() ) ); - connect( rotation[2], SIGNAL( textChanged(const QString&) ), this, SLOT( changeValue() ) ); - connect( scale[0], SIGNAL( textChanged(const QString&) ), this, SLOT( changeValue() ) ); - connect( scale[1], SIGNAL( textChanged(const QString&) ), this, SLOT( changeValue() ) ); - connect( scale[2], SIGNAL( textChanged(const QString&) ), this, SLOT( changeValue() ) ); - - - //Option still experimental : disabled !!!! - textScale->hide(); - scale[0]->hide(); - scale[1]->hide(); - scale[2]->hide(); -} - -void QTransformationWidget::setDefaultValues() -{ - //******************************************************************************** - //Default values - translation[0]->setValue(0); - translation[1]->setValue(0); - translation[2]->setValue(0); - - rotation[0]->setValue(0); - rotation[1]->setValue(0); - rotation[2]->setValue(0); - - scale[0]->setValue(1); - scale[1]->setValue(1); - scale[2]->setValue(1); -} - -bool QTransformationWidget::isDefaultValues() const -{ - return ( (translation[0]->getValue() == 0 && translation[1]->getValue() == 0 && translation[2]->getValue() == 0 ) && - (rotation[0]->getValue() == 0 && rotation[1]->getValue() == 0 && rotation[2]->getValue() == 0 ) && - (scale[0]->getValue() == 1 && scale[1]->getValue() == 1 && scale[2]->getValue() == 1 ) ); -} - -void QTransformationWidget::applyTransformation(simulation::Node *node) -{ - sofa::simulation::TransformationVisitor transform(sofa::core::execparams::defaultInstance()); - transform.setTranslation(translation[0]->getValue(),translation[1]->getValue(),translation[2]->getValue()); - transform.setRotation(rotation[0]->getValue(),rotation[1]->getValue(),rotation[2]->getValue()); - transform.setScale(scale[0]->getValue(),scale[1]->getValue(),scale[2]->getValue()); - transform.execute(node); -} - - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/QTransformationWidget.h b/Sofa/GUI/Qt/src/sofa/gui/qt/QTransformationWidget.h deleted file mode 100644 index 9cc7cc5edef..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/QTransformationWidget.h +++ /dev/null @@ -1,57 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include -#include "WDoubleLineEdit.h" - -#include -#include -#include - -namespace sofa::gui::qt -{ - -struct ModifyObjectFlags; -class QTransformationWidget : public QGroupBox -{ - Q_OBJECT -public: - QTransformationWidget(QWidget* parent, QString name); - unsigned int getNumWidgets() const { return numWidgets_;}; - - void setDefaultValues(); - bool isDefaultValues() const; - void applyTransformation(simulation::Node *node); -public slots: - void changeValue() {emit TransformationDirty(true);} -signals: - void TransformationDirty(bool); -protected: - const unsigned int numWidgets_; - - WDoubleLineEdit* translation[3]; - WDoubleLineEdit* rotation[3]; - WDoubleLineEdit* scale[3]; -}; - - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/QVisitorControlPanel.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/QVisitorControlPanel.cpp deleted file mode 100644 index 98589716b37..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/QVisitorControlPanel.cpp +++ /dev/null @@ -1,142 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#include "QVisitorControlPanel.h" -#include - -#include -#include -#include -#include -#include - -#if !defined(INFINITY) -#define INFINITY 9.0e10 -#endif - - -namespace sofa::gui::qt -{ - -QVisitorControlPanel::QVisitorControlPanel(QWidget* parent): QWidget(parent) -{ - QVBoxLayout *vbox=new QVBoxLayout(this); - vbox->setMargin(0); - vbox->setSpacing(0); - - //Filter the results to quickly find a visitor - QWidget *filterResult = new QWidget(this); - QHBoxLayout *hboxFilers=new QHBoxLayout(filterResult); - - textFilter = new QLineEdit(filterResult); - QPushButton *findFilter = new QPushButton(QString("Find"), filterResult); - findFilter->setAutoDefault(true); - - hboxFilers->addWidget(new QLabel(QString("Focus on:"), filterResult)); - hboxFilers->addWidget(textFilter); - hboxFilers->addWidget(findFilter); - - connect(findFilter, SIGNAL(clicked()), this, SLOT(filterResults())); - connect(textFilter, SIGNAL(returnPressed()), this, SLOT(filterResults())); - - //Clear Button - QPushButton *clearButton = new QPushButton(QString("Clear"), this); - clearButton->setAutoDefault(false); - connect(clearButton, SIGNAL(clicked()), this, SIGNAL(clearGraph())); - - - //Parameters to configure the export of the state vectors - QWidget *exportStateParameters = new QWidget(this); - - QHBoxLayout *hboxParameters=new QHBoxLayout(exportStateParameters); - - QCheckBox *activation=new QCheckBox(QString("Trace State Vector"), exportStateParameters); - - spinIndex = new WDoubleLineEdit(exportStateParameters, "index"); - spinIndex->setMinValue( (double)-INFINITY ); - spinIndex->setMaxValue( (double)INFINITY ); - spinIndex->setIntValue(sofa::simulation::Visitor::GetFirstIndexStateVector()); - spinIndex->setMaximumWidth(50); - spinRange = new WDoubleLineEdit(exportStateParameters, "range"); - spinRange->setMinValue( (double)-INFINITY ); - spinRange->setMaxValue( (double)INFINITY ); - spinRange->setIntValue(sofa::simulation::Visitor::GetRangeStateVector()); - spinRange->setMaximumWidth(50); - - connect(activation, SIGNAL(toggled(bool)), this, SLOT(activateTraceStateVectors(bool))); - connect(spinIndex, SIGNAL(editingFinished()), this, SLOT(changeFirstIndex())); - connect(spinRange, SIGNAL(editingFinished()), this, SLOT(changeRange())); - - - hboxParameters->addWidget(activation); - hboxParameters->addItem(new QSpacerItem(100,10)); - hboxParameters->addWidget(new QLabel(QString("First Index"), exportStateParameters)); - hboxParameters->addWidget(spinIndex); - - hboxParameters->addWidget(new QLabel(QString("Range"), exportStateParameters)); - hboxParameters->addWidget(spinRange); - - hboxParameters->addStretch(); - - //Configure the main window - vbox->addWidget(filterResult); - vbox->addWidget(exportStateParameters); - vbox->addWidget(clearButton); - - activateTraceStateVectors(sofa::simulation::Visitor::IsExportStateVectorEnabled()); - -} - -void QVisitorControlPanel::activateTraceStateVectors(bool active) -{ - sofa::simulation::Visitor::EnableExportStateVector(active); - spinIndex->setEnabled(active); - spinRange->setEnabled(active); -} -void QVisitorControlPanel::changeFirstIndex() -{ - WDoubleLineEdit *w=(WDoubleLineEdit *) sender(); - int value=w->getIntValue(); - changeFirstIndex(value); -} -void QVisitorControlPanel::changeRange() -{ - WDoubleLineEdit *w=(WDoubleLineEdit *) sender(); - int value=w->getIntValue(); - changeRange(value); -} - -void QVisitorControlPanel::changeFirstIndex(int idx) -{ - sofa::simulation::Visitor::SetFirstIndexStateVector(idx); -} -void QVisitorControlPanel::changeRange(int range) -{ - sofa::simulation::Visitor::SetRangeStateVector(range); -} -void QVisitorControlPanel::filterResults() -{ - emit focusOn(textFilter->text()); -} - - - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/QVisitorControlPanel.h b/Sofa/GUI/Qt/src/sofa/gui/qt/QVisitorControlPanel.h deleted file mode 100644 index 80b29a5b69f..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/QVisitorControlPanel.h +++ /dev/null @@ -1,54 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once - -#include - -#include "WDoubleLineEdit.h" - -namespace sofa::gui::qt -{ - -class QVisitorControlPanel : public QWidget -{ - Q_OBJECT -public: - QVisitorControlPanel(QWidget* parent); - - void changeFirstIndex(int); - void changeRange(int); -public slots: - void activateTraceStateVectors(bool); - void changeFirstIndex(); - void changeRange(); - void filterResults(); -signals: - void focusOn(QString); - void clearGraph(); -protected: - QLineEdit *textFilter; - WDoubleLineEdit *spinIndex; - WDoubleLineEdit *spinRange; -}; - - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/QtMessageRedirection.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/QtMessageRedirection.cpp deleted file mode 100644 index 853c5fcc15d..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/QtMessageRedirection.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU Lesser General Public License as published by * -* the Free Software Foundation; either version 2.1 of the License, or (at * -* your option) any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * -* for more details. * -* * -* You should have received a copy of the GNU Lesser General Public License * -* along with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#include -#include -#include - -void sofa::gui::qt::redirectQtMessages(QtMsgType type, - const QMessageLogContext& context, - const QString& msg) -{ - SOFA_UNUSED(context); - const QByteArray localMsg = msg.toLocal8Bit(); - switch (type) - { - case QtDebugMsg: - msg_info("Qt") << localMsg.constData(); - break; - case QtInfoMsg: - msg_info("Qt") << localMsg.constData(); - break; - case QtWarningMsg: - msg_warning("Qt") << localMsg.constData(); - break; - case QtCriticalMsg: - msg_error("Qt") << localMsg.constData(); - break; - case QtFatalMsg: - msg_fatal("Qt") << localMsg.constData(); - break; - } -} diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/QtMessageRedirection.h b/Sofa/GUI/Qt/src/sofa/gui/qt/QtMessageRedirection.h deleted file mode 100644 index b763e6d7cdf..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/QtMessageRedirection.h +++ /dev/null @@ -1,29 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU Lesser General Public License as published by * -* the Free Software Foundation; either version 2.1 of the License, or (at * -* your option) any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * -* for more details. * -* * -* You should have received a copy of the GNU Lesser General Public License * -* along with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once - -#include - -namespace sofa::gui::qt -{ -void redirectQtMessages(QtMsgType type, const QMessageLogContext& context, const QString& msg); -} diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/RGBAColorDataWidget.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/RGBAColorDataWidget.cpp deleted file mode 100644 index 91cdcf25164..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/RGBAColorDataWidget.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#include "RGBAColorDataWidget.h" - -namespace sofa::gui::qt::rgbacolordatawidget_h -{ -helper::Creator DWClass("default",true); - -bool RGBAColorDataWidget::createWidgets() -{ - - m_colorPicker = new QRGBAColorPicker(this); - - QHBoxLayout* hlayout = new QHBoxLayout(this); - hlayout->addWidget(m_colorPicker); - - connect(m_colorPicker, SIGNAL( hasChanged() ), this, SLOT( setWidgetDirty() ) ); - - readFromData(); - - return true; -} - -void RGBAColorDataWidget::setDataReadOnly(bool readOnly) -{ - m_colorPicker->setEnabled(!readOnly); -} - -void RGBAColorDataWidget::readFromData() -{ - m_colorPicker->setColor( getData()->getValue() ); -} - -void RGBAColorDataWidget::writeToData() -{ - RGBAColor* color = getData()->beginEdit(); - (*color) = m_colorPicker->getColor() ; - - getData()->endEdit(); -} - -} //namespace sofa::gui::qt::rgbacolordatawidget_h diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/RGBAColorDataWidget.h b/Sofa/GUI/Qt/src/sofa/gui/qt/RGBAColorDataWidget.h deleted file mode 100644 index bbb4ba9e602..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/RGBAColorDataWidget.h +++ /dev/null @@ -1,75 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "QRGBAColorPicker.h" -#include "DataWidget.h" - -namespace sofa::gui::qt::rgbacolordatawidget_h -{ -using sofa::type::RGBAColor ; -using sofa::core::objectmodel::Data ; -using sofa::gui::qt::QRGBAColorPicker ; - -class RGBAColorDataWidget : public TDataWidget -{ - Q_OBJECT - -public: - RGBAColorDataWidget(QWidget* parent, - const char* name, - Data* data): - TDataWidget(parent,name,data) {} - - virtual bool createWidgets(); - virtual void setDataReadOnly(bool readOnly); - virtual unsigned int numColumnWidget() {return 1;} - -protected: - virtual void readFromData(); - virtual void writeToData(); - QLineEdit* m_nameEdit; - QRGBAColorPicker* m_colorPicker; -}; - -} // namespace sofa::gui::qt::rgbacolordatawidget_h - -namespace sofa::gui::qt -{ - using sofa::gui::qt::rgbacolordatawidget_h::RGBAColorDataWidget; -} // namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/RealGUI.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/RealGUI.cpp deleted file mode 100644 index e83572b0a84..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/RealGUI.cpp +++ /dev/null @@ -1,2335 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#include "RealGUI.h" -#include - -#ifdef SOFA_DUMP_VISITOR_INFO -#include "WindowVisitor.h" -#include "GraphVisitor.h" -#endif - -#if SOFA_GUI_QT_HAVE_QT_CHARTS -#include "SofaWindowProfiler.h" -#endif - -#if SOFA_GUI_QT_HAVE_NODEEDITOR -#include "SofaWindowDataGraph.h" -#endif - - -#include -#include -#include "QSofaListView.h" -#include "QDisplayPropertyWidget.h" -#include "FileManagement.h" -#include "DisplayFlagsDataWidget.h" -#include "SofaPluginManager.h" -#include "SofaMouseManager.h" -#include "SofaVideoRecorderManager.h" -#include "WDoubleLineEdit.h" -#include "QSofaStatWidget.h" -#include "viewer/SofaViewer.h" - -#include -#include -#include -#include -#include -#include -#include - -#include -using sofa::helper::system::SetDirectory; - -#include -using sofa::helper::system::FileSystem; - -#include -using sofa::helper::Utils; - -#include -using sofa::helper::system::DataRepository; - -#include -using sofa::gui::common::GuiDataRepository; - -#include -using sofa::simulation::SceneLoaderFactory; - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if (QT_VERSION < QT_VERSION_CHECK(5, 11, 0)) -#include -#endif - -#include -#include -#include -#include - -#include -using sofa::core::objectmodel::IdleEvent; - -#include -using sofa::simulation::SimulationStartEvent; - -#include -using sofa::simulation::SimulationStopEvent; - -#include -using sofa::helper::system::FileMonitor; - -#include -using sofa::core::ObjectFactory; - -#if(SOFA_GUI_QT_HAVE_QT5_WEBENGINE) -#include "panels/QDocBrowser.h" -using sofa::gui::qt::DocBrowser; -#endif - -using sofa::core::ExecParams; - -#include - - -using namespace sofa::gui::common; - -namespace sofa::gui::qt -{ - -using sofa::core::objectmodel::BaseObject; -using namespace sofa::helper::system::thread; -using namespace sofa::simulation; -using namespace sofa::core::visual; - -/// Custom QApplication class handling FileOpen events for MacOS -class QSOFAApplication : public QApplication -{ -public: - QSOFAApplication(int &argc, char ** argv) - : QApplication(argc,argv) - { - QCoreApplication::setOrganizationName("Sofa Consortium"); - QCoreApplication::setOrganizationDomain("sofa"); - QCoreApplication::setApplicationName("runSofa"); - } - -#if QT_VERSION < 0x050000 - static inline QString translate(const char * context, const char * key, const char * disambiguation, - QCoreApplication::Encoding encoding = QCoreApplication::UnicodeUTF8, int n = -1) - { return QApplication::translate(context, key, disambiguation, encoding, n); } -#else - static inline QString translate(const char * context, const char * key, - const char * disambiguation = Q_NULLPTR, int n = -1) - { return QApplication::translate(context, key, disambiguation, n); } -#endif - -protected: - bool event(QEvent *event) override - { - switch (event->type()) - { - case QEvent::FileOpen: - { - if(this->topLevelWidgets().count() < 1) - return false; - return true; - } - default: - return QApplication::event(event); - } - } -}; - -RealGUI* gui = nullptr; -QApplication* application = nullptr; - -const char* progname=""; - - - -class RealGUIFileListener : public sofa::helper::system::FileEventListener -{ -public: - RealGUIFileListener(RealGUI* realgui){ - m_realgui = realgui; - } - ~RealGUIFileListener() override{} - - void fileHasChanged(const std::string& filename) override - { - m_realgui->fileOpen(filename, false, true); - } - RealGUI* m_realgui; -}; - - -//======================= STATIC METHODS ========================= { - -void RealGUI::setupSurfaceFormat() -{ - static std::once_flag flag; - std::call_once(flag, [] - { - QSurfaceFormat format; - if(!SOFA_GUI_QT_ENABLE_VSYNC) - { - format.setSwapInterval(0); //Setting an interval value of 0 will turn the vertical refresh syncing off - } - - static constexpr int vmajor = 3, vminor = 2; - format.setVersion(vmajor,vminor); //Sets the desired major and minor OpenGL versions. - format.setProfile(QSurfaceFormat::CompatibilityProfile); //Sets the desired OpenGL context profile. CompatibilityProfile = Functionality from earlier OpenGL versions is available. - format.setOption(QSurfaceFormat::DeprecatedFunctions, true); //Used to request that deprecated functions be included in the OpenGL context profile. If not specified, you should get a forward compatible context without support functionality marked as deprecated. This requires OpenGL version 3.0 or higher. - format.setSwapBehavior(QSurfaceFormat::DoubleBuffer); - - if (mArgumentParser) - { - unsigned int viewerMSAANbSampling = 0; - mArgumentParser->getValueFromKey("msaa", viewerMSAANbSampling); - if (viewerMSAANbSampling > 1) - { - msg_info("RealGUI") << "Set multisampling anti-aliasing (MSAA) with " << viewerMSAANbSampling << " samples." ; - format.setSamples(static_cast(viewerMSAANbSampling)); - } - } - - QSurfaceFormat::setDefaultFormat(format); - }); -} - -BaseGUI* RealGUI::CreateGUI ( const char* name, sofa::simulation::Node::SPtr root, const char* filename ) -{ - setupSurfaceFormat(); - - CreateApplication(); - - // create interface - gui = new RealGUI ( name ); - if ( root ) - { - gui->setScene ( root, filename ); - gui->setWindowFilePath(QString(filename)); - } - - InitApplication(gui); - return gui; -} - -//------------------------------------ - -void RealGUI::SetPixmap(std::string pixmap_filename, QPushButton* b) -{ - if ( DataRepository.findFile ( pixmap_filename ) ) - pixmap_filename = DataRepository.getFile ( pixmap_filename ); - - b->setIcon(QIcon(QPixmap(QPixmap::fromImage(QImage(pixmap_filename.c_str()))))); -} - -//------------------------------------ - -void RealGUI::CreateApplication(int /*_argc*/, char** /*_argv*/) -{ - int *argc = new int; - char **argv=new char*[2]; - *argc = 1; - argv[0] = strdup ( BaseGUI::GetProgramName() ); - argv[1]=nullptr; - application = new QSOFAApplication ( *argc,argv ); - - //force locale to Standard C - //(must be done immediately after the QApplication has been created) - const QLocale locale(QLocale::C); - QLocale::setDefault(locale); -} - -//------------------------------------ - -void RealGUI::InitApplication( RealGUI* _gui) -{ - const QString pathIcon=(DataRepository.getFirstPath() + std::string( "/icons/SOFA.png" )).c_str(); - application->setWindowIcon(QIcon(pathIcon)); - - if(SOFA_GUI_QT_ENABLE_NATIVE_MENU) - { - // Use the OS'native menu instead of the Qt one - _gui->menubar->setNativeMenuBar(true); - } - else - { - // Use the qt menu instead of the native one in order to standardize the way the menu is showed on every OS - _gui->menubar->setNativeMenuBar(false); - } - // show the gui - _gui->show(); // adding extra line in the console? -} -//======================= STATIC METHODS ========================= } - - - - -//======================= CONSTRUCTOR - DESTRUCTOR ========================= { -RealGUI::RealGUI ( const char* viewername) - : - fpsLabel(nullptr), - timeLabel(nullptr), - - #ifdef SOFA_DUMP_VISITOR_INFO - windowTraceVisitor(nullptr), - handleTraceVisitor(nullptr), - #endif - - m_sofaMouseManager(nullptr), - #if SOFAGUIQT_HAVE_QT5_CHARTS - m_windowTimerProfiler(nullptr), - #endif - #if SOFAGUIQT_HAVE_NODEEDITOR - m_sofaWindowDataGraph(nullptr), - #endif - simulationGraph(nullptr), - m_dumpState(false), - m_dumpStateStream(nullptr), - m_exportGnuplot(false), - m_animateOBJ(false), - m_animationOBJcounter(0), - m_displayComputationTime(false), - m_fullScreen(false), - m_viewer(nullptr), - m_clockBeforeLastStep(0), - propertyWidget(nullptr), - currentTab ( nullptr ), - statWidget(nullptr), - timerStep(nullptr), - backgroundImage(nullptr), - pluginManagerDialog(nullptr), - recentlyOpenedFilesManager(BaseGUI::getConfigDirectoryPath() + "/runSofa.ini"), - m_saveReloadFile(false), - displayFlag(nullptr), -#if(SOFA_GUI_QT_HAVE_QT5_WEBENGINE) - m_docbrowser(nullptr), -#endif - m_animationState(false), - m_frameCounter(0), - m_viewerMSAANbSampling(1) -{ - setupUi(this); - - ExpandAllButton->setIcon(QIcon(":/RealGUI/expandAll")); - CollapseAllButton->setIcon(QIcon(":/RealGUI/collapseAll")); - sceneGraphRefreshToggleButton->setIcon(QIcon(":/RealGUI/sceneGraphRefresh")); - for (auto* button : {ExpandAllButton, CollapseAllButton, sceneGraphRefreshToggleButton}) - { - button->setFixedWidth(button->height()); - } - - parseOptions(); - - createPluginManager(); - - createRecentFilesMenu(); // configure Recently Opened Menu - - QDoubleValidator *dtValidator = new QDoubleValidator(dtEdit); - dtValidator->setBottom(0.000000001); - dtEdit->setValidator(dtValidator); - - timerStep = new QTimer(this); - connect ( timerStep, SIGNAL ( timeout() ), this, SLOT ( step() ) ); - connect ( this, SIGNAL ( quit() ), this, SLOT ( fileExit() ) ); - connect ( startButton, SIGNAL ( toggled ( bool ) ), this , SLOT ( playpauseGUI ( bool ) ) ); - connect ( ReloadSceneButton, SIGNAL ( clicked() ), this, SLOT ( fileReload() ) ); - connect ( dtEdit, SIGNAL ( textChanged ( const QString& ) ), this, SLOT ( setDt ( const QString& ) ) ); - connect ( realTimeCheckBox, SIGNAL ( stateChanged ( int ) ), this, SLOT ( updateDtEditState() ) ); - connect ( stepButton, SIGNAL ( clicked() ), this, SLOT ( step() ) ); - connect ( dumpStateCheckBox, SIGNAL ( toggled ( bool ) ), this, SLOT ( dumpState ( bool ) ) ); - connect ( displayComputationTimeCheckBox, SIGNAL ( toggled ( bool ) ), this, SLOT ( displayComputationTime ( bool ) ) ); - connect ( exportGnuplotFilesCheckbox, SIGNAL ( toggled ( bool ) ), this, SLOT ( setExportGnuplot ( bool ) ) ); - connect ( tabs, SIGNAL ( currentChanged ( int ) ), this, SLOT ( currentTabChanged ( int ) ) ); - - connect ( ResetViewButton, SIGNAL ( clicked() ), this, SLOT ( resetView() ) ); - connect ( SaveViewButton, SIGNAL ( clicked() ), this, SLOT ( saveView() ) ); - connect ( screenshotButton, SIGNAL ( clicked() ), this, SLOT ( screenshot() ) ); - connect ( sizeW, SIGNAL ( valueChanged ( int ) ), this, SLOT ( setSizeW ( int ) ) ); - connect ( sizeH, SIGNAL ( valueChanged ( int ) ), this, SLOT ( setSizeH ( int ) ) ); - - /// We activate this timer only if the interactive mode is enabled (ie livecoding+mouse mouve event). - if(m_enableInteraction){ - timerIdle = new QTimer(this); - connect ( timerIdle, SIGNAL ( timeout() ), this, SLOT ( emitIdle() ) ); - timerIdle->start(50); - } - - this->setDockOptions(QMainWindow::AnimatedDocks | QMainWindow::AllowTabbedDocks); -#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) - dockWidget->setFeatures(QDockWidget::AllDockWidgetFeatures); -#else - dockWidget->setFeatures(QDockWidget::DockWidgetClosable|QDockWidget::DockWidgetMovable|QDockWidget::DockWidgetFloatable); -#endif - dockWidget->setAllowedAreas(Qt::RightDockWidgetArea | Qt::LeftDockWidgetArea); - - connect(dockWidget, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)), this, SLOT(toolsDockMoved())); - - //Status Bar Configuration - fpsLabel = new QLabel ( "9999.9 FPS", statusBar() ); - fpsLabel->setMinimumSize ( fpsLabel->sizeHint() ); - fpsLabel->clear(); - - timeLabel = new QLabel ( "Time: 999.9999", statusBar() ); - timeLabel->setMinimumSize ( timeLabel->sizeHint() ); - timeLabel->clear(); - statusBar()->addWidget ( fpsLabel ); - statusBar()->addWidget ( timeLabel ); - - statWidget = new QSofaStatWidget(TabStats); - TabStats->layout()->addWidget(statWidget); - - // create al widgets first - m_sofaMouseManager = new SofaMouseManager(this); - - createSimulationGraph(); - - //disable widget, can be bothersome with objects with a lot of data - //createPropertyWidget(); - - //viewer - informationOnPickCallBack = InformationOnPickCallBack(this); - - viewerMap.clear(); - - createViewer(viewername, true); - - currentTabChanged ( tabs->currentIndex() ); - - createBackgroundGUIInfos(); // add GUI for Background Information - - createWindowVisitor(); - - createAdvancedTimerProfilerWindow(); - - m_sofaMouseManager->hide(); - SofaVideoRecorderManager::getInstance()->hide(); - - centerWindow(); - - tabs->removeTab(tabs->indexOf(TabVisualGraph)); - -#if(SOFA_GUI_QT_HAVE_QT5_WEBENGINE) - m_docbrowser = new DocBrowser(this); - /// Signal to the realGUI that the visibility has changed (eg: to update the menu bar) - connect(m_docbrowser, SIGNAL(visibilityChanged(bool)), this, SLOT(docBrowserVisibilityChanged(bool))); -#endif - - // Trigger QDialog for "About" section - connect(helpAboutAction, SIGNAL(triggered()), this, SLOT(showAbout())); - - m_filelistener = new RealGUIFileListener(this); -} - -//------------------------------------ - -RealGUI::~RealGUI() -{ - if( displayFlag != nullptr ) - delete displayFlag; - -#ifdef SOFA_DUMP_VISITOR_INFO - delete windowTraceVisitor; - delete handleTraceVisitor; -#endif - - removeViewer(); - - FileMonitor::removeListener(m_filelistener); - delete m_filelistener; -} -//======================= CONSTRUCTOR - DESTRUCTOR ========================= } - - - -//======================= OPTIONS DEFINITIONS ========================= { -#ifdef SOFA_DUMP_VISITOR_INFO -void RealGUI::setTraceVisitors(bool b) -{ - exportVisitorCheckbox->setChecked(b); -} -#endif - - -//------------------------------------ - - -//======================= OPTIONS DEFINITIONS ========================= } - - -//======================= METHODS ========================= { - -void RealGUI::docBrowserVisibilityChanged(bool visibility) -{ - if(visibility) - helpShowDocBrowser->setText("Hide doc browser"); - else - helpShowDocBrowser->setText("Show doc browser"); -} - -void RealGUI::stepMainLoop () { - application->processEvents(); -} - -int RealGUI::mainLoop() -{ - int retcode; - if (windowFilePath().isNull()) - { - retcode = application->exec(); - } - else - { - const std::string &filename=windowFilePath().toStdString(); - const std::string &extension=SetDirectory::GetExtension(filename.c_str()); - if (extension == "simu") fileOpenSimu(filename); - retcode = application->exec(); - } - return exitApplication(retcode); -} - -//------------------------------------ - -int RealGUI::closeGUI() -{ - QSettings settings; - QScreen* screen = widget->window()->windowHandle()->screen(); - settings.beginGroup("viewer"); - settings.setValue("screenNumber", QGuiApplication::screens().indexOf(screen)); - settings.endGroup(); - delete this; - return 0; -} - -//------------------------------------ - -sofa::simulation::Node* RealGUI::currentSimulation() -{ - return mSimulation.get(); -} - -//------------------------------------ - - -void RealGUI::fileOpen ( std::string filename, bool temporaryFile, bool reload ) -{ - std::vector expandedNodes; - - if(reload) - { - saveView(); - - if(simulationGraph) - simulationGraph->getExpandedNodes(expandedNodes); - } - - const std::string &extension=SetDirectory::GetExtension(filename.c_str()); - if (extension == "simu") - { - return fileOpenSimu(filename); - } - - startButton->setChecked(false); - startDumpVisitor(); - update(); - - //Hide all the dialogs to modify the graph - emit ( newScene() ); - - if ( DataRepository.findFile (filename) ) - filename = DataRepository.getFile ( filename ); - else - return; - - sofa::simulation::xml::numDefault = 0; - - if( currentSimulation() ) this->unloadScene(); - - const std::vector sceneArgs = ArgumentParser::extra_args(); - mSimulation = sofa::simulation::node::load ( filename, reload, sceneArgs ); - - sofa::simulation::node::initRoot(mSimulation.get()); - if ( mSimulation == nullptr ) - { - msg_warning("RealGUI")<<"Failed to load "<loadHtml( filename ) ; -#endif - } - - configureGUI(mSimulation.get()); - - this->setWindowFilePath(filename.c_str()); - setExportGnuplot(exportGnuplotFilesCheckbox->isChecked()); - stopDumpVisitor(); - - if(!expandedNodes.empty()) - { - simulationGraph->expandPathFrom(expandedNodes); - } - -#if SOFA_GUI_QT_HAVE_QT_CHARTS - if (m_windowTimerProfiler) - m_windowTimerProfiler->resetGraph(); -#endif - -#if SOFA_GUI_QT_HAVE_NODEEDITOR - if (m_sofaWindowDataGraph) - m_sofaWindowDataGraph->resetNodeGraph(currentSimulation()); -#endif - -} - - -//------------------------------------ - -void RealGUI::emitIdle() -{ - // Update all the registered monitor. - FileMonitor::updates(0); - - IdleEvent hb; - Node* groot = m_viewer->getScene(); - if (groot) - { - groot->propagateEvent(core::execparams::defaultInstance(), &hb); - } - - getSofaViewer()->getQWidget()->update(); -} - -/// This open popup the file selection windows. -void RealGUI::popupOpenFileSelector() -{ - const std::string filename(this->windowFilePath().toStdString()); - - // build the filter with the SceneLoaderFactory - std::string filter, allKnownFilters = "All known ("; - SceneLoaderFactory::SceneLoaderList* loaders = SceneLoaderFactory::getInstance()->getEntries(); - for (SceneLoaderFactory::SceneLoaderList::iterator it=loaders->begin(); it!=loaders->end(); ++it) - { - if (it!=loaders->begin()) filter +=";;"; - filter += (*it)->getFileTypeDesc(); - filter += " ("; - SceneLoader::ExtensionList extensions; - (*it)->getExtensionList(&extensions); - for (SceneLoader::ExtensionList::iterator itExt=extensions.begin(); itExt!=extensions.end(); ++itExt) - { - if (itExt!=extensions.begin()) filter +=" "; - filter+="*."; - filter+=(*itExt); - - allKnownFilters+="*."+(*itExt); - if (*it!=loaders->back() || itExt!=extensions.end()-1) allKnownFilters += " "; - } - filter+=")"; - } - allKnownFilters+=")"; - - filter += ";;Simulation (*.simu)"; - - filter = allKnownFilters+";;"+filter+";;All (*)"; // the first filter is selected by default - - QString selectedFilter( tr(allKnownFilters.c_str()) ); // this does not select the desired filter - - QString s = getOpenFileName ( this, filename.empty() ?nullptr:filename.c_str(), - filter.c_str(), - "open file dialog", "Choose a file to open", &selectedFilter - ); - if ( s.length() >0 ) - { - if (s.endsWith( ".simu") ) - fileOpenSimu(s.toStdString()); - else - fileOpen (s.toStdString()); - } -} - -//------------------------------------ - -void RealGUI::fileOpenSimu ( std::string s ) -{ - std::ifstream in(s.c_str()); - - if (!in.fail()) - { - std::string filename; - std::string initT, endT, dT, writeName; - in - >> filename - >> initT >> initT - >> endT >> endT >> endT - >> dT >> dT - >> writeName >> writeName; - in.close(); - - if ( DataRepository.findFile (filename) ) - { - filename = DataRepository.getFile ( filename ); - simulationName = s; - const std::string::size_type pointSimu = simulationName.rfind(".simu"); - simulationName.resize(pointSimu); - fileOpen(filename.c_str()); - - dtEdit->setText(QString(dT.c_str())); - } - } -} - -//------------------------------------ - -void RealGUI::setSceneWithoutMonitor (Node::SPtr root, const char* filename, bool temporaryFile) -{ - if (filename) - { - if (!temporaryFile) - recentlyOpenedFilesManager.openFile(filename); - m_saveReloadFile=temporaryFile; - setTitle ( filename ); -#if(SOFA_GUI_QT_HAVE_QT5_WEBENGINE) - if (m_docbrowser && filename) - { - m_docbrowser->loadHtml( filename ); - } -#endif - } - - if (root) - { - //Check the validity of the BBox - const sofa::type::BoundingBox& nodeBBox = root->getContext()->f_bbox.getValue(); - if(nodeBBox.isNegligeable()) - { - msg_warning("RealGUI") << "Global Bounding Box seems very small; Your viewer settings (based on the bbox) are likely invalid, switching to default value of [-1,-1,-1,1,1,1]." - << "This is caused by using component which does not implement properly the computeBBox function." - << "You can remove this warning by manually forcing a value in the parameter bbox=\"minX minY minZ maxX maxY maxZ\" in your root node \n"; - const sofa::type::BoundingBox b(-1.0,-1.0,-1.0,1.0,1.0,1.0); - root->f_bbox.setValue(b); - } - - - mSimulation = root; - eventNewTime(); - startButton->setChecked(root->getContext()->getAnimate() ); - - dtEdit->setText ( QString::number ( root->getDt() ) ); - simulationGraph->setRoot(root.get()); - simulationGraph->collapseAll(); - simulationGraph->expandToDepth(0); - simulationGraph->resizeColumnToContents(0); - statWidget->CreateStats(root.get()); - - getViewer()->setScene( root, filename ); - getViewer()->load(); - getViewer()->resetView(); - createDisplayFlags( root ); - - getSofaViewer()->getQWidget()->setFocus(); - getSofaViewer()->getQWidget()->show(); - getSofaViewer()->getQWidget()->update(); - - resetScene(); - } -} - -void RealGUI::setScene(Node::SPtr root, const char* filename, bool temporaryFile) -{ - if(m_enableInteraction && filename){ - FileMonitor::removeListener(m_filelistener); - FileMonitor::addFile(filename, m_filelistener); - } - setSceneWithoutMonitor(root, filename, temporaryFile) ; -#if(SOFA_GUI_QT_HAVE_QT5_WEBENGINE) - if (m_docbrowser && filename) - { - m_docbrowser->loadHtml( filename ) ; - } -#endif -} - -//------------------------------------ - -void RealGUI::unloadScene(bool _withViewer) -{ - if(_withViewer && getViewer()) - getViewer()->unload(); - - sofa::simulation::node::unload ( currentSimulation() ); - - if(_withViewer && getViewer()) - getViewer()->setScene(nullptr); -} - -//------------------------------------ - -void RealGUI::setTitle ( std::string windowTitle ) -{ - std::string str = "SOFA v" + std::string(SOFA_VERSION_STR); - if ( !windowTitle.empty() ) - { - str += " - "; - str += windowTitle; - } -#ifdef WIN32 - setWindowTitle ( str.c_str() ); -#else - this->setWindowTitle(QString(str.c_str()) ); -#endif - setWindowFilePath( windowTitle.c_str() ); -} - -//------------------------------------ - -void RealGUI::fileReload() -{ - std::string filename(this->windowFilePath().toStdString()); - QString s = filename.c_str(); - - if ( filename.empty() ) - { - msg_error("RealGUI") << "Reload failed: no file loaded."; - return; - } - - if (s.endsWith( ".simu") ) - fileOpenSimu(s.toStdString()); - else - fileOpen ( s.toStdString(),m_saveReloadFile ); -} - -//------------------------------------ - -void RealGUI::fileExit() -{ - //Hide all opened ModifyObject windows - emit ( newScene() ); - startButton->setChecked ( false); - this->close(); -} - -void RealGUI::editRecordDirectory() -{ - const std::string filename(this->windowFilePath().toStdString()); - std::string record_directory; - const QString s = getExistingDirectory ( this, filename.empty() ?nullptr:filename.c_str(), "open directory dialog", "Choose a directory" ); - if (s.length() > 0) - { - record_directory = s.toStdString(); - if (record_directory.at(record_directory.size()-1) != '/') - record_directory+="/"; - } -} - -//------------------------------------ - -void RealGUI::editGnuplotDirectory() -{ - const std::string filename(this->windowFilePath().toStdString()); - const QString s = getExistingDirectory ( this, filename.empty() ?nullptr:filename.c_str(), "open directory dialog", "Choose a directory" ); - if (s.length() > 0) - { - gnuplotDirectory = s.toStdString(); - if (gnuplotDirectory.at(gnuplotDirectory.size()-1) != '/') - gnuplotDirectory+="/"; - setExportGnuplot(exportGnuplotFilesCheckbox->isChecked()); - } -} - -//------------------------------------ - -void RealGUI::showDocBrowser() -{ -#if(SOFA_GUI_QT_HAVE_QT5_WEBENGINE) - m_docbrowser->flipVisibility(); -#else - msg_warning("RealGUI") << "Doc browser has been disabled because Qt5WebEngine is not available"; -#endif -} - -//------------------------------------ - -void RealGUI::showAbout() -{ - //create the QDialog for About - AboutSOFADialog* aboutSOFA_dialog = new sofa::gui::qt::AboutSOFADialog(this); - aboutSOFA_dialog->show(); -} - -//------------------------------------ - -void RealGUI::showPluginManager() -{ - pluginManagerDialog->updatePluginsListView(); - pluginManagerDialog->show(); -} - -//------------------------------------ - -void RealGUI::showMouseManager() -{ - m_sofaMouseManager->updateContent(); - m_sofaMouseManager->show(); -} - -//------------------------------------ - -void RealGUI::showVideoRecorderManager() -{ - SofaVideoRecorderManager::getInstance()->show(); -} - -//------------------------------------ - -void RealGUI::showWindowDataGraph() -{ -#if SOFA_GUI_QT_HAVE_NODEEDITOR - std::cout << "RealGUI::showWindowDataGraph()" << std::endl; - //m_sofaMouseManager->createGraph(); - if (m_sofaWindowDataGraph == nullptr) - { - createSofaWindowDataGraph(); - } - m_sofaWindowDataGraph->show(); - -#endif -} - -//------------------------------------ - -void RealGUI::setViewerResolution ( int w, int h ) -{ - const QSize winSize = size(); - const QSize viewSize = ( getViewer() ) ? getSofaViewer()->getQWidget()->size() : QSize(0,0); - -#if (QT_VERSION < QT_VERSION_CHECK(5, 11, 0)) - const QRect screen = QApplication::desktop()->availableGeometry(QApplication::desktop()->screenNumber(this)); -#else - const QRect screen = QGuiApplication::primaryScreen()->availableGeometry(); -#endif - QSize newWinSize(winSize.width() - viewSize.width() + w, winSize.height() - viewSize.height() + h); - if (newWinSize.width() > screen.width()) newWinSize.setWidth(screen.width()-20); - if (newWinSize.height() > screen.height()) newWinSize.setHeight(screen.height()-20); - - this->resize(newWinSize); -} - -//------------------------------------ - -void RealGUI::setFullScreen (bool enable) -{ - if (enable == m_fullScreen) return; - - if (enable) - { - optionTabs->hide(); - } - else if (m_fullScreen) - { - optionTabs->show(); - } - - if (enable) - { - msg_info("RealGUI") << "Set Full Screen Mode"; - showFullScreen(); - m_fullScreen = true; - - dockWidget->setFloating(true); - dockWidget->setVisible(false); - } - else - { - msg_info("RealGUI") << "Set Windowed Mode"; - showNormal(); - m_fullScreen = false; - dockWidget->setVisible(true); - dockWidget->setFloating(false); - } - - if (enable) - { - menuBar()->hide(); - statusBar()->hide(); - } - else - { - menuBar()->show(); - statusBar()->show(); - } -} - -void RealGUI::centerWindow() -{ - //Center the application -#if (QT_VERSION < QT_VERSION_CHECK(5, 11, 0)) - const QRect screen = QApplication::desktop()->availableGeometry(QApplication::desktop()->primaryScreen()); -#else - const QRect screen = QGuiApplication::primaryScreen()->availableGeometry(); -#endif - this->move( ( screen.width() - this->width() ) / 2, ( screen.height() - this->height()) / 2 ); -} - -//------------------------------------ - -void RealGUI::setBackgroundColor(const sofa::type::RGBAColor& c) -{ - background[0]->setText(QString::number(c[0])); - background[1]->setText(QString::number(c[1])); - background[2]->setText(QString::number(c[2])); - updateBackgroundColour(); -} - -//------------------------------------ - -void RealGUI::setBackgroundImage(const std::string& c) -{ - backgroundImage->setText(QString(c.c_str())); - updateBackgroundImage(); -} - -//------------------------------------ - -void RealGUI::setViewerConfiguration(sofa::component::setting::ViewerSetting* viewerConf) -{ - const type::Vec<2,int> &res=viewerConf->d_resolution.getValue(); - - if (viewerConf->d_fullscreen.getValue()) - setFullScreen(); - else - setViewerResolution(res[0], res[1]); - getViewer()->configure(viewerConf); -} - -//------------------------------------ - -void RealGUI::setMouseButtonConfiguration(sofa::component::setting::MouseButtonSetting *button) -{ - m_sofaMouseManager->updateOperation(button); -} - -//------------------------------------ - -void RealGUI::setDumpState(bool b) -{ - dumpStateCheckBox->setChecked(b); -} - -//------------------------------------ - -void RealGUI::setLogTime(bool b) -{ - displayComputationTimeCheckBox->setChecked(b); -} - -//------------------------------------ - -void RealGUI::setExportState(bool b) -{ - exportGnuplotFilesCheckbox->setChecked(b); -} - -//------------------------------------ - -void RealGUI::setGnuplotPath(const std::string &path) -{ - gnuplotDirectory = path; -} - -//------------------------------------ - -void RealGUI::createViewer(const char* _viewerName, bool _updateViewerList/*=false*/) -{ - if(_updateViewerList) - { - this->updateViewerList(); - // the viewer with the key viewerName is already created - if( m_viewer != nullptr && !viewerMap.begin()->first.compare( std::string(_viewerName) ) ) - return; - } - - for (std::map< helper::SofaViewerFactory::Key, QAction*>::const_iterator iter_map = viewerMap.begin(); - iter_map != viewerMap.end(); ++iter_map ) - { - if( strcmp( iter_map->first.c_str(), _viewerName ) == 0 ) - { - removeViewer(); - ViewerQtArgument viewerArg = ViewerQtArgument("viewer", this->widget, m_viewerMSAANbSampling); - registerViewer( helper::SofaViewerFactory::CreateObject(iter_map->first, viewerArg) ); - //see to put on checkable - iter_map->second->setChecked(true); - } - else - iter_map->second->setChecked(false); - } - - mGuiName = _viewerName; - initViewer( getViewer() ); -} - -//------------------------------------ - -void RealGUI::registerViewer(BaseViewer* _viewer) -{ - // Change our viewer - if(_viewer == nullptr) - { - msg_error("RealGUI")<<"when registerViewer, the viewer is nullptr"; - return; - } - - sofa::gui::qt::viewer::SofaViewer* tmpViewer = dynamic_cast(_viewer); - if(tmpViewer != nullptr) - { - const sofa::gui::qt::viewer::SofaViewer* old = m_viewer; - m_viewer = tmpViewer; - delete old; - } - else - { - msg_error("RealGUI")<<"when registerViewer, the viewer can't be cast as sofa::gui::qt::viewer::SofaViewer*"; - } -} - -//------------------------------------ - -BaseViewer* RealGUI::getViewer() -{ - return m_viewer; -} - -//------------------------------------ - -sofa::gui::qt::viewer::SofaViewer* RealGUI::getSofaViewer() -{ - return m_viewer; -} - - -//------------------------------------ - -void RealGUI::removeViewer() -{ - if(m_viewer != nullptr) - { - getSofaViewer()->removeViewerTab(tabs); - delete m_viewer; - m_viewer = nullptr; - } -} - -//------------------------------------ - -void RealGUI::dragEnterEvent( QDragEnterEvent* event) -{ - event->accept(); -} - -//------------------------------------ - -void RealGUI::dropEvent(QDropEvent* event) -{ - QString text; - //Q3TextDrag::decode(event, text); - if (event->mimeData()->hasText()) - text = event->mimeData()->text(); - std::string filename(text.toStdString()); - -#ifdef WIN32 - filename = filename.substr(8); //removing file:/// -#else - filename = filename.substr(7); //removing file:// -#endif - - if (filename[filename.size()-1] == '\n') - { - filename.resize(filename.size()-1); - filename[filename.size()-1]='\0'; - } - - if (filename.rfind(".simu") != std::string::npos) - fileOpenSimu(filename); - else fileOpen(filename); -} - -//------------------------------------ - -void RealGUI::init() -{ - m_frameCounter = 0; - m_animateOBJ = false; - m_animationOBJcounter = 0; - m_dumpState = false; - m_dumpStateStream = 0; - m_displayComputationTime = false; - m_exportGnuplot = false; - gnuplotDirectory = ""; - m_fullScreen = false; -} - -//------------------------------------ - -void RealGUI::createDisplayFlags(Node::SPtr root) -{ - if( displayFlag != nullptr) - { - gridLayout1->removeWidget(displayFlag); - delete displayFlag; - displayFlag = nullptr; - } - - sofa::component::visual::VisualStyle* visualStyle = nullptr; - - if( root ) - { - root->get(visualStyle); - if(visualStyle) - { - displayFlag = new DisplayFlagsDataWidget(tabView, "displayFlagwidget", &visualStyle->d_displayFlags, true); - displayFlag->createWidgets(); - displayFlag->updateWidgetValue(); - connect( displayFlag, SIGNAL( WidgetDirty(bool) ), this, SLOT(showhideElements() )); - displayFlag->setMinimumSize(50,100); - gridLayout1->addWidget(displayFlag,0,0); - connect(tabs,SIGNAL(currentChanged(int)),displayFlag, SLOT( updateWidgetValue() )); - } - } -} - -//------------------------------------ - -// Update sofa Simulation with the time step -void RealGUI::eventNewStep() -{ - static ctime_t beginTime[10]; - static const ctime_t timeTicks = CTime::getRefTicksPerSec(); - const Node* root = currentSimulation(); - - if ( m_frameCounter==0 ) - { - const ctime_t t = CTime::getRefTime(); - for ( int i=0; i<10; i++ ) - beginTime[i] = t; - } - - ++m_frameCounter; - if ( ( m_frameCounter%10 ) == 0 ) - { - const ctime_t curtime = CTime::getRefTime(); - const int i = ( ( m_frameCounter/10 ) %10 ); - const double fps = ( ( double ) timeTicks / ( curtime - beginTime[i] ) ) * ( m_frameCounter<100?m_frameCounter:100 ); - showFPS(fps); - - beginTime[i] = curtime; - } - - if ( m_displayComputationTime && ( m_frameCounter%100 ) == 0 && root!=nullptr ) - { - /// @TODO: use AdvancedTimer in GUI to display time statistics - } -} - -void RealGUI::showFPS(double fps) -{ - if (fpsLabel) - { - char buf[100]; - sprintf ( buf, "%.1f FPS", fps ); - fpsLabel->setText ( buf ); - } -} - -//------------------------------------ - -void RealGUI::eventNewTime() -{ - const Node* root = currentSimulation(); - if (root && timeLabel) - { - const double time = root->getTime(); - char buf[100]; - sprintf ( buf, "Time: %.3g, Steps: %i", time, m_frameCounter ); - timeLabel->setText ( buf ); - } -} - -//------------------------------------ - -void RealGUI::keyPressEvent ( QKeyEvent * e ) -{ - if (e->modifiers()) return; - - // ignore if there are modifiers (i.e. CTRL of SHIFT) - switch ( e->key() ) - { - case Qt::Key_O: - // --- export to OBJ - { - exportOBJ ( currentSimulation() ); - break; - } - - case Qt::Key_P: - // --- export to a succession of OBJ to make a video - { - m_animateOBJ = !m_animateOBJ; - m_animationOBJcounter = 0; - break; - } - case Qt::Key_Space: - { - playpauseGUI(!startButton->isChecked()); - break; - } - case Qt::Key_Backspace: - { - resetScene(); - break; - } - case Qt::Key_F11: - // --- fullscreen mode - { - setFullScreen(!m_fullScreen); - break; - } - case Qt::Key_Escape: - { - emit(quit()); - break; - } - case Qt::Key_S: - { - screenshot(); - break; - } - default: - { - if (m_viewer) - m_viewer->keyPressEvent(e); - break; - } - } -} - -//------------------------------------ - -void RealGUI::startDumpVisitor() -{ -#ifdef SOFA_DUMP_VISITOR_INFO - Node* root = currentSimulation(); - if (root && this->exportVisitorCheckbox->isChecked()) - { - m_dumpVisitorStream.str(""); - Visitor::startDumpVisitor(&m_dumpVisitorStream, root->getTime()); - } -#endif -} - -//------------------------------------ - -void RealGUI::stopDumpVisitor() -{ -#ifdef SOFA_DUMP_VISITOR_INFO - if (this->exportVisitorCheckbox->isChecked()) - { - Visitor::stopDumpVisitor(); - m_dumpVisitorStream.flush(); - //Creation of the graph - std::string xmlDoc=m_dumpVisitorStream.str(); - handleTraceVisitor->load(xmlDoc); - m_dumpVisitorStream.str(""); - } -#endif -} - -//------------------------------------ - -void RealGUI::initViewer(BaseViewer* _viewer) -{ - if(_viewer == nullptr) - { - msg_error("RealGUI")<<"when initViewer, the viewer is nullptr"; - return; - } - init(); //init data member from RealGUI for the viewer initialisation in the GUI - - // Is our viewer not a qt::viewer::SofaViewer ? - sofa::gui::qt::viewer::SofaViewer* sofaViewer = dynamic_cast(_viewer); - if( sofaViewer == nullptr ) - { - msg_error("RealGUI") << "initViewer failed as given _viewer is not of type sofa::gui::qt::viewer::SofaViewer*"; - } - else - { - this->mainWidgetLayout->addWidget(sofaViewer->getQWidget()); - - sofaViewer->getQWidget()->setFocusPolicy ( Qt::StrongFocus ); - - sofaViewer->getQWidget()->setSizePolicy ( QSizePolicy ( ( QSizePolicy::Policy ) 7, - ( QSizePolicy::Policy ) 7 - //, 100, 1, - //sofaViewer->getQWidget()->sizePolicy().hasHeightForWidth() ) - )); - - sofaViewer->getQWidget()->setMinimumSize ( QSize ( 0, 0 ) ); - sofaViewer->getQWidget()->setMouseTracking ( true ); - sofaViewer->configureViewerTab(tabs); - - connect ( sofaViewer->getQWidget(), SIGNAL ( resizeW ( int ) ), sizeW, SLOT ( setValue ( int ) ) ); - connect ( sofaViewer->getQWidget(), SIGNAL ( resizeH ( int ) ), sizeH, SLOT ( setValue ( int ) ) ); - connect ( sofaViewer->getQWidget(), SIGNAL ( quit ( ) ), this, SLOT ( fileExit ( ) ) ); - connect(simulationGraph, SIGNAL(focusChanged(sofa::core::objectmodel::BaseObject*)), - sofaViewer->getQWidget(), SLOT(fitObjectBBox(sofa::core::objectmodel::BaseObject*)) - ); - connect(simulationGraph, SIGNAL( focusChanged(sofa::core::objectmodel::BaseNode*) ), - sofaViewer->getQWidget(), SLOT( fitNodeBBox(sofa::core::objectmodel::BaseNode*) ) - ); - - // setGUI - textEdit1->setText ( sofaViewer->helpString() ); - connect ( this, SIGNAL( newStep()), sofaViewer->getQWidget(), SLOT( update())); - - sofaViewer->getQWidget()->setFocus(); - sofaViewer->getQWidget()->show(); - sofaViewer->getQWidget()->update(); - - sofaViewer->getPickHandler()->addCallBack(&informationOnPickCallBack ); - } - - m_sofaMouseManager->setPickHandler(_viewer->getPickHandler()); -} - -//------------------------------------ - -void RealGUI::parseOptions() -{ - - if (mArgumentParser) { - mArgumentParser->getValueFromKey("interactive", m_enableInteraction); - mArgumentParser->getValueFromKey("msaa", m_viewerMSAANbSampling); - - if (m_enableInteraction) - msg_warning("runSofa") << "you activated the interactive mode. This is currently an experimental feature " - "that may change or be removed in the future. "; - } -} - -//------------------------------------ - -void RealGUI::createPluginManager() -{ - pluginManagerDialog = new SofaPluginManager(this); - pluginManagerDialog->hide(); - this->connect( pluginManagerDialog, SIGNAL( libraryAdded() ), this, SLOT( updateViewerList() )); - this->connect( pluginManagerDialog, SIGNAL( libraryRemoved() ), this, SLOT( updateViewerList() )); -} - -void RealGUI::createSofaWindowDataGraph() -{ -#if SOFA_GUI_QT_HAVE_NODEEDITOR - m_sofaWindowDataGraph = new SofaWindowDataGraph(this, currentSimulation()); - m_sofaWindowDataGraph->hide(); -#endif -} - -//------------------------------------ - -void RealGUI::createRecentFilesMenu() -{ - fileMenu->removeAction(Action); - - //const int indexRecentlyOpened=fileMenu->count()-2; - const int indexRecentlyOpened=fileMenu->actions().count(); - - QMenu *recentMenu = recentlyOpenedFilesManager.createWidget(this); - fileMenu->insertMenu(fileMenu->actions().at(indexRecentlyOpened-1), - recentMenu); - connect(recentMenu, SIGNAL(triggered(QAction *)), this, SLOT(fileRecentlyOpened(QAction *))); -} - -//------------------------------------ - -void RealGUI::createBackgroundGUIInfos() -{ - QWidget *colour = new QWidget(TabPage); - QHBoxLayout *colourLayout = new QHBoxLayout(colour); - colourLayout->addWidget(new QLabel(QString("Colour "),colour)); - - for (unsigned int i=0; i<3; ++i) - { - std::ostringstream s; - s<<"background" <setMinValue( 0.0f); - background[i]->setMaxValue( 1.0f); - background[i]->setValue( 1.0f); - background[i]->setMaximumSize(50, 20); - - colourLayout->addWidget(background[i]); - connect( background[i], SIGNAL( returnPressed() ), this, SLOT( updateBackgroundColour() ) ); - } - - QWidget *image = new QWidget(TabPage); - QHBoxLayout *imageLayout = new QHBoxLayout(image); - imageLayout->addWidget(new QLabel(QString("Image "),image)); - - backgroundImage = new QLineEdit(image); - backgroundImage->setText("backgroundImage"); - if ( getViewer() ) - backgroundImage->setText( QString(getViewer()->getBackgroundImage().c_str()) ); - else - backgroundImage->setText( QString() ); - imageLayout->addWidget(backgroundImage); - connect( backgroundImage, SIGNAL( returnPressed() ), this, SLOT( updateBackgroundImage() ) ); - - ((QVBoxLayout*)(TabPage->layout()))->insertWidget(1,colour); - ((QVBoxLayout*)(TabPage->layout()))->insertWidget(2,image); -} - -//------------------------------------ -void RealGUI::createSimulationGraph() -{ - simulationGraph = new QSofaListView(SIMULATION,TabGraph,"SimuGraph"); - TabGraph->layout()->addWidget(simulationGraph); - - connect ( ExportGraphButton, SIGNAL ( clicked() ), simulationGraph, SLOT ( Export() ) ); - connect ( ExpandAllButton, SIGNAL ( clicked() ), simulationGraph, SLOT ( expandAll() ) ); - connect ( CollapseAllButton, SIGNAL ( clicked() ), simulationGraph, SLOT ( ExpandRootNodeOnly() ) ); - connect ( sceneGraphRefreshToggleButton, &QPushButton::clicked , this, &RealGUI::onSceneGraphRefreshButtonClicked ); - connect(simulationGraph, &QSofaListView::dirtynessChanged, this, &RealGUI::sceneGraphViewDirtynessChanged); - connect(simulationGraph, &QSofaListView::lockingChanged, this, &RealGUI::sceneGraphViewLockingChanged); - - connect(simulationGraph, SIGNAL( RootNodeChanged(sofa::simulation::Node*, const char*) ), this, SLOT ( newRootNode(sofa::simulation::Node* , const char*) ) ); - connect(simulationGraph, SIGNAL( NodeRemoved() ), this, SLOT( update() ) ); - connect(simulationGraph, SIGNAL( Lock(bool) ), this, SLOT( lockAnimation(bool) ) ); - connect(simulationGraph, SIGNAL( RequestExportOBJ(sofa::simulation::Node*, bool) ), this, SLOT( exportOBJ(sofa::simulation::Node*, bool) ) ); - connect(simulationGraph, SIGNAL( RequestActivation(sofa::simulation::Node*, bool) ), this, SLOT( activateNode(sofa::simulation::Node*, bool) ) ); - connect(simulationGraph, SIGNAL( RequestSleeping(sofa::simulation::Node*, bool) ), this, SLOT( setSleepingNode(sofa::simulation::Node*, bool) ) ); - connect(simulationGraph, SIGNAL( Updated() ), this, SLOT( redraw() ) ); - connect(simulationGraph, SIGNAL( NodeAdded() ), this, SLOT( update() ) ); - connect(simulationGraph, SIGNAL( dataModified( QString ) ), this, SLOT( appendToDataLogFile(QString ) ) ); - connect(this, SIGNAL( newScene() ), simulationGraph, SLOT( CloseAllDialogs() ) ); - connect(this, SIGNAL( newStep() ), simulationGraph, SLOT( UpdateOpenedDialogs() ) ); - - std::ifstream file( BaseGUI::getConfigDirectoryPath() + "/sceneGraphLock" ); - if(file.is_open()) - { - bool isLocked; - file >> isLocked; - if (isLocked) - { - simulationGraph->lock(); - } - else - { - simulationGraph->unLock(); - } - file.close(); - } - else - { - simulationGraph->unLock(); - } -} - -// This slot is called when the sceneGraph view is set to dirty -void RealGUI::sceneGraphViewDirtynessChanged(bool isDirty) -{ - if(isDirty) - { - sceneGraphRefreshToggleButton->setIcon(QIcon(":/RealGUI/sceneGraphRefresh-dirty")); - } - else if(simulationGraph->isLocked()) - { - sceneGraphRefreshToggleButton->setIcon(QIcon(":/RealGUI/sceneGraphRefresh-locked")); - } - else - { - sceneGraphRefreshToggleButton->setIcon(QIcon(":/RealGUI/sceneGraphRefresh-unlocked")); - } -} - -// This slot is called when the sceneGraph view has been locked/unlocked. -// The locking state indicates how the view is taking into account the scene graph data changes. -// when locked, the vue is not updated. When unlocked all changes are taken into account. -void RealGUI::sceneGraphViewLockingChanged(bool isLocked) -{ - if(isLocked) - { - sceneGraphRefreshToggleButton->setIcon(QIcon(":/RealGUI/sceneGraphRefresh-locked")); - } - else - { - sceneGraphRefreshToggleButton->setIcon(QIcon(":/RealGUI/sceneGraphRefresh-unlocked")); - } -} - -// The scene graph update button has three states -// State 0: unlocked (all the changes on the graph are immediately taken into account) -// State 1: locked (the changes on the graph are not done but the simulation graph is set to dirty if -// there is some changes on the graph. A click on the button unlocks the graph (go to state 0). -// State 2: dirty, in that state the button reflect the fact that the scene graph view has changed but not displayed. -// A click on the button refreshes the graph view but does not change the Lock/Unlock state -void RealGUI::onSceneGraphRefreshButtonClicked() -{ - if(simulationGraph->isLocked()) - { - simulationGraph->unLock(); - } - else - { - simulationGraph->lock(); - } - - std::ofstream file( BaseGUI::getConfigDirectoryPath() + "/sceneGraphLock" ); - if(file) - { - file << simulationGraph->isLocked(); - file.close(); - } -} - -void RealGUI::createPropertyWidget() -{ - ModifyObjectFlags modifyObjectFlags = ModifyObjectFlags(); - modifyObjectFlags.setFlagsForSofa(); - - propertyWidget = new QDisplayPropertyWidget(modifyObjectFlags); - - QDockWidget *dockProperty=new QDockWidget(this); - - dockProperty->setAllowedAreas(Qt::RightDockWidgetArea | Qt::LeftDockWidgetArea); - dockProperty->setMaximumSize(QSize(300,300)); - dockProperty->setWidget(propertyWidget); - - connect(dockProperty, SIGNAL(dockLocationChanged(QDockWidget::DockWidgetArea)), - this, SLOT(propertyDockMoved(QDockWidget::DockWidgetArea))); - simulationGraph->setPropertyWidget(propertyWidget); -} - -//------------------------------------ - -void RealGUI::createWindowVisitor() -{ - pathDumpVisitor = SetDirectory::GetParentDir(DataRepository.getFirstPath().c_str()) + std::string( "/dumpVisitor.xml" ); -#ifndef SOFA_DUMP_VISITOR_INFO - //Remove option to see visitor trace - this->exportVisitorCheckbox->hide(); -#else - //Main window containing a QListView only - windowTraceVisitor = new WindowVisitor(this); - windowTraceVisitor->graphView->setSortingEnabled(false); - windowTraceVisitor->hide(); - connect ( exportVisitorCheckbox, SIGNAL ( toggled ( bool ) ), this, SLOT ( setExportVisitor ( bool ) ) ); - connect(windowTraceVisitor, SIGNAL(WindowVisitorClosed(bool)), this->exportVisitorCheckbox, SLOT(setChecked(bool))); - handleTraceVisitor = new GraphVisitor(windowTraceVisitor); -#endif -} - -void RealGUI::createAdvancedTimerProfilerWindow() -{ -#if SOFA_GUI_QT_HAVE_QT_CHARTS - m_windowTimerProfiler = new SofaWindowProfiler(this); - m_windowTimerProfiler->hide(); - connect( displayTimeProfiler, SIGNAL ( toggled ( bool ) ), this, SLOT ( displayProflierWindow ( bool ) ) ); - connect( m_windowTimerProfiler, SIGNAL(closeWindow(bool)), this->displayTimeProfiler, SLOT(setChecked(bool))); -#else - displayTimeProfiler->setEnabled(false); -#endif -} - -void RealGUI::newRootNode(sofa::simulation::Node* root, const char* path) -{ - const std::string filename(this->windowFilePath().toStdString()); - const std::string message="You are about to change the root node of the scene : " + filename + - "to the root node : " + std::string(path) + - "\nThis implies that the simulation singleton has to change its root node.\nDo you want to proceed ?"; - if ( QMessageBox::warning ( this, "New root node: ",message.c_str(), QMessageBox::Yes | QMessageBox::Default, QMessageBox::No ) != QMessageBox::Yes ) - return; - - if(path != nullptr && root != nullptr) - { - getViewer()->setScene(root , path); - getViewer()->load(); - getViewer()->resetView(); - - getSofaViewer()->getQWidget()->update(); - statWidget->CreateStats(root); - } -} - -//------------------------------------ - -void RealGUI::activateNode(sofa::simulation::Node* node, bool activate) -{ - const QSofaListView* sofalistview = (QSofaListView*)sender(); - - if (activate) - node->setActive(true); - simulation::DeactivationVisitor v(sofa::core::execparams::defaultInstance(), activate); - node->executeVisitor(&v); - - using core::objectmodel::BaseNode; - std::list< BaseNode* > nodeToProcess; - nodeToProcess.push_front((BaseNode*)node); - - std::list< BaseNode* > nodeToChange; - //Breadth First approach to activate all the nodes - while (!nodeToProcess.empty()) - { - //We take the first element of the list - Node* n= (Node*)nodeToProcess.front(); - nodeToProcess.pop_front(); - nodeToChange.push_front(n); - //We add to the list of node to process all its children - for(Node::ChildIterator it = n->child.begin(), itend = n->child.end(); it != itend; ++it) - nodeToProcess.push_back(it->get()); - } - - const ActivationFunctor activator( activate, sofalistview->getListener() ); - std::for_each(nodeToChange.begin(),nodeToChange.end(),activator); - nodeToChange.clear(); - update(); - - if ( sofalistview == simulationGraph && activate ) - { - if (node == currentSimulation()) - { - sofa::simulation::node::initRoot(node); - } - else - { - sofa::simulation::node::init(node); - } - } -} - -//------------------------------------ - -void RealGUI::setSleepingNode(sofa::simulation::Node* node, bool sleeping) -{ - node->setSleeping(sleeping); -} - -//------------------------------------ - -void RealGUI::lockAnimation(bool value) -{ - if(value) - { - m_animationState = startButton->isChecked(); - playpauseGUI(false); - } - else - { - playpauseGUI(m_animationState); - } -} - -//------------------------------------ - -void RealGUI::fileRecentlyOpened(QAction *action) -{ - //fileOpen(recentlyOpenedFilesManager.getFilename((unsigned int)id)); - fileOpen(action->text().toStdString()); -} - -//------------------------------------ - -void RealGUI::playpauseGUI ( bool startSimulation ) -{ - startButton->setChecked ( startSimulation ); - - /// If there is no root node we do nothing. - Node* root = currentSimulation(); - if (root==nullptr) - return; - - /// Set the animation 'on' in the getContext() - currentSimulation()->getContext()->setAnimate ( startSimulation ); - - if(startSimulation) - { - SimulationStopEvent startEvt; - root->propagateEvent(core::execparams::defaultInstance(), &startEvt); - m_clockBeforeLastStep = 0; - timerStep->start(0); - return; - } - - SimulationStartEvent stopEvt; - root->propagateEvent(core::execparams::defaultInstance(), &stopEvt); - - timerStep->stop(); - return; -} - -//------------------------------------ - -void RealGUI::interactionGUI ( bool ) -{ -} - - -//------------------------------------ - -//called at each step of the rendering -void RealGUI::step() -{ - SIMULATION_LOOP_SCOPE - sofa::helper::AdvancedTimer::begin("Animate"); - - Node* root = currentSimulation(); - if ( root == nullptr ) return; - - startDumpVisitor(); - - if ( !getViewer()->ready() ) return; - - //root->setLogTime(true); - - // If dt is zero, the actual value of dt will be taken from the graph. - double dt = 0.0; - if (realTimeCheckBox->isChecked() && startButton->isChecked()) - { - const clock_t currentClock = clock(); - // If animation has already started - if (m_clockBeforeLastStep != 0) - { - // then dt <- "time since last step" - dt = (double)(currentClock - m_clockBeforeLastStep) / CLOCKS_PER_SEC; - // dt = std::min(dt, dtEdit->text().toDouble()); - } - m_clockBeforeLastStep = currentClock; - } - - sofa::simulation::node::animate(root, dt); - sofa::simulation::node::updateVisual(root); - - if ( m_dumpState ) - sofa::simulation::node::dumpState ( root, *m_dumpStateStream ); - if ( m_exportGnuplot ) - exportGnuplot(root,gnuplotDirectory); - - getViewer()->wait(); - - eventNewStep(); - eventNewTime(); - - if ( m_animateOBJ ) - { -#ifdef CAPTURE_PERIOD - static int counter = 0; - if ( ( counter++ % CAPTURE_PERIOD ) ==0 ) -#endif - { - exportOBJ ( currentSimulation(), false ); - ++m_animationOBJcounter; - } - } - - stopDumpVisitor(); - emit newStep(); - if ( !currentSimulation()->getContext()->getAnimate() ) - startButton->setChecked ( false ); - -#if SOFA_GUI_QT_HAVE_QT_CHARTS - if (displayTimeProfiler->isChecked()) - { - m_windowTimerProfiler->pushStepData(); - } -#endif - - sofa::helper::AdvancedTimer::end("Animate"); -} - -//------------------------------------ - -void RealGUI::updateDtEditState() -{ - dtEdit->setEnabled(!realTimeCheckBox->isChecked()); -} - -void RealGUI::setDt(const QString& value) -{ - const double dt = value.toDouble(); - // Input is validated, but value may be 0 anywway, while it is being entered. - if (dt > 0.0) - currentSimulation()->getContext()->setDt(dt); -} - -//------------------------------------ - -// Reset the simulation to t=0 -void RealGUI::resetScene() -{ - Node* root = currentSimulation(); - startDumpVisitor(); - emit ( newScene() ); - if (root) - { - m_frameCounter=0; - - sofa::simulation::node::reset(root); - eventNewTime(); - emit newStep(); - } - getViewer()->getPickHandler()->reset(); - stopDumpVisitor(); -} - -//------------------------------------ - -void RealGUI::screenshot() -{ - QString filename; - - const bool pngSupport = helper::io::Image::FactoryImage::getInstance()->hasKey("png") - || helper::io::Image::FactoryImage::getInstance()->hasKey("PNG"); - const bool bmpSupport = helper::io::Image::FactoryImage::getInstance()->hasKey("bmp") - || helper::io::Image::FactoryImage::getInstance()->hasKey("BMP"); - - if(!pngSupport && !bmpSupport) - { - QMessageBox::warning(this, tr("runSofa"), - tr("Screenshot is not available (PNG or BMP support not found).\n"), - QMessageBox::Cancel); - return; - } - std::string imageString = "Images (*.bmp)"; - if(pngSupport) - imageString = "Images (*.png)"; - - filename = getSaveFileName ( this, - getViewer()->screenshotName().c_str(), - imageString.c_str(), - "save file dialog" - "Choose a filename to save under" - ); - - viewer::SofaViewer* sofaViewer = getSofaViewer(); - if( sofaViewer ) - sofaViewer->getQWidget()->repaint(); - - if ( filename != "" ) - { - QString prefix; - const int end = filename.lastIndexOf('_'); - if (end > -1) { - prefix = filename.mid( - 0, - end+1 - ); - } else { - prefix = QString::fromStdString( - SetDirectory::GetFileNameWithoutExtension(filename.toStdString().c_str()) + "_"); - } - - if (!prefix.isEmpty()) - getViewer()->setPrefix ( prefix.toStdString(), false ); - - getViewer()->screenshot ( filename.toStdString() ); - } -} - -//------------------------------------ - -void RealGUI::showhideElements() -{ - displayFlag->updateDataValue(); - getSofaViewer()->getQWidget()->update(); -} - -//------------------------------------ - -void RealGUI::update() -{ - getSofaViewer()->getQWidget()->update(); - statWidget->CreateStats(currentSimulation()); -} - -//------------------------------------ - -void RealGUI::updateBackgroundColour() -{ - if(getViewer()) - getViewer()->setBackgroundColour(background[0]->text().toFloat(),background[1]->text().toFloat(),background[2]->text().toFloat()); - - getSofaViewer()->getQWidget()->update(); -} - -//------------------------------------ - -void RealGUI::updateBackgroundImage() -{ - if(getViewer()) - getViewer()->setBackgroundImage( backgroundImage->text().toStdString() ); - - getSofaViewer()->getQWidget()->update(); -} - -//------------------------------------ - -void RealGUI::clear() -{ - simulationGraph->setRoot(currentSimulation()); - statWidget->CreateStats(currentSimulation()); -} - -//---------------------------------- - -void RealGUI::redraw() -{ - emit newStep(); -} - -//------------------------------------ - -void RealGUI::exportOBJ (simulation::Node* root, bool exportMTL ) -{ - if ( !root ) return; - - SCOPED_TIMER_VARNAME(exportOBJTimer, "exportOBJ"); - - const std::string sceneFileName(this->windowFilePath ().toStdString()); - std::ostringstream ofilename; - if ( !sceneFileName.empty() ) - { - const char* begin = sceneFileName.c_str(); - const char* end = strrchr ( begin,'.' ); - if ( !end ) end = begin + sceneFileName.length(); - ofilename << std::string ( begin, end ); - } - else - ofilename << "scene"; - - std::stringstream oss; - oss.width ( 5 ); - oss.fill ( '0' ); - oss << m_animationOBJcounter; - - ofilename << '_' << ( oss.str().c_str() ); - ofilename << ".obj"; - const std::string filename = ofilename.str(); - std::cout << "Exporting OBJ Scene "<execute( v ); - exportGnuplot(root,gnuplotDirectory); - } -} - -//------------------------------------ - -#ifdef SOFA_DUMP_VISITOR_INFO -void RealGUI::setExportVisitor ( bool exp ) -{ - if (exp) - { - windowTraceVisitor->show(); - handleTraceVisitor->clear(); - } - else - { - windowTraceVisitor->hide(); - } -} -#else -void RealGUI::setExportVisitor ( bool ) -{ -} -#endif - -void RealGUI::displayProflierWindow (bool value) -{ -#if SOFA_GUI_QT_HAVE_QT_CHARTS - if (m_windowTimerProfiler == nullptr) - return; - - m_windowTimerProfiler->activateATimer(value); - if (value) - m_windowTimerProfiler->show(); - else - m_windowTimerProfiler->hide(); -#else - SOFA_UNUSED(value); -#endif -} - - -//------------------------------------ - -void RealGUI::currentTabChanged ( int index ) -{ - QWidget* widget = tabs->widget(index); - - if ( widget == currentTab ) return; - - if ( currentTab == nullptr ) - currentTab = widget; - - if ( widget == TabGraph ) - simulationGraph->update(); - else if (widget == TabStats) - statWidget->CreateStats(currentSimulation()); - - currentTab = widget; -} - -//------------------------------------ - -void RealGUI::changeViewer() -{ - QObject* obj = const_cast( QObject::sender() ); - if( !obj) return; - - QAction* action = static_cast(obj); - action->setChecked(true); - - std::map< helper::SofaViewerFactory::Key, QAction* >::const_iterator iter_map; - for ( iter_map = viewerMap.begin(); iter_map != viewerMap.end(); ++iter_map ) - { - if ( iter_map->second == action ) - { - this->unloadScene(); - removeViewer(); - createViewer(iter_map->first.c_str()); - } - else - { - (*iter_map).second->setChecked(false); - } - } - - // Reload the scene - const std::string filename(this->windowFilePath().toStdString()); - fileOpen ( filename.c_str() ); // keep the current display flags -} - -//------------------------------------ - -void RealGUI::updateViewerList() -{ - // the current list of viewer key with associate QAction - type::vector< helper::SofaViewerFactory::Key > currentKeys; - std::map< helper::SofaViewerFactory::Key, QAction*>::const_iterator iter_map; - for ( iter_map = viewerMap.begin(); iter_map != viewerMap.end(); ++iter_map ) - currentKeys.push_back((*iter_map).first); - std::sort(currentKeys.begin(),currentKeys.end()); - - // the new list (most recent since we load/unload viewer plugin) - type::vector< helper::SofaViewerFactory::Key > updatedKeys; - helper::SofaViewerFactory::getInstance()->uniqueKeys(std::back_inserter(updatedKeys)); - std::sort(updatedKeys.begin(),updatedKeys.end()); - - type::vector< helper::SofaViewerFactory::Key > diffKeys; - std::set_symmetric_difference(currentKeys.begin(), - currentKeys.end(), - updatedKeys.begin(), - updatedKeys.end(), - std::back_inserter(diffKeys) - ); - - bool viewerRemoved=false; - type::vector< helper::SofaViewerFactory::Key >::const_iterator it; - for( it = diffKeys.begin(); it != diffKeys.end(); ++it) - { - // delete old - std::map< helper::SofaViewerFactory::Key, QAction* >::iterator itViewerMap; - if( (itViewerMap = viewerMap.find(*it)) != viewerMap.end() ) - { - if( (*itViewerMap).second->isChecked() ) - { - this->unloadScene(); - removeViewer(); - viewerRemoved = true; - } - //(*itViewerMap).second->disconnect(View); - View->removeAction( (*itViewerMap).second); - viewerMap.erase(itViewerMap); - } - else // add new - { - QAction* action = new QAction(this); - action->setText( helper::SofaViewerFactory::getInstance()->getViewerName(*it) ); - //action->setMenuText( helper::SofaViewerFactory::getInstance()->getAcceleratedViewerName(*it) ); - action->setToolTip( helper::SofaViewerFactory::getInstance()->getAcceleratedViewerName(*it) ); - - //action->setToggleAction(true); - action->setCheckable(true); - - - //action->addTo(View); - View->addAction(action); - - viewerMap[*it] = action; - action->setEnabled(true); - connect(action, SIGNAL( triggered() ), this, SLOT( changeViewer() ) ); - } - } - - // if we unloaded a viewer plugin actually in use - if( viewerRemoved && !viewerMap.empty() ) - { - createViewer(viewerMap.begin()->first.c_str()); - viewerMap.begin()->second->setChecked(true); - } -} - -void RealGUI::toolsDockMoved() -{ - QDockWidget* dockWindow = qobject_cast(sender()); - if(!dockWindow) - return; - - if(dockWindow->isFloating()) - dockWindow->resize(500, 700); -} - -void RealGUI::propertyDockMoved(Qt::DockWidgetArea /*a*/) -{ - QDockWidget* dockWindow = qobject_cast(sender()); - if(!dockWindow) - return; - - if(dockWindow->isFloating()) - dockWindow->resize(500, 700); -} - -namespace -{ - -std::string getFormattedLocalTimeFromTimestamp(time_t timestamp) -{ - const tm *timeinfo = localtime(×tamp); - std::ostringstream oss; - oss << std::setfill('0') - << std::setw(2) << timeinfo->tm_mday << "/" // Day - << std::setw(2) << (timeinfo->tm_mon + 1) << "/" // Month - << std::setw(4) << (1900 + timeinfo->tm_year) << " " // Year - << std::setw(2) << timeinfo->tm_hour << ":" // Hours - << std::setw(2) << timeinfo->tm_min << ":" // Minutes - << std::setw(2) << timeinfo->tm_sec; // Seconds - return oss.str(); -} - -std::string getFormattedLocalTime() -{ - return getFormattedLocalTimeFromTimestamp( time( nullptr ) ); -} - -} // namespace - -//------------------------------------ -void RealGUI::appendToDataLogFile(QString dataModifiedString) -{ - const std::string filename = this->windowFilePath().toStdString() + std::string(".log"); - - std::ofstream ofs( filename.c_str(), std::ofstream::out | std::ofstream::app ); - - if (ofs.good()) - { - if (m_modifiedLogFiles.find(filename) == m_modifiedLogFiles.end()) - { - ofs << std::endl << "--- NEW SESSION: " << getFormattedLocalTime() << " ---" << std::endl; - m_modifiedLogFiles.insert(filename); - } - - ofs << dataModifiedString.toStdString(); - } - - ofs.close(); -} - -//======================= SIGNALS-SLOTS ========================= } - -} // namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/RealGUI.h b/Sofa/GUI/Qt/src/sofa/gui/qt/RealGUI.h deleted file mode 100644 index ae82e9f131e..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/RealGUI.h +++ /dev/null @@ -1,437 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include - -#include -#include - -#include -#include "GraphListenerQListView.h" -#include "QMenuFilesRecentlyOpened.h" -#include "AboutSOFADialog.h" -#include "PickHandlerCallBacks.h" - -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -class WDoubleLineEdit; -class QDragEnterEvent; - -namespace sofa::gui::qt -{ -#if(SOFA_GUI_QT_HAVE_QT5_WEBENGINE) -class DocBrowser ; -#endif - -//enum TYPE{ NORMAL, PML, LML}; -enum SCRIPT_TYPE { PHP, PERL }; - -class QSofaListView; -class QDisplayPropertyWidget; -class QSofaStatWidget; -class GraphListenerQListView; -class DisplayFlagsDataWidget; -class SofaPluginManager; -#ifdef SOFA_DUMP_VISITOR_INFO -class WindowVisitor; -class GraphVisitor; -#endif - -class SofaMouseManager; - -#if SOFA_GUI_QT_HAVE_QT_CHARTS -class SofaWindowProfiler; -#endif - -#if SOFA_GUI_QT_HAVE_NODEEDITOR -class SofaWindowDataGraph; -#endif - -namespace viewer -{ -class SofaViewer; -} - - -class SOFA_GUI_QT_API RealGUI : public QMainWindow, public Ui::GUI, public sofa::gui::common::BaseGUI -{ - Q_OBJECT - -//-----------------STATIC METHODS------------------------{ -public: - static void setupSurfaceFormat(); - static common::BaseGUI* CreateGUI(const char* name, sofa::simulation::Node::SPtr groot = nullptr, const char* filename = nullptr); - - static void SetPixmap(std::string pixmap_filename, QPushButton* b); - -protected: - static void CreateApplication(int _argc=0, char** _argv=nullptr); - static void InitApplication( RealGUI* _gui); -//-----------------STATIC METHODS------------------------} - - - -//-----------------CONSTRUCTOR - DESTRUCTOR ------------------------{ -public: - RealGUI( const char* viewername); - - ~RealGUI() override; -//-----------------CONSTRUCTOR - DESTRUCTOR ------------------------} - - - -//-----------------OPTIONS DEFINITIONS------------------------{ -//public: - -#ifdef SOFA_DUMP_VISITOR_INFO - void setTraceVisitors(bool) override; -#endif - - void showFPS(double fps) override; - -protected: - QLabel* fpsLabel; - QLabel* timeLabel; - - -private: - -#ifdef SOFA_DUMP_VISITOR_INFO - WindowVisitor* windowTraceVisitor; - GraphVisitor* handleTraceVisitor; -#endif - SofaMouseManager* m_sofaMouseManager; -#if SOFA_GUI_QT_HAVE_QT_CHARTS - SofaWindowProfiler* m_windowTimerProfiler; -#endif - -#if SOFA_GUI_QT_HAVE_NODEEDITOR - SofaWindowDataGraph* m_sofaWindowDataGraph; -#endif -//-----------------OPTIONS DEFINITIONS------------------------} - - - -//-----------------DATAS MEMBER------------------------{ -public: - //TODO: make a protected data with an accessor - QSofaListView* simulationGraph; - -protected: - bool m_dumpState; - std::ofstream* m_dumpStateStream; - std::ostringstream m_dumpVisitorStream; - bool m_exportGnuplot; - bool m_animateOBJ; - int m_animationOBJcounter;// save a succession of .obj indexed by _animationOBJcounter - bool m_displayComputationTime; - bool m_fullScreen; - sofa::gui::qt::viewer::SofaViewer* m_viewer; - // Clock before the last simulation step (or zero if the - // simulation hasn't run yet). - clock_t m_clockBeforeLastStep; - - // Component Properties - QDisplayPropertyWidget* propertyWidget; - - /// list of all viewer key name (for creation) mapped to its QAction in the GUI - std::map< helper::SofaViewerFactory::Key, QAction* > viewerMap; - InformationOnPickCallBack informationOnPickCallBack; - - QWidget* currentTab; - QSofaStatWidget* statWidget; - QTimer* timerStep; - QTimer* timerIdle; - WDoubleLineEdit *background[3]; - QLineEdit *backgroundImage; - SofaPluginManager* pluginManagerDialog; - QMenuFilesRecentlyOpened recentlyOpenedFilesManager; - - std::string simulationName; - std::string gnuplotDirectory; - std::string pathDumpVisitor; - - /// Keep track of log files that have been modified since the GUI started - std::set m_modifiedLogFiles; - - bool m_enableInteraction {false}; -private: - //currently unused: scale is experimental - float m_objectScale[2]; - bool m_saveReloadFile; - DisplayFlagsDataWidget* displayFlag {nullptr}; -#if(SOFA_GUI_QT_HAVE_QT5_WEBENGINE) - DocBrowser* m_docbrowser {nullptr}; -#endif - bool m_animationState; - int m_frameCounter; - unsigned int m_viewerMSAANbSampling; -//-----------------DATAS MEMBER------------------------} - - - -//-----------------METHODS------------------------{ -public: - void stepMainLoop () override; - - int mainLoop() override; - int closeGUI() override; - sofa::simulation::Node* currentSimulation() override; - virtual void fileOpen(std::string filename, bool temporaryFile=false, bool reload=false); - - // virtual void fileOpen(); - virtual void fileOpenSimu(std::string filename); - virtual void setScene(Node::SPtr groot, const char* filename=nullptr, bool temporaryFile=false) override; - virtual void setSceneWithoutMonitor(Node::SPtr groot, const char* filename=nullptr, bool temporaryFile=false); - - virtual void unloadScene(bool _withViewer = true); - - virtual void setTitle( std::string windowTitle ); - - void setViewerResolution(int w, int h) override; - void setFullScreen() override { setFullScreen(true); } - virtual void setFullScreen(bool enable); - void centerWindow() override; - void setBackgroundColor(const sofa::type::RGBAColor& c) override; - virtual void setBackgroundImage(const std::string& i) override; - void setViewerConfiguration(sofa::component::setting::ViewerSetting* viewerConf) override; - void setMouseButtonConfiguration(sofa::component::setting::MouseButtonSetting *button) override; - - //Configuration methods - void setDumpState(bool) override; - void setLogTime(bool) override; - void setExportState(bool) override; - virtual void setGnuplotPath(const std::string & path) override; - - /// create a viewer according to the argument key - /// \note the viewerMap have to be initialize at least once before - /// \arg _updateViewerList is used only if you want to reactualise the viewerMap in the GUI - /// TODO: find a better way to propagate the argument when we construct the viewer - virtual void createViewer(const char* _viewerName, bool _updateViewerList=false); - - /// Used to directly replace the current viewer - void registerViewer(common::BaseViewer* _viewer) override; - - common::BaseViewer* getViewer() override; - - /// A way to know if our viewer is embedded or not... (see initViewer) - /// TODO: Find a better way to do this - sofa::gui::qt::viewer::SofaViewer* getSofaViewer(); - - virtual void removeViewer(); - - void dragEnterEvent( QDragEnterEvent* event) override; - - void dropEvent(QDropEvent* event) override; - -protected: - /// init data member from RealGUI for the viewer initialisation in the GUI - void init(); - void createDisplayFlags(Node::SPtr root); - void loadSimulation(bool one_step=false); //? where is the implementation ? - void eventNewStep(); - void eventNewTime(); - void keyPressEvent ( QKeyEvent * e ) override; - void startDumpVisitor(); - void stopDumpVisitor(); - - /// init the viewer for the GUI (embedded or not we have to connect some info about viewer in the GUI) - void initViewer(common::BaseViewer* _viewer) override; - - virtual int exitApplication(unsigned int _retcode = 0) - { - return _retcode; - } - - void sleep(float seconds, float init_time) - { - [[maybe_unused]] unsigned int t = 0; - const clock_t goal = (clock_t) (seconds + init_time); - while (goal > clock()/(float)CLOCKS_PER_SEC) t++; - } - - sofa::simulation::Node::SPtr mSimulation; - - sofa::helper::system::FileEventListener* m_filelistener {nullptr}; -private: - void addViewer();//? where is the implementation ? - - /// Parse options from the RealGUI constructor - void parseOptions(); - - void createPluginManager(); - void createSofaWindowDataGraph(); - - /// configure Recently Opened Menu - void createRecentFilesMenu(); - - void createBackgroundGUIInfos(); - void createSimulationGraph(); - void createPropertyWidget(); - void createWindowVisitor(); - void createAdvancedTimerProfilerWindow(); - -public slots: - virtual void newRootNode(sofa::simulation::Node* root, const char* path); - virtual void activateNode(sofa::simulation::Node* , bool ); - virtual void setSleepingNode(sofa::simulation::Node*, bool); - virtual void lockAnimation(bool); - virtual void fileRecentlyOpened(QAction * action); - virtual void playpauseGUI(bool value); - virtual void interactionGUI(bool value); - virtual void step(); - virtual void emitIdle(); - // virtual void setDt(double); - virtual void setDt(const QString&); - // Disable dtEdit when realTimeCheckBox is checked - virtual void updateDtEditState(); - virtual void resetScene(); - virtual void screenshot(); - virtual void showhideElements(); - virtual void update(); - virtual void updateBackgroundColour(); - virtual void updateBackgroundImage(); - - // Propagate signal to call viewer method in case of it is not a widget - virtual void resetView() {if(getViewer())getViewer()->resetView(); } - virtual void saveView() {if(getViewer())getViewer()->saveView(); } - virtual void setSizeW ( int _valW ) {if(getViewer())getViewer()->setSizeW(_valW); } - virtual void setSizeH ( int _valH ) {if(getViewer())getViewer()->setSizeH(_valH); } - - virtual void clear(); - /// refresh the visualization window - void redraw() override; - virtual void exportOBJ(sofa::simulation::Node* node, bool exportMTL=true); - virtual void dumpState(bool); - virtual void displayComputationTime(bool); - virtual void setExportGnuplot(bool); - virtual void setExportVisitor(bool); - virtual void displayProflierWindow(bool); - virtual void currentTabChanged(int index); - - virtual void popupOpenFileSelector(); - virtual void fileReload(); - virtual void fileExit(); - virtual void helpAbout() { /* TODO */ } - virtual void editRecordDirectory(); - virtual void editGnuplotDirectory(); - virtual void showDocBrowser() ; - virtual void showAbout() ; - virtual void showPluginManager(); - virtual void showMouseManager(); - virtual void showVideoRecorderManager(); - virtual void showWindowDataGraph(); - virtual void toolsDockMoved(); - -protected slots: - /// Allow to dynamically change viewer. Called when click on another viewer in GUI Qt viewer list (see viewerMap). - /// TODO: find a better way to propagate the argument when we construct the viewer - virtual void changeViewer(); - - /// Update the viewerMap and create viewer if we haven't yet one (the first of the list) - /// TODO: find a better way to propagate the argument when we construct the viewer - virtual void updateViewerList(); - - /// Update the scenegraph and activate the automatic refresh. - virtual void onSceneGraphRefreshButtonClicked(); - - /// Update the SceneGraph update button to reflect the dirtiness status. - virtual void sceneGraphViewDirtynessChanged(bool isDirty); - - /// Update the SceneGraph update button to reflect the locking status. - virtual void sceneGraphViewLockingChanged(bool isLocked); - - void propertyDockMoved(Qt::DockWidgetArea a); - - void appendToDataLogFile(QString); - - void docBrowserVisibilityChanged(bool) ; - -signals: - void reload(); - void newScene(); - void newStep(); - void quit(); -//-----------------SIGNALS-SLOTS------------------------} - -}; - - - - - -struct ActivationFunctor -{ - ActivationFunctor(bool act, GraphListenerQListView* l):active(act), listener(l) - { - pixmap_filename= std::string("textures/media-record.png"); - if ( sofa::helper::system::DataRepository.findFile ( pixmap_filename ) ) - pixmap_filename = sofa::helper::system::DataRepository.getFile ( pixmap_filename ); - } - void operator()(core::objectmodel::BaseNode* n) - { - if (active) - { - //Find the corresponding node in the Qt Graph - QTreeWidgetItem *item=listener->items[n]; - //Remove the text - QString desact_text = item->text(0); - desact_text.remove(QString("Deactivated "), Qt::CaseInsensitive); - item->setText(0,desact_text); - //Remove the icon - const QPixmap *p = getPixmap(n, false,false, false); - item->setIcon(0, QIcon(*p)); -// item->setOpen(true); - item->setExpanded(true); - } - else - { - //Find the corresponding node in the Qt Graph - QTreeWidgetItem *item=listener->items[n]; - //Remove the text - item->setText(0, QString("Deactivated ") + item->text(0)); - item->setIcon(0, QIcon(QPixmap::fromImage(QImage(pixmap_filename.c_str())))); - - item->setExpanded(false); - } - } -protected: - std::string pixmap_filename; - bool active; - GraphListenerQListView* listener; -}; - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/SimpleDataWidget.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/SimpleDataWidget.cpp deleted file mode 100644 index 7942456a333..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/SimpleDataWidget.cpp +++ /dev/null @@ -1,352 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ - -#include "SimpleDataWidget.h" -#include -#include -#include - -namespace sofa::gui::qt -{ - -using sofa::helper::Creator; -using sofa::type::fixed_array; -using namespace sofa::type; -using namespace sofa::defaulttype; - -Creator > DWClass_bool("default",true); -Creator > DWClass_char("default",true); -Creator > DWClass_uchar("default",true); -Creator > DWClass_int("default",true); -Creator > DWClass_uint("default",true); -Creator > DWClass_float("default",true); -Creator > DWClass_double("default",true); -Creator > DWClass_string("default",true); - - - -Creator > > DWClass_Vec1i("default",true); -Creator > > DWClass_Vec1u("default",true); -Creator > > DWClass_Vec1f("default",true); -Creator > > DWClass_Vec1d("default",true); -Creator > > DWClass_Vec2i("default",true); -Creator > > DWClass_Vec2u("default",true); -Creator > > DWClass_Vec2f("default",true); -Creator > > DWClass_Vec2d("default",true); -Creator > > DWClass_Vec3i("default",true); -Creator > > DWClass_Vec3u("default",true); -Creator > > DWClass_Vec3f("default",true); -Creator > > DWClass_Vec3d("default",true); -Creator > > DWClass_Vec4i("default",true); -Creator > > DWClass_Vec4u("default",true); -Creator > > DWClass_Vec4f("default",true); -Creator > > DWClass_Vec4d("default",true); -Creator > > DWClass_Vec6i("default",true); -Creator > > DWClass_Vec6u("default",true); -Creator > > DWClass_Vec6f("default",true); -Creator > > DWClass_Vec6d("default",true); -Creator > > DWClass_Vec8i("default",true); -Creator > > DWClass_Vec8u("default",true); - -Creator > > DWClass_fixed_array1i("default",true); -Creator > > DWClass_fixed_array1u("default",true); -Creator > > DWClass_fixed_array2i("default",true); -Creator > > DWClass_fixed_array2u("default",true); -Creator > > DWClass_fixed_array3i("default",true); -Creator > > DWClass_fixed_array3u("default",true); -Creator > > DWClass_fixed_array4i("default",true); -Creator > > DWClass_fixed_array4u("default",true); -Creator > > DWClass_fixed_array6i("default",true); -Creator > > DWClass_fixed_array6u("default",true); -Creator > > DWClass_fixed_array8i("default",true); -Creator > > DWClass_fixed_array8u("default",true); - -Creator > DWClass_Edge ("default",true); -Creator > DWClass_Triangle ("default",true); -Creator > DWClass_Quad ("default",true); -Creator > DWClass_Tetrahedron("default",true); -Creator > DWClass_Hexahedron ("default",true); - -Creator > > DWClass_fixed_array1f("default",true); -Creator > > DWClass_fixed_array1d("default",true); -Creator > > DWClass_fixed_array2f("default",true); -Creator > > DWClass_fixed_array2d("default",true); -Creator > > DWClass_fixed_array3f("default",true); -Creator > > DWClass_fixed_array3d("default",true); -Creator > > DWClass_fixed_array4f("default",true); -Creator > > DWClass_fixed_array4d("default",true); -Creator > > DWClass_fixed_array6f("default",true); -Creator > > DWClass_fixed_array6d("default",true); -Creator > > DWClass_fixed_array8f("default",true); -Creator > > DWClass_fixed_array8d("default",true); - -Creator > > DWClass_Quatf("default",true); -Creator > > DWClass_Quatd("default",true); - - -using sofa::helper::Polynomial_LD; -Creator > >DWClass_PolynomialLD5d("default",true); -Creator > >DWClass_PolynomialLD4d("default",true); -Creator > >DWClass_PolynomialLD3d("default",true); -Creator > >DWClass_PolynomialLD2d("default",true); -Creator > >DWClass_PolynomialLD1d("default",true); -Creator > >DWClass_PolynomialLD5f("default",true); -Creator > >DWClass_PolynomialLD4f("default",true); -Creator > >DWClass_PolynomialLD3f("default",true); -Creator > >DWClass_PolynomialLD2f("default",true); -Creator > >DWClass_PolynomialLD1f("default",true); - -Creator > > DWClass_Mat22f("default",true); -Creator > > DWClass_Mat22d("default",true); -Creator > > DWClass_Mat23f("default",true); -Creator > > DWClass_Mat23d("default",true); -Creator > > DWClass_Mat33f("default",true); -Creator > > DWClass_Mat33d("default",true); -Creator > > DWClass_Mat34f("default",true); -Creator > > DWClass_Mat34d("default",true); -Creator > > DWClass_Mat44f("default",true); -Creator > > DWClass_Mat44d("default",true); -Creator > > DWClass_Mat66f("default",true); -Creator > > DWClass_Mat66d("default",true); -Creator > DWClass_TagSet("default",true); - - -Creator> > > DWClass_CRSCVec1f("default",true); -Creator> > > DWClass_CRSCVec2f("default",true); -Creator> > > DWClass_CRSCVec3f("default",true); -Creator> > > DWClass_CRSCVec6f("default",true); -Creator> > > DWClass_CRSCVec1d("default",true); -Creator> > > DWClass_CRSCVec2d("default",true); -Creator> > > DWClass_CRSCVec3d("default",true); -Creator> > > DWClass_CRSCVec6d("default",true); - -//////////////////////////////////////////////////////////////// -/// OptionsGroup support -//////////////////////////////////////////////////////////////// - -//these functions must be written here for effect of writeToData -Creator DWClass_OptionsGroup("default",true); - -bool RadioDataWidget::createWidgets() -{ - QVBoxLayout* layout = new QVBoxLayout(this); - const sofa::helper::OptionsGroup m_radiotrick = getData()->getValue(); - const unsigned int LIMIT_NUM_BUTTON=4; - buttonMode=m_radiotrick.size() < LIMIT_NUM_BUTTON; - if (buttonMode) - { - buttonList=new QButtonGroup(this); - - for(unsigned int i=0; isetChecked(true); - layout->addWidget(m_radiobutton); - buttonList->addButton(m_radiobutton,i); - } - connect(buttonList, SIGNAL(buttonClicked(int)), this, SLOT(setWidgetDirty())) ; - } - else - { - comboList=new QComboBox(this); - comboList->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); - - const sofa::helper::OptionsGroup m_radiotrick = getData()->getValue(); - QStringList list; - for(unsigned int i=0; iinsertItems(0, list); - - comboList->setCurrentIndex(m_radiotrick.getSelectedId()); - - connect(comboList, SIGNAL(activated(int)), this, SLOT(setWidgetDirty())); - layout->addWidget(comboList); - - } - - return true; -} -void RadioDataWidget::setDataReadOnly(bool readOnly) -{ - if (buttonMode) - { - const QList buttons = buttonList->buttons(); - for (int i = 0; i < buttons.size(); ++i) - { - buttons.at(i)->setEnabled(!readOnly); - } - } - else - { - comboList->setEnabled(!readOnly); - } -} - -void RadioDataWidget::readFromData() -{ - const sofa::helper::OptionsGroup m_radiotrick = getData()->getValue(); - - if (buttonMode) - { - buttonList->button(m_radiotrick.getSelectedId())->setChecked(true); - } - else - { - comboList->setCurrentIndex(m_radiotrick.getSelectedId()); - } -} -void RadioDataWidget::writeToData() -{ - sofa::helper::OptionsGroup m_radiotrick = getData()->getValue(); - if (buttonMode) - { - m_radiotrick.setSelectedItem((unsigned int)buttonList->checkedId ()); - } - else - { - m_radiotrick.setSelectedItem((unsigned int)comboList->currentIndex()); - } - - this->getData()->setValue(m_radiotrick); -} - -Creator DWClass_SelectableItem("default",true); - -SelectableItemWidget::SelectableItemWidget(QWidget* parent, const char* name, - core::BaseData* m_data, const helper::BaseSelectableItem* item) -: TDataWidget(parent, name, m_data, item) -, m_selectableItem(item) -{} - -bool SelectableItemWidget::createWidgets() -{ - if (Tdata && Tdata->getValueTypeString() != "SelectableItem" || - baseData && baseData->getValueTypeString() != "SelectableItem") - { - return false; - } - - QVBoxLayout* layout = new QVBoxLayout(this); - static constexpr unsigned int LIMIT_NUM_BUTTON = 4; - - assert(m_selectableItem); - const std::size_t nbItems = m_selectableItem->getNumberOfItems(); - m_buttonMode = nbItems < LIMIT_NUM_BUTTON; - const auto* items = m_selectableItem->getItemsData(); - - const auto getItem = [](const sofa::helper::Item* item) - { - std::stringstream ss; - ss << item->key; - if (!item->description.empty()) - { - ss << " (" << item->description << ")"; - } - return ss.str(); - }; - - if (m_buttonMode) - { - m_buttonList = new QButtonGroup(this); - - for (std::size_t i = 0; i < nbItems; i++) - { - const helper::Item* item_i = items + i; - - QRadioButton * m_radiobutton = new QRadioButton(QString(getItem(item_i).c_str()), this); - if (i == m_selectableItem->getSelectedId()) - { - m_radiobutton->setChecked(true); - } - layout->addWidget(m_radiobutton); - m_buttonList->addButton(m_radiobutton, i); - } - connect(m_buttonList, SIGNAL(buttonClicked(int)), this, SLOT(setWidgetDirty())) ; - } - else - { - m_comboList=new QComboBox(this); - m_comboList->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); - - QStringList list; - for (std::size_t i = 0; i < nbItems; i++) - { - const helper::Item* item_i = items + i; - list << getItem(item_i).c_str(); - } - - m_comboList->insertItems(0, list); - - m_comboList->setCurrentIndex(m_selectableItem->getSelectedId()); - - connect(m_comboList, SIGNAL(activated(int)), this, SLOT(setWidgetDirty())); - layout->addWidget(m_comboList); - - } - - return true; -} - -void SelectableItemWidget::setDataReadOnly(const bool readOnly) -{ - if (m_buttonMode) - { - const QList buttons = m_buttonList->buttons(); - for (auto& button : buttons) - { - button->setEnabled(!readOnly); - } - } - else - { - m_comboList->setEnabled(!readOnly); - } -} - -void SelectableItemWidget::readFromData() -{ - if (m_buttonMode) - { - m_buttonList->button(m_selectableItem->getSelectedId())->setChecked(true); - } - else - { - m_comboList->setCurrentIndex(m_selectableItem->getSelectedId()); - } -} - -void SelectableItemWidget::writeToData() -{ - if (m_buttonMode) - { - const_cast(m_selectableItem)->setSelectedId(static_cast(m_buttonList->checkedId())); - } - else - { - const_cast(m_selectableItem)->setSelectedId(static_cast(m_comboList->currentIndex())); - } -} - - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/SimpleDataWidget.h b/Sofa/GUI/Qt/src/sofa/gui/qt/SimpleDataWidget.h deleted file mode 100644 index 3583f2c29a8..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/SimpleDataWidget.h +++ /dev/null @@ -1,1031 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include "DataWidget.h" -#include "ModifyObject.h" -#include -#include -//#include -#include -#include -#include -#include "WDoubleLineEdit.h" -#include - -#include -#include -#include - -#include - -#include -#include -#include -#include - -#if !defined(INFINITY) -#define INFINITY 9.0e10 -#endif -namespace sofa::gui::qt -{ - -using sofa::type::Quat; - -/// This class is used to specify how to graphically represent a data type, -/// by default using a simple QLineEdit -template -class data_widget_trait -{ -public: - typedef T data_type; - typedef QLineEdit Widget; - static Widget* create(QWidget* parent, const data_type& /*d*/) - { - Widget* w = new Widget(parent); - w->setFocusPolicy(Qt::StrongFocus); - return w; - } - static void readFromData(Widget* w, const data_type& d) - { - std::ostringstream o; - o << d; - if (o.str() != w->text().toStdString()) - w->setText(QString(o.str().c_str())); - } - static void writeToData(Widget* w, data_type& d) - { - const std::string s = w->text().toStdString(); - std::istringstream i(s); - i >> d; - } - static void setReadOnly(Widget* w, bool readOnly) - { - w->setEnabled(!readOnly); - w->setReadOnly(readOnly); - } - static void connectChanged(Widget* w, DataWidget* datawidget) - { - datawidget->connect(w, SIGNAL( textChanged(const QString&) ), datawidget, SLOT(setWidgetDirty())); - } -}; - - - - - -/// This class is used to create and manage the GUI of a data type, -/// using data_widget_trait to know which widgets to use -template -class data_widget_container -{ -public: - typedef T data_type; - typedef data_widget_trait helper; - typedef typename helper::Widget Widget; - typedef QHBoxLayout Layout; - Widget* w; - Layout* container_layout; - data_widget_container() : w(nullptr),container_layout(nullptr) { } - - bool createLayout(DataWidget* parent) - { - if(parent->layout() != nullptr) return false; - container_layout = new QHBoxLayout(parent); - //parent->setLayout(container_layout); - return true; - } - - bool createLayout(QLayout* layout) - { - if(container_layout) return false; - container_layout = new QHBoxLayout(); - layout->addItem(container_layout); - return true; - } - - bool createWidgets(DataWidget* parent, const data_type& d, bool readOnly) - { - w = helper::create(parent,d); - if (w == nullptr) return false; - - helper::readFromData(w, d); - if (readOnly) - helper::setReadOnly(w, readOnly); - else - helper::connectChanged(w, parent); - return true; - } - void setReadOnly(bool readOnly) - { - if(w){ - w->setEnabled(!readOnly); - helper::setReadOnly(w, readOnly); - } - } - void readFromData(const data_type& d) - { - helper::readFromData(w, d); - } - void writeToData(data_type& d) - { - helper::writeToData(w, d); - } - - void insertWidgets() - { - assert(w); - container_layout->addWidget(w); - } -}; - -/// This class manages the GUI of a BaseData, using the corresponding instance of data_widget_container -template > -class SimpleDataWidget : public TDataWidget -{ - -protected: - typedef T data_type; - Container container; - typedef data_widget_trait helper; - - -public: - typedef sofa::core::objectmodel::Data MyTData; - SimpleDataWidget(QWidget* parent,const char* name, MyTData* d): - TDataWidget(parent,name,d) - {} - virtual bool createWidgets() - { - const data_type& d = this->getData()->getValue(); - if (!container.createWidgets(this, d, ! this->isEnabled() ) ) - return false; - - container.createLayout(this); - container.insertWidgets(); - - return true; - } - virtual void setDataReadOnly(bool readOnly) - { - container.setReadOnly(readOnly); - } - - virtual void readFromData() - { - container.readFromData(this->getData()->getValue()); - } - - virtual void setReadOnly(bool readOnly) - { - container.setReadOnly(readOnly); - } - - virtual void writeToData() - { - data_type& d = *this->getData()->beginEdit(); - container.writeToData(d); - this->getData()->endEdit(); - } - virtual unsigned int numColumnWidget() { return 5; } -}; - -//////////////////////////////////////////////////////////////// -/// std::string support -//////////////////////////////////////////////////////////////// - -template<> -class data_widget_trait < std::string > -{ -public: - typedef std::string data_type; - typedef QLineEdit Widget; - static Widget* create(QWidget* parent, const data_type& /*d*/) - { - Widget* w = new Widget(parent); - w->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); - return w; - } - static void readFromData(Widget* w, const data_type& d) - { - if (w->text().toStdString() != d) - w->setText(QString(d.c_str())); - } - static void writeToData(Widget* w, data_type& d) - { - d = w->text().toStdString(); - } - static void setReadOnly(Widget* w, bool readOnly) - { - w->setEnabled(!readOnly); - w->setReadOnly(readOnly); - } - static void connectChanged(Widget* w, DataWidget* datawidget) - { - datawidget->connect(w, SIGNAL( textChanged(const QString&) ), datawidget, SLOT(setWidgetDirty()) ); - } -}; - -//////////////////////////////////////////////////////////////// -/// bool support -//////////////////////////////////////////////////////////////// - -template<> -class data_widget_trait < bool > -{ -public: - typedef bool data_type; - typedef QCheckBox Widget; - static Widget* create(QWidget* parent, const data_type& /*d*/) - { - Widget* w = new Widget(parent); - return w; - } - static void readFromData(Widget* w, const data_type& d) - { - if (w->isChecked() != d) - w->setChecked(d); - } - static void writeToData(Widget* w, data_type& d) - { - d = (data_type) (w->isChecked()); - } - static void setReadOnly(Widget* w, bool readOnly) - { - w->setEnabled(!readOnly); - } - static void connectChanged(Widget* w, DataWidget* datawidget) - { - datawidget->connect(w, SIGNAL( toggled(bool) ), datawidget, SLOT(setWidgetDirty())); - } -}; - -//////////////////////////////////////////////////////////////// -/// float and double support -//////////////////////////////////////////////////////////////// - -template < typename T > -class real_data_widget_trait -{ -public: - typedef T data_type; - typedef WDoubleLineEdit Widget; - static Widget* create(QWidget* parent, const data_type& /*d*/) - { - Widget* w = new Widget(parent, "real"); - - w->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); - w->setMinValue( (data_type)-INFINITY ); - w->setMaxValue( (data_type)INFINITY ); - w->setMinimumWidth(20); - return w; - } - static void readFromData(Widget* w, const data_type& d) - { - if (d != w->getDisplayedValue()) - w->setValue(d); - } - static void writeToData(Widget* w, data_type& d) - { - d = (data_type) w->getDisplayedValue(); - } - static void setReadOnly(Widget* w, bool readOnly) - { - w->setEnabled(!readOnly); - } - static void connectChanged(Widget* w, DataWidget* datawidget) - { - datawidget->connect(w, SIGNAL( textChanged(const QString&) ), datawidget, SLOT(setWidgetDirty())); - } -}; - -template<> -class data_widget_trait < float > : public real_data_widget_trait< float > -{}; - -template<> -class data_widget_trait < double > : public real_data_widget_trait< double > -{}; - -//////////////////////////////////////////////////////////////// -/// int, unsigned int, char and unsigned char support -//////////////////////////////////////////////////////////////// - -template -class int_data_widget_trait -{ -public: - typedef T data_type; - typedef QSpinBox Widget; - static Widget* create(QWidget* parent, const data_type& /*d*/) - { - Widget* w = new Widget(parent); - w->setMinimum(vmin); - w->setMaximum(vmax); - w->setSingleStep(1); - - w->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); - return w; - } - static void readFromData(Widget* w, const data_type& d) - { - if ((int)d != w->value()) - w->setValue((int)d); - } - static void writeToData(Widget* w, data_type& d) - { - d = (data_type) w->value(); - } - static void setReadOnly(Widget* w, bool readOnly) - { - w->setEnabled(!readOnly); - } - static void connectChanged(Widget* w, DataWidget* datawidget) - { - datawidget->connect(w, SIGNAL( valueChanged(int) ), datawidget, SLOT(setWidgetDirty())); - } -}; - -template<> -class data_widget_trait < int > : public int_data_widget_trait < int, INT_MIN, INT_MAX > -{}; - -template<> -class data_widget_trait < unsigned int > : public int_data_widget_trait < unsigned int, 0, INT_MAX > -{}; - -template<> -class data_widget_trait < char > : public int_data_widget_trait < char, -128, 127 > -{}; - -template<> -class data_widget_trait < unsigned char > : public int_data_widget_trait < unsigned char, 0, 255 > -{}; - -//////////////////////////////////////////////////////////////// -/// arrays and vectors support -//////////////////////////////////////////////////////////////// - -/// This class is used to get properties of a data type in order to display it as a table or a list -template -class vector_data_trait -{ -public: - - typedef T data_type; - /// Type of a row if this data type is viewed in a table or list - typedef T value_type; - /// Number of dimensions of this data type - enum { NDIM = 0 }; - enum { SIZE = 1 }; - /// Get the number of rows - static Size size(const data_type&) { return SIZE; } - /// Get the name of a row, or nullptr if the index should be used instead - static const char* header(const data_type& /*d*/, Size /*i*/ = 0) - { - return nullptr; - } - /// Get a row - static const value_type* get(const data_type& d, Size i = 0) - { - return (i == 0) ? &d : nullptr; - } - /// Set a row - static void set( const value_type& v, data_type& d, Size i = 0) - { - if (i == 0) - d = v; - } - /// Resize - static void resize( Size /*s*/, data_type& /*d*/) - { - } -}; - - -template::value_type> > -class fixed_vector_data_widget_container -{ -public: - typedef T data_type; - typedef vector_data_trait vhelper; - typedef typename vhelper::value_type value_type; - typedef QHBoxLayout Layout; - enum { N = vhelper::SIZE }; - Container w[N]; - Layout* container_layout; - - fixed_vector_data_widget_container():container_layout(nullptr) {} - - bool createLayout(DataWidget* parent) - { - if(parent->layout() != nullptr) return false; - container_layout = new QHBoxLayout(parent); - return true; - } - - bool createLayout(QLayout* layout) - { - if(container_layout) return false; - container_layout = new QHBoxLayout(); - layout->addItem(container_layout); - return true; - } - - bool createWidgets(DataWidget* parent, const data_type& d, bool readOnly) - { - for (sofa::Size i=0; iaddWidget(w[i].w); - } - } -}; - -template::value_type >::value_type> > -class fixed_grid_data_widget_container -{ -public: - - typedef T data_type; - typedef vector_data_trait rhelper; - typedef typename rhelper::value_type row_type; - typedef vector_data_trait vhelper; - typedef typename vhelper::value_type value_type; - enum { L = rhelper::SIZE }; - enum { C = vhelper::SIZE }; - typedef QGridLayout Layout; - Container w[L][C]; - Layout* container_layout; - fixed_grid_data_widget_container():container_layout(nullptr) {} - - bool createLayout(QWidget* parent) - { - if( parent->layout() != nullptr ) return false; - container_layout = new Layout(parent /*,L,C */); - return true; - } - bool createLayout(QLayout* layout) - { - if(container_layout != nullptr ) return false; - container_layout = new Layout( /*,L,C */); - layout->addItem(container_layout); - return true; - } - - bool createWidgets(DataWidget* parent, const data_type& d, bool readOnly) - { - for (sofa::Size y=0; yaddWidget(w[y][x].w,y,x); - } - } - } -}; - -//////////////////////////////////////////////////////////////// -/// sofa::type::fixed_array support -//////////////////////////////////////////////////////////////// - -template -class vector_data_trait < sofa::type::fixed_array > -{ -public: - typedef sofa::type::fixed_array data_type; - typedef T value_type; - enum { NDIM = 1 }; - enum { SIZE = N }; - static sofa::Size size(const data_type&) { return SIZE; } - static const char* header(const data_type& /*d*/, sofa::Size /*i*/ = 0) - { - return nullptr; - } - static const value_type* get(const data_type& d, sofa::Index i = 0) - { - return ((unsigned)i < (unsigned)size(d)) ? &(d[i]) : nullptr; - } - static void set( const value_type& v, data_type& d, sofa::Index i = 0) - { - if ((unsigned)i < (unsigned)size(d)) - d[i] = v; - } - static void resize( sofa::Size /*s*/, data_type& /*d*/) - { - } - -}; - -template -class data_widget_container < sofa::type::fixed_array > : public fixed_vector_data_widget_container < sofa::type::fixed_array > -{}; - - -//////////////////////////////////////////////////////////////// -/// Topological edges/triangles/... support -//////////////////////////////////////////////////////////////// - -template<> -class vector_data_trait < sofa::core::topology::Topology::Edge > - : public vector_data_trait < sofa::type::fixed_array < sofa::core::topology::Topology::PointID, 2 > > -{ -}; - -template<> -class data_widget_container < sofa::core::topology::Topology::Edge > : public fixed_vector_data_widget_container < sofa::core::topology::Topology::Edge > -{}; - -template<> -class vector_data_trait < sofa::core::topology::Topology::Triangle > - : public vector_data_trait < sofa::type::fixed_array < sofa::core::topology::Topology::PointID, 3 > > -{ -}; - -template<> -class data_widget_container < sofa::core::topology::Topology::Triangle > : public fixed_vector_data_widget_container < sofa::core::topology::Topology::Triangle > -{}; - -template<> -class vector_data_trait < sofa::core::topology::Topology::Quad > - : public vector_data_trait < sofa::type::fixed_array < sofa::core::topology::Topology::PointID, 4 > > -{ -}; - -template<> -class data_widget_container < sofa::core::topology::Topology::Quad > : public fixed_vector_data_widget_container < sofa::core::topology::Topology::Quad > -{}; - -template<> -class vector_data_trait < sofa::core::topology::Topology::Tetrahedron > - : public vector_data_trait < sofa::type::fixed_array < sofa::core::topology::Topology::PointID, 4 > > -{ -}; - -template<> -class data_widget_container < sofa::core::topology::Topology::Tetrahedron > : public fixed_vector_data_widget_container < sofa::core::topology::Topology::Tetrahedron > -{}; - -template<> -class vector_data_trait < sofa::core::topology::Topology::Hexahedron > - : public vector_data_trait < sofa::type::fixed_array < sofa::core::topology::Topology::PointID, 8 > > -{ -}; - -template<> -class data_widget_container < sofa::core::topology::Topology::Hexahedron > : public fixed_vector_data_widget_container < sofa::core::topology::Topology::Hexahedron > -{}; - -//////////////////////////////////////////////////////////////// -/// sofa::defaulttype::Vec support -//////////////////////////////////////////////////////////////// - -template -class vector_data_trait < sofa::type::Vec > -{ -public: - typedef sofa::type::Vec data_type; - typedef T value_type; - typedef typename data_type::Size Size; - enum { NDIM = 1 }; - enum { SIZE = N }; - static Size size(const data_type&) { return SIZE; } - static const char* header(const data_type& /*d*/, Size /*i*/ = 0) - { - return nullptr; - } - static const value_type* get(const data_type& d, Size i = 0) - { - return (i < size(d)) ? &(d[i]) : nullptr; - } - static void set( const value_type& v, data_type& d, Size i = 0) - { - if (i < size(d)) - d[i] = v; - } - static void resize( Size /*s*/, data_type& /*d*/) - { - } -}; - -template<> -inline const char* vector_data_trait < sofa::type::Vec<2, float> >::header(const data_type& /*d*/, Size i) -{ - switch(i) - { - case 0: return "X"; - case 1: return "Y"; - } - return nullptr; -} - -template<> -inline const char* vector_data_trait < sofa::type::Vec<2, double> >::header(const data_type& /*d*/, Size i) -{ - switch(i) - { - case 0: return "X"; - case 1: return "Y"; - } - return nullptr; -} - -template<> -inline const char* vector_data_trait < sofa::type::Vec<3, float> >::header(const data_type& /*d*/, Size i) -{ - switch(i) - { - case 0: return "X"; - case 1: return "Y"; - case 2: return "Z"; - } - return nullptr; -} - -template<> -inline const char* vector_data_trait < sofa::type::Vec<3, double> >::header(const data_type& /*d*/, Size i) -{ - switch(i) - { - case 0: return "X"; - case 1: return "Y"; - case 2: return "Z"; - } - return nullptr; -} - -template -class data_widget_container < sofa::type::Vec > : public fixed_vector_data_widget_container < sofa::type::Vec > -{}; - -//////////////////////////////////////////////////////////////// -/// std::helper::Quater support -//////////////////////////////////////////////////////////////// - -template -class vector_data_trait < Quat > -{ -public: - typedef Quat data_type; - typedef T value_type; - enum { NDIM = 1 }; - enum { SIZE = 4 }; - static sofa::Size size(const data_type&) { return SIZE; } - static const char* header(const data_type& /*d*/, sofa::Index i = 0) - { - switch(i) - { - case 0: return "qX"; - case 1: return "qY"; - case 2: return "qZ"; - case 3: return "qW"; - } - return nullptr; - } - static const value_type* get(const data_type& d, sofa::Index i = 0) - { - return ((unsigned)i < (unsigned)size(d)) ? &(d[i]) : nullptr; - } - static void set( const value_type& v, data_type& d, sofa::Index i = 0) - { - if ((unsigned)i < (unsigned)size(d)) - d[i] = v; - } - static void resize( sofa::Size /*s*/, data_type& /*d*/) - { - } -}; - -template -class data_widget_container < Quat > : public fixed_vector_data_widget_container < Quat > -{}; - - -//////////////////////////////////////////////////////////////// -/// sofa::helper::Polynomial_LD support -//////////////////////////////////////////////////////////////// -using sofa::helper::Polynomial_LD; - -template -class data_widget_trait < Polynomial_LD > -{ -public: - typedef Polynomial_LD data_type; - typedef QLineEdit Widget; - static Widget* create(QWidget* parent, const data_type& ) - { - Widget* w = new Widget(parent); - return w; - } - static void readFromData(Widget* w, const data_type& d) - { - auto length = d.getString().length(); - if (w->text().toStdString() != d.getString()) - { - w->setMaxLength(length+2); w->setReadOnly(true); - w->setText(QString(d.getString().c_str())); - } - } - static void writeToData(Widget* , data_type& ) - { - } - static void setReadOnly(Widget* w, bool readOnly) - { - w->setEnabled(!readOnly); - w->setReadOnly(readOnly); - } - static void connectChanged(Widget* w, DataWidget* datawidget) - { - datawidget->connect(w, SIGNAL( textChanged(const QString&) ), datawidget, SLOT(setWidgetDirty()) ); - } -}; - - -#ifdef TODOLINK -//////////////////////////////////////////////////////////////// -/// sofa::core::objectmodel::ObjectRef -//////////////////////////////////////////////////////////////// - -using sofa::core::objectmodel::ObjectRef; - -template<> -class data_widget_trait < ObjectRef > -{ -public: - typedef ObjectRef data_type; - typedef QLineEdit Widget; - static Widget* create(QWidget* parent, const data_type& d) - { - Widget* w = new Widget(parent); - w->setText(QString(d.getPath().c_str())); - return w; - } - static void readFromData(Widget* w, const data_type& d) - { - std::ostringstream _outref; _outref<text().toStdString() != _outref.str()) - w->setText(QString(_outref.str().c_str())); - } - static void writeToData(Widget* w, data_type& d) - { - bool canwrite = d.setPath ( w->text().toStdString() ); - if(!canwrite) - msg_info()<<"cannot set Path "<text().toStdString()<setReadOnly(readOnly); - } - static void connectChanged(Widget* w, DataWidget* datawidget) - { - datawidget->connect(w, SIGNAL( textChanged(const QString&) ), datawidget, SLOT(setWidgetDirty()) ); - } -}; - -//////////////////////////////////////////////////////////////// -/// support sofa::core::objectmodel::VectorObjectRef; -//////////////////////////////////////////////////////////////// - -using sofa::core::objectmodel::VectorObjectRef; -template<> -class vector_data_trait < sofa::core::objectmodel::VectorObjectRef > -{ -public: - typedef sofa::core::objectmodel::VectorObjectRef data_type; - typedef sofa::core::objectmodel::ObjectRef value_type; - - static sofa::Size size(const data_type& d) { return d.size(); } - static const char* header(const data_type& , sofa::Index i = 0) - { - std::ostringstream _header; _header< -class vector_data_trait < sofa::type::Mat > -{ -public: - typedef sofa::type::Mat data_type; - typedef typename data_type::Line value_type; - enum { NDIM = 1 }; - enum { SIZE = L }; - static sofa::Size size(const data_type&) { return SIZE; } - static const char* header(const data_type& /*d*/, sofa::Index /*i*/ = 0) - { - return nullptr; - } - static const value_type* get(const data_type& d, sofa::Index i = 0) - { - return ((unsigned)i < (unsigned)size(d)) ? &(d[i]) : nullptr; - } - static void set( const value_type& v, data_type& d, sofa::Index i = 0) - { - if ((unsigned)i < (unsigned)size(d)) - d[i] = v; - } - static void resize( sofa::Size /*s*/, data_type& /*d*/) - { - } -}; - -template -class data_widget_container < sofa::type::Mat > : public fixed_grid_data_widget_container < sofa::type::Mat > -{}; - -//////////////////////////////////////////////////////////////// -/// sofa::linearalgebra::CompressedRowSparseMatrixConstraint support -//////////////////////////////////////////////////////////////// - -template -class data_widget_trait > -{ -public: - typedef sofa::linearalgebra::CompressedRowSparseMatrixConstraint data_type; - typedef QLineEdit Widget; - static Widget* create(QWidget* parent, const data_type& /*d*/) - { - Widget* w = new Widget(parent); - w->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); - return w; - } - static void readFromData(Widget* w, const data_type& d) - { - std::ostringstream oss; - d.prettyPrint(oss); - w->setText(QString(oss.str().c_str())); - } - static void writeToData(Widget* /* w */, const data_type& /* d */) - { - // not supported by this type - // TODO: CompressedRowSparseMatrixConstraint needs a parser for its pretty output - } - static void setReadOnly(Widget* w, bool readOnly) - { - w->setEnabled(!readOnly); - w->setReadOnly(readOnly); - } - static void connectChanged(Widget* w, DataWidget* datawidget) - { - datawidget->connect(w, SIGNAL( textChanged(const QString&) ), datawidget, SLOT(setWidgetDirty()) ); - } - -}; - -//////////////////////////////////////////////////////////////// -/// OptionsGroup support -//////////////////////////////////////////////////////////////// - - -class RadioDataWidget : public TDataWidget -{ - Q_OBJECT -public : - - ///The class constructor takes a TData since it creates - ///a widget for a that particular data type. - RadioDataWidget(QWidget* parent, const char* name, - core::objectmodel::Data* m_data) - : TDataWidget(parent,name,m_data) {} - - ///In this method we create the widgets and perform the signal / slots connections. - virtual bool createWidgets(); - virtual void setDataReadOnly(bool readOnly); - -protected: - ///Implements how update the widgets knowing the data value. - virtual void readFromData(); - - ///Implements how to update the data, knowing the widget value. - virtual void writeToData(); - - QButtonGroup *buttonList; - QComboBox *comboList; - bool buttonMode; -}; - -class SelectableItemWidget final : public TDataWidget -{ - Q_OBJECT -public : - - SelectableItemWidget(QWidget* parent, const char* name, - core::BaseData* m_data, const helper::BaseSelectableItem* item); - - bool createWidgets() override; - void setDataReadOnly(bool readOnly) override; - -protected: - void readFromData() override; - - void writeToData() override; - - QButtonGroup *m_buttonList { nullptr }; - QComboBox *m_comboList { nullptr }; - bool m_buttonMode { false }; - - const helper::BaseSelectableItem* m_selectableItem { nullptr }; -}; - - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/SofaMouseManager.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/SofaMouseManager.cpp deleted file mode 100644 index b6fb316bf2f..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/SofaMouseManager.cpp +++ /dev/null @@ -1,157 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#include "SofaMouseManager.h" -#include -#include "QMouseOperations.h" - -#include -#include - -#include - -using namespace sofa::gui::common; - -namespace sofa::gui::qt -{ -SofaMouseManager::SofaMouseManager(QWidget *parent) - : QDialog(parent) - , gui(new Ui_MouseManager) -{ - gui->setupUi(this); - - connect( gui->LeftOperationCombo, SIGNAL(activated(int)), this, SLOT( selectOperation(int) )); - connect( gui->MiddleOperationCombo, SIGNAL(activated(int)), this, SLOT( selectOperation(int) )); - connect( gui->RightOperationCombo, SIGNAL(activated(int)), this, SLOT( selectOperation(int) )); - - RegisterOperation("Attach").add< QAttachOperation >(); - RegisterOperation("AddFrame").add< AddFrameOperation >(); - RegisterOperation("SaveCameraViewPoint").add< QAddRecordedCameraOperation >(); - RegisterOperation("StartNavigation").add< QStartNavigationOperation >(); - RegisterOperation("Fix") .add< QFixOperation >(); - RegisterOperation("Incise").add< QInciseOperation >(); - RegisterOperation("Remove").add< QTopologyOperation >(); - RegisterOperation("Suture").add< QAddSutureOperation >(); - RegisterOperation("ConstraintAttach").add< ConstraintAttachOperation >(); -} - -SofaMouseManager::~SofaMouseManager() -{ -} - -void SofaMouseManager::updateContent() -{ - gui->LeftOperationCombo->clear(); - gui->MiddleOperationCombo->clear(); - gui->RightOperationCombo->clear(); - mapIndexOperation.clear(); - - if (mapIndexOperation.empty()) - { - const OperationFactory::RegisterStorage ®istry = OperationFactory::getInstance()->registry; - - int idx=0; - for (OperationFactory::RegisterStorage::const_iterator it=registry.begin(); it!=registry.end(); ++it) - { - gui->LeftOperationCombo ->addItem(QString(OperationFactory::GetDescription(it->first).c_str())); - gui->MiddleOperationCombo->addItem(QString(OperationFactory::GetDescription(it->first).c_str())); - gui->RightOperationCombo ->addItem(QString(OperationFactory::GetDescription(it->first).c_str())); - - if (OperationFactory::GetDescription(it->first) == OperationFactory::GetDescription(usedOperations[LEFT])) - gui->LeftOperationCombo->setCurrentIndex(idx); - if (OperationFactory::GetDescription(it->first) == OperationFactory::GetDescription(usedOperations[MIDDLE])) - gui->MiddleOperationCombo->setCurrentIndex(idx); - if (OperationFactory::GetDescription(it->first) == OperationFactory::GetDescription(usedOperations[RIGHT])) - gui->RightOperationCombo->setCurrentIndex(idx); - - mapIndexOperation.insert(std::make_pair(idx++, it->first)); - } - } -} - -void SofaMouseManager::setPickHandler(PickHandler *picker) -{ - pickHandler=picker; - updateContent(); - updateOperation(LEFT, "Attach"); - updateOperation(MIDDLE, "Incise"); - updateOperation(RIGHT, "Remove"); -} - - -void SofaMouseManager::selectOperation(int operation) -{ - const QComboBox *combo = (QComboBox*)(sender()); - const std::string operationName=mapIndexOperation[operation]; - - if (combo == gui->LeftOperationCombo) updateOperation(LEFT, operationName); - else if (combo == gui->MiddleOperationCombo) updateOperation(MIDDLE, operationName); - else if (combo == gui->RightOperationCombo) updateOperation(RIGHT, operationName); -} - -void SofaMouseManager::updateOperation( sofa::component::setting::MouseButtonSetting* setting) -{ - //By changing the operation, we delete the previous operation - Operation *operation=pickHandler->changeOperation( setting); - updateOperation(operation); -} - -void SofaMouseManager::updateOperation( MOUSE_BUTTON button, const std::string &id) -{ - //By changing the operation, we delete the previous operation - Operation *operation=pickHandler->changeOperation( button, id); - updateOperation(operation); -} - - -void SofaMouseManager::updateOperation( Operation* operation) -{ - if (!operation || operation->getMouseButton()==NONE ) return; - usedOperations[operation->getMouseButton()] = operation->getId(); - - QWidget* qoperation=dynamic_cast(operation); - if (!qoperation) return; - - switch(operation->getMouseButton()) - { - case LEFT: - { - gui->LeftButton->layout()->addWidget(qoperation); - break; - } - case MIDDLE: - { - gui->MiddleButton->layout()->addWidget(qoperation); - break; - } - case RIGHT: - { - gui->RightButton->layout()->addWidget(qoperation); - break; - } - default: - { - } - } -} - -} // namespace sofa::gui::qt - diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/SofaMouseManager.h b/Sofa/GUI/Qt/src/sofa/gui/qt/SofaMouseManager.h deleted file mode 100644 index 0a8ab1f278d..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/SofaMouseManager.h +++ /dev/null @@ -1,67 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include -#include -#include - -class Ui_MouseManager; - -namespace sofa::gui::qt -{ - - -class SofaMouseManager : public QDialog -{ - Q_OBJECT - std::unique_ptr gui; -public: - - SofaMouseManager(QWidget *parent); - ~SofaMouseManager() override; - - void updateContent(); - - void setPickHandler(common::PickHandler *); - - std::map< int, std::string >& getMapIndexOperation() - { - return mapIndexOperation; - } - - void updateOperation( sofa::component::setting::MouseButtonSetting* setting); - void updateOperation(common::MOUSE_BUTTON button, const std::string &id); - - -public slots: - void selectOperation(int); - -protected: - void updateOperation(common::Operation* op); - common::PickHandler *pickHandler; - std::map< int, std::string > mapIndexOperation; - - type::fixed_array< std::string, common::NONE > usedOperations; -}; - - -} // namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/SofaPluginManager.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/SofaPluginManager.cpp deleted file mode 100644 index 6d29032d0fa..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/SofaPluginManager.cpp +++ /dev/null @@ -1,336 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#include "SofaPluginManager.h" -#include "FileManagement.h" -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - - -#include -#include - -namespace sofa::gui::qt -{ - -#define LOCATION_COLUMN 3 - -SofaPluginManager::SofaPluginManager(QWidget *parent) - : QDialog(parent) -{ - setupUi(this); - // SIGNAL / SLOTS CONNECTIONS - this->connect(buttonAdd, SIGNAL(clicked() ), this, SLOT( addLibrary() )); - this->connect(buttonRemove, SIGNAL(clicked() ), this, SLOT( removeLibrary() )); - - this->connect(listPlugins, SIGNAL(itemSelectionChanged() ), this, SLOT(updateComponentList() )); - this->connect(listPlugins, SIGNAL(itemSelectionChanged() ), this, SLOT(updateDescription() )); - - listPlugins->setHeaderLabels(QStringList() << "Name" << "License" << "Version" << "Location"); - listComponents->setHeaderLabels(QStringList() << "Component list"); - - loadPluginsFromIniFile(); - updatePluginsListView(); -} - - - -void SofaPluginManager::updatePluginsListView() -{ - typedef sofa::helper::system::PluginManager::PluginMap PluginMap; - PluginMap& map = sofa::helper::system::PluginManager::getInstance().getPluginMap(); - typedef PluginMap::iterator PluginIterator; - listPlugins->clear(); - for( PluginIterator iter = map.begin(); iter != map.end(); ++iter ) - { - sofa::helper::system::Plugin& plugin = iter->second; - QString slicense = plugin.getModuleLicense(); - QString sname = plugin.getModuleName(); - QString sversion = plugin.getModuleVersion(); - QString sfile = (iter->first).c_str(); - - QTreeWidgetItem * item = new QTreeWidgetItem(listPlugins); - - if (std::find(m_loadedPlugins.begin(), m_loadedPlugins.end(), plugin.getModuleName()) != m_loadedPlugins.end() || - std::find(m_loadedPlugins.begin(), m_loadedPlugins.end(), iter->first) != m_loadedPlugins.end()) - { - for (unsigned int i = 0; i < 4; ++i) - { - item->setForeground(i, QColor::fromRgb(0, 0, 255)); - item->setToolTip(i, QString(std::string{"This plugin has been loaded by the GUI from the file " + m_pluginsIniFile}.c_str())); - } - } - - item->setText(0, sname); - item->setText(1, slicense); - item->setText(2, sversion); - item->setText(3, sfile); - //item->setSelected(true); - //listPlugins->addTopLevelItem(item); - } -} - -void SofaPluginManager::addLibrary() -{ - // compute the plugin directory path - QDir dir = QCoreApplication::applicationDirPath(); -#if defined (WIN32) - dir.cd("../bin"); -#else - dir.cd("../lib"); -#endif - QString pluginPath = dir.canonicalPath(); - //get the lib to load -#if defined (__APPLE__) - QString sfile = getOpenFileName ( this, pluginPath, "dynamic library (*.dylib*)", "load library dialog", "Choose the component library to load" ); -#elif defined (WIN32) - QString sfile = getOpenFileName ( this, pluginPath, "dynamic library (*.dll)", "load library dialog", "Choose the component library to load" ); -#else - QString sfile = getOpenFileName ( this, pluginPath, "dynamic library (*.so)", "load library dialog", "Choose the component library to load" ); -#endif - if(sfile=="") - return; -#ifndef _DEBUG - if(sfile.contains(QString("d.")) == true) - if(QMessageBox::question(this, "library loading warning","This plugin lib seems to be in debug mode whereas you are currently in release mode.\n Are you sure you want to load this lib?",QMessageBox::Yes,QMessageBox::No) != QMessageBox::Yes) - return; -#else - if(sfile.contains(QString("d.")) == false) - if(QMessageBox::question(this, "library loading warning","This plugin lib seems to be in release mode whereas you are currently in debug mode.\n Are you sure you want to load this lib?",QMessageBox::Yes,QMessageBox::No) != QMessageBox::Yes) - return; -#endif - std::stringstream sstream; - - const std::string pluginFile = std::string(sfile.toStdString()); - const auto status = sofa::helper::system::PluginManager::getInstance().loadPluginByPath(pluginFile,&sstream); - if(status == sofa::helper::system::PluginManager::PluginLoadStatus::SUCCESS) - { - typedef sofa::helper::system::Plugin Plugin; - if( ! sstream.str().empty()) - { - QMessageBox * mbox = new QMessageBox(this); - mbox->setWindowTitle("library loading warning"); - mbox->setIcon(QMessageBox::Warning); - mbox->setText(sstream.str().c_str()); - mbox->show(); - } - const Plugin* plugin = sofa::helper::system::PluginManager::getInstance().getPlugin(pluginFile); - if(!plugin) - { - // This should not happen as we are protected by if(loadPluginByPath(...)) - msg_error("SofaPluginManager") << "plugin should be loaded: " << pluginFile << msgendl; - return; - } - - if ( (plugin->moduleIsInitialized.func && plugin->moduleIsInitialized()) - || !plugin->moduleIsInitialized.func) - { - if (const char* moduleName = plugin->getModuleName()) - { - core::ObjectFactory::getInstance()->registerObjectsFromPlugin(moduleName); - } - } - - const QString slicense = plugin->getModuleLicense(); - const QString sname = plugin->getModuleName(); - const QString sversion = plugin->getModuleVersion(); - - //QTreeWidgetItem * item = new QTreeWidgetItem(listPlugins, sname, slicense, sversion, pluginFile.c_str()); - QTreeWidgetItem * item = new QTreeWidgetItem(listPlugins); - item->setText(0, sname); - item->setText(1, slicense); - item->setText(2, sversion); - item->setText(3, pluginFile.c_str()); - listPlugins->addTopLevelItem(item); - - //item->setSelectable(true); - savePluginsToIniFile(); - emit( libraryAdded() ); - } - else if (status == sofa::helper::system::PluginManager::PluginLoadStatus::ALREADY_LOADED) - { - if( !sstream.str().empty()) - { - QMessageBox * mbox = new QMessageBox(this); - mbox->setWindowTitle("library loading warning"); - mbox->setIcon(QMessageBox::Warning); - mbox->setText(sstream.str().c_str()); - mbox->show(); - } - savePluginsToIniFile(); - } - else - { - QMessageBox * mbox = new QMessageBox(this); - mbox->setWindowTitle("library loading error"); - mbox->setIcon(QMessageBox::Critical); - mbox->setText(sstream.str().c_str()); - mbox->show(); - } - - -} - - - -void SofaPluginManager::removeLibrary() -{ - //get the selected item - if(listPlugins->selectedItems().count() < 1) - return; - - const QTreeWidgetItem * curItem = listPlugins->selectedItems()[0]; - std::stringstream sstream; - if (!curItem) return; - - const std::string location( curItem->text(LOCATION_COLUMN).toStdString() ); //get the location value - - if( sofa::helper::system::PluginManager::getInstance().unloadPlugin(location,&sstream) ) - { - //listPlugins->removeItem(curItem); - delete curItem; - - savePluginsToIniFile(); - emit( libraryRemoved() ); - - if(this->listPlugins->selectedItems().count() < 1) - { - description->clear(); - listComponents->clear(); - } - } - else - { - std::string errlog; - sstream >> errlog; - QMessageBox * mbox = new QMessageBox(this); - mbox->setWindowTitle("library unloading error"); - mbox->setIcon(QMessageBox::Critical); - mbox->setText(errlog.c_str()); - mbox->show(); - } - -} - -void SofaPluginManager::updateComponentList() -{ - if(this->listPlugins->selectedItems().count() < 1) - return; - - const QTreeWidgetItem* curItem = this->listPlugins->selectedItems()[0]; - - if(curItem == nullptr ) return; - //update the component list when an item is selected - listComponents->clear(); - - const std::string location( curItem->text(LOCATION_COLUMN).toStdString() ); //get the location value - - typedef sofa::helper::system::Plugin Plugin; - const Plugin* plugin = sofa::helper::system::PluginManager::getInstance().getPlugin(location); - if(!plugin) - { - msg_warning("SofaPluginManager") << "plugin is not loaded: " << location << msgendl; - return; - } - - std::string componentListStr{}; - const char* tempComponentList = plugin->getModuleComponentList(); - - // the plugin does not implement getModuleComponentList(), or returns nothing. - if (tempComponentList == nullptr) - { - const char* pluginNameStr = plugin->getModuleName(); - componentListStr = sofa::core::ObjectFactory::getInstance()->listClassesFromTarget(pluginNameStr); - } - else - { - componentListStr = tempComponentList; - } - - QString cpts(componentListStr.data()); - cpts.replace(", ","\n"); - cpts.replace(",","\n"); - std::istringstream in(cpts.toStdString()); - - while (!in.eof()) - { - std::string componentText; - in >> componentText; - //QTreeWidgetItem *item=new QTreeWidgetItem(listComponents,curItem); - - QTreeWidgetItem * item = new QTreeWidgetItem(listComponents); - item->setText(0, componentText.c_str()); - } -} - - -void SofaPluginManager::updateDescription() -{ - if(this->listPlugins->selectedItems().count() < 1) - return; - - const QTreeWidgetItem* curItem = this->listPlugins->selectedItems()[0]; - - if(curItem == nullptr ) return; - //update the component list when an item is selected - description->clear(); - - const std::string location( curItem->text(LOCATION_COLUMN).toStdString() ); //get the location value - - typedef sofa::helper::system::Plugin Plugin; - const Plugin* plugin = sofa::helper::system::PluginManager::getInstance().getPlugin(location); - if(!plugin) - { - msg_warning("SofaPluginManager") << "plugin is not loaded: " << location << msgendl; - return; - } - description->setText(QString(plugin->getModuleDescription())); -} - -void SofaPluginManager::savePluginsToIniFile() -{ - m_pluginsIniFile = sofa::gui::common::BaseGUI::getConfigDirectoryPath() + "/loadedPlugins.ini"; - sofa::helper::system::PluginManager::getInstance().writeToIniFile(m_pluginsIniFile); -} - -void SofaPluginManager::loadPluginsFromIniFile() -{ - m_pluginsIniFile = sofa::gui::common::BaseGUI::getConfigDirectoryPath() + "/loadedPlugins.ini"; - msg_info("SofaPluginManager") << "Loading automatically plugin list in " << m_pluginsIniFile; - sofa::helper::system::PluginManager::getInstance().readFromIniFile(m_pluginsIniFile, m_loadedPlugins); - - for (const auto& loadedPlugin : m_loadedPlugins) - { - core::ObjectFactory::getInstance()->registerObjectsFromPlugin(loadedPlugin); - } -} - - -} // namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/SofaPluginManager.h b/Sofa/GUI/Qt/src/sofa/gui/qt/SofaPluginManager.h deleted file mode 100644 index 725df093277..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/SofaPluginManager.h +++ /dev/null @@ -1,65 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include -#include -#include - -#include - -#include - - -namespace sofa::gui::qt -{ - -class SOFA_GUI_QT_API SofaPluginManager: public QDialog, public Ui_PluginManager -{ - Q_OBJECT -public: - SofaPluginManager(QWidget *parent); - - -signals: - - void libraryAdded(); - void libraryRemoved(); - -public slots: - - void addLibrary(); - void removeLibrary(); - - void updateComponentList(); - void updateDescription(); -public: - void updatePluginsListView(); -private: - void savePluginsToIniFile(); - - std::string m_pluginsIniFile; ///< path to the saved/loaded list of plugins by SofaPluginManager - type::vector m_loadedPlugins; ///< list of plugins loaded by SofaPluginManager - void loadPluginsFromIniFile(); -}; - - -} // namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/SofaSceneGraphWidget.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/SofaSceneGraphWidget.cpp deleted file mode 100644 index 788ce512f4c..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/SofaSceneGraphWidget.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#include "SofaSceneGraphWidget.h" - -namespace sofa::gui::qt -{ - -void SofaSceneGraphWidget::setViewToDirty() -{ - if(!m_isLocked) - return; - - if(m_isDirty) - return; - - m_isDirty = true; - emit dirtynessChanged(m_isDirty); -} - -bool SofaSceneGraphWidget::isDirty() -{ - return m_isDirty; -} - -bool SofaSceneGraphWidget::isLocked() -{ - return m_isLocked; -} - -void SofaSceneGraphWidget::lock() -{ - if(m_isLocked) - return; - - m_isLocked = true; - emit lockingChanged(m_isLocked); -} - -void SofaSceneGraphWidget::unLock() -{ - if(!m_isLocked) - return; - - m_isLocked = false; - - if(m_isDirty) - update(); - - emit lockingChanged(m_isLocked); -} - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/SofaSceneGraphWidget.h b/Sofa/GUI/Qt/src/sofa/gui/qt/SofaSceneGraphWidget.h deleted file mode 100644 index a5b45e62e91..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/SofaSceneGraphWidget.h +++ /dev/null @@ -1,118 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include - -#include -#include -#include -#include -#include - - -#include -#include -#include -#include - -#include - -namespace sofa::gui::qt -{ - -class AddObject; -class GraphListenerQListView; -class QDisplayPropertyWidget; - -enum ObjectModelType { typeNode, typeObject, typeData }; -typedef union ObjectModelPtr -{ - sofa::simulation::Node* Node; - core::objectmodel::BaseObject* Object; - core::objectmodel::BaseData* Data; -} ObjectModelPtr; - -typedef struct ObjectModel -{ -public: - ObjectModelType type; - ObjectModelPtr ptr; - bool isNode() { return type == typeNode; } - bool isObject() { return type == typeObject; } - bool isData() { return type == typeData; } - bool isBase() { return isNode() || isObject(); } - sofa::core::objectmodel::Base* asBase() - { - if( isNode() ) - return sofa::core::castToBase(ptr.Node); - if( isObject() ) - return dynamic_cast(ptr.Object); - return nullptr; - } -} ObjectModel; - -enum SofaListViewAttribute -{ - SIMULATION, - VISUAL, - MODELER -}; - -class SOFA_GUI_QT_API SofaSceneGraphWidget : public QTreeWidget -{ - Q_OBJECT -public: - SofaSceneGraphWidget(QWidget* parent) : QTreeWidget(parent){} - ~SofaSceneGraphWidget(){} - - void lock(); - void unLock(); - - /// Returns true if the view is not synchronized anymore with the simulation graph. - /// To re-syncronize the view you can: - /// - call unfreeze() so any future change will be reflected - /// - call update(), to update one time the graph. - bool isDirty(); - - /// Returns true if the view updates for any scene graph change is disable. - bool isLocked(); - - /// call this method to indicate that the internal model has changed - /// and thus the view is now dirty. - void setViewToDirty(); - -Q_SIGNALS: - /// Connect to this signal to be notified when the dirtiness status of the QSofaListView changed. - void dirtynessChanged(bool isDirty); - - /// Connect to this signal to be notified when the locking status changed - void lockingChanged(bool isLocked); - -protected: - /// Indicate that the view is de-synchronized with the real content of the simulation graph. - /// This can happen if the graph has been freezed (i.e. not graphically updated) for performance - /// reason while simulating complex scenes. - bool m_isDirty; - bool m_isLocked; -}; - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/SofaVideoRecorderManager.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/SofaVideoRecorderManager.cpp deleted file mode 100644 index cd20a7f1652..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/SofaVideoRecorderManager.cpp +++ /dev/null @@ -1,167 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#include "SofaVideoRecorderManager.h" -#include - -namespace sofa::gui::qt -{ - -CaptureOptionsWidget::CaptureOptionsWidget( QWidget * parent) - : QWidget(parent) -{ - - QVBoxLayout *layout=new QVBoxLayout(this); - - QHBoxLayout *HLayoutFramerate = new QHBoxLayout(); - QLabel *labelFramerate=new QLabel(QString("Framerate (in img/s): "), this); - framerateSpinBox = new QSpinBox(this); - framerateSpinBox->setMinimum(1); - framerateSpinBox->setMaximum(120); - framerateSpinBox->setValue(60); - HLayoutFramerate->addWidget (labelFramerate); - HLayoutFramerate->addWidget (framerateSpinBox); - - realtimeCheckBox = new QCheckBox(QString("Real-Time recording"), this); - - QHBoxLayout *HLayoutFrameskip = new QHBoxLayout(); - QLabel *labelFrameskip=new QLabel(QString("Skip frames before capture (fast replay): "), this); - frameskipSpinBox = new QSpinBox(this); - frameskipSpinBox->setMinimum(0); - frameskipSpinBox->setMaximum(100); - frameskipSpinBox->setValue(0); - HLayoutFrameskip->addWidget (labelFrameskip); - HLayoutFrameskip->addWidget (frameskipSpinBox); - - layout->addLayout(HLayoutFramerate); - layout->addWidget(realtimeCheckBox); - layout->addLayout(HLayoutFrameskip); - - //this->addLayout(layout); -} - -MovieOptionsWidget::MovieOptionsWidget( QWidget * parent) - : QWidget(parent) -{ - //Build codec list - listCodecs.push_back(Codec("mp4", "yuv420p", "Video: h264 (Windows Media Player, QuickTime and compatible with most other players) ")); - listCodecs.push_back(Codec("mp4", "yuv444p", "Video: h264 (VLC media player) ")); - - QVBoxLayout *layout=new QVBoxLayout(this); - - QHBoxLayout *HLayoutCodec = new QHBoxLayout(); - QLabel *labelCodec=new QLabel(QString("Codec: "), this); - codecComboBox = new QComboBox(this); - for(unsigned int i=0; iaddItem(QString(listCodecs[i].description.c_str())); - codecComboBox->setCurrentIndex(0); - HLayoutCodec->addWidget (labelCodec); - HLayoutCodec->addWidget (codecComboBox); - - QHBoxLayout *HLayoutBitrate = new QHBoxLayout(); - QLabel *labelBitrate=new QLabel(QString("Bitrate (in KB/s): "), this); - bitrateSpinBox = new QSpinBox(this); - bitrateSpinBox->setMinimum(100); - bitrateSpinBox->setMaximum(40960); - bitrateSpinBox->setValue(5000); - HLayoutBitrate->addWidget (labelBitrate); - HLayoutBitrate->addWidget (bitrateSpinBox); - -// labelBitrate->setVisible(false); -// bitrateSpinBox->setVisible(false); - - layout->addLayout(HLayoutCodec); - layout->addLayout(HLayoutBitrate); - - //this->addLayout(layout); -} - -SofaVideoRecorderManager::SofaVideoRecorderManager(QWidget *parent) - : QDialog(parent) -{ - setupUi(this); - captureOptionsWidget = new CaptureOptionsWidget(this); - movieOptionsWidget = new MovieOptionsWidget(this); - - internalAddWidget(VideoRecorderOptionGroupBox, captureOptionsWidget); - internalAddWidget(VideoRecorderOptionGroupBox, movieOptionsWidget); - - MovieRecordingTypeRadioButton->setChecked(true); - onChangeRecordingType(); -} - - -std::string SofaVideoRecorderManager::getCodecExtension() -{ - const unsigned int index = movieOptionsWidget->codecComboBox->currentIndex(); - return movieOptionsWidget->listCodecs[index].extension; -} - -std::string SofaVideoRecorderManager::getCodecName() -{ - const unsigned int index = movieOptionsWidget->codecComboBox->currentIndex(); - return movieOptionsWidget->listCodecs[index].codec; -} - -unsigned int SofaVideoRecorderManager::getFramerate() -{ - return captureOptionsWidget->framerateSpinBox->value(); -} - -unsigned int SofaVideoRecorderManager::getBitrate() -{ - return movieOptionsWidget->bitrateSpinBox->value()*1024; -} - -bool SofaVideoRecorderManager::realtime() -{ - return captureOptionsWidget->realtimeCheckBox->isChecked(); -} - -unsigned int SofaVideoRecorderManager::getFrameskip() -{ - return captureOptionsWidget->frameskipSpinBox->value(); -} - - -void SofaVideoRecorderManager::updateContent() -{ - movieOptionsWidget->setHidden(currentRecordingType != MOVIE); -} - -void SofaVideoRecorderManager::onChangeRecordingType() -{ - currentRecordingType = (MovieRecordingTypeRadioButton->isChecked()) ? MOVIE : SCREENSHOTS; - - updateContent(); -} - -void SofaVideoRecorderManager::internalAddWidget(QWidget* parent, QWidget* widgetToAdd) -{ - parent->layout()->addWidget(widgetToAdd); -} - -void SofaVideoRecorderManager::close() -{ - this->hide(); -} - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/SofaVideoRecorderManager.h b/Sofa/GUI/Qt/src/sofa/gui/qt/SofaVideoRecorderManager.h deleted file mode 100644 index 325da2331e9..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/SofaVideoRecorderManager.h +++ /dev/null @@ -1,110 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ - -#pragma once -#include -#include - -#include - -#include -#include -#include -#include - -namespace sofa::gui::qt -{ - -class CaptureOptionsWidget : public QWidget -{ - Q_OBJECT -public: - - CaptureOptionsWidget( QWidget * parent = nullptr); - - QSpinBox* framerateSpinBox; - QCheckBox* realtimeCheckBox; - QSpinBox* frameskipSpinBox; -}; - -class MovieOptionsWidget : public QWidget -{ - Q_OBJECT -public: - //Codec = - struct Codec - { - std::string extension; - std::string codec; - std::string description; - Codec(std::string e, std::string c, std::string d) : extension(e), codec(c), description(d) {} - Codec(std::string e, std::string d) : extension(e), codec(), description(d) {} - }; - - - MovieOptionsWidget( QWidget * parent = nullptr); - - QComboBox* codecComboBox; - QSpinBox* bitrateSpinBox; - - std::vector< Codec > listCodecs; -}; - -class SofaVideoRecorderManager: public QDialog, public Ui_VideoRecorderManager -{ - Q_OBJECT -public: - enum RecordingType { SCREENSHOTS, MOVIE }; - - SofaVideoRecorderManager(QWidget *parent = Q_NULLPTR); - - static SofaVideoRecorderManager* getInstance() - { - static SofaVideoRecorderManager instance; - return &instance; - } - - void updateContent(); - std::string getCodecExtension(); - std::string getCodecName(); - unsigned int getFramerate(); - unsigned int getBitrate(); - bool realtime(); - unsigned int getFrameskip(); - RecordingType getRecordingType() { return currentRecordingType; } - - //helper function - static void internalAddWidget(QWidget* parent, QWidget* widgetToAdd); - -public slots: - virtual void onChangeRecordingType(); - virtual void close(); - -protected: - RecordingType currentRecordingType; - - CaptureOptionsWidget* captureOptionsWidget; - MovieOptionsWidget* movieOptionsWidget; - QWidget* screenshotsOptionsWidget; -}; - -} // namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/SofaWindowDataGraph.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/SofaWindowDataGraph.cpp deleted file mode 100644 index 2b27ad91beb..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/SofaWindowDataGraph.cpp +++ /dev/null @@ -1,270 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#include "SofaWindowDataGraph.h" -#include "dataGraph/SofaComponentNodeModel.h" - -#include - -#include -#include -#include -#include -#include - -#include -#include - - -namespace sofa::gui::qt -{ -using namespace sofa::helper; - - -using QtNodes::DataModelRegistry; -using QtNodes::FlowScene; -using QtNodes::FlowView; -using QtNodes::ConnectionStyle; - -static std::shared_ptr registerDataModels() -{ - auto ret = std::make_shared(); - - std::vector results; - sofa::core::ObjectFactory::getInstance()->getAllEntries(results); - - ret->registerModel(); - //for (auto compo : results) - //{ - // std::cout << compo->className << std::endl; - // ret->registerModel(QString::fromStdString(compo->className)); - //} - - - /* - We could have more models registered. - All of them become items in the context meny of the scene. - - ret->registerModel(); - ret->registerModel(); - - */ - - return ret; -} - - -static -void -setConnecStyle() -{ - ConnectionStyle::setConnectionStyle( - R"( - { - "ConnectionStyle": { - "LineWidth": 3.0, - "UseDataDefinedColors": true - } - } - )"); -} - - - -///////////////////////////////////////// ProfilerChartView /////////////////////////////////// - -SofaWindowDataGraph::SofaWindowDataGraph(QWidget *parent, sofa::simulation::Node* scene) - : QDialog(parent) - , m_rootNode(scene) - , m_scaleX(10) - , m_scaleY(30) - , m_posX(0) - , m_posY(0) - , debugNodeGraph(false) -{ - setConnecStyle(); - Qt::WindowFlags flags = windowFlags(); - flags |= Qt::WindowMaximizeButtonHint; - flags |= Qt::WindowContextHelpButtonHint; - setWindowFlags(flags); - - m_graphScene = new FlowScene(registerDataModels()); - - m_exceptions = { "RequiredPlugin", "VisualStyle", "DefaultVisualManagerLoop", "InteractiveCamera" }; - - QVBoxLayout* layout = new QVBoxLayout(this); - m_graphView = new FlowView(m_graphScene, this); - layout->addWidget(m_graphView); - this->setLayout(layout); - - resize(1000, 800); - - // start from parsing root node - parseSimulationNode(m_rootNode); - - // then connect all data - connectNodeData(); -} - - -SofaWindowDataGraph::~SofaWindowDataGraph() -{ - std::cout << "SofaWindowDataGraph::~SofaWindowDataGraph()" << std::endl; - clearNodeData(); - // todo check if m_graphView need to be deleted. Normally no as child of QtWidget RealGui. - //delete m_graphView; -} - -void SofaWindowDataGraph::clearNodeData() -{ - if (m_graphScene != nullptr) - { - msg_info_when(debugNodeGraph, "SofaWindowDataGraph") << "clear before: " << m_graphScene->allNodes().size(); - //m_graphScene->clear(); - delete m_graphScene; - m_graphScene = new FlowScene(registerDataModels()); - m_graphView->setScene(m_graphScene); - - msg_info_when(debugNodeGraph, "SofaWindowDataGraph") << "clear after: " << m_graphScene->allNodes().size(); - } - m_posX = 0; - m_posY = 0; -} - -void SofaWindowDataGraph::resetNodeGraph(sofa::simulation::Node* scene) -{ - m_rootNode = scene; - clearNodeData(); - - // start from parsing root node - parseSimulationNode(m_rootNode); - - // then connect all data - connectNodeData(); -} - - -void SofaWindowDataGraph::parseSimulationNode(sofa::simulation::Node* node, int posX) -{ - msg_info_when(debugNodeGraph, "SofaWindowDataGraph") << m_posY << " ### Child Name: " << node->getName(); - // first parse the list BaseObject inside this node - std::vector bObjects = node->getNodeObjects(); - m_posX = posX; - int maxData = 0; - for (auto bObject : bObjects) - { - bool skip = false; - for (auto except : m_exceptions) - { - if (except == bObject->getClassName()) - { - msg_info_when(debugNodeGraph, "SofaWindowDataGraph") << "skip: " << except; - skip = true; - break; - } - } - - if (skip) - continue; - - size_t nbrData = addSimulationObject(bObject); - if (nbrData > maxData) - maxData = nbrData; - - // space between cells - m_posX += 14 * m_scaleX; - } - - if (bObjects.size() >= 4) { - m_posY += (maxData + 10) * m_scaleY; - m_posX = posX + 30 * m_scaleX; - } - - // second move to child nodes - for (auto simuNode : node->getChildren()) - { - parseSimulationNode(dynamic_cast(simuNode), m_posX); - } -} - - -size_t SofaWindowDataGraph::addSimulationObject(sofa::core::objectmodel::BaseObject* bObject) -{ - const std::string& name = bObject->getClassName() + " - " + bObject->getName(); - msg_info_when(debugNodeGraph, "SofaWindowDataGraph") << "addSimulationObject: " << name; - - QtNodes::Node& fromNode = m_graphScene->createNode(std::make_unique(bObject, debugNodeGraph)); - fromNode.setObjectName(QString::fromStdString(bObject->getName())); - - SofaComponentNodeModel* model = dynamic_cast(fromNode.nodeDataModel()); - model->setCaption(name); - - auto& fromNgo = fromNode.nodeGraphicsObject(); - fromNgo.setPos(m_posX, m_posY); - m_posX += name.length() * m_scaleX; - - return model->getNbrData(); -} - - -void SofaWindowDataGraph::connectNodeData() -{ - std::vector nodes = m_graphScene->allNodes(); - - for (auto node : nodes) - { - // get connections - SofaComponentNodeModel* childNode = dynamic_cast(node->nodeDataModel()); - - if (childNode->getNbrConnections() == 0) - continue; - - const std::map >& connections = childNode->getDataConnections(); - - for (auto connection : connections) - { - bool parentFound = false; - for (auto pNode : nodes) - { - QString pObjName = pNode->objectName(); - if (pObjName.compare(connection.second.first) == 0) - { - parentFound = true; - SofaComponentNodeModel* parentNode = dynamic_cast(pNode->nodeDataModel()); - QtNodes::PortIndex parentId = parentNode->getDataInputId(connection.second.second); - QtNodes::PortIndex childId = childNode->getDataInputId(connection.first); - - m_graphScene->createConnection(*node, childId, *pNode, parentId); - - break; - } - } - - if (!parentFound) - { - msg_error("SofaWindowDataGraph") << "Object not found while creating connection between " << node->objectName().toStdString() << " and child: " << connection.second.first.toStdString(); - continue; - } - } - } -} - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/SofaWindowDataGraph.h b/Sofa/GUI/Qt/src/sofa/gui/qt/SofaWindowDataGraph.h deleted file mode 100644 index 4c901242fc6..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/SofaWindowDataGraph.h +++ /dev/null @@ -1,87 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include -#include - -#define NODE_EDITOR_SHARED - -namespace QtNodes -{ - class FlowScene; - class FlowView; -} - - -namespace sofa::gui::qt -{ - /** - * This Class provide an interface with the library QtNodes to display Data Graph connection inside a QDialog. - * It will take a SOFA simulation scene and create Graph nodes for each Sofa component and display connections between Data. - */ -class SofaWindowDataGraph : public QDialog -{ - Q_OBJECT -public: - /// Default constructor of the Widget, given a QWidget as parent and a pointer to the current simulation scene. - SofaWindowDataGraph(QWidget *parent, sofa::simulation::Node* scene); - - ~SofaWindowDataGraph(); - - /// Method to be called when graph need to be recomputed (like reloading scene). Take a pointer to the root node of the scene. - void resetNodeGraph(sofa::simulation::Node* scene); - -protected: - /// Internal method to parse all Sofa component inside a Sofa simulation Node. Will call @sa addSimulationObject for each compoenent then will iterate on children nodes. - void parseSimulationNode(sofa::simulation::Node* node, int posX = 0); - - /// Internal method to create a Node for this sofa BaseObject. - size_t addSimulationObject(sofa::core::objectmodel::BaseObject* bObject); - - /// Internal method to create all connection between component on the graph. - void connectNodeData(); - - /// Internal method to clear the graph structures - void clearNodeData(); - -protected: - /// Pointer to the graphScene used to store nodes. - QtNodes::FlowScene* m_graphScene; - /// Pointer to the view of the graph. - QtNodes::FlowView* m_graphView; - - /// Point to the root node of the current simulation. - sofa::simulation::Node* m_rootNode; - - /// List of component name not to be display in the graph. - std::vector m_exceptions; - - SReal m_scaleX; ///< Scale parameter to apply between nodes for display in abscissa. - SReal m_scaleY; ///< Scale parameter to apply between nodes for display in ordinate. - - int m_posX; ///< Increment position counter on abscissa for Node display. - int m_posY; ///< Increment position counter on ordinate for Node display. - - bool debugNodeGraph; ///< parameter to activate graph logs. False by default. -}; - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/SofaWindowProfiler.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/SofaWindowProfiler.cpp deleted file mode 100644 index 40b3e2269b2..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/SofaWindowProfiler.cpp +++ /dev/null @@ -1,763 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#include "SofaWindowProfiler.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace sofa::gui::qt -{ -using namespace sofa::helper; - -typedef sofa::helper::system::thread::ctime_t ctime_t; -typedef sofa::helper::system::thread::CTime CTime; - - -///////////////////////////////////////// ProfilerChartView /////////////////////////////////// - -ProfilerChartView::ProfilerChartView(QChart *chart, QWidget *parent, int bufferSize) - : QChartView(chart, parent) - , m_bufferSize(bufferSize) - , m_pointSelected(-1) - , m_maxY(1000) -{ - -} - -void ProfilerChartView::mousePressEvent(QMouseEvent *event) -{ - auto const valueInSeries = chart()->mapToValue(event->localPos()); - - const int width = valueInSeries.x(); - - if (width >= 0 && width < m_bufferSize) - { - m_pointSelected = width; - updateSelection(m_pointSelected); - emit pointSelected(m_pointSelected); - } - else - m_pointSelected = -1; -} - -void ProfilerChartView::updateSelection(int x) -{ - m_pointSelected = x; - m_lineSelect = chart()->mapToPosition(QPointF(x, m_maxY)); - m_lineOrigin = chart()->mapToPosition(QPointF(x, 0)); - - this->scene()->update(this->sceneRect()); -} - - -void ProfilerChartView::drawForeground(QPainter *painter, const QRectF &) -{ - if (m_pointSelected == -1) - return; - - painter->drawLine(m_lineOrigin, m_lineSelect); -} - - - -// quick method to convert freq time into ms -SReal convertInMs(ctime_t t, int nbIter=1) -{ - static SReal timer_freqd = SReal(CTime::getTicksPerSec()); - return 1000.0 * SReal(t) / SReal (timer_freqd * nbIter); -} - -///////////////////////////////////////// AnimationSubStepData /////////////////////////////////// - -SofaWindowProfiler::AnimationSubStepData::AnimationSubStepData(int level, std::string name, ctime_t start) - : m_level(level) - , m_name(std::move(name)) - , m_nbrCall(1) - , m_start(start) -{ - -} - -SofaWindowProfiler::AnimationSubStepData::~AnimationSubStepData() -{ - for (unsigned int i=0; icomputeTimeAndPercentage(invTotalMs); - totalChildrenMs += m_children[i]->m_totalMs; - } - - // now that all children are update, compute ms and % - m_totalMs = convertInMs(m_end - m_start); - m_selfMs = m_totalMs - totalChildrenMs; - - m_selfPercent = m_selfMs * invTotalMs; - m_totalPercent = m_totalMs * invTotalMs; - } - else // leaf - { - // compute ms: - m_totalMs = convertInMs(m_end - m_start); - if (m_nbrCall != 1) - m_selfMs = SReal(m_totalMs / m_nbrCall); - else - m_selfMs = m_totalMs; - - // compute % - m_selfPercent = m_selfMs * invTotalMs; - m_totalPercent = m_totalMs * invTotalMs; - } -} - -SReal SofaWindowProfiler::AnimationSubStepData::getStepMs(const std::string& stepName, const std::string& parentName) -{ - SReal result = 0.0; - if (parentName == m_name) - { - for (unsigned int i=0; im_name == stepName) - return m_children[i]->m_totalMs; - } - } - else - { - for (unsigned int i=0; igetStepMs(stepName, parentName); - if (result != 0.0) - return result; - } - } - - return 0.0; -} - - - -///////////////////////////////////////// AnimationStepData /////////////////////////////////// - -SofaWindowProfiler::AnimationStepData::AnimationStepData(int step, const std::string& idString) - : m_stepIteration(step) - , m_totalMs(0.0) - , m_idString(idString) - , m_overheadMs(0.) -{ - m_subSteps.clear(); - - const bool res = processData(idString); - if (!res) // error clear data - { - for (unsigned int i=0; i _records = sofa::helper::AdvancedTimer::getRecords(idString); - - m_totalTimers = 0; - - //AnimationSubStepData* currentSubStep = nullptr; - std::stack processStack; - int level = 0; - ctime_t t0 = 0; - const ctime_t tEnd = CTime::getTime(); - ctime_t tCurr; - for (unsigned int ri = 0; ri < _records.size(); ++ri) - { - const Record& rec = _records[ri]; - - if (level == 0) // main step - { - t0 = rec.time; - level++; - continue; - } - - tCurr = rec.time - t0; - - if (rec.type == Record::RBEGIN || rec.type == Record::RSTEP_BEGIN || rec.type == Record::RSTEP) - { -// for (int i=0; im_tag = std::string(AdvancedTimer::IdObj(rec.obj)); - - if (level == 1) // Add top level step - m_subSteps.push_back(currentSubStep); - else - { - if (processStack.empty()) - { - msg_error("SofaWindowProfiler") << "No parent found to add step: " << currentSubStep->m_name; - delete currentSubStep; - return false; - } - else if (processStack.top()->m_level + 1 != currentSubStep->m_level) - { - msg_warning("SofaWindowProfiler") << "Problem of level coherence between step: " << currentSubStep->m_name << " with level: " << currentSubStep->m_level - << " and parent step: " << processStack.top()->m_name << " with level: " << processStack.top()->m_level; - } - - // add next step to the hierarchy - processStack.top()->m_children.push_back(currentSubStep); - } - - // add step into the stack for parent/child order - processStack.push(currentSubStep); - ++level; - } - - if (rec.type == Record::REND || rec.type == Record::RSTEP_END) - { - ++m_totalTimers; - --level; -// for (int i=0; im_name) - { - msg_error("SofaWindowProfiler") << "Not the same name to end step between logs: " << rec.label << " and top stack: " << processStack.top()->m_name; - return false; - } - - AnimationSubStepData* currentSubStep = processStack.top(); - processStack.pop(); - - currentSubStep->m_end = tCurr; - } - } - // compute total MS step: - m_totalMs = convertInMs(tEnd - t0); - - // update percentage - const SReal invTotalMs = 100. / m_totalMs; - SReal totalChildrenMs = 0.0; - for (unsigned int i=0; icomputeTimeAndPercentage(invTotalMs); - totalChildrenMs += m_subSteps[i]->m_totalMs; - } - - m_selfMs = m_totalMs - totalChildrenMs; - m_selfPercent = 100. * m_selfMs / m_totalMs; - - return true; -} - - -SReal SofaWindowProfiler::AnimationStepData::getStepMs(const std::string& stepName, const std::string& parentName) -{ - SReal result = 0.0; - if (parentName.empty()) - { - for (unsigned int i=0; im_name == stepName) - return m_subSteps[i]->m_totalMs; - } - } - else - { - for (unsigned int i=0; igetStepMs(stepName, parentName); - if (result != 0.0) - return result; - } - } - - return 0.0; -} - - -SofaWindowProfiler::AnimationStepData::~AnimationStepData() -{ - for (unsigned int i=0; isetRange(0, m_bufferSize-1); - step_scroller->setMinimumWidth(200); - step_scroller->setMaximumWidth(200); - connect(step_scroller, SIGNAL(valueChanged(int)), this, SLOT(updateSummaryLabels(int))); - connect(step_scroller, SIGNAL(valueChanged(int)), this, SLOT(updateTree(int))); - - connect(step_scroller, SIGNAL(valueChanged(int)), m_chartView, SLOT(updateSelection(int))); - connect(m_chartView, SIGNAL(pointSelected(int)), this, SLOT(updateFromSelectedStep(int))); - - ExpandAllButton->setIcon(QIcon(":/RealGUI/expandAll")); - CollapseAllButton->setIcon(QIcon(":/RealGUI/collapseAll")); - for (auto* button : {ExpandAllButton, CollapseAllButton}) - { - button->setFixedWidth(button->height()); - } - - connect ( ExpandAllButton, SIGNAL ( clicked() ), tree_steps, SLOT ( expandAll() ) ); - connect ( CollapseAllButton, SIGNAL ( clicked() ), this, SLOT ( expandRootNodeOnly() ) ); -} - - -void SofaWindowProfiler::activateATimer(bool activate) -{ - sofa::helper::AdvancedTimer::setEnabled("Animate", activate); - sofa::helper::AdvancedTimer::setInterval("Animate", 1); - sofa::helper::AdvancedTimer::setOutputType("Animate", "gui"); -} - - -void SofaWindowProfiler::pushStepData() -{ - const ctime_t start = CTime::getRefTime(); - - m_profilingData.pop_front(); - const static std::string idString = "Animate"; - m_profilingData.push_back(new AnimationStepData(m_step, idString)); - m_step++; - - updateChart(); - - m_profilingData.back()->m_overheadMs = convertInMs(CTime::getRefTime() - start); -} - - -void SofaWindowProfiler::resetGraph() -{ - if (m_step == 0) - return; - - for(unsigned int i=0; ireplace(i, 0.0, 0.0); - m_selectionSeries->replace(i, 0.0, 0.0); - m_selectionSeries->setName("Selected SubStep"); - if (m_profilingData[i] && m_profilingData[i]->m_stepIteration != -1) - { - delete m_profilingData[i]; - m_profilingData[i] = new AnimationStepData(); - } - } - - step_scroller->setValue(1); // for rest by changing 2 times value - step_scroller->setValue(0); - m_step = 0; - m_selectedStep = ""; - m_selectedParentStep = ""; -} - - -void SofaWindowProfiler::createTreeView() -{ - //list of the columns description - //- first: column names - //- second: tooltip (description of the column) - const std::vector< std::pair< QString, QString > > columnsLabels = { - {"Hierarchy Step Name", "Label of the measured step"}, - {"Total (%)", "Percentage of duration of this step compared to the duration of the root step"}, - {"Self (%)", "- If the step has child steps: percentage of the duration " - "of this step minus the sum of durations of its children, compared to " - "the duration of the root step.\n" - "- If the step has no child step: percentage of the average duration " - "of this step in case of multiple calls of this step during this time step, " - "compared to the duration of the root step."}, - {"Time (ms)", "Duration in milliseconds of this step"}, - {"Self (ms)", "- If the step has child steps: duration in milliseconds of " - "this step minus the sum of durations of its children.\n" - "- If the step has no child step: average duration in milliseconds of " - "this step in case of multiple calls of this step during this time step."} - }; - - // set column names - QStringList columnNames; - - for (std::size_t i = 0; i < columnsLabels.size(); ++i) - { - columnNames << columnsLabels[i].first; - tree_steps->headerItem()->setToolTip(i, columnsLabels[i].second); - } - tree_steps->setHeaderLabels(columnNames); - - tree_steps->headerItem()->setToolTip(1, QString("Percentage of duration of this step compared to the duration of the root step")); - tree_steps->headerItem()->setToolTip(2, - QString("- If the step has child steps: percentage of the duration " - "of this step minus the sum of durations of its children, compared to " - "the duration of the root step.\n" - "- If the step has no child step: percentage of the average duration " - "of this step in case of multiple calls of this step during this time step, " - "compared to the duration of the root step.")); - tree_steps->headerItem()->setToolTip(3, QString("Duration in milliseconds of this step")); - tree_steps->headerItem()->setToolTip(4, - QString("- If the step has child steps: duration in milliseconds of " - "this step minus the sum of durations of its children.\n" - "- If the step has no child step: average duration in milliseconds of " - "this step in case of multiple calls of this step during this time step.")); - - // set column properties - tree_steps->header()->setStretchLastSection(false); - tree_steps->header()->setSectionResizeMode(0, QHeaderView::Stretch); - - connect(tree_steps, SIGNAL(itemClicked(QTreeWidgetItem*,int)), this, SLOT(onStepSelected(QTreeWidgetItem*,int))); -} - - -void SofaWindowProfiler::createChart() -{ - m_series = new QLineSeries(); - QPen pen = m_series->pen(); - pen.setColor(Qt::red); - pen.setWidth(2); - m_series->setName("Full Animation Step"); - m_series->setPen(pen); - - m_selectionSeries = new QLineSeries(); - m_selectionSeries->setName("Selected SubStep"); - - for(unsigned int i=0; iappend(i, 0.0f); - m_selectionSeries->append(i, 0.0f); - } - - m_chart = new QChart(); - m_chart->legend()->setShowToolTips(true); - m_chart->addSeries(m_series); - m_chart->addSeries(m_selectionSeries); - m_axisY = new QValueAxis(); - m_chart->addAxis(m_axisY, Qt::AlignLeft); - m_series->attachAxis(m_axisY); - m_selectionSeries->attachAxis(m_axisY); - - m_chart->setTitle("Steps durations (in ms)"); - m_axisY->setRange(0, 1000); - - m_chartView = new ProfilerChartView(m_chart, this, m_bufferSize); - m_chartView->setRenderHint(QPainter::Antialiasing); - - Layout_graph->addWidget(m_chartView); -} - - -void SofaWindowProfiler::updateChart() -{ - bool updateAxis = false; - - QVector seriesPoints; - QVector selectedStepPoints; - std::unordered_map > checkedSeriesPoints; - - int cpt = 0; - for (auto* stepData : m_profilingData) - { - seriesPoints.push_back(QPointF(cpt, stepData->m_totalMs)); - - if (!m_selectedStep.empty()) - { - const SReal value = stepData->getStepMs(m_selectedStep, m_selectedParentStep); - selectedStepPoints.push_back(QPointF(cpt, value)); - } - - for (const auto& checkedSeries : m_checkedSeries) - { - const SReal value = stepData->getStepMs(checkedSeries.first, checkedSeries.second.checkedParentStep); - checkedSeriesPoints[checkedSeries.first].push_back(QPointF(cpt, value)); - } - - if (m_fpsMaxAxis < stepData->m_totalMs){ - m_fpsMaxAxis = stepData->m_totalMs; - updateAxis = true; - } - - // keep max ms value - if (m_maxFps < stepData->m_totalMs) - m_maxFps = stepData->m_totalMs; - - cpt++; - } - - m_series->replace(seriesPoints); - m_selectionSeries->replace(selectedStepPoints); - for (const auto& checkedSeries : m_checkedSeries) - { - auto it = checkedSeriesPoints.find(checkedSeries.first); - if (it != checkedSeriesPoints.end()) - { - checkedSeries.second.lineSeries->replace(it->second); - } - } - - // if needed enlarge the Y axis to cover new data - if (updateAxis){ - m_axisY->setRange(0, m_fpsMaxAxis*1.1); - m_chartView->updateYMax(m_fpsMaxAxis*1.1); - } - - // every loop on buffer size check if Y axis can be reduced - if ((m_step% m_bufferSize) == 0) - { - if (m_maxFps < m_fpsMaxAxis) - m_fpsMaxAxis = m_maxFps; - - m_maxFps = 0; - m_axisY->setRange(0, m_fpsMaxAxis*1.1); - m_chartView->updateYMax(m_fpsMaxAxis*1.1); - } - - // m_chartView->update(); - - // update all widgets from value sliced - updateSummaryLabels(step_scroller->value()); - updateTree(step_scroller->value()); -} - - -void SofaWindowProfiler::updateFromSelectedStep(int step) -{ - // only update scroller as all the connection are already made from the scroller value change. - step_scroller->setValue(step); -} - - -void SofaWindowProfiler::updateSummaryLabels(int step) -{ - const AnimationStepData* stepData = m_profilingData.at(step); - label_stepValue->setText(QString::number(stepData->m_stepIteration)); - label_timeValue->setText(QString::number(stepData->m_totalMs)); - label_overheadValue->setText(QString::number(stepData->m_overheadMs)); - label_timersCounterValue->setText(QString::number(stepData->m_totalTimers)); -} - -void SofaWindowProfiler::updateTree(int step) -{ - const AnimationStepData* stepData = m_profilingData.at(step); - - tree_steps->setUpdatesEnabled(false); - - //clear old values - tree_steps->clear(); - - QList root; - root << addTreeItem(stepData); - tree_steps->addTopLevelItems(root); - - //Expand the first two levels - for (int i = 0; i < tree_steps->topLevelItemCount(); ++i) - { - auto* item = tree_steps->topLevelItem(i); - item->setExpanded(true); - for (int j = 0; j < item->childCount(); ++j) - { - item->child(i)->setExpanded(true); - } - } - - tree_steps->setUpdatesEnabled(true); -} - -QTreeWidgetItem* SofaWindowProfiler::addTreeItem(AnimationSubStepData* subStep) -{ - QStringList columns; - columns << QString::fromStdString(subStep->m_name); - columns << QString::number(subStep->m_totalPercent, 'g', 2); - columns << QString::number(subStep->m_selfPercent, 'g', 2); - columns << QString::number(subStep->m_totalMs); - columns << QString::number(subStep->m_selfMs); - - // add item to the tree - QTreeWidgetItem* treeItem = new QTreeWidgetItem(columns); - - if (m_checkedSeries.find(subStep->m_name) == m_checkedSeries.end()) - { - treeItem->setCheckState(0, Qt::Unchecked); - } - else - { - treeItem->setCheckState(0, Qt::Checked); - } - - treeItem->setSelected(m_selectedStep == subStep->m_name); - - if (subStep->m_level <= 2) - { - QFont font = QApplication::font(); - font.setBold(true); - - for (int i = 0; icolumnCount(); i++) - treeItem->setFont(i, font); - } - - QList children; - // process children - for (auto* child : subStep->m_children) - children << addTreeItem(child); - treeItem->addChildren(children); - - return treeItem; -} - -QTreeWidgetItem* SofaWindowProfiler::addTreeItem(const AnimationStepData* step) -{ - QStringList columns; - columns << QString::fromStdString(step->m_idString); - columns << "100"; - columns << QString::number(step->m_selfPercent, 'g', 2); - columns << QString::number(step->m_totalMs); - columns << QString::number(step->m_selfMs); - - QTreeWidgetItem* treeItem = new QTreeWidgetItem(columns); - - QFont font = QApplication::font(); - font.setBold(true); - - for (int i = 0; icolumnCount(); i++) - treeItem->setFont(i, font); - - // add new step items - QList children; - for (auto* substep : step->m_subSteps) - { - children << addTreeItem(substep); - } - - treeItem->addChildren(children); - - return treeItem; -} - -void SofaWindowProfiler::onStepSelected(QTreeWidgetItem *item, int /*column*/) -{ - if (item->parent()) - m_selectedParentStep = item->parent()->text(0).toStdString(); - - m_selectedStep = item->text(0).toStdString(); - - if (item->checkState(0)) - { - if (m_checkedSeries.find(m_selectedStep) == m_checkedSeries.end()) - { - QLineSeries* checkedLineSeries = new QLineSeries; - for (unsigned int i = 0; i < m_bufferSize; ++i) - { - const SReal value = (i < m_profilingData.size()) ? - m_profilingData[i]->getStepMs(m_selectedStep, m_selectedParentStep) : 0.f; - checkedLineSeries->append(i, value); - } - - m_chart->addSeries(checkedLineSeries); - checkedLineSeries->setName(QString(m_selectedStep.c_str())); - checkedLineSeries->attachAxis(m_axisY); - - m_checkedSeries.insert({m_selectedStep, {checkedLineSeries, m_selectedParentStep}}); - } - } - else - { - const auto it = m_checkedSeries.find(m_selectedStep); - if (it != m_checkedSeries.end()) - { - m_chart->removeSeries(it->second.lineSeries); - m_checkedSeries.erase(it); - } - - if (item->parent()) - { - m_chart->addSeries(m_selectionSeries); - int cpt = 0; - QVector seriesPoints; - for (auto* stepData : m_profilingData) - { - const SReal value = stepData->getStepMs(m_selectedStep, m_selectedParentStep); - seriesPoints << QPointF(cpt++, value); - } - m_selectionSeries->replace(seriesPoints); - m_selectionSeries->setName(QString(m_selectedStep.c_str())); - } - } -} - -void SofaWindowProfiler::expandRootNodeOnly() const -{ - tree_steps->expandToDepth(0); -} -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/SofaWindowProfiler.h b/Sofa/GUI/Qt/src/sofa/gui/qt/SofaWindowProfiler.h deleted file mode 100644 index 8a8b7e42064..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/SofaWindowProfiler.h +++ /dev/null @@ -1,265 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include -#include "PieWidget.h" -#include "QVisitorControlPanel.h" - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - - -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) -using namespace QtCharts; -#endif - -namespace sofa::gui::qt -{ - -typedef sofa::helper::system::thread::ctime_t ctime_t; - -/** - * @brief The ProfilerChartView class is a override of QtCharts::QChartView - * to be able to catch mouse selection and update all widgets of \sa SofaWindowProfiler - * Will also override drawForeground to draw a line to show the selected step. - */ -class ProfilerChartView : public QChartView -{ - Q_OBJECT -public: - ProfilerChartView(QChart *chart, QWidget *parent, int bufferSize); - - /// method to update the max value of the Y axis (for line rendering). - void updateYMax(int y) {m_maxY = y;} - -protected: - /// Override to catch mouse selection on the graph. - virtual void mousePressEvent(QMouseEvent *event); - /// Override to draw line at the step selected. - virtual void drawForeground(QPainter *painter, const QRectF &rect); - -signals: - /// signal emitted when a step has been selected on the graph @param int is the step number - void pointSelected(int); - -public slots: - /// method to update the selection on the graph. - void updateSelection(int x); - -protected: - /// copy of the series size to check if selection is not out of bound - int m_bufferSize; - - /// 2D point of the line to draw the selection - QPointF m_lineSelect; - QPointF m_lineOrigin; - - /// Step number selected on the graph. -1 if none - int m_pointSelected; - /// Stored value of the Y axis max. - int m_maxY; -}; - -/** - * @brief The SofaWindowProfiler class - * This class is a QDialog widget to display information recorded by AdvancedTimer mechanism - * At each step, info will be gathered from the AdvancedTimer using class sofa::helper::StepData - * Info will be displayed by: - * - plotting the step duration into a graph - * - Showing information duration/step number - * - Showing all substep of an animation step with their own duration in ms and the corresponding percentage over the whole step. - */ -class SofaWindowProfiler: public QDialog, public Ui_WindowProfiler -{ - Q_OBJECT -public: - SofaWindowProfiler(QWidget* parent); - - /// method called when window is shown to activate AdvancedTimer recording. - void activateATimer(bool activate); - - /// main method to iterate on the AdvancedTimer Data and update the info in the widgets - void pushStepData(); - - /// Method to clear all Data and reset graph - void resetGraph(); - - /** - * @brief The AnimationSubStepData Internal class to store data for each step of the animation. Correspond to one AdvancedTimer::begin/end - * Data stored/computed will be step name, its time in ms and the corresponding % inside the whole step. - * the total ms and percentage it represent if this step has substeps. - * Buffer of AnimationSubStepData corresponding to its children substeps - */ - class AnimationSubStepData - { - public: - AnimationSubStepData(int level, std::string name, ctime_t start); - virtual ~AnimationSubStepData(); - - int m_level; - std::string m_name; - int m_nbrCall; - ctime_t m_start; - ctime_t m_end; - - std::string m_tag; - SReal m_totalMs; - SReal m_totalPercent; - SReal m_selfMs; - SReal m_selfPercent; - - void computeTimeAndPercentage(SReal invTotalMs); - // Method to get a given step duration (ms) given its name and parent name - SReal getStepMs(const std::string& stepName, const std::string& parentName); - - sofa::type::vector m_children; - }; - - /** - * @brief The AnimationStepData internal class to store all info of a animation step recorded by AdvancedTimer - * Data stored/computed will be the step number, and the total time in ms of the step. - * All Data will then be stored inside a tree of \sa AnimationSubStepData tree. - */ - class AnimationStepData - { - public: - // default constructor for empty data. - AnimationStepData() - : m_stepIteration(-1) - , m_totalMs(0.0) - , m_overheadMs(0.) - {} - - AnimationStepData(int step, const std::string& idString); - - // Method to get a given step duration (ms) given its name and parent name - SReal getStepMs(const std::string& stepName, const std::string& parentName); - - virtual ~AnimationStepData(); - int m_stepIteration; - SReal m_totalMs; - SReal m_selfMs {}; ///< Difference between the total time and the time of all children - SReal m_selfPercent {}; ///< Difference between the total time and the time of all children as a percentage - std::string m_idString; ///< Name of the timer - - sofa::type::vector m_subSteps; - - /// The overhead due to timers processing. In milliseconds - SReal m_overheadMs; - - /// Total number of timers in this step - unsigned int m_totalTimers {}; - protected: - bool processData(const std::string& idString); - }; - -protected: - /// Method called at creation to init the chart - void createChart(); - /// Method called at creation to init the QTreeWidget - void createTreeView(); - - /// Method called at each iteration to update the chart - void updateChart(); - /// Method to add new QTreeWidgetItem item inside the QTreeWidget using the data from \sa AnimationSubStepData - QTreeWidgetItem* addTreeItem(AnimationSubStepData* subStep); - - QTreeWidgetItem* addTreeItem(const AnimationStepData* step); - -public slots: - void closeEvent( QCloseEvent* ) override - { - emit(closeWindow(false)); - } - - /// Method to update all widgets from select absisse on the graph - void updateFromSelectedStep(int step); - - /// Method called when a given @param step is triggered to update summary information - void updateSummaryLabels(int step); - /// Method called when a given @param step is triggered to update the QTreeView - void updateTree(int step); - /// Method called when a QTreeWidgetItem is selected in the Tree view. - void onStepSelected(QTreeWidgetItem *item, int column); - - void expandRootNodeOnly() const; - -signals: - void closeWindow(bool); - -protected: - /// Pointer to the chart Data - QChart *m_chart; - /// Pointer to Y Axis - QValueAxis *m_axisY; - /// Pointer to the \sa ProfilerChartView class to handle chart drawing/selection - ProfilerChartView* m_chartView; - - /// Current animation step internally recorded. - int m_step; - /// Size of the buffer data stored. (i.e number of stepData info stored) - unsigned int m_bufferSize; - /// Bigger step encountered in ms. - SReal m_maxFps; - /// Current Y max value of the graph (max ms encountered x1.1) - SReal m_fpsMaxAxis; - - /// Buffer of \sa AnimationStepData (data for each step), deque size correspond to \sa m_bufferSize - std::deque m_profilingData; - - /// Series of step duration in ms to be plot on the graph. size = \sa m_bufferSize - QLineSeries *m_series; - - /// Series of selection substep duration in ms to be plot on the graph. size = \sa m_bufferSize - QLineSeries *m_selectionSeries; - - struct CheckedSeries - { - QLineSeries* lineSeries; - std::string checkedParentStep; - }; - std::unordered_map m_checkedSeries; - - /// Name of the substep selected in the Tree - std::string m_selectedStep; - /// Name of the parent of the substep selected in the Tree - std::string m_selectedParentStep; -}; - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/StructDataWidget.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/StructDataWidget.cpp deleted file mode 100644 index bb2db368d10..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/StructDataWidget.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ - -#include "StructDataWidget.h" -#include -#include - -namespace sofa::gui::qt -{ - -using sofa::helper::Creator; -using sofa::type::fixed_array; -using namespace sofa::defaulttype; - -Creator > > DWClass_RigidCoord2f("default",true); -Creator > > DWClass_RigidCoord2d("default",true); -Creator > > DWClass_RigidMass2f("default",true); -Creator > > DWClass_RigidMass2d("default",true); -Creator > > DWClass_RigidCoord3f("default",true); -Creator > > DWClass_RigidCoord3d("default",true); -Creator > > DWClass_RigidMass3f("default",true); -Creator > > DWClass_RigidMass3d("default",true); - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/StructDataWidget.h b/Sofa/GUI/Qt/src/sofa/gui/qt/StructDataWidget.h deleted file mode 100644 index 761380daa54..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/StructDataWidget.h +++ /dev/null @@ -1,564 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include "SimpleDataWidget.h" -#include - -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include - -namespace sofa::gui::qt -{ - -//////////////////////////////////////////////////////////////// -/// Generic data structures support -//////////////////////////////////////////////////////////////// - -template -class struct_data_trait -{ -public: - typedef T data_type; - enum { NVAR = 1 }; - static void set( data_type& /*d*/ ) - { - } -}; - -template -class struct_data_trait_var -{ -public: - typedef T data_type; - typedef T value_type; - static const char* name() { return nullptr; } - static const value_type* get(const data_type& d) { return &d; } - static void set( const value_type& v, data_type& d) { d = v; } - static bool readOnly() { return false; } - static bool isCheckable() { return false; } - static bool isChecked(const data_type& /*d*/) { return true; } - static void setChecked(bool /*b*/, data_type& /*d*/) {} -}; - -template::NVAR > -class struct_data_widget_container -{ -public: - typedef T data_type; - typedef struct_data_trait shelper; - typedef struct_data_trait_var vhelper; - typedef typename vhelper::value_type value_type; - typedef data_widget_container Container; - typedef struct_data_widget_container PrevContainer; - typedef QVBoxLayout MasterLayout; - typedef QHBoxLayout Layout; - PrevContainer p; - Container w; - QCheckBox* check; - QLabel* label; - Layout* container_layout; - MasterLayout* master_layout; - struct_data_widget_container() : check(nullptr),label(nullptr),container_layout(nullptr),master_layout(nullptr) {} - - void setMasterLayout(MasterLayout* layout) - { - p.setMasterLayout(layout); - master_layout = layout; - } - - bool createLayout( DataWidget* parent ) - { - if( parent->layout() != nullptr ) - { - return false; - } - master_layout = new QVBoxLayout(parent); - setMasterLayout(master_layout); - return true; - } - - bool createLayout( QLayout* layout) - { - container_layout = new QHBoxLayout(); - layout->addItem(container_layout); - return true; - } - - void insertWidgets() - { - p.insertWidgets(); - createLayout(master_layout); - - if(check) - { - container_layout->addWidget(check); - } - if(label) - { - container_layout->addWidget(label); - } - w.createLayout(container_layout); // create the layout for the rest of the widgets - w.insertWidgets(); // insert them accordingly - - } - - bool createWidgets(DataWidget * parent, const data_type& d, bool readOnly ) - { - - if (!p.createWidgets( parent, d, readOnly)) - return false; - - const char* name = vhelper::name(); - const bool checkable = vhelper::isCheckable(); - if (checkable) - { - check = new QCheckBox(parent); - if (name && *name) - { - check->setText(QString(name)); - } - } - else - { - if (name && *name && N > 1) - { - label = new QLabel(QString(name),parent); - } - } - if (!w.createWidgets(parent, *vhelper::get(d), readOnly || vhelper::readOnly())) - return false; - - if (checkable) - { - const bool isChecked = vhelper::isChecked(d); - check->setChecked(isChecked); - if (readOnly || vhelper::readOnly()) - check->setEnabled(false); - else - { - if (!isChecked) - w.setReadOnly(true); - parent->connect(check, SIGNAL( toggled(bool) ),parent, SLOT( setWidgetDirty() )); - parent->connect(check, SIGNAL( toggled(bool) ),parent, SLOT( setReadOnly(bool) )); - - } - } - return true; - } - - void setReadOnly(bool readOnly) - { - p.setReadOnly(readOnly); - w.setReadOnly(readOnly || vhelper::readOnly() || (check && !(check->checkState() == Qt::Checked))); - } - void readFromData(const data_type& d) - { - p.readFromData(d); - if (check) - { - const bool wasChecked = (check->checkState() == Qt::Checked); - const bool isChecked = vhelper::isChecked(d); - if (isChecked != wasChecked) - { - check->setChecked(isChecked); - if (check->isEnabled()) - w.setReadOnly(!isChecked); - } - } - w.readFromData(*vhelper::get(d)); - } - void readConstantsFromData(const data_type& d) - { - p.readConstantsFromData(d); - if (vhelper::readOnly()) - { - if (check) - { - check->setChecked(vhelper::isChecked(d)); - } - w.readFromData(*vhelper::get(d)); - } - } - void writeToData(data_type& d) - { - p.writeToData(d); - if (check) - { - bool isChecked = (check->checkState() == Qt::Checked); - vhelper::setChecked(isChecked, d); - } - value_type v = *vhelper::get(d); - w.writeToData(v); - vhelper::set(v,d); - if ( N == struct_data_trait::NVAR ) - { - shelper::set(d); - readConstantsFromData(d); // reread constant fields - } - } -}; - -template -class struct_data_widget_container< T, 0 > -{ -public: - typedef T data_type; - typedef struct_data_trait shelper; - typedef QVBoxLayout MasterLayout; - typedef QHBoxLayout Layout ; - MasterLayout* master_layout; - Layout* container_layout; - struct_data_widget_container():master_layout(nullptr),container_layout(nullptr) {} - - void setMasterLayout(MasterLayout* /*layout*/) - { - } - - bool createLayout( DataWidget* /*parent*/ ) - { - return true; - } - - bool createLayout( QLayout* /*layout*/) - { - return true; - } - - bool createWidgets(DataWidget * /*parent*/, const data_type& /*d*/, bool /*readOnly*/) - { - return true; - } - void setReadOnly(bool /*readOnly*/) - { - } - void readFromData(const data_type& /*d*/) - { - } - void readConstantsFromData(const data_type& /*d*/) - { - } - void writeToData(data_type& /*d*/) - { - } - - void insertWidgets() - { - } - -}; - -template -class default_struct_data_trait_var -{ -public: - typedef T data_type; - static const char* name() { return nullptr; } - static const char* shortname() { return nullptr; } - static bool readOnly() { return false; } - static bool isCheckable() { return false; } - static bool isChecked(const data_type& /*d*/) { return true; } - static void setChecked(bool /*b*/, data_type& /*d*/) {} -}; - -#define STRUCT_DATA_VAR(parent, vid, vname, sname, vtype, var) \ - class struct_data_trait_var < parent, vid > : public default_struct_data_trait_var < parent, vid > \ - { \ - public: \ - typedef parent data_type; \ - typedef vtype value_type; \ - static const char* name() { return vname; } \ - static const char* shortname() { return sname; } \ - static const value_type* get(const data_type& d) { return &(d.var); } \ - static void set( const value_type& v, data_type& d) { d.var = v; } \ - } - -#define STRUCT_DATA_VAR_READONLY(parent, vid, vname, sname, vtype, var) \ - class struct_data_trait_var < parent, vid > : public default_struct_data_trait_var < parent, vid > \ - { \ - public: \ - typedef parent data_type; \ - typedef vtype value_type; \ - static const char* name() { return vname; } \ - static const char* shortname() { return sname; } \ - static bool readOnly() { return true; } \ - static const value_type* get(const data_type& d) { return &(d.var); } \ - static void set( const value_type& v, data_type& d) { d.var = v; } \ - } - -#define STRUCT_DATA_VAR_CHECK(parent, vid, vname, sname, vtype, var, check) \ - class struct_data_trait_var < parent, vid > : public default_struct_data_trait_var < parent, vid > \ - { \ - public: \ - typedef parent data_type; \ - typedef vtype value_type; \ - static const char* name() { return vname; } \ - static const char* shortname() { return sname; } \ - static const value_type* get(const data_type& d) { return &(d.var); } \ - static void set( const value_type& v, data_type& d) { d.var = v; } \ - static bool isCheckable() { return true; } \ - static bool isChecked(const data_type& d) { return d.check; } \ - static void setChecked(bool b, data_type& d) { d.check = b; } \ - } - -// A comma can't appear in a macro argument... -#define COMMA , - - -//////////////////////////////////////////////////////////////// -/// Rigids (as data-structures) support -//////////////////////////////////////////////////////////////// - -template -class struct_data_trait < sofa::defaulttype::RigidCoord > -{ -public: - typedef sofa::defaulttype::RigidCoord data_type; - enum { NVAR = 2 }; - static void set( data_type& /*d*/ ) - { - } -}; -template STRUCT_DATA_VAR(sofa::defaulttype::RigidCoord<3 COMMA T>, 0, "Center", "", typename data_type::Vec3, getCenter()); -template STRUCT_DATA_VAR(sofa::defaulttype::RigidCoord<3 COMMA T>, 1, "Orientation", "", typename data_type::Quat, getOrientation()); - -template STRUCT_DATA_VAR(sofa::defaulttype::RigidCoord<2 COMMA T>, 0, "Center", "", typename data_type::Vec2, getCenter()); -template STRUCT_DATA_VAR(sofa::defaulttype::RigidCoord<2 COMMA T>, 1, "Orientation", "A", typename data_type::Real, getOrientation()); - -template -class data_widget_container < sofa::defaulttype::RigidCoord > : public struct_data_widget_container < sofa::defaulttype::RigidCoord > -{}; - -template -class struct_data_trait < sofa::defaulttype::RigidMass > -{ -public: - typedef sofa::defaulttype::RigidMass data_type; - enum { NVAR = 4 }; - static void set( data_type& d) - { - d.recalc(); - } -}; - -template STRUCT_DATA_VAR(sofa::defaulttype::RigidMass, 0, "Mass", "Mass", T, mass); -template STRUCT_DATA_VAR(sofa::defaulttype::RigidMass, 1, "Volume", "Vol", T, volume); -template STRUCT_DATA_VAR(sofa::defaulttype::RigidMass<2 COMMA T>, 2, "Inertia Matrix", "Inertia", T, inertiaMatrix); -template STRUCT_DATA_VAR(sofa::defaulttype::RigidMass<3 COMMA T>, 2, "Inertia Matrix", "Inertia", typename data_type::Mat3x3, inertiaMatrix); -template STRUCT_DATA_VAR_READONLY(sofa::defaulttype::RigidMass<2 COMMA T>, 3, "Inertia Mass Matrix", "InertialMass", T, inertiaMassMatrix); -template STRUCT_DATA_VAR_READONLY(sofa::defaulttype::RigidMass<3 COMMA T>, 3, "Inertia Mass Matrix", "InertialMass", typename data_type::Mat3x3, inertiaMassMatrix); - -template -class data_widget_container < sofa::defaulttype::RigidMass > : public struct_data_widget_container < sofa::defaulttype::RigidMass > -{}; - - -//////////////////////////////////////////////////////////////// -/// sofa::component::forcefield::LinearSpring support -//////////////////////////////////////////////////////////////// - -#define CLASS typename sofa::component::solidmechanics::spring::LinearSpring< T > - -template -class struct_data_trait < CLASS > -{ -public: - typedef CLASS data_type; - enum { NVAR = 5 }; - static void set( data_type& /*d*/) - { - } -}; - -template STRUCT_DATA_VAR(CLASS, 0, "Index 1", "Index 1", sofa::Index, m1); -template STRUCT_DATA_VAR(CLASS, 1, "Index 2", "Index 2", sofa::Index, m2); -template STRUCT_DATA_VAR(CLASS, 2, "Stiffness", "Ks", T, ks); -template STRUCT_DATA_VAR(CLASS, 3, "Damping", "Kd", T, kd); -template STRUCT_DATA_VAR(CLASS, 4, "Rest Length", "L", T, initpos); - -template -class data_widget_container < CLASS > : public struct_data_widget_container < CLASS > -{}; - -#undef CLASS - -//////////////////////////////////////////////////////////////// -/// sofa::component::forcefield::JointSpring support -//////////////////////////////////////////////////////////////// - -#define CLASS typename sofa::component::solidmechanics::spring::JointSpring< T > - -template -class struct_data_trait < CLASS > -{ -public: - typedef CLASS data_type; - enum { NVAR = 27 }; - static void set( data_type& /*d*/) - { - } -}; - -template STRUCT_DATA_VAR(CLASS, 0, "Index 1", "Index 1", sofa::Index, m1); -template STRUCT_DATA_VAR(CLASS, 1, "Index 2", "Index 2", sofa::Index, m2); -template STRUCT_DATA_VAR(CLASS, 2, "Trans X Axis", "Trans X Axis", bool, freeMovements[0]); -template STRUCT_DATA_VAR(CLASS, 3, "Trans Y Axis", "Trans Y Axis", bool, freeMovements[1]); -template STRUCT_DATA_VAR(CLASS, 4, "Trans Z Axis", "Trans Z Axis", bool, freeMovements[2]); -template STRUCT_DATA_VAR(CLASS, 5, "Rot X Axis", "Rot X Axis", bool, freeMovements[3]); -template STRUCT_DATA_VAR(CLASS, 6, "Rot Y Axis", "Rot Y Axis", bool, freeMovements[4]); -template STRUCT_DATA_VAR(CLASS, 7, "Rot Z Axis", "Rot Z Axis", bool, freeMovements[5]); -template STRUCT_DATA_VAR(CLASS, 8, "Soft Stiffness Translation", "Soft Ks Trans", typename data_type::Real, softStiffnessTrans); -template STRUCT_DATA_VAR(CLASS, 9, "Hard Stiffness Translation", "Hard Ks Trans", typename data_type::Real, hardStiffnessTrans); -template STRUCT_DATA_VAR(CLASS, 10, "Soft Stiffness Rotation", "Soft Ks Rot", typename data_type::Real, softStiffnessRot); -template STRUCT_DATA_VAR(CLASS, 11, "Hard Stiffness Rotation", "Hard Ks Rot", typename data_type::Real, hardStiffnessRot); -template STRUCT_DATA_VAR(CLASS, 12, "Bloc Stiffness Rotation", "Bloc Ks Rot", typename data_type::Real, blocStiffnessRot); -template STRUCT_DATA_VAR(CLASS, 13, "Damping", "Kd", typename data_type::Real, kd); -template STRUCT_DATA_VAR(CLASS, 14, "Min Angle X", "Min Angle X", typename data_type::Real, limitAngles[0]); -template STRUCT_DATA_VAR(CLASS, 15, "Max Angle X", "Max Angle X", typename data_type::Real, limitAngles[1]); -template STRUCT_DATA_VAR(CLASS, 16, "Min Angle Y", "Min Angle Y", typename data_type::Real, limitAngles[2]); -template STRUCT_DATA_VAR(CLASS, 17, "Max Angle Y", "Max Angle Y", typename data_type::Real, limitAngles[3]); -template STRUCT_DATA_VAR(CLASS, 18, "Min Angle Z", "Min Angle Z", typename data_type::Real, limitAngles[4]); -template STRUCT_DATA_VAR(CLASS, 19, "Max Angle Z", "Max Angle Z", typename data_type::Real, limitAngles[5]); - -template STRUCT_DATA_VAR(CLASS, 20, "Initial length of the spring X", "L init spring X", typename data_type::Real, initTrans[0]); -template STRUCT_DATA_VAR(CLASS, 21, "Initial length of the spring Y", "L init spring Y", typename data_type::Real, initTrans[1]); -template STRUCT_DATA_VAR(CLASS, 22, "Initial length of the spring Z", "L init spring Z", typename data_type::Real, initTrans[2]); - -template STRUCT_DATA_VAR(CLASS, 23, "Initial rotation of the spring X", "Rot init spring X", SReal, initRot[0]); -template STRUCT_DATA_VAR(CLASS, 24, "Initial rotation of the spring Y", "Rot init spring Y", SReal, initRot[1]); -template STRUCT_DATA_VAR(CLASS, 25, "Initial rotation of the spring Z", "Rot init spring Z", SReal, initRot[2]); -template STRUCT_DATA_VAR(CLASS, 26, "Initial rotation of the spring W", "Rot init spring W", SReal, initRot[3]); - -template -class data_widget_container < CLASS > : public struct_data_widget_container < CLASS > -{}; - -#undef CLASS - -//////////////////////////////////////////////////////////////// -/// sofa::component::forcefield::GearSpring support -//////////////////////////////////////////////////////////////// - -#define CLASS typename sofa::component::solidmechanics::spring::GearSpring< T > - -template -class struct_data_trait < CLASS > -{ -public: - typedef CLASS data_type; - enum { NVAR = 10 }; - static void set( data_type& /*d*/) - { - } -}; - -template STRUCT_DATA_VAR(CLASS, 0, "Parent 1", "Parent 1", unsigned int, p1); -template STRUCT_DATA_VAR(CLASS, 1, "Index 1", "Index 1", unsigned int, m1); -template STRUCT_DATA_VAR(CLASS, 2, "Parent 2", "Parent 2", unsigned int, p2); -template STRUCT_DATA_VAR(CLASS, 3, "Index 2", "Index 2", unsigned int, m2); -template STRUCT_DATA_VAR(CLASS, 4, "Axis 1", "Axis 1", unsigned int, freeAxis[0]); -template STRUCT_DATA_VAR(CLASS, 5, "Axis 2", "Axis 2", unsigned int, freeAxis[1]); -template STRUCT_DATA_VAR(CLASS, 6, "Pivot Stiffness Translation", "Ks Trans", typename data_type::Real, hardStiffnessTrans); -template STRUCT_DATA_VAR(CLASS, 7, "Gear Stiffness Rotation", "Gear Ks Rot", typename data_type::Real, softStiffnessRot); -template STRUCT_DATA_VAR(CLASS, 8, "Pivot Stiffness Rotation", "Pivot Ks Rot", typename data_type::Real, hardStiffnessRot); -template STRUCT_DATA_VAR(CLASS, 9, "Damping", "Kd", typename data_type::Real, kd); - -template -class data_widget_container < CLASS > : public struct_data_widget_container < CLASS > -{}; - -#undef CLASS - - -//////////////////////////////////////////////////////////////// -/// sofa::type::Material support -//////////////////////////////////////////////////////////////// - -template<> -class struct_data_trait < sofa::type::Material > -{ -public: - typedef sofa::type::Material data_type; - enum { NVAR = 6 }; - static void set( data_type& /*d*/) - { - } -}; - -template<> STRUCT_DATA_VAR(sofa::type::Material, 0, "Name", "Name", std::string, name); -template<> STRUCT_DATA_VAR_CHECK(sofa::type::Material, 1, "Ambient", "Amb", sofa::type::RGBAColor, ambient, useAmbient); -template<> STRUCT_DATA_VAR_CHECK(sofa::type::Material, 2, "Diffuse", "Diff", sofa::type::RGBAColor, diffuse, useDiffuse); -template<> STRUCT_DATA_VAR_CHECK(sofa::type::Material, 3, "Specular", "Spec", sofa::type::RGBAColor, specular, useSpecular); -template<> STRUCT_DATA_VAR_CHECK(sofa::type::Material, 4, "Emissive", "Emm", sofa::type::RGBAColor, emissive, useEmissive); -template<> STRUCT_DATA_VAR_CHECK(sofa::type::Material, 5, "Shininess", "Shin", float, shininess, useShininess); - -template<> -class data_widget_container < sofa::type::Material > : public struct_data_widget_container < sofa::type::Material > -{}; - - - -//////////////////////////////////////////////////////////////// -/// sofa::component::interactionforcefield::PenalityContactForceField Contact support -//////////////////////////////////////////////////////////////// - -#define CLASS typename sofa::component::collision::response::contact::PenalityContact< T > - -template -class struct_data_trait < CLASS > -{ -public: - typedef CLASS data_type; - enum { NVAR = 9 }; - static void set(data_type& /*d*/) - { - } -}; - -template STRUCT_DATA_VAR(CLASS, 0, "VertexId 1", "VertexId 1", sofa::Index, m1); -template STRUCT_DATA_VAR(CLASS, 1, "VertexId 2", "VertexId 2", sofa::Index, m2); -template STRUCT_DATA_VAR(CLASS, 2, "ElemId 1", "ElemId 1", sofa::Index, index1); -template STRUCT_DATA_VAR(CLASS, 3, "ElemId 2", "ElemId 2", sofa::Index, index2); - -template STRUCT_DATA_VAR(CLASS, 4, "Normal", "Norm", typename data_type::Deriv, norm); -template STRUCT_DATA_VAR(CLASS, 5, "Distance", "Dist", typename data_type::Real, dist); - -template STRUCT_DATA_VAR(CLASS, 6, "Stiffness", "Ks", typename data_type::Real, ks); -template STRUCT_DATA_VAR(CLASS, 7, "Penetration", "Pene", typename data_type::Real, pen); -template STRUCT_DATA_VAR(CLASS, 8, "Age", "Age", int, age); - -template -class data_widget_container < CLASS > : public struct_data_widget_container < CLASS > -{}; - -#undef CLASS - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/TableDataWidget.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/TableDataWidget.cpp deleted file mode 100644 index e422128db4e..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/TableDataWidget.cpp +++ /dev/null @@ -1,128 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ - -#include "TableDataWidget.h" -#include -#include -#include - -namespace sofa::gui::qt -{ - -using sofa::helper::Creator; -using sofa::type::fixed_array; -using namespace sofa::type; -using namespace sofa::defaulttype; - -Creator, TABLE_HORIZONTAL > > DWClass_vectori("default",true); -Creator, TABLE_HORIZONTAL > > DWClass_vectoru("default",true); -Creator, TABLE_HORIZONTAL > > DWClass_vectorf("default",true); -Creator, TABLE_HORIZONTAL > > DWClass_vectord("default",true); -Creator > > DWClass_vectorstring("default",true); - -Creator > DWClass_SeqEdges ("default",true); -Creator > DWClass_SeqTriangles ("default",true); -Creator > DWClass_SeqQuads ("default",true); -Creator > DWClass_SeqTetrahedra("default",true); -Creator > DWClass_SeqHexahedra ("default",true); - -Creator > > > DWClass_vectorVec1i("default",true); -Creator > > > DWClass_vectorVec1u("default",true); -Creator > > > DWClass_vectorVec1f("default",true); -Creator > > > DWClass_vectorVec1d("default",true); -Creator > > > DWClass_vectorVec2i("default",true); -Creator > > > DWClass_vectorVec2u("default",true); -Creator > > > DWClass_vectorVec2f("default",true); -Creator > > > DWClass_vectorVec2d("default",true); -Creator > > > DWClass_vectorVec3i("default",true); -Creator > > > DWClass_vectorVec3u("default",true); -Creator > > > DWClass_vectorVec3f("default",true); -Creator > > > DWClass_vectorVec3d("default",true); -Creator > > > DWClass_vectorVec4i("default",true); -Creator > > > DWClass_vectorVec4u("default",true); -Creator > > > DWClass_vectorVec4f("default",true); -Creator > > > DWClass_vectorVec4d("default",true); -Creator > > > DWClass_vectorVec6i("default",true); -Creator > > > DWClass_vectorVec6u("default",true); -Creator > > > DWClass_vectorVec6f("default",true); -Creator > > > DWClass_vectorVec6d("default",true); -Creator > > > DWClass_vectorVec8i("default",true); -Creator > > > DWClass_vectorVec8u("default",true); - -Creator > > > DWClass_vectorA1i("default",true); -Creator > > > DWClass_vectorA1u("default",true); -Creator > > > DWClass_vectorA2i("default",true); -Creator > > > DWClass_vectorA2u("default",true); -Creator > > > DWClass_vectorA3i("default",true); -Creator > > > DWClass_vectorA3u("default",true); -Creator > > > DWClass_vectorA3f("default",true); -Creator > > > DWClass_vectorA3d("default",true); -Creator > > > DWClass_vectorA4i("default",true); -Creator > > > DWClass_vectorA4u("default",true); -Creator > > > DWClass_vectorA6i("default",true); -Creator > > > DWClass_vectorA6u("default",true); -Creator > > > DWClass_vectorA8i("default",true); -Creator > > > DWClass_vectorA8u("default",true); - -#if !defined(_MSC_VER) && !defined(__clang__) -Creator > > > DWClass_stdvectorA1i("default",true); -Creator > > > DWClass_stdvectorA1u("default",true); -Creator > > > DWClass_stdvectorA2i("default",true); -Creator > > > DWClass_stdvectorA2u("default",true); -Creator > > > DWClass_stdvectorA3i("default",true); -Creator > > > DWClass_stdvectorA3u("default",true); -Creator > > > DWClass_stdvectorA4i("default",true); -Creator > > > DWClass_stdvectorA4u("default",true); -Creator > > > DWClass_stdvectorA6i("default",true); -Creator > > > DWClass_stdvectorA6u("default",true); -Creator > > > DWClass_stdvectorA8i("default",true); -Creator > > > DWClass_stdvectorA8u("default",true); -#endif - -Creator > > > DWClass_vectorQuatf("default",true); -Creator > > > DWClass_vectorQuatd("default",true); - -Creator > > > DWClass_vectorPolynomialLD5d("default",true); -Creator > > > DWClass_vectorPolynomialLD4d("default",true); -Creator > > > DWClass_vectorPolynomialLD3d("default",true); -Creator > > > DWClass_vectorPolynomialLD2d("default",true); - -Creator > > > DWClass_vectorPolynomialLD5f("default",true); -Creator > > > DWClass_vectorPolynomialLD4f("default",true); -Creator > > > DWClass_vectorPolynomialLD3f("default",true); -Creator > > > DWClass_vectorPolynomialLD2f("default",true); - -Creator > > > DWClass_vectorRigidCoord2f("default",true); -Creator > > > DWClass_vectorRigidCoord2d("default",true); -Creator > > > DWClass_vectorRigidCoord3f("default",true); -Creator > > > DWClass_vectorRigidCoord3d("default",true); - -Creator > > > DWClass_vectorLinearSpringf("default",true); -Creator > > > DWClass_vectorLinearSpringd("default",true); - -Creator > > > DWClass_vectorJointSpring3f("default",true); -Creator > > > DWClass_vectorGearSpring3f("default",true); - -Creator > > > DWClass_vectorPenalityContact("default",true); - - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/TableDataWidget.h b/Sofa/GUI/Qt/src/sofa/gui/qt/TableDataWidget.h deleted file mode 100644 index fbcc179beff..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/TableDataWidget.h +++ /dev/null @@ -1,47 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include "SimpleDataWidget.h" -#include "StructDataWidget.h" -#include - -#include "QModelViewTableDataContainer.h" - - -namespace sofa::gui::qt -{ - -template -class TableDataWidget : public SimpleDataWidget > -{ -public: - typedef T data_type; - typedef SimpleDataWidget > Inherit; - typedef sofa::core::objectmodel::Data MyData; -public: - TableDataWidget(QWidget* parent,const char* name, MyData* d) : Inherit(parent,name,d) {} - virtual unsigned int sizeWidget() {return 8;} - virtual unsigned int numColumnWidget() { return 1; } -}; - - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/VideoRecorderManager.ui b/Sofa/GUI/Qt/src/sofa/gui/qt/VideoRecorderManager.ui deleted file mode 100644 index 863031a3b83..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/VideoRecorderManager.ui +++ /dev/null @@ -1,188 +0,0 @@ - - - VideoRecorderManager - - - true - - - - 0 - 0 - 600 - 400 - - - - VideoRecorderManager - - - - - - - 0 - 0 - - - - - 0 - 60 - - - - RecordingType - - - - - 20 - 20 - 380 - 20 - - - - List of Screenshots - - - true - - - - - - 20 - 40 - 430 - 16 - - - - Movie file encoded with FFMPEG - - - - - - - - - 0 - 0 - - - - Options - - - - - - - - Qt::Vertical - - - QSizePolicy::Expanding - - - - 20 - 80 - - - - - - - - - - Qt::Horizontal - - - QSizePolicy::Expanding - - - - 237 - 20 - - - - - - - - &Close - - - Alt+C - - - true - - - - - - - - - - - - buttonClose - clicked() - VideoRecorderManager - close() - - - 20 - 20 - - - 20 - 20 - - - - - ScreenshotsRecordingTypeRadioButton - clicked(bool) - VideoRecorderManager - onChangeRecordingType() - - - 20 - 20 - - - 20 - 20 - - - - - MovieRecordingTypeRadioButton - clicked(bool) - VideoRecorderManager - onChangeRecordingType() - - - 20 - 20 - - - 20 - 20 - - - - - - onChangeRecordingType() - - diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/VisitorGUI.ui b/Sofa/GUI/Qt/src/sofa/gui/qt/VisitorGUI.ui deleted file mode 100644 index 053428f04c9..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/VisitorGUI.ui +++ /dev/null @@ -1,89 +0,0 @@ - - - VisitorGUI - - - - 0 - 0 - 1000 - 550 - - - - VisitorGUI - - - - - - Qt::Vertical - - - - - - - Qt::Horizontal - - - - - Graph - - - false - - - true - - - - - Time - - - false - - - false - - - - - Type - - - false - - - false - - - - - Value - - - false - - - true - - - - - - - - - - - - - - graphView - - - - diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/WDoubleLineEdit.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/WDoubleLineEdit.cpp deleted file mode 100644 index 552e61850b3..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/WDoubleLineEdit.cpp +++ /dev/null @@ -1,158 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -/* -------------------------------------------------------- */ -#include -#include "WDoubleLineEdit.h" -#include -/* -------------------------------------------------------- */ - -WDoubleLineEdit::WDoubleLineEdit(QWidget *parent,const char *name) : QLineEdit(parent /*,name */) -{ - setObjectName(name); - setFocusPolicy( Qt::StrongFocus ); - - m_iPercent=-1; - m_fMinValue=0.0; - m_fMaxValue=1.0; - m_fValue=0.0; - m_bFirst=true; - m_DblValid=new QDoubleValidator(m_fMinValue,m_fMaxValue,20,this); - m_isDragging = false ; - setValidator(m_DblValid); - - connect(this,SIGNAL(returnPressed()), - this,SLOT (slotReturnPressed())); - - m_bInternal=false; - this->setText(QString("%1").arg(m_fValue)); - this->setCursorPosition(0); - this->setSelection(0, 0); -} -/* -------------------------------------------------------- */ -void WDoubleLineEdit::slotReturnPressed() -{ - m_bInternal=true; - - slotCalcValue(text().toDouble()); - -} -/* -------------------------------------------------------- */ -void WDoubleLineEdit::slotCalcValue(double f, bool isEditted) -{ - int p; - - if (f < m_fMinValue) - f=m_fMinValue; - else if (f > m_fMaxValue) - f=m_fMaxValue; - if (f != m_fValue || m_bFirst) - { - m_bFirst=false; - m_fValue=f; - if(isEditted) - emit (valueEdited(f)); - else - emit (valueChanged(f)); - - p=(int)(100.0*(f - m_fMinValue)/(m_fMaxValue - m_fMinValue)); - if (p != m_iPercent) - { - emit (valuePercentChanged(p)); - m_iPercent=p; - } - update(); - } - - this->setText(QString("%1").arg(m_fValue)); - this->setCursorPosition(0); - this->setSelection(0, 0); -} -/* -------------------------------------------------------- */ -void WDoubleLineEdit::slotCalcValue(const QString& s) -{ - slotCalcValue(s.toDouble()); -} -/* -------------------------------------------------------- */ -void WDoubleLineEdit::setValue(double f) -{ - m_bInternal=true; - slotCalcValue(f); -} - -void WDoubleLineEdit::setIntValue(int f) -{ - setValue(static_cast(f)); -} - -/* -------------------------------------------------------- */ -void WDoubleLineEdit::setValuePercent(int p) -{ - if (!m_bInternal) - setValue(m_fMinValue + (m_fMaxValue - m_fMinValue)*((double)p)/99.0); - else - m_bInternal=false; -} - -/* -------------------------------------------------------- */ -int WDoubleLineEdit::valuePercent() -{ - return ((int)(99.0*(m_fValue - m_fMinValue)/(m_fMaxValue - m_fMinValue))); -} - -/* -------------------------------------------------------- */ -void WDoubleLineEdit::keyPressEvent(QKeyEvent *e) -{ - if (e->key() == Qt::Key_Escape) - { - this->setText(QString("%1").arg(m_fValue)); - this->setCursorPosition(0); - this->setSelection(0, 0); - } - else - QLineEdit::keyPressEvent(e); -} -/* -------------------------------------------------------- */ -void WDoubleLineEdit::mouseMoveEvent(QMouseEvent *event) { - if(m_isDragging){ - const double dt=(event->x() - m_prevMousePosition.x())/100.0 ; - m_prevMousePosition = event->pos() ; - slotCalcValue(dt + m_fValue, false) ; - emit valueEdited(m_fValue) ; - } - QLineEdit::mouseMoveEvent(event) ; -} - -void WDoubleLineEdit::mousePressEvent(QMouseEvent *event) { - if(event->button() == Qt::LeftButton){ - m_isDragging = true ; - m_prevMousePosition = event->pos() ; - } - QLineEdit::mousePressEvent(event) ; -} - -void WDoubleLineEdit::mouseReleaseEvent(QMouseEvent *event) { - if(event->button() == Qt::LeftButton){ - m_isDragging = false ; - m_prevMousePosition = event->pos() ; - } - QLineEdit::mouseReleaseEvent(event) ; -} diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/WDoubleLineEdit.h b/Sofa/GUI/Qt/src/sofa/gui/qt/WDoubleLineEdit.h deleted file mode 100644 index 27f4aeedd8c..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/WDoubleLineEdit.h +++ /dev/null @@ -1,101 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -/* -------------------------------------------------------- */ -#pragma once -#include -#include -#include - -#include - -/* -------------------------------------------------------- */ -class SOFA_GUI_QT_API WDoubleLineEdit : public QLineEdit -{ - Q_OBJECT - Q_PROPERTY( double minValue READ minValue WRITE setMinValue ) - Q_PROPERTY( double maxValue READ maxValue WRITE setMaxValue ) - Q_PROPERTY( double Value READ Value WRITE setValue ) - Q_PROPERTY( int intValue READ intValue WRITE setIntValue ) - -protected: - - int m_iPercent; - double m_fMinValue; - double m_fMaxValue; - bool m_bFirst; - mutable double m_fValue; - QDoubleValidator *m_DblValid; - double m_bInternal; - - bool m_isDragging ; - QPoint m_prevMousePosition ; - - void checkValue(); - void keyPressEvent(QKeyEvent *) override; - - void mouseMoveEvent(QMouseEvent *) override ; - void mousePressEvent(QMouseEvent *) override ; - void mouseReleaseEvent(QMouseEvent *) override ; - -public: - - WDoubleLineEdit(QWidget *parent,const char *name); - - double minValue() const { return (m_fMinValue);} - double getMinValue() { emit(returnPressed()); return minValue();} - void setMinValue(double f) {m_fMinValue=f; m_DblValid->setBottom(m_fMinValue); } - - - double maxValue() const { return (m_fMaxValue);} - double getMaxValue() { emit(returnPressed()); return maxValue();} - void setMaxValue(double f) {m_fMaxValue=f; m_DblValid->setTop(m_fMaxValue); } - - double Value() const { return (m_fValue);} - double getValue() { emit(returnPressed()); return Value();} - void setValue(double f); - - int intValue() const { return static_cast(m_fValue);} - int getIntValue() { emit(returnPressed()); return intValue();} - void setIntValue(int f); - - int valuePercent(); - - //Return the value displayed: WARNING!! NO VALIDATION IS MADE! - double getDisplayedValue() {return text().toDouble();} - int getIntDisplayedValue() {return static_cast(text().toDouble());} - -signals: - void valueEdited(double) ; - void valueChanged(double); - void valuePercentChanged(int); - -protected slots: - - void slotCalcValue(const QString&); - void slotCalcValue(double, bool isEditted=false); - void slotReturnPressed(); - -public slots: - - void setValuePercent(int p); - -}; diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/WindowProfiler.ui b/Sofa/GUI/Qt/src/sofa/gui/qt/WindowProfiler.ui deleted file mode 100644 index fd2aff80bd8..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/WindowProfiler.ui +++ /dev/null @@ -1,233 +0,0 @@ - - - WindowProfiler - - - true - - - - 0 - 0 - 950 - 760 - - - - AdvancedTimer profiler window - - - false - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - QLayout::SetFixedSize - - - - - - 75 - true - true - - - - Summary - - - - - - - - - Step Number: - - - - - - - Overhead due to the process of the timers. Close this window to avoid the overhead. - - - Overhead (ms) - - - - - - - 0 - - - - - - - Time (ms) - - - - - - - 0 - - - - - - - 0 - - - - - - - Number of timers in the current step - - - Timers Counter: - - - - - - - 0 - - - - - - - - - Qt::Horizontal - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Collapse all - - - - - - - - - - Expand all - - - - - - - - - - - - 5 - - - - 1 - - - - - 2 - - - - - 3 - - - - - 4 - - - - - 5 - - - - - - - - - - - - - - - - diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/WindowVisitor.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/WindowVisitor.cpp deleted file mode 100644 index 1129c61fb2b..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/WindowVisitor.cpp +++ /dev/null @@ -1,276 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#include - -#include -#include -#include -#include -#include - -namespace sofa::gui::qt -{ -QPixmap *WindowVisitor::icons[WindowVisitor::OTHER+1]; - -WindowVisitor::WindowVisitor(QWidget *parent) - : QDialog(parent) -{ - setupUi(this); - - this->setContextMenuPolicy(Qt::CustomContextMenu); - connect(graphView, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT( rightClick(const QPoint&))); - - QImage * img[OTHER+1]; - img[NODE] = new QImage(10,10,QImage::Format_ARGB32); - - img[NODE]->fill(qRgba(0,0,0,0)); - // Workaround for qt 3.x where fill() does not set the alpha channel - for (int y=0 ; y < 10 ; y++) - for (int x=0 ; x < 10 ; x++) - img[NODE]->setPixel(x,y,qRgba(0,0,0,0)); - - for (int y=0 ; y < 10 ; y++) - img[NODE]->setPixel(0,y,qRgba(0,0,0,255)); - - - img[OTHER] = new QImage(img[NODE]->copy()); - - //BORDER!!!!------------------------------------- - for (int x=1; x <10 ; x++) - { - img[NODE] ->setPixel(x,0,qRgba(0,0,0,255)); - img[NODE] ->setPixel(x,9,qRgba(0,0,0,255)); - } - for (int y=0 ; y < 10 ; y++) - { - img[NODE] ->setPixel(0,y,qRgba(0,0,0,255)); - img[NODE] ->setPixel(9,y,qRgba(0,0,0,255)); - } - //----------------------------------------------- - - img[COMMENT] = new QImage(img[NODE]->copy()); - img[COMPONENT] = new QImage(img[NODE]->copy()); - - for (int y=0 ; y < 9 ; y++) - for (int x=0 ; x < 9 ; x++) - { - img[NODE] ->setPixel(x,y,qRgba(125,125,125,255)); - img[COMMENT]->setPixel(x,y,qRgba(255,255,0,255)); - img[COMPONENT]->setPixel(x,y,qRgba(255,0,255,255)); - } - - for (int y=0 ; y < 5 ; y++) - for (int x=0 ; x < 2*y+1 ; x++) - { - img[OTHER]->setPixel(x,y,qRgba(0,0,255,255)); - } - for (int y=5 ; y < 10 ; y++) - for (int x=0 ; x < 2*(10-y) ; x++) - { - img[OTHER] ->setPixel(x,y,qRgba(0,0,255,255)); - } - icons[NODE] = new QPixmap(QPixmap::fromImage(*img[NODE])); - icons[COMMENT] = new QPixmap(QPixmap::fromImage(*img[COMMENT])); - icons[COMPONENT] = new QPixmap(QPixmap::fromImage(*img[COMPONENT])); - icons[OTHER] = new QPixmap(QPixmap::fromImage(*img[OTHER])); - - statsWidget=new QWidget(splitterStats); - - QGridLayout *statsLayout=new QGridLayout(statsWidget); - - typeOfCharts = new QComboBox(statsWidget); - QStringList list; - list << "Latest execution" - << "Most time-consuming step" - << "Total execution time"; - - - splitterStats->addWidget(statsWidget); - typeOfCharts->insertItems(0,list); - - chartsComponent=new ChartsWidget("Component (name)", statsWidget); - chartsVisitor =new ChartsWidget("Visitor", statsWidget); - - statsLayout->addWidget(typeOfCharts,0,0); - statsLayout->addWidget(chartsComponent,1,0); - statsLayout->addWidget(chartsVisitor,2,0); - connect(typeOfCharts, SIGNAL(activated(int)), this, SLOT(setCurrentCharts(int))); - - - //Add Control Panel - controlPanel = new QVisitorControlPanel(splitterWindow); - connect( controlPanel, SIGNAL(focusOn(QString)), this, SLOT(focusOn(QString))); - connect( controlPanel, SIGNAL(clearGraph()), this, SLOT(clearGraph())); - controlPanel->setMaximumHeight(110); - -} - - -void WindowVisitor::setCharts(std::vector< dataTime >&latestC, std::vector< dataTime >&maxTC, std::vector< dataTime >&totalC, - std::vector< dataTime >&latestV, std::vector< dataTime >&maxTV, std::vector< dataTime >&totalV) -{ - componentsTime=latestC; - componentsTimeMax=maxTC; - componentsTimeTotal=totalC; - visitorsTime=latestV; - visitorsTimeMax=maxTV; - visitorsTimeTotal=totalV; - setCurrentCharts(typeOfCharts->currentIndex()); -} - - - -void WindowVisitor::setCurrentCharts(int type) -{ - switch(type) - { - case 0: - chartsComponent->setChart(componentsTime, componentsTime.size()); - chartsVisitor->setChart(visitorsTime, visitorsTime.size()); - break; - case 1: - chartsComponent->setChart(componentsTimeMax, componentsTimeMax.size()); - chartsVisitor->setChart(visitorsTimeMax, visitorsTimeMax.size()); - break; - case 2: - chartsComponent->setChart(componentsTimeTotal, componentsTimeTotal.size()); - chartsVisitor->setChart(visitorsTimeTotal, visitorsTimeTotal.size()); - break; - } -} - -void WindowVisitor::rightClick( const QPoint& point) -{ - QTreeWidgetItem *item = graphView->itemAt( point ); - - if (!item) return; - - QMenu *contextMenu = new QMenu ( this ); - contextMenu->setObjectName( "ContextMenu"); - - if(item->childCount()) - { - contextMenu->addAction("Collapse", this,SLOT(collapseNode())); - contextMenu->addAction("Expand", this,SLOT(expandNode())); - - contextMenu->exec ( this->mapToGlobal(point)); - } -} - -void WindowVisitor::focusOn(QString text) -{ - if(graphView->topLevelItemCount() < 1) return; - - bool found = false; - for(int i=0 ; itopLevelItemCount() && !found; i++) - { - QTreeWidgetItem *item = graphView->topLevelItem(i); - found = setFocusOn(item, text); - } - - graphView->clearSelection(); - -} - -bool WindowVisitor::setFocusOn(QTreeWidgetItem *item, QString text) -{ - for ( int c=0; ccolumnCount(); ++c) - { - if (item->text(c).contains(text, Qt::CaseInsensitive)) - { - if ( !graphView->currentItem() || - graphView->visualItemRect(graphView->currentItem()).topLeft().y() < graphView->visualItemRect(item).topLeft().y() ) - { -// graphView->ensureItemVisible(item); - graphView->scrollToItem(item); - graphView->clearSelection(); - graphView->setCurrentItem(item); - item->setExpanded(true); - return true; - } - } - } - - bool found = false; - for(int i=0 ; ichildCount() && !found; i++) - { - QTreeWidgetItem *child = item->child(i); - found = setFocusOn(child, text); - - } - - return found; -} - -void WindowVisitor::expandNode() -{ - expandNode(graphView->currentItem()); -} - -void WindowVisitor::expandNode(QTreeWidgetItem* item) -{ - if (!item) return; - - item->setExpanded( true ); - if ( item != NULL ) - { - QTreeWidgetItem* child; - - for(int i=0 ; ichildCount() ; i++) - { - child = item->child(i); - child->setExpanded( true ); - expandNode(item); - } - } -} - -void WindowVisitor::collapseNode() -{ - collapseNode(graphView->currentItem()); - QTreeWidgetItem* item = graphView->currentItem(); - QTreeWidgetItem* child; - - for(int i=0 ; ichildCount() ; i++) - { - child = item->child(i); - collapseNode(child); - } - - graphView->currentItem()->setExpanded(true); -} -void WindowVisitor::collapseNode(QTreeWidgetItem* item) -{ - if (!item) return; - - item->setExpanded(false); - - QTreeWidgetItem* child; - - for(int i=0 ; ichildCount() ; i++) - { - child = item->child(i); - collapseNode(child); - } -} - -} // namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/WindowVisitor.h b/Sofa/GUI/Qt/src/sofa/gui/qt/WindowVisitor.h deleted file mode 100644 index 70a2dd602aa..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/WindowVisitor.h +++ /dev/null @@ -1,125 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -namespace sofa::gui::qt -{ - -class WindowVisitor: public QDialog, public Ui_VisitorGUI -{ - Q_OBJECT -public: - enum componentType {NODE, COMMENT, COMPONENT, VECTOR, OTHER}; - WindowVisitor(QWidget *parent); - - void collapseNode(QTreeWidgetItem* item); - void expandNode(QTreeWidgetItem* item); - - void setCharts(std::vector< dataTime >&latestC, std::vector< dataTime >&maxTC, std::vector< dataTime >&totalC, - std::vector< dataTime >&latestV, std::vector< dataTime >&maxTV, std::vector< dataTime >&totalV); - - -public slots: - void setCurrentCharts(int); - - void rightClick( const QPoint& point); - - void collapseNode(); - void expandNode(); - - void focusOn(QString focus); - - static componentType getComponentType(std::string name) - { - if (name == "Node") - return NODE; - else if (name == "Component") - return COMPONENT; - else if (name == "Vector") - return VECTOR; - else - return OTHER; - } - - static QPixmap* getPixmap(componentType t) {return icons[t];} - - void closeEvent( QCloseEvent* ) - { - emit(WindowVisitorClosed(false)); - hide(); - clearGraph(); - } - - void clearGraph() - { - graphView->clear(); - chartsComponent->clear(); - chartsVisitor->clear(); - - componentsTime.clear(); - componentsTimeMax.clear(); - componentsTimeTotal.clear(); - - - visitorsTime.clear(); - visitorsTimeMax.clear(); - visitorsTimeTotal.clear(); - } - -signals: - void WindowVisitorClosed(bool); -public: - - QWidget *statsWidget; -protected: - bool setFocusOn(QTreeWidgetItem *item, QString text); - - static QPixmap *icons[OTHER+1]; - - std::vector< dataTime > componentsTime; - std::vector< dataTime > visitorsTime; - - std::vector< dataTime > componentsTimeTotal; - std::vector< dataTime > visitorsTimeTotal; - - std::vector< dataTime > componentsTimeMax; - std::vector< dataTime > visitorsTimeMax; - - QVisitorControlPanel *controlPanel; - ChartsWidget *chartsComponent; - ChartsWidget *chartsVisitor; - QComboBox *typeOfCharts; -}; - -} // namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/config.h.in b/Sofa/GUI/Qt/src/sofa/gui/qt/config.h.in deleted file mode 100644 index 139c5da8873..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/config.h.in +++ /dev/null @@ -1,52 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once - -#include - -#cmakedefine01 SOFA_GUI_QT_ENABLE_QTVIEWER -#cmakedefine01 SOFA_GUI_QT_ENABLE_QGLVIEWER -#cmakedefine01 SOFA_GUI_QT_ENABLE_NATIVE_MENU -#cmakedefine01 SOFA_GUI_QT_ENABLE_VSYNC - -#cmakedefine01 SOFA_GUI_QT_HAVE_QT5_CHARTS -#cmakedefine01 SOFA_GUI_QT_HAVE_QT6_CHARTS -#cmakedefine01 SOFA_GUI_QT_HAVE_QT5_WEBENGINE -#cmakedefine01 SOFA_GUI_QT_HAVE_QT6_WEBENGINECORE - -#define SOFA_GUI_QT_HAVE_QT_CHARTS (SOFA_GUI_QT_HAVE_QT5_CHARTS || SOFA_GUI_QT_HAVE_QT6_CHARTS) -#define SOFA_GUI_QT_HAVE_QT_WEBENGINE (SOFA_GUI_QT_HAVE_QT5_WEBENGINE || SOFA_GUI_QT_HAVE_QT6_WEBENGINECORE) - -// Equals to SOFA_GUI_QT_ENABLE_NODEGRAPH -#cmakedefine01 SOFA_GUI_QT_HAVE_NODEEDITOR - -#ifdef SOFA_BUILD_SOFA_GUI_QT -# define SOFA_GUI_QT_API SOFA_EXPORT_DYNAMIC_LIBRARY -#else -# define SOFA_GUI_QT_API SOFA_IMPORT_DYNAMIC_LIBRARY -#endif - -namespace sofa::gui::qt -{ - constexpr const char* MODULE_NAME = "@PROJECT_NAME@"; - constexpr const char* MODULE_VERSION = "@PROJECT_VERSION@"; -} // namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/dataGraph/SofaComponentNodeModel.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/dataGraph/SofaComponentNodeModel.cpp deleted file mode 100644 index c724024a69a..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/dataGraph/SofaComponentNodeModel.cpp +++ /dev/null @@ -1,213 +0,0 @@ -#include "SofaComponentNodeModel.h" -#include - -SofaComponentNodeData::SofaComponentNodeData() - : m_bData(nullptr) -{ - -} - -SofaComponentNodeData::SofaComponentNodeData(sofa::core::objectmodel::BaseData* bData) - : m_bData(bData) -{ - -} - -sofa::core::objectmodel::BaseData* SofaComponentNodeData::getData() -{ - //m_bData->updateIfDirty(); ?? - return m_bData; -} - -NodeDataType SofaComponentNodeData::type() const -{ - return NodeDataType{ "SofaComponentNodeData", - "My Sofa Node Data" }; -} - - -SofaComponentNodeModel::SofaComponentNodeModel(std::string name) - : NodeDataModel() - , debugNodeGraph(true) - , m_SofaObject(nullptr) -{ - m_uniqName = QString::fromStdString(name); - m_caption = m_uniqName; -} - -SofaComponentNodeModel::SofaComponentNodeModel(sofa::core::objectmodel::BaseObject* _sofaObject, bool debugMode) - : NodeDataModel() - , debugNodeGraph(debugMode) - , m_SofaObject(_sofaObject) -{ - if (m_SofaObject == nullptr) - { - msg_error("SofaComponentNodeModel") << "Sofa BaseObject is null, Node graph initialisation failed."; - m_uniqName = "ErrorNode"; - } - else - { - parseSofaObjectData(); - } -} - - -void SofaComponentNodeModel::parseSofaObjectData() -{ - if (m_SofaObject == nullptr) - { - msg_error("SofaComponentNodeModel") << "Sofa BaseObject is null, Node graph parseSofaObjectData failed."; - return; - } - - // first add this object name as first Data (to be used for the links representation) - QString SObjectName = QString::fromStdString(m_SofaObject->getName()); - m_data.push_back(std::pair(SObjectName, "name")); - - // parse links - const sofa::core::objectmodel::Base::VecLink& links = m_SofaObject->getLinks(); - for (auto link : links) - { - const std::string& name = link->getName(); - // ignore unnamed link - if (link->getName().empty()) - continue; - - // ignore link to context - if (link->getName() == "context") - continue; - - if (!link->storePath() && 0 == link->getSize()) - continue; - - const std::string valuetype = link->getValueTypeString(); - - msg_info_when(debugNodeGraph, "SofaComponentNodeModel") << "## link: " << name << " | link->getSize(): " << link->getSize() << " | valuetype: " << valuetype << " | path: " << link->storePath(); - - std::string linkPath = link->getLinkedPath(); - linkPath.erase(0, 1); // remove @ - std::size_t found = linkPath.find_last_of("/"); - if (found != std::string::npos) // remove path - linkPath.erase(0, found); - - msg_info_when(debugNodeGraph, "SofaComponentNodeModel") << " # baselink: " << linkPath; - QString parentObject = QString::fromStdString(linkPath); - m_dataConnections[SObjectName] = std::pair(parentObject, parentObject); - } - - // parse all Data - sofa::type::vector allData = m_SofaObject->getDataFields(); - for (auto data : allData) - { - QString name = QString::fromStdString(data->getName()); - QString group = QString::fromStdString(data->getGroup()); - - bool toBeIgnored = false; - for (auto idata : ignoredData) - { - if (name.compare(idata) == 0) - { - toBeIgnored = true; - break; - } - } - - if (group == "Visualization") - toBeIgnored = true; - - - if (toBeIgnored) - continue; - - sofa::core::objectmodel::BaseData* pData = data->getParent(); - if (pData) - { - QString parentObject = QString::fromStdString(pData->getOwner()->getName()); - QString parentData = QString::fromStdString(pData->getName()); - msg_info_when(debugNodeGraph, "SofaComponentNodeModel") << "- Parent: " << pData->getName() << " owwner: " << pData->getOwner()->getName(); - m_dataConnections[name] = std::pair(parentObject, parentData); - } - - if (!group.isEmpty()) - { - msg_info_when(debugNodeGraph, "SofaComponentNodeModel") << name.toStdString() << " -> " << data->getGroup(); - } - m_data.push_back(std::pair(name, QString::fromStdString(data->getValueTypeString()))); - m_Nodedata.push_back(std::make_shared(data)); - } -} - - -QtNodes::PortIndex SofaComponentNodeModel::getDataInputId(const QString& dataName) -{ - int cpt = 0; - for (auto data : m_data) - { - if (data.first.compare(dataName) == 0) - return cpt; - - cpt++; - } - - return QtNodes::INVALID; -} - - -unsigned int SofaComponentNodeModel::nPorts(PortType portType) const -{ - return m_data.size(); -} - -NodeDataType SofaComponentNodeModel::dataType(PortType portType, PortIndex portIndex) const -{ - if (portIndex >= 0 && portIndex < m_data.size()) - { - NodeDataType NType; - NType.id = m_data[portIndex].second; - NType.name = m_data[portIndex].first; - return NType; - } - - return SofaComponentNodeData().type(); -} - -std::shared_ptr SofaComponentNodeModel::outData(PortIndex port) -{ - // because the first port is the name of the component not stored in m_Nodedata: - port--; - - if (port > 0 && port < m_Nodedata.size()) - return m_Nodedata[port]; - else { - msg_warning(m_caption.toStdString()) << "Method SofaComponentNodeModel::outData port: " << port << " out of bounds: " << m_Nodedata.size(); - return nullptr; - } -} - -void SofaComponentNodeModel::setInData(std::shared_ptr data, int port) -{ - auto parentNodeData = std::dynamic_pointer_cast(data); - if (parentNodeData == nullptr) - { - msg_warning(m_caption.toStdString()) << "Method SofaComponentNodeModel::setInData SofaComponentNodeData cast failed."; - return; - } - - // because the first port is the name of the component not stored in m_Nodedata: - port--; - - if (port < 0 || port >= m_Nodedata.size()) - { - msg_warning(m_caption.toStdString()) << "Method SofaComponentNodeModel::setInData port: " << port << " out of bounds: " << m_Nodedata.size(); - return; - } - - // here you will implement the Data setParent in SOFA! - std::shared_ptr childNodeData = this->m_Nodedata[port]; - sofa::core::objectmodel::BaseData* childData = childNodeData->getData(); - sofa::core::objectmodel::BaseData* parentData = parentNodeData->getData(); - - msg_info_when(debugNodeGraph, m_caption.toStdString()) << "Here connect: {" << parentData->getOwner()->getName() << ", " << parentData->getName() << "} -> {" - << childData->getOwner()->getName() << ", " << childData->getName() << "}"; - -} \ No newline at end of file diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/dataGraph/SofaComponentNodeModel.h b/Sofa/GUI/Qt/src/sofa/gui/qt/dataGraph/SofaComponentNodeModel.h deleted file mode 100644 index 19f7b102595..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/dataGraph/SofaComponentNodeModel.h +++ /dev/null @@ -1,119 +0,0 @@ -#pragma once - -#define NODE_EDITOR_SHARED - -#include - -#include -#include - -#include - -using QtNodes::NodeData; -using QtNodes::NodeDataType; -using QtNodes::NodeDataModel; -using QtNodes::PortType; -using QtNodes::PortIndex; - -namespace sofa::core::objectmodel -{ - class BaseObject; - class BaseData; -} // namespace sofa::core::objectmodel - -/// The class can potentially incapsulate any user data which -/// need to be transferred within the Node Editor graph -class SofaComponentNodeData : public NodeData -{ -public: - SofaComponentNodeData(); - SofaComponentNodeData(sofa::core::objectmodel::BaseData* bData); - - NodeDataType type() const override; - - sofa::core::objectmodel::BaseData* getData(); - -protected: - sofa::core::objectmodel::BaseData* m_bData; -}; - - -static const char* ignoredData[] = { "name", "printLog", "tags", "bbox", "listening", "componentState" }; - -//------------------------------------------------------------------------------ -/** -* This Class is a NodeDataModel specialisation to represent a Sofa component on the QtNodes graph. -* It will take a SOFA BaseObject as target and parse all Data, storing Data, Links and connections with parents components. -*/ -class SofaComponentNodeModel : public NodeDataModel -{ - Q_OBJECT - -public: - /// Default empty Object constructor with 0 Data - SofaComponentNodeModel(std::string name = "EmptyNode"); - - /// constructor with a Sofa BaseObject as target - SofaComponentNodeModel(sofa::core::objectmodel::BaseObject* _sofaObject, bool debugMode = false); - - virtual ~SofaComponentNodeModel() {} - - /// Interface for caption. - QString caption() const override { return m_caption; } - void setCaption(std::string str) { m_caption = QString::fromStdString(str); } - - /// Interface for name. - QString name() const override { return m_uniqName; } - - /// Return the number of Data. - size_t getNbrData() { return m_data.size(); } - - /// Return the number of connection with other Node components - size_t getNbrConnections() { return m_dataConnections.size(); } - - /// return the list of connections @sa m_dataConnections - const std::map >& getDataConnections() { return m_dataConnections; } - - /// Return the PortIndex of a Data given its Name. - QtNodes::PortIndex getDataInputId(const QString& dataName); - - ///Interface for QtNodes - ///{ - /// Override method to return the number of ports - unsigned int nPorts(PortType portType) const override; - - /// Override method to give the type of Data per Port - NodeDataType dataType(PortType portType, PortIndex portIndex) const override; - - /// Override method to return the NodeData given a port - std::shared_ptr outData(PortIndex port) override; - - /// Override method to set input Data - void setInData(std::shared_ptr data, int port) override; - - /// Override method for more advance node gui. Not yet used. - QWidget* embeddedWidget() override { return nullptr; } - ///} - -protected: - /// Internal method to parse all Data of a Sofa component and create the corresponding ports - void parseSofaObjectData(); - -protected: - QString m_caption; ///< caption to be display on the Graph - QString m_uniqName; ///< unique name to refer to this node - - bool debugNodeGraph; ///< parameter to activate graph logs. False by default. - - /// Vector of Data/port hold by this component/Node. vector of pair{DataName, DataType} - std::vector < std::pair < QString, QString> > m_data; - - /// vector of SofaComponentNodeData class holding pointer to the Data. To replace @sa m_data when api is validated. - std::vector < std::shared_ptr > m_Nodedata; - - /// Map to store all connection between this node and other. map.key = this data name, map.value = pair{ComponentName, DataName} - std::map > m_dataConnections; - - /// Pointer to the sofa object. - sofa::core::objectmodel::BaseObject* m_SofaObject; -}; diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/fwd.h b/Sofa/GUI/Qt/src/sofa/gui/qt/fwd.h deleted file mode 100644 index 2c7bddde741..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/fwd.h +++ /dev/null @@ -1,30 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include - -namespace sofa::gui::qt -{ - -class QSofaListView; - -} diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/init.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/init.cpp deleted file mode 100644 index 1012b86a10b..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/init.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU Lesser General Public License as published by * -* the Free Software Foundation; either version 2.1 of the License, or (at * -* your option) any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * -* for more details. * -* * -* You should have received a copy of the GNU Lesser General Public License * -* along with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#include - -#include -#include -#include - -namespace sofa::gui::qt -{ - - extern "C" { - SOFA_EXPORT_DYNAMIC_LIBRARY void initExternalModule(); - SOFA_EXPORT_DYNAMIC_LIBRARY const char* getModuleName(); - SOFA_EXPORT_DYNAMIC_LIBRARY const char* getModuleVersion(); - } - - void initExternalModule() - { - init(); - } - - const char* getModuleName() - { - return MODULE_NAME; - } - - const char* getModuleVersion() - { - return MODULE_VERSION; - } - - void init() - { - static bool first = true; - if (first) - { -#if SOFA_GUI_QT_ENABLE_QGLVIEWER - sofa::gui::common::GUIManager::RegisterGUI("qglviewer", &sofa::gui::qt::RealGUI::CreateGUI, nullptr, 3); -#endif - -#if SOFA_GUI_QT_ENABLE_QTVIEWER - sofa::gui::common::GUIManager::RegisterGUI("qt", &sofa::gui::qt::RealGUI::CreateGUI, nullptr, 2); -#endif - - // if ObjectStateListener is triggered (either by changing the message number, or by - // changing the component name) in a thread different than the main thread (=UI thread), - // a Qt event is launched through the queued connection system. For that, the event - // parameters must be known to Qt's meta-object system. This is what qRegisterMetaType - // does in the following instruction. - qRegisterMetaType >("QVector"); - - qInstallMessageHandler(redirectQtMessages); - - first = false; - } - } - -} // namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/init.h b/Sofa/GUI/Qt/src/sofa/gui/qt/init.h deleted file mode 100644 index 09befd79dd1..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/init.h +++ /dev/null @@ -1,28 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU Lesser General Public License as published by * -* the Free Software Foundation; either version 2.1 of the License, or (at * -* your option) any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * -* for more details. * -* * -* You should have received a copy of the GNU Lesser General Public License * -* along with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include - -namespace sofa::gui::qt -{ - void SOFA_GUI_QT_API init(); -} // namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/panels/QDocBrowser.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/panels/QDocBrowser.cpp deleted file mode 100644 index 0e0ae9693f4..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/panels/QDocBrowser.cpp +++ /dev/null @@ -1,318 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#include -#include -#include -#include -#include -#include - -#include -using sofa::simulation::SceneLoaderFactory ; - -#include -using sofa::helper::system::DataRepository ; - -#include -using sofa::helper::system::FileSystem ; - -#include "QDocBrowser.h" -#include "../RealGUI.h" -#include - -#include - -using namespace sofa::gui::common; - -namespace sofa::gui::qt -{ - -///////////////////////////// PRIVATE OBJECTS ////////////////////////////////// -/// May be moved to their own .cpp/.hh if one day someone needs them. - -SofaEnrichedPage::SofaEnrichedPage(QObject* parent) : QWebEnginePage(parent) -{ -} - -bool SofaEnrichedPage::isSofaTarget(const QUrl& u) -{ - if( u.fileName() == QString("sofa") && u.hasQuery() ) - { - return true ; - }else if( u.isLocalFile() && ! u.hasQuery() ) - { - return true ; - } - return false; -} - -bool SofaEnrichedPage::acceptNavigationRequest(const QUrl & url, - QWebEnginePage::NavigationType type, - bool ) -{ - if (type == QWebEnginePage::NavigationTypeLinkClicked) - { - if( isSofaTarget(url) ) - { - emit linkClicked(url); - return false; - } - } - return true; -} - - - -//////////////////////////////// BrowserHistory //////////////////////////////// -/// Hold an history entry which include the .html file, the sofa scene file and the -/// root directory. This was needed to implement the backward function of the -/// doc browser as the internal one implemented in QTextBrowser was only capable -/// of storing the .html file. -class BrowserHistoryEntry -{ -public: - std::string m_htmlfile ; - std::string m_scenefile ; - std::string m_rootdir ; - - BrowserHistoryEntry(const std::string& html, - const std::string& scene, - const std::string& rootdir) - { - m_htmlfile = html ; - m_scenefile = scene ; - m_rootdir = rootdir ; - } -}; - -class BrowserHistory -{ -public: - std::vector m_history ; - void push(const std::string& html, const std::string& scene, const std::string& rootdir) ; - BrowserHistoryEntry current() ; - BrowserHistoryEntry pop() ; - int size() ; -}; - -int BrowserHistory::size() -{ - return m_history.size() ; -} - -BrowserHistoryEntry BrowserHistory::current(){ - if(m_history.size()==0) - throw std::exception() ; - - return *(m_history.end()-1) ; -} - -void BrowserHistory::push(const std::string& html, const std::string& scene, const std::string& rootdir) -{ - m_history.push_back(BrowserHistoryEntry(html, scene, rootdir)); -} - -BrowserHistoryEntry BrowserHistory::pop() -{ - if(m_history.size()==0) - throw std::exception() ; - - BrowserHistoryEntry entry = *(m_history.end()-1) ; - - if(m_history.size()>1) - m_history.pop_back() ; - - return entry ; -} -//////////////////////////////////////////////////////////////////////////////// - - -QString asQStr(const std::string& c) -{ - return QString(c.c_str()) ; -} - -std::string asStr(const QString& s) -{ - return s.toStdString() ; -} - - -DocBrowser::~DocBrowser() -{ - -} - -DocBrowser::DocBrowser(RealGUI* g) : QDialog(g) -{ - m_realgui = g ; - m_browserhistory = new BrowserHistory() ; - - /// Create a top level window - setGeometry(0,0,600,600); - setWindowTitle("Doc browser"); - - /// Create the first button line - QVBoxLayout *verticalLayout = new QVBoxLayout(this); - QWidget *bg = new QWidget() ; - QHBoxLayout *bgl = new QHBoxLayout(bg) ; - bg->setLayout(bgl); - verticalLayout->addWidget(bg) ; - - QPushButton* prev=new QPushButton() ; - prev->setIcon(QIcon(asQStr(common::GuiDataRepository.getFile("icons/back.png")))) ; - connect(prev, SIGNAL(clicked()), this, SLOT(goToPrev())); - bgl->addWidget(prev); - - /// Here we do an autocompletion and search for components. - m_lineEdit = new QLineEdit(); - bgl->addWidget(m_lineEdit); - - QPushButton* home=new QPushButton(); - home->setIcon(QIcon(asQStr(common::GuiDataRepository.getFile("icons/home.png")))); - connect(home, SIGNAL(clicked()), this, SLOT(goToHome())); - bgl->addWidget(home) ; - - /// Add the html browser to visualize the documentation - m_htmlPage = new QWebEngineView(this); - SofaEnrichedPage* pp = new SofaEnrichedPage(); - m_htmlPage->setPage(pp); - - verticalLayout->addWidget(m_htmlPage, 1); - - /// We want click on internal links (file://) to be routed to the the goTo function to - /// load the sofa file. - connect(m_htmlPage, SIGNAL(urlChanged(const QUrl&)), this, SLOT(goTo(const QUrl&))); - connect(pp, SIGNAL(linkClicked(const QUrl&)), this, SLOT(onLinkClicked(const QUrl&))); -} - -void DocBrowser::loadHtml(const std::string& filename) -{ - if (filename.empty()) - { - return; - } - - std::string htmlfile = filename ; - std::string rootdir = FileSystem::getParentDirectory(filename) ; - - const QUrl currenturl = m_htmlPage->page()->url() ; - - if(currenturl.isLocalFile() && currenturl.path() == asQStr(htmlfile)) - { - return ; - } - - const std::string extension=FileSystem::getExtension(filename); - htmlfile.resize(htmlfile.size()-extension.size()-1); - htmlfile+=".html"; - - /// Check if either the scene specific html or default provided can be loaded. - /// If so...load the page and add the entry into the history. - if (!DataRepository.findFile(htmlfile, "", NULL)) - return; - - m_htmlPage->load( QUrl::fromLocalFile(QString(htmlfile.c_str())) ); - - constexpr bool showView = true ; - setVisible(showView); -} - -void DocBrowser::goToPrev() -{ - m_htmlPage->pageAction(QWebEnginePage::Back)->trigger() ; -} - -void DocBrowser::onLinkClicked(const QUrl& u) -{ - msg_info("DocBrowser") << " query to load " << asStr(u.path()) ; - if( u.fileName() == QString("sofa") && u.hasQuery() ) - { - m_realgui->playpauseGUI(true) ; - return ; - } - - if( u.isLocalFile() && ! u.hasQuery() ) - { - const QFileInfo theFile(u.toLocalFile()); - const std::string sofafile = asStr( theFile.absoluteDir().absoluteFilePath(u.toLocalFile()) ); - const std::string extension = FileSystem::getExtension(sofafile) ; - - /// Check if the path is pointing to a sofa scene. If so - /// open the scene - const auto exts = SceneLoaderFactory::getInstance()->extensions() ; - if ( std::find(exts.begin(), exts.end(), extension) != exts.end() ) - { - m_realgui->fileOpen(sofafile, false, false) ; - return ; - } - } - m_htmlPage->load(u) ; -} - -void DocBrowser::goTo(const QUrl& u) -{ - msg_info("DocBrowser") << "Go to " << asStr(u.path()) ; - if( u.isLocalFile() && u.hasQuery() ) - { - const QUrlQuery q { u.query() } ; - if( !q.hasQueryItem("sofafile") ) { - msg_info("DocBrowser") << "Does not have associated sofa file. " ; - return ; - } - - const QFileInfo htmlfile(u.toLocalFile()); - const std::string sofafile = asStr( htmlfile.absoluteDir().absoluteFilePath(q.queryItemValue("sofafile")) ); - const std::string extension = FileSystem::getExtension(sofafile) ; - - /// Check if the path is pointing to a sofa scene. If so - /// open the scene - const auto exts = SceneLoaderFactory::getInstance()->extensions() ; - if ( std::find(exts.begin(), exts.end(), extension) == exts.end() ){ - msg_warning("DocBrowser") << "Unsupported sofa file format. " ; - return ; - } - m_realgui->fileOpen(sofafile, false, false) ; - return ; - } -} - -void DocBrowser::goToHome() -{ - loadHtml(GuiDataRepository.getFile("docs/runsofa.html").c_str()); -} - -void DocBrowser::flipVisibility() -{ - if(isVisible()) - hide(); - else - show(); - emit visibilityChanged(isVisible()) ; -} - -void DocBrowser::showEvent(QShowEvent *) -{ - emit visibilityChanged(isVisible()) ; -} - -} // namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/panels/QDocBrowser.h b/Sofa/GUI/Qt/src/sofa/gui/qt/panels/QDocBrowser.h deleted file mode 100644 index 5a7f34d9824..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/panels/QDocBrowser.h +++ /dev/null @@ -1,90 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -/****************************************************************************** -* Contributors: -* - damien.marchal@univ-lille1.fr -*******************************************************************************/ - -#include -#include -#include -#include - -////////////////////////////// FORWARD DECLARATION //////////////////////////// -class QLineEdit ; -class QTextBrowser ; - -namespace sofa::gui::qt -{ - class RealGUI ; - class BrowserHistory ; -} // namespace sofa::gui::qt - - -/////////////////////////////// DECLARATION ////////////////////////////////// -namespace sofa::gui::qt -{ - -class SofaEnrichedPage : public QWebEnginePage -{ - Q_OBJECT -public: - SofaEnrichedPage(QObject* parent = 0) ; - bool isSofaTarget(const QUrl &url) ; - - bool acceptNavigationRequest(const QUrl & url, QWebEnginePage::NavigationType type, bool); -signals: - void linkClicked(const QUrl&); -}; - -class SOFA_GUI_QT_API DocBrowser : public QDialog -{ -Q_OBJECT - -public: - DocBrowser(RealGUI* g) ; - ~DocBrowser() override ; - - void loadHtml(const std::string& filename) ; - virtual void showEvent(QShowEvent*) override ; - -public slots: - void onLinkClicked(const QUrl& url) ; - void goToPrev() ; - void goTo(const QUrl& u) ; - void goToHome() ; - void flipVisibility() ; - -signals: - void visibilityChanged(bool) ; - -private: - /// This is use to retrieve the execution context when browsing the - /// history of sofa scene execution. - BrowserHistory* m_browserhistory ; - - QLineEdit* m_lineEdit; - QWebEngineView* m_htmlPage; - RealGUI* m_realgui ; -} ; - -} // namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/qt.conf.h b/Sofa/GUI/Qt/src/sofa/gui/qt/qt.conf.h deleted file mode 100644 index 25fe40ce48d..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/qt.conf.h +++ /dev/null @@ -1,137 +0,0 @@ -/**************************************************************************** -** Resource object code -** -** Created by: The Resource Compiler for Qt version 5.15.2 -*****************************************************************************/ - -#include -#include -using sofa::helper::system::FileSystem; - -#include -#include -#include -#include -#include - -static std::vector qt_resource_data = {}; - -static const unsigned char qt_resource_name[] = { - // qt - 0x0,0x2, - 0x0,0x0,0x7,0x84, - 0x0,0x71, - 0x0,0x74, - // etc - 0x0,0x3, - 0x0,0x0,0x6c,0xa3, - 0x0,0x65, - 0x0,0x74,0x0,0x63, - // qt.conf - 0x0,0x7, - 0x8,0x74,0xa6,0xa6, - 0x0,0x71, - 0x0,0x74,0x0,0x2e,0x0,0x63,0x0,0x6f,0x0,0x6e,0x0,0x66, -}; - -static const unsigned char qt_resource_struct[] = { - // : - 0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1, - // :/qt - 0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2, - // :/qt/etc - 0x0,0x0,0x0,0xa,0x0,0x2,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x3, - // :/qt/etc/qt.conf - 0x0,0x0,0x0,0x16,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0, -}; - -#ifdef QT_NAMESPACE -# define QT_RCC_PREPEND_NAMESPACE(name) ::QT_NAMESPACE::name -# define QT_RCC_MANGLE_NAMESPACE0(x) x -# define QT_RCC_MANGLE_NAMESPACE1(a, b) a##_##b -# define QT_RCC_MANGLE_NAMESPACE2(a, b) QT_RCC_MANGLE_NAMESPACE1(a,b) -# define QT_RCC_MANGLE_NAMESPACE(name) QT_RCC_MANGLE_NAMESPACE2( \ - QT_RCC_MANGLE_NAMESPACE0(name), QT_RCC_MANGLE_NAMESPACE0(QT_NAMESPACE)) -#else -# define QT_RCC_PREPEND_NAMESPACE(name) name -# define QT_RCC_MANGLE_NAMESPACE(name) name -#endif - -#ifdef QT_NAMESPACE -namespace QT_NAMESPACE { -#endif - -bool qRegisterResourceData(int, const unsigned char *, const unsigned char *, const unsigned char *); -bool qUnregisterResourceData(int, const unsigned char *, const unsigned char *, const unsigned char *); - -#ifdef QT_NAMESPACE -} -#endif - - -namespace sofa::gui::qt { -bool loadQtConfWithCustomPrefix(const std::string& qtConfPath, const std::string& prefix) -{ - if( ! qt_resource_data.empty() ) - { - msg_warning("qt.conf.h") << "loadQtConfWithCustomPrefix can only be called once."; - return false; - } - - // Qt wants paths with slashes - const std::string qtConfPathClean = FileSystem::cleanPath(qtConfPath, FileSystem::SLASH); - const std::string prefixClean = FileSystem::cleanPath(prefix, FileSystem::SLASH); - - if ( ! FileSystem::isDirectory(prefixClean) ) - { - msg_warning("qt.conf.h") << "Directory not found " << prefixClean; - return false; - } - - std::ifstream inputFile(qtConfPathClean); - if ( ! inputFile.is_open() ) - { - msg_warning("qt.conf.h") << "Cannot open file " << qtConfPathClean; - return false; - } - - std::stringstream output; - std::string inputLine; - while ( std::getline(inputFile, inputLine) ) - { - if ( inputLine.find("Prefix") != std::string::npos ) - { - output << " Prefix = " << prefixClean; - } - else - { - output << inputLine; - } -#if defined(WIN32) - output << '\r' << '\n'; -#elif defined(__APPLE__) - output << '\r'; -#else - output << '\n'; -#endif - } - - std::vector data = std::vector(std::istreambuf_iterator(output), std::istreambuf_iterator()); - int dataSize = data.size(); - qt_resource_data.resize(4 + dataSize); - - for ( int i = 0 ; i < 4 + dataSize ; i++ ) - { - if ( i < 4 ) // first 4 bytes are for size - { - qt_resource_data[3 - i] = static_cast( (dataSize >> (i * 8)) & 0xFF ); - } - else // next bytes are for data - { - qt_resource_data[i] = static_cast( data[i - 4] ); - } - } - - return QT_RCC_PREPEND_NAMESPACE(qRegisterResourceData)(1, qt_resource_struct, qt_resource_name, &qt_resource_data[0]); -} -} // namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/resources/RealGUI.qrc b/Sofa/GUI/Qt/src/sofa/gui/qt/resources/RealGUI.qrc deleted file mode 100644 index beb9e99031e..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/resources/RealGUI.qrc +++ /dev/null @@ -1,9 +0,0 @@ - - - icons/collapseAll.svg - icons/expandAll.svg - icons/sceneGraphRefresh-locked.svg - icons/sceneGraphRefresh-dirty.svg - icons/sceneGraphRefresh-unlocked.svg - - diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/back.png b/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/back.png deleted file mode 100644 index 27301b6b526e5e28f123120e33ef5594f228655f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 519 zcmV+i0{H!jP)e zSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{00D1FL_t(Y$L-clPQpMG2k`OU+cqn`tSy)d-s8Yh(CLmt)2BwJz4q*$z|(mLgVW|M$S zBxOaC84)m~j=rxD?<kZ*C8bZwrzYbVbV3Y2Z9*cRkqJP|%m2YL0G-f>>5%nWj%ncQiMa1m zSF?;Dh|IfwFO^cy{NY~}Oe>1w`op02It+uwpa1|)(X#z8u6u(%SNO-H%`Mpte-xkI zbA%I}r>^VteUA>F2k!s?002ov JPDHLkV1k`;+8h7? diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/back.xpm b/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/back.xpm deleted file mode 100644 index a89a22568bd..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/back.xpm +++ /dev/null @@ -1,55 +0,0 @@ -/* XPM */ -static char * back_xpm[] = { -"29 27 25 1", -" c None", -". c #619E78", -"+ c #5E9C76", -"@ c #6AA57E", -"# c #619E7A", -"$ c #6CA681", -"% c #B2DAB7", -"& c #B3DBB7", -"* c #BAE0BD", -"= c #6AA480", -"- c #6BA581", -"; c #6AA47E", -"> c #B1D9B6", -", c #629F79", -"' c #6CA781", -") c #B3DBB8", -"! c #69A37E", -"~ c #619E7B", -"{ c #609F79", -"] c #6BA680", -"^ c #68A37E", -"/ c #69A37D", -"( c #B0D9B5", -"_ c #67A37D", -": c #69A47E", -" ", -" ", -" ", -" ", -" .+ ", -" @+ ", -" #$%+ ", -" #$&*+ ", -" #=&**+ ", -" #-&***+ ", -" #-&****+ ", -" ;>*****+++++++++++++++ ", -" ;>********************+ ", -" .-&*********************+ ", -" ,')*********************+ ", -" !>********************+ ", -" ~$%*****+++++++++++++++ ", -" {]&****+ ", -" ^>***+ ", -" /(**+ ", -" /(*+ ", -" _(+ ", -" :+ ", -" + ", -" ", -" ", -" "}; diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/collapseAll.svg b/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/collapseAll.svg deleted file mode 100644 index f373614688b..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/collapseAll.svg +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - - - - - - - - - diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/expandAll.svg b/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/expandAll.svg deleted file mode 100644 index 4a1bcbcbf21..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/expandAll.svg +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/graphicon.ico b/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/graphicon.ico deleted file mode 100644 index adf0a2071a539814323c2b09f3e68c2d70895ce2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1406 zcmeH^&ra%45XQe&ThVI8TB~TSC{?lktG2>yNMJ`uNLUdbfCX%L6kdP@1V|ukcm=j> z*s&ul;0+)okPx`X+=x3jcX|CLGs*8eIcG9+0FH&%3sybI34jJ5$dXy`v;6db6321i zc^-lwKomttk_1_np(qMeRfVQ$&~+V#VZby^Se6Cbw&6GqT-QY;5y| zdcFQ>^xw1J4gA*|phv}X;3p>kfk%0ycj+i_!9Nf^@E3t2>HT)gAv!rDIy)h9wr;P4 z5Rp$K(tJcA;mQ^+pH1PK=vpwJF9n@JjBg=M+V?heZW+8QBJD~&#Aq?OhXFYyJdeP}I-G5BCo ziIHeh-)y2OO?{}THGR++Obcy|Hfo?DG$poZp|ArimzL#H3zY3HGduJ7(7G^^PDXlD4|pd1V8nw-XHdT*`+*Cz_03d zSk0XAT@7m8#CWBp2hi^xlzaOml>k>1MLAg8bhd>6G$w%G*1u|2l!aOY$29OfpHWim zG}~>tFtCP2_|4fKqrn zPGQ#lI|>T&QR2u$YHG61&+iYwEqS!w{sEaG%w@kUe>{|4_G4pXqqZX8v3Is-m?&FM z8D_sVGo39i5|gZ%7G26zRmH&2DEfx3BcQ0TZmU5?+4C@%5_J~`13pZ3{RD4YGfW(d zB3lj|wmh&bt1Aib#3XwAq}vmdH&v+WkpNK-?fK&Dln(Igx~fXvX!;Nut#pV2S5#={ zk~ZiA&d>YM(?5g(&j^z9OJLphGLps3AY|n#p_F1yx&m+8_m~^$fkVhgiPMG+|GNM7 zybt~EL0OVKK`^9UUI={hM$@@=0szPBcF-rwH&KVJfN-3?ZYHO1qQ^ag@f+jV_*f;Z zTVFzAwmqVj;k!NnZ^tS8^+!9hbFxq@=EIV%uV+P3FfcSsFJ9^zCo-u$=gyrcPJn`l;V2$QAHQmG<2OO`|L)iH?rY`@6whPT5ayJTvToj1{c%B2t zad0_Ba`93(0Kl-U=%o=1kBlKjd>q2-2aqh3Mtyv-Ml-AxRj^i6Va{_EqwUT3v*l+L zDh6awaG#$l7&+Er*B}Q<226wavq6E zsiE%-05F7fcJmb|Z#W5|dT-p|mYO*|8^X?gC~r84Zes-)LOMf$wTD}`%%-?N42B24 zy%-FbO>v?2aO;+3H5jQ;N)cR8z;Y}I!z>0PYhRY4CYgcf?lyEnqN*qpsbb6i-v@4~ zPfP*-2}CK?1zk-#sYYW9NGT105JDDztM%8kFt@=76`SD*j@ diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/home.xpm b/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/home.xpm deleted file mode 100644 index 89a8cfb2f9d..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/home.xpm +++ /dev/null @@ -1,150 +0,0 @@ -/* XPM */ -static char * home_xpm[] = { -"40 40 107 2", -" c None", -". c #A87050", -"+ c #A7714F", -"@ c #A86F51", -"# c #A66F4E", -"$ c #B8805F", -"% c #E2A883", -"& c #E2A882", -"* c #B77F5E", -"= c #A66F4F", -"- c #A7704E", -"; c #A76F4E", -"> c #CF9672", -", c #F5BB94", -"' c #FFC49C", -") c #A97150", -"! c #AF7756", -"~ c #D9A07C", -"{ c #FBC198", -"] c #FEC39C", -"^ c #FBC098", -"/ c #D9A07B", -"( c #AF7757", -"_ c #A87251", -": c #A56D4D", -"< c #BE8663", -"[ c #E9AE89", -"} c #F9BE97", -"| c #D59E7A", -"1 c #AD7A5B", -"2 c #E8AE88", -"3 c #AB7352", -"4 c #A97252", -"5 c #D19874", -"6 c #F6BC94", -"7 c #EEB48D", -"8 c #C59171", -"9 c #A67152", -"0 c #B69785", -"a c #CDD1D3", -"b c #CDD0D2", -"c c #B59784", -"d c #A67253", -"e c #EFB48E", -"f c #F6BB94", -"g c #D19774", -"h c #A87151", -"i c #AA7253", -"j c #A66C4C", -"k c #A56E4E", -"l c #BD8562", -"m c #E6AC86", -"n c #FCC199", -"o c #B27F61", -"p c #AA7A5E", -"q c #C2B7AF", -"r c #D7EAF4", -"s c #DBF2FF", -"t c #A87150", -"u c #A86F4F", -"v c #CB936F", -"w c #F2B891", -"x c #F3B891", -"y c #C99270", -"z c #A67252", -"A c #B4907C", -"B c #CCCDCF", -"C c #DAF1FE", -"D c #CC936F", -"E c #A16A4A", -"F c #D79E78", -"G c #FABF97", -"H c #E9B089", -"I c #BF8B6B", -"J c #B89E8E", -"K c #D0D8DD", -"L c #D0D7DC", -"M c #C08C6B", -"N c #D29A77", -"O c #AD8066", -"P c #C3BBB7", -"Q c #D8EBF6", -"R c #AC8066", -"S c #C5906F", -"T c #B59683", -"U c #CDD2D4", -"V c #B18165", -"W c #A67355", -"X c #BBA699", -"Y c #D2DEE5", -"Z c #BBA598", -"` c #B18065", -" . c #A36B4B", -".. c #83879C", -"+. c #DAEEFB", -"@. c #7496C4", -"#. c #D6EAF9", -"$. c #D9EFFC", -"%. c #DAF0FE", -"&. c #DBF1FE", -"*. c #D9EEFC", -"=. c #DBF1FF", -"-. c #DAF0FD", -";. c #D8EDFB", -">. c #D9EFFD", -",. c #8FB2C6", -"'. c #D9F0FD", -" ", -" . + ", -" @ # $ % & * = - ", -" ; # > , ' ' ' ' , > # ; ", -" ) ! ~ { ' ' ' ] ] ' ' ' ^ / ( _ ", -" - : < [ ' ' ' ' } | 1 1 | } ' ' ' ' 2 < : - ", -" 3 4 5 6 ' ' ' ' 7 8 9 0 a b c d 8 e ' ' ' ' f g h i ", -" j k l m ] ' ' ' n ~ o p q r s s s s r q p o ~ n ' ' ' ] m l = j ", -" t u v w ' ' ' ' x y z A B C s s s s s s s s C B A z y x ' ' ' ' w D u t ", -" E F G ' ' ' ' H I 9 J K s s s s s s s s s s s s s s L J 9 M H ' ' ' ' G F E ", -" E ' ' ' } N 1 O P Q s s s s s s s s s s s s s s s s s s Q P R 1 N } ' ' ' E ", -" E ' 7 S d T U s s s s s s s s s s s s s s s s s s s s s s s s U T d S 7 ' E ", -" E V W X Y s s s s s s s s s s s s s s s s s s s s s s s s s s s s Y Z W ` E ", -" ...+.s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s +... . ", -" @.s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s @. ", -" @.s s s s s s s s s s s s s s C #.$.C C C s s s s s s s s s s s s s s @. ", -" @.s s s s s s s s s s s s s s C %.&.s s s s s s s s s s s s s s s s s @. ", -" @.s s s s s s s s s s C s s C C C s s s s s s s C s s s s s s s s s s @. ", -" @.s s s s s s s s s C C C C C C s s s s s s s s C %.s s s s s s s s s @. ", -" @.s s s s s s s s C s C s s s &.s s s s s s s s C *.=.s s s s s s s s @. ", -" @.s s s s s s s s C C s s s s s s s s s s s s s &.-.;.s s s s s s s s @. ", -" @.s s s s s s s s C C C s s s s s s s s s s s s C C $.s s s s s s s s @. ", -" @.s s s s s s s s s C s s s s s s s s s s s s s s C s s s s s s s s s @. ", -" @.s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s @. ", -" @.s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s @. ", -" @.s s s s s s s s s C s s s s s s s s s s s s s s s s s s s s s s s s @. ", -" @.s s s s s s s s >.C s s s ,.,.,.,.,.,.,.,.s s s s C s s s s s s s s @. ", -" @.s s s s s s s s $.%.&.s s ,.,.,.,.,.,.,.,.s s s C C s s s s s s s s @. ", -" @.s s s s s s s s C *.C s s ,.,.,.,.,.,.,.,.s s s C =.s s s s s s s s @. ", -" @.s s s s s s s s s >.'.C s ,.,.,.,.,.,.,.,.s s C C s s s s s s s s s @. ", -" @.s s s s s s s s s s C s s ,.,.,.,.,.,.,.,.s s C s s s s s s s s s s @. ", -" @.s s s s s s s s s s s s s ,.,.,.,.,.,.,.,.s s s s s s s s s s s s s @. ", -" @.s s s s s s s s s s s s s ,.,.,.,.,.,.,.,.s s s s s s s s s s s s s @. ", -" @.s s s s s s s s s s s s s ,.,.,.,.,.,.,.,.s s s s s s s s s s s s s @. ", -" @.s s s s s s s s s s s s s ,.,.,.,.,.,.,.,.s s s s s s s s s s s s s @. ", -" @.s s s s s s s s s s s s s ,.,.,.,.,.,.,.,.s s s s s s s s s s s s s @. ", -" @.s s s s s s s s s s s s s ,.,.,.,.,.,.,.,.s s s s s s s s s s s s s @. ", -" @.s s s s s s s s s s s s s ,.,.,.,.,.,.,.,.s s s s s s s s s s s s s @. ", -" @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@. ", -" "}; diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/icondata.xpm b/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/icondata.xpm deleted file mode 100644 index d74935752fd..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/icondata.xpm +++ /dev/null @@ -1,32 +0,0 @@ -/* XPM */ -static const char * icondata_xpm[] = { -"20 20 9 1", -" c None", -". c #000000", -"+ c #00007F", -"@ c #0000BF", -"# c #0000FF", -"$ c #000098", -"% c #0000E6", -"& c #00008F", -"* c #00003F", -" ", -" ", -" ", -" ", -" . ", -" +@. ", -" +##@$ ", -" +####%@. ", -" +#######@$ ", -" +#########@$$ ", -" +###########%&* ", -" +#########%@+ ", -" +#######@$ ", -" +####%@+ ", -" +##%@ ", -" +@ ", -" . ", -" ", -" ", -" "}; diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/iconerror.xpm b/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/iconerror.xpm deleted file mode 100644 index 316886636ed..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/iconerror.xpm +++ /dev/null @@ -1,27 +0,0 @@ -/* XPM */ -static const char * iconerror_xpm[] = { -"20 20 4 1", -" c None", -". c #000000", -"+ c #C8C8C8", -"@ c #FF4D4D", -" ", -" . ", -" .+ ", -" ...+ ", -" .@.+ ", -" ..@..+ ", -" .@.@.+ ", -" ..@.@..+ ", -" .@@.@@.+ ", -" ..@@.@@..+ ", -" .@@@.@@@.+ ", -" ..@@@.@@@..+ ", -" .@@@@.@@@@.+ ", -" ..@@@@.@@@@..+ ", -" .@@@@@@@@@@@.+ ", -" ..@@@@...@@@@..+ ", -" .@@@@@...@@@@@.+ ", -" ..@@@@@@@@@@@@@..+ ", -" .................+ ", -" +++++++++++++++++ "}; diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/iconinfo.xpm b/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/iconinfo.xpm deleted file mode 100644 index 5af3d7f69bc..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/iconinfo.xpm +++ /dev/null @@ -1,27 +0,0 @@ -/* XPM */ -static const char * iconinfo_xpm[] = { -"20 20 4 1", -" c None", -". c #000000", -"+ c #C8C8C8", -"@ c #77E87A", -" ", -" . ", -" .+ ", -" ...+ ", -" .@.+ ", -" ..@..+ ", -" .@.@.+ ", -" ..@.@..+ ", -" .@@.@@.+ ", -" ..@@.@@..+ ", -" .@@@.@@@.+ ", -" ..@@@.@@@..+ ", -" .@@@@.@@@@.+ ", -" ..@@@@.@@@@..+ ", -" .@@@@@@@@@@@.+ ", -" ..@@@@...@@@@..+ ", -" .@@@@@...@@@@@.+ ", -" ..@@@@@@@@@@@@@..+ ", -" .................+ ", -" +++++++++++++++++ "}; diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/iconmultinode.xpm b/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/iconmultinode.xpm deleted file mode 100644 index 1e7579a6034..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/iconmultinode.xpm +++ /dev/null @@ -1,27 +0,0 @@ -/* XPM */ -static const char * iconmultinode_xpm[] = { -"20 20 4 1", -" c None", -". c #000000", -"+ c #9F9F9F", -"@ c #5C5656", -" ", -" ", -" ", -" ...... ", -" .++++. ", -" .++++++...... ", -" .+++++++.@@@@. ", -" .+++++++.@@@@@@. ", -" .++++++.@@@@@@@@. ", -".++++++.@@@@@@@@@@. ", -".++++++.@@@@@@@@@@. ", -".+++++.@@@@@@@@@@@@.", -" .++++.@@@@@@@@@@@@.", -" .++++.@@@@@@@@@@@@.", -" .++++.@@@@@@@@@@. ", -" .+++.@@@@@@@@@@. ", -" .+++.@@@@@@@@. ", -" ......@@@@@@. ", -" .@@@@. ", -" ...... "}; diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/iconnode.xpm b/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/iconnode.xpm deleted file mode 100644 index 9a18662b8f3..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/iconnode.xpm +++ /dev/null @@ -1,26 +0,0 @@ -/* XPM */ -static const char * iconnode_xpm[] = { -"20 20 3 1", -" c None", -". c #000000", -"+ c #DEDEDE", -" ", -" ", -" ", -" ...... ", -" .++++. ", -" .++++++. ", -" .++++++++. ", -" .++++++++++. ", -" .++++++++++. ", -".++++++++++++. ", -".++++++++++++. ", -".++++++++++++. ", -" .++++++++++. ", -" .++++++++++. ", -" .++++++++. ", -" .++++++. ", -" .++++. ", -" ...... ", -" ", -" "}; diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/iconsleep.xpm b/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/iconsleep.xpm deleted file mode 100644 index ab5428929ec..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/iconsleep.xpm +++ /dev/null @@ -1,26 +0,0 @@ -/* XPM */ -static const char * iconsleep_xpm[] = { -"20 20 3 1", -" c None", -". c #000000", -"+ c #6F6F6F", -" ", -" ", -" ", -" ...... ", -" .++++. ", -" .++++++. ", -" .++++++++. ", -" .++++++++++. ", -" .++++++++++. ", -".++++++++++++. ", -".++++++++++++. ", -".++++++++++++. ", -" .++++++++++. ", -" .++++++++++. ", -" .++++++++. ", -" .++++++. ", -" .++++. ", -" ...... ", -" ", -" "}; diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/iconwarning.xpm b/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/iconwarning.xpm deleted file mode 100644 index aa87f73bf3d..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/iconwarning.xpm +++ /dev/null @@ -1,27 +0,0 @@ -/* XPM */ -static const char * iconwarning_xpm[] = { -"20 20 4 1", -" c None", -". c #000000", -"+ c #C8C8C8", -"@ c #FFFF00", -" ", -" . ", -" .+ ", -" ...+ ", -" .@.+ ", -" ..@..+ ", -" .@.@.+ ", -" ..@.@..+ ", -" .@@.@@.+ ", -" ..@@.@@..+ ", -" .@@@.@@@.+ ", -" ..@@@.@@@..+ ", -" .@@@@.@@@@.+ ", -" ..@@@@.@@@@..+ ", -" .@@@@@@@@@@@.+ ", -" ..@@@@...@@@@..+ ", -" .@@@@@...@@@@@.+ ", -" ..@@@@@@@@@@@@@..+ ", -" .................+ ", -" +++++++++++++++++ "}; diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/openFile.png b/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/openFile.png deleted file mode 100644 index 5bac04160cdcfb46a14fc1e5fc28400d51f465a5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1247 zcmV<51R(o~P))oM+BKVoV^Qz|~%T4-XFQXqxU0yD#K@15Z?Gxv3_3xrH5^|5l}m)xA3 zd%paC-{YKo@W1_ydH&+8K0n}lykTAV(b&xFO+Z=3#|zvK>;_Ume5u2VCHp-)A6(zH zu`#l>rJ*9y)lo-K&S&3?AN=<2XBS%l8)#k`EZEw$sdh(0b-1&mxl(RxjBw<5w|4YI z&uSohCqU=L?)Q3IBO0uj|1E@z5Q3?g#vAXS9|;x)N?V&MLT!x^@_ZhI5c58c;M!=M zEqjjb0?sT=alvd~Ac*ZK2uI)wfe`cSO4-k$r?yq+3TDz4w(Vfq0@Ji8F7Q#dO5PDy zbbthKd1(fr(;vR0?yQY3MxHC^IL)d`lbqc~hgV^alpuPH||XCRXRp>Wzs-CV9j zsH!?AEU(1?`)p#8ZGI4McpeDd|!JXH+^y~HR zWdWM1Ms3QOQFNxJR7xw`sBEdEqAEh3I|rE<*a~C64{0BOid?w_UIYfI=YY~iHV64cE9uqosA{Du=gSI z{ayw}CIAK(G^JdC&*z(y0znEws}boL9NWgW3`{f2jfrUvy!tik?`y{}3=G4dtgMX9 zo3}6+li2sl$Ecb{YIZJ~``B*D;L@dEG~t3@UWutsBeLnm>-zL-U(?XsfoYo9wv8l7 zNRmV-6r#Sqp20!bf8-lVWhrr|hejro(Oke^Sd5`72-{e!?Oj1HZg!TeC$K2K@Wm#^ zDU1&h4V2S=`Fa450sS_>ytzmoAKA2m`=_`o1kF`NgyOwi&VK-r1GzCe!e-IO+G>aE z)3cl{&#(So_q~-dtS6QSxGs5lKFpl*2;pFwCN;8+-{|L@*@W-}NJJ;-wECzkmypO~ zLzS1N3K)+vcGXy>$pEHd5-2DnolY~W>nK{1*tCk8)CtN#Hiiq??jJ HZU3LTv~< z)sGdl>y{*MzJ)5C_G{tP^ zf$YG*z=iKVJ=K%Qiu1sQrGw{RL)zCCeO_AVJqWY|f&Xmi{{f*~TxW|idpH09002ov JPDHLkV1lIJQ-c5i diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/sceneGraphRefresh-dirty.svg b/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/sceneGraphRefresh-dirty.svg deleted file mode 100644 index 24a0392cdf0..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/sceneGraphRefresh-dirty.svg +++ /dev/null @@ -1,205 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/sceneGraphRefresh-locked.svg b/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/sceneGraphRefresh-locked.svg deleted file mode 100644 index ed98f1a5d03..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/sceneGraphRefresh-locked.svg +++ /dev/null @@ -1,199 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/sceneGraphRefresh-unlocked.svg b/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/sceneGraphRefresh-unlocked.svg deleted file mode 100644 index a9fd939492d..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/resources/icons/sceneGraphRefresh-unlocked.svg +++ /dev/null @@ -1,201 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/sofa-logo-alpha-text.png b/Sofa/GUI/Qt/src/sofa/gui/qt/sofa-logo-alpha-text.png deleted file mode 100644 index 7b8fa847a37539f9030ea66306671fc064abafcf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34666 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�)Zf}EN1&PeC4lR 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. * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include -#include - -#include - -namespace sofa::gui::qt::viewer -{ - -class EngineBackend -{ -public: - EngineBackend() = default; - virtual ~EngineBackend() = default; - - virtual void setPickingMethod(sofa::gui::common::PickHandler* pick, sofa::component::setting::ViewerSetting* viewerConf) =0; - virtual void setPrefix(const std::string& prefix) =0; - virtual const std::string screenshotName() =0; - virtual void screenshot(const std::string& filename, int compression_level = -1) =0; - virtual void setBackgroundImage(helper::io::Image* image) =0; - virtual void drawBackgroundImage(const int screenWidth, const int screenHeight)=0; - - virtual bool initRecorder(int width, int height, unsigned int framerate, unsigned int bitrate, const std::string& codecExtension="", const std::string& codecName="") =0; - virtual void endRecorder() =0; - virtual void addFrameRecorder() =0; - - -private: - -}; - -} // namespace sofa::gui::qt::viewer diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/viewer/GLBackend.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/viewer/GLBackend.cpp deleted file mode 100644 index 9c6b852a209..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/viewer/GLBackend.cpp +++ /dev/null @@ -1,154 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#include "GLBackend.h" - -#include -#include -using sofa::helper::system::FileSystem; -#include -using sofa::helper::system::SetDirectory; -#include -using sofa::helper::Utils; - -namespace sofa::gui::qt::viewer -{ - -GLBackend::GLBackend() - : m_texLogo(nullptr) -{ -} - -GLBackend::~GLBackend() -{ -} - -void GLBackend::setPickingMethod(sofa::gui::common::PickHandler* pick, sofa::component::setting::ViewerSetting* viewerConf) -{ - if (viewerConf->d_objectPickingMethod.getValue().getSelectedId() == gui::common::PickHandler::RAY_CASTING) - pick->setPickingMethod( gui::common::PickHandler::RAY_CASTING ); - else - pick->setPickingMethod( gui::common::PickHandler::SELECTION_BUFFER); -} - -void GLBackend::setPrefix(const std::string& prefix) -{ - m_capture.setPrefix(prefix); - m_videoRecorderFFMPEG.setPrefix(prefix); -} - -const std::string GLBackend::screenshotName() -{ - return m_capture.findFilename().c_str(); -} - -void GLBackend::screenshot(const std::string& filename, int compression_level) -{ - m_capture.saveScreen(filename, compression_level); -} - -void GLBackend::setBackgroundImage(helper::io::Image* image) -{ - if(m_texLogo) - { - delete m_texLogo; - m_texLogo = nullptr; - } - - m_texLogo = new gl::Texture( image ); - m_texLogo->init(); -} - -bool GLBackend::initRecorder( int width, int height, unsigned int framerate, unsigned int bitrate, const std::string& codecExtension, const std::string& codecName) -{ - bool res = true; - std::string ffmpeg_exec_path = ""; - const std::string ffmpegIniFilePath = Utils::getSofaPathTo("etc/SofaGuiQt.ini"); - std::map iniFileValues = Utils::readBasicIniFile(ffmpegIniFilePath); - if (iniFileValues.find("FFMPEG_EXEC_PATH") != iniFileValues.end()) - { - // get absolute path of FFMPEG executable - ffmpeg_exec_path = SetDirectory::GetRelativeFromProcess( iniFileValues["FFMPEG_EXEC_PATH"].c_str() ); - } - - const std::string videoFilename = m_videoRecorderFFMPEG.findFilename(framerate, bitrate / 1024, codecExtension); - - res = m_videoRecorderFFMPEG.init(ffmpeg_exec_path, videoFilename, width, height, framerate, bitrate, codecName); - - return res; -} - -void GLBackend::endRecorder() -{ - m_videoRecorderFFMPEG.finishVideo(); -} - -void GLBackend::addFrameRecorder() -{ - m_videoRecorderFFMPEG.addFrame(); -} - -void GLBackend::drawBackgroundImage(const int screenWidth, const int screenHeight) -{ - if(!m_texLogo) - return; - - if(!m_texLogo->getImage()) - return; - - const int w = m_texLogo->getImage()->getWidth(); - const int h = m_texLogo->getImage()->getHeight(); - - glEnable(GL_TEXTURE_2D); - glDisable(GL_DEPTH_TEST); - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glOrtho(-0.5, screenWidth, -0.5, screenHeight, -1.0, 1.0); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - - m_texLogo->bind(); - - const double coordWidth = int(screenWidth / w) + 1; - const double coordHeight = int(screenHeight / h) + 1; - - glColor3f(1.0f, 1.0f, 1.0f); - glBegin(GL_QUADS); - glTexCoord2d(0.0, 0.0); glVertex3d( -w*coordWidth, -h*coordHeight, 0.0 ); - glTexCoord2d(coordWidth*2.0, 0.0); glVertex3d( w*coordWidth, -h*coordHeight, 0.0 ); - glTexCoord2d(coordWidth*2.0, coordHeight*2.0); glVertex3d( w*coordWidth, h*coordHeight, 0.0 ); - glTexCoord2d(0.0, coordHeight*2.0); glVertex3d( -w*coordWidth, h*coordHeight, 0.0 ); - glEnd(); - - glBindTexture(GL_TEXTURE_2D, 0); - - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - - glDisable(GL_TEXTURE_2D); -} - - -} // namespace sofa::gui::qt::viewer diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/viewer/GLBackend.h b/Sofa/GUI/Qt/src/sofa/gui/qt/viewer/GLBackend.h deleted file mode 100644 index 4cdce326426..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/viewer/GLBackend.h +++ /dev/null @@ -1,60 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include - -#include -#include -#include - -#include -#include -#include - - -namespace sofa::gui::qt::viewer -{ - -class SOFA_GUI_QT_API GLBackend : public EngineBackend -{ -public: - GLBackend(); - virtual ~GLBackend(); - - void setPickingMethod(sofa::gui::common::PickHandler* pick, sofa::component::setting::ViewerSetting* viewerConf); - void setPrefix(const std::string& prefix); - const std::string screenshotName(); - void screenshot(const std::string& filename, int compression_level = -1); - void setBackgroundImage(helper::io::Image* image); - void drawBackgroundImage(const int screenWidth, const int screenHeight); - - bool initRecorder(int width, int height, unsigned int framerate, unsigned int bitrate,const std::string& codecExtension="", const std::string& codecName=""); - void endRecorder(); - void addFrameRecorder(); - -private: - gl::Capture m_capture; - gl::Texture* m_texLogo; - gl::VideoRecorderFFMPEG m_videoRecorderFFMPEG; -}; - -} // namespace sofa::gui::qt::viewer diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/viewer/OglModelPolicy.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/viewer/OglModelPolicy.cpp deleted file mode 100644 index 6224e508faa..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/viewer/OglModelPolicy.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ - -#include -#include -#include - -namespace sofa::gui::qt::viewer -{ - -void OglModelPolicy::load() -{ - drawTool = std::unique_ptr(new sofa::gl::DrawToolGL()); - - // Replace generic visual models with OglModel - sofa::core::ObjectFactory::AddAlias("VisualModel", "OglModel", true, - &classVisualModel); - vparams->drawTool() = drawTool.get(); - vparams->setSupported(sofa::core::visual::API_OpenGL); -} - -void OglModelPolicy::unload() -{ - sofa::core::ObjectFactory::ResetAlias("VisualModel", classVisualModel); - vparams->drawTool() = nullptr; - -} - -} // namespace sofa::gui::qt::viewer diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/viewer/OglModelPolicy.h b/Sofa/GUI/Qt/src/sofa/gui/qt/viewer/OglModelPolicy.h deleted file mode 100644 index 88d0f2bedd8..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/viewer/OglModelPolicy.h +++ /dev/null @@ -1,57 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include - -#include -#include - -#include - -namespace sofa::gui::qt::viewer -{ - -class SOFA_GUI_QT_API OglModelPolicy : public VisualModelPolicy -{ -protected: - sofa::core::ObjectFactory::ClassEntry::SPtr classVisualModel; - std::unique_ptr drawTool; -public: - void load() override; - void unload() override; -}; - - -template < typename VisualModelPolicyType > -class SOFA_GUI_QT_API CustomPolicySofaViewer : public VisualModelPolicyType, public sofa::gui::qt::viewer::SofaViewer -{ -public: - using VisualModelPolicyType::load; - using VisualModelPolicyType::unload; - CustomPolicySofaViewer() { load(); } - ~CustomPolicySofaViewer() override { unload(); } -protected: -}; - -typedef CustomPolicySofaViewer< OglModelPolicy > OglModelSofaViewer; - -} // namespace sofa::gui::qt::viewer diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/viewer/SofaViewer.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/viewer/SofaViewer.cpp deleted file mode 100644 index 10b28313f20..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/viewer/SofaViewer.cpp +++ /dev/null @@ -1,554 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#include "SofaViewer.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace sofa::gui::common; - -namespace sofa::gui::qt::viewer -{ - -SofaViewer::SofaViewer() - : BaseViewer() - , m_isControlPressed(false) -{ - colourPickingRenderCallBack = ColourPickingRenderCallBack(this); -} - -SofaViewer::~SofaViewer() -{ -} - -void SofaViewer::redraw() -{ - getQWidget()->update(); -} - -void SofaViewer::keyPressEvent(QKeyEvent * e) -{ - - if (currentCamera) - { - sofa::core::objectmodel::KeypressedEvent kpe(e->key()); - currentCamera->manageEvent(&kpe); - } - - switch (e->key()) - { - case Qt::Key_T: - { - if (!currentCamera) break; - - if (currentCamera->getCameraType() == core::visual::VisualParams::ORTHOGRAPHIC_TYPE) - setCameraMode(core::visual::VisualParams::PERSPECTIVE_TYPE); - else - setCameraMode(core::visual::VisualParams::ORTHOGRAPHIC_TYPE); - break; - } - case Qt::Key_Shift: - { - if (!getPickHandler()) break; - const int viewport[4] = {}; - getPickHandler()->activateRay(viewport[2],viewport[3], groot.get()); - break; - } - case Qt::Key_B: - // --- change background - { - _background = (_background + 1) % 4; - if(_background==0) - { - setBackgroundImage("textures/SOFA_logo.bmp"); - } - if (_background==1) - { - setBackgroundImage("textures/SOFA_logo_white.bmp"); - } - break; - } - case Qt::Key_R: - // --- draw axis - { - m_bShowAxis = !m_bShowAxis; - break; - } - case Qt::Key_V: - // --- save video - { - if (m_isVideoButtonPressed == false) - { - if (!_video) - { - switch (SofaVideoRecorderManager::getInstance()->getRecordingType()) - { - case SofaVideoRecorderManager::SCREENSHOTS: - { - //_video = !_video; - break; - } - case SofaVideoRecorderManager::MOVIE: - { - SofaVideoRecorderManager* videoManager = SofaVideoRecorderManager::getInstance(); - const unsigned int bitrate = videoManager->getBitrate(); - const unsigned int framerate = videoManager->getFramerate(); - - const int width = getQWidget()->width(); - const int height = getQWidget()->height(); - m_backend->initRecorder(width, height, framerate, bitrate, videoManager->getCodecExtension(), videoManager->getCodecName()); - - break; - } - default: - break; - } - if (SofaVideoRecorderManager::getInstance()->realtime()) - { - const unsigned int framerate = SofaVideoRecorderManager::getInstance()->getFramerate(); - msg_info("SofaViewer") << "Starting capture timer ( " << framerate << " Hz )"; - const unsigned int interv = (1000 + framerate - 1) / framerate; - captureTimer.start(interv); - } - } - else - { - if (captureTimer.isActive()) - { - msg_info("SofaViewer") << "Stopping capture timer "; - captureTimer.stop(); - } - switch (SofaVideoRecorderManager::getInstance()->getRecordingType()) - { - case SofaVideoRecorderManager::SCREENSHOTS: - break; - case SofaVideoRecorderManager::MOVIE: - { - m_backend->endRecorder(); - break; - } - default: - break; - } - } - - _video = !_video; - } - - m_isVideoButtonPressed = true; - break; - } - case Qt::Key_W: - // --- save current view - { - if (!currentCamera) break; - saveView(); - break; - } - case Qt::Key_F1: - // --- enable stereo mode - { - currentCamera->setStereoEnabled(!currentCamera->getStereoEnabled()); - msg_info("SofaViewer") << "Stereoscopic View " << (currentCamera->getStereoEnabled() ? "Enabled" : "Disabled"); - break; - } - case Qt::Key_F2: - // --- reduce shift distance - { - currentCamera->setStereoShift(currentCamera->getStereoShift()-0.1); - msg_info("SofaViewer") << "Stereo separation = " << currentCamera->getStereoShift(); - break; - } - case Qt::Key_F3: - // --- increase shift distance - { - currentCamera->setStereoShift(currentCamera->getStereoShift()+0.1); - msg_info("SofaViewer") << "Stereo separation = " << currentCamera->getStereoShift(); - break; - } - case Qt::Key_F4: - { - // --- Switch between parallax and toedIn stereovision - switch (currentCamera->getStereoStrategy()) { - case sofa::component::visual::BaseCamera::PARALLEL: - currentCamera->setStereoStrategy(sofa::component::visual::BaseCamera::TOEDIN); - msg_info("SofaViewer") << "Stereo Strategy: TOEDIN"; - break; - case sofa::component::visual::BaseCamera::TOEDIN: - currentCamera->setStereoStrategy(sofa::component::visual::BaseCamera::PARALLEL); - msg_info("SofaViewer") << "Stereo Strategy: Parallel"; - break; - default: - currentCamera->setStereoStrategy(sofa::component::visual::BaseCamera::PARALLEL); - break; - } - break; - } - case Qt::Key_F5: - // --- enable binocular mode - { - //_stereoMode = (StereoMode)(((int)_stereoMode+1)%(int)NB_STEREO_MODES); - currentCamera->setStereoMode((sofa::component::visual::BaseCamera::StereoMode)(((int)currentCamera->getStereoMode()+1)%(int)sofa::component::visual::BaseCamera::NB_STEREO_MODES)); - switch (currentCamera->getStereoMode()) - { - case sofa::component::visual::BaseCamera::STEREO_INTERLACED: - msg_info("SofaViewer") << "Stereo mode: Interlaced"; - break; - case sofa::component::visual::BaseCamera::STEREO_SIDE_BY_SIDE: - msg_info("SofaViewer") << "Stereo mode: Side by Side"; break; - case sofa::component::visual::BaseCamera::STEREO_SIDE_BY_SIDE_HALF: - msg_info("SofaViewer") << "Stereo mode: Side by Side Half"; break; - case sofa::component::visual::BaseCamera::STEREO_FRAME_PACKING: - msg_info("SofaViewer") << "Stereo mode: Frame Packing"; break; - case sofa::component::visual::BaseCamera::STEREO_TOP_BOTTOM: - msg_info("SofaViewer") << "Stereo mode: Top Bottom"; break; - case sofa::component::visual::BaseCamera::STEREO_TOP_BOTTOM_HALF: - msg_info("SofaViewer") << "Stereo mode: Top Bottom Half"; break; - case sofa::component::visual::BaseCamera::STEREO_AUTO: - msg_info("SofaViewer") << "Stereo mode: Automatic"; break; - case sofa::component::visual::BaseCamera::STEREO_NONE: - msg_info("SofaViewer") << "Stereo mode: None"; break; - default: - msg_info("SofaViewer") << "Stereo mode: INVALID"; break; - } - break; - } - case Qt::Key_Control: - { - m_isControlPressed = true; - dmsg_info("SofaViewer")<<"QtViewer::keyPressEvent, CONTROL pressed"; - break; - } - default: - { - e->ignore(); - } - } -} - -void SofaViewer::keyReleaseEvent(QKeyEvent * e) -{ - sofa::core::objectmodel::KeyreleasedEvent kre(e->key()); - if (isControlPressed()) - { - sofa::core::objectmodel::KeyreleasedEvent keyEvent(e->key()); - if (groot) - groot->propagateEvent(core::execparams::defaultInstance(), &keyEvent); - } - - currentCamera->manageEvent(&kre); - switch (e->key()) - { - case Qt::Key_V: - { - m_isVideoButtonPressed = false; - break; - } - case Qt::Key_Shift: - { - if (getPickHandler()) - getPickHandler()->deactivateRay(); - break; - } - - case Qt::Key_Control: - { - m_isControlPressed = false; - - // Send Control Release Info to a potential ArticulatedRigid Instrument - sofa::core::objectmodel::MouseEvent mouseEvent( - sofa::core::objectmodel::MouseEvent::Reset); - if (groot) - groot->propagateEvent(core::execparams::defaultInstance(), &mouseEvent); - [[fallthrough]]; - } - default: - { - e->ignore(); - } - } - - - -} - -bool SofaViewer::isControlPressed() const -{ - return m_isControlPressed; -} - -// ---------------------- Here are the Mouse controls ---------------------- -void SofaViewer::wheelEvent(QWheelEvent *e) -{ - if (!currentCamera) return; - // -#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) - sofa::core::objectmodel::MouseEvent me(sofa::core::objectmodel::MouseEvent::Wheel,e->delta()); -#else - sofa::core::objectmodel::MouseEvent me(sofa::core::objectmodel::MouseEvent::Wheel,e->angleDelta().y()); -#endif - currentCamera->manageEvent(&me); - - getQWidget()->update(); - if (groot) - groot->propagateEvent(core::execparams::defaultInstance(), &me); -} - -void SofaViewer::mouseMoveEvent ( QMouseEvent *e ) -{ - if (!currentCamera) return; - // - sofa::core::objectmodel::MouseEvent me(sofa::core::objectmodel::MouseEvent::Move,e->x(), e->y()); - currentCamera->manageEvent(&me); - - getQWidget()->update(); - if (groot) - groot->propagateEvent(core::execparams::defaultInstance(), &me); -} - -void SofaViewer::mousePressEvent ( QMouseEvent * e) -{ - if (!currentCamera) return; - // - sofa::core::objectmodel::MouseEvent* mEvent = nullptr; - if (e->button() == Qt::LeftButton) { - mEvent = new sofa::core::objectmodel::MouseEvent(sofa::core::objectmodel::MouseEvent::LeftPressed, e->x(), e->y()); - } else if (e->button() == Qt::RightButton) { - mEvent = new sofa::core::objectmodel::MouseEvent(sofa::core::objectmodel::MouseEvent::RightPressed, e->x(), e->y()); -#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) - } else if (e->button() == Qt::MidButton) { -#else - } else if (e->button() == Qt::MiddleButton) { -#endif - mEvent = new sofa::core::objectmodel::MouseEvent(sofa::core::objectmodel::MouseEvent::MiddlePressed, e->x(), e->y()); - } else { - // A fallback event to rules them all... - mEvent = new sofa::core::objectmodel::MouseEvent(sofa::core::objectmodel::MouseEvent::AnyExtraButtonPressed, e->x(), e->y()); - } - currentCamera->manageEvent(mEvent); - - getQWidget()->update(); - if (groot) - groot->propagateEvent(core::execparams::defaultInstance(), mEvent); -} - -void SofaViewer::mouseReleaseEvent ( QMouseEvent * e) -{ - if (!currentCamera) return; - // - sofa::core::objectmodel::MouseEvent* mEvent = nullptr; - if (e->button() == Qt::LeftButton) { - mEvent = new sofa::core::objectmodel::MouseEvent(sofa::core::objectmodel::MouseEvent::LeftReleased, e->x(), e->y()); - } else if (e->button() == Qt::RightButton) { - mEvent = new sofa::core::objectmodel::MouseEvent(sofa::core::objectmodel::MouseEvent::RightReleased, e->x(), e->y()); -#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) - } else if (e->button() == Qt::MidButton) { -#else - } else if (e->button() == Qt::MiddleButton) { -#endif - mEvent = new sofa::core::objectmodel::MouseEvent(sofa::core::objectmodel::MouseEvent::MiddleReleased, e->x(), e->y()); - } else { - // A fallback event to rules them all... - mEvent = new sofa::core::objectmodel::MouseEvent(sofa::core::objectmodel::MouseEvent::AnyExtraButtonReleased, e->x(), e->y()); - } - - currentCamera->manageEvent(mEvent); - - getQWidget()->update(); - if (groot) - groot->propagateEvent(core::execparams::defaultInstance(), mEvent); -} - -bool SofaViewer::mouseEvent(QMouseEvent *e) -{ - if (!currentCamera) return true; - - const int w = this->getWidth(); - const int h = this->getHeight(); - - MousePosition mousepos; - mousepos.screenWidth = w; - mousepos.screenHeight = h; - mousepos.x = e->x(); - mousepos.y = e->y(); - - if (e->modifiers() & Qt::ShiftModifier) - { - - getPickHandler()->activateRay(w,h, groot.get()); - getPickHandler()->updateMouse2D( mousepos ); - - //_sceneTransform.ApplyInverse(); - switch (e->type()) - { - case QEvent::MouseButtonPress: - - if (e->button() == Qt::LeftButton) - { - getPickHandler()->handleMouseEvent(PRESSED, LEFT); - } - else if (e->button() == Qt::RightButton) // Shift+Rightclick to remove triangles - { - getPickHandler()->handleMouseEvent(PRESSED, RIGHT); - } -#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) - else if (e->button() == Qt::MidButton) // Shift+Midclick (by 2 steps defining 2 input points) to cut from one point to another -#else - else if (e->button() == Qt::MiddleButton) // Shift+Midclick (by 2 steps defining 2 input points) to cut from one point to another -#endif - { - getPickHandler()->handleMouseEvent(PRESSED, MIDDLE); - } - break; - case QEvent::MouseButtonRelease: - //if (e->button() == Qt::LeftButton) - { - - if (e->button() == Qt::LeftButton) - { - getPickHandler()->handleMouseEvent(RELEASED, LEFT); - } - else if (e->button() == Qt::RightButton) - { - getPickHandler()->handleMouseEvent(RELEASED, RIGHT); - } -#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) - else if (e->button() == Qt::MidButton) -#else - else if (e->button() == Qt::MiddleButton) -#endif - { - getPickHandler()->handleMouseEvent(RELEASED, MIDDLE); - } - } - break; - default: - break; - } - moveRayPickInteractor(e->x(), e->y()); - } - else - { - getPickHandler()->activateRay(w,h, groot.get()); - } - return true; -} - -void SofaViewer::captureEvent() -{ - if (_video) - { - bool skip = false; - const unsigned int frameskip = SofaVideoRecorderManager::getInstance()->getFrameskip(); - if (frameskip) - { - static unsigned int skipcounter = 0; - if (skipcounter < frameskip) - { - skip = true; - ++skipcounter; - } - else - { - skip = false; - skipcounter = 0; - } - } - if (!skip) - { - switch (SofaVideoRecorderManager::getInstance()->getRecordingType()) - { - case SofaVideoRecorderManager::SCREENSHOTS : - screenshot(screenshotName(), 1); - break; - case SofaVideoRecorderManager::MOVIE : - m_backend->addFrameRecorder(); - - break; - default : - break; - } - } - } -} - - -void SofaViewer::configure(sofa::component::setting::ViewerSetting* viewerConf) -{ - BaseViewer::configure(viewerConf); - - m_backend->setPickingMethod(pick.get(), viewerConf); - -} - -//Functions needed to take a screenshot -const std::string SofaViewer::screenshotName() -{ - return m_backend->screenshotName(); - -} - -void SofaViewer::setPrefix(const std::string& prefix, bool prependDirectory) -{ - const std::string fullPrefix = (prependDirectory) - ? helper::system::FileSystem::append(sofa::gui::common::BaseGUI::getScreenshotDirectoryPath(), prefix) - : prefix; - - m_backend->setPrefix(fullPrefix); -} - -void SofaViewer::screenshot(const std::string& filename, int compression_level) -{ - m_backend->screenshot(filename, compression_level); -} - -void SofaViewer::setBackgroundImage(std::string imageFileName) -{ - if( sofa::helper::system::DataRepository.findFile(imageFileName) ) - { - backgroundImageFile = sofa::helper::system::DataRepository.getFile(imageFileName); - - std::string extension = sofa::helper::system::SetDirectory::GetExtension(imageFileName.c_str()); - std::transform(extension.begin(),extension.end(),extension.begin(),::tolower ); - - helper::io::Image* image = helper::io::Image::FactoryImage::getInstance()->createObject(extension,backgroundImageFile); - if( !image ) - { - type::vector validExtensions; - helper::io::Image::FactoryImage::getInstance()->uniqueKeys(std::back_inserter(validExtensions)); - msg_warning("SofaViewer") << "Could not create file '" << imageFileName <<"'" << msgendl - << " Valid extensions: " << validExtensions; - } - else - { - m_backend->setBackgroundImage(image); - } - } -} - -} // namespace sofa::gui::qt::viewer diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/viewer/SofaViewer.h b/Sofa/GUI/Qt/src/sofa/gui/qt/viewer/SofaViewer.h deleted file mode 100644 index 56c08cd1323..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/viewer/SofaViewer.h +++ /dev/null @@ -1,103 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include - -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include - -namespace sofa::gui::qt::viewer -{ - -enum -{ - BTLEFT_MODE = 101, BTRIGHT_MODE = 102, BTMIDDLE_MODE = 103, -}; - -class SOFA_GUI_QT_API SofaViewer : public sofa::gui::common::BaseViewer -{ - -public: - SofaViewer(); - ~SofaViewer() override; - - /// Optional QTabWidget GUI for a concreate viewer. - virtual void removeViewerTab(QTabWidget *) {} - /// Optional QTabWidget GUI for a concreate viewer. - virtual void configureViewerTab(QTabWidget *) {} - - virtual QWidget* getQWidget()=0; - virtual QString helpString() const =0; - - //************************************************************* - // QT - //************************************************************* - //SLOTS - void captureEvent() override; - - // ---------------------- Here are the Keyboard controls ---------------------- - virtual void keyPressEvent(QKeyEvent * e); - virtual void keyReleaseEvent(QKeyEvent * e); - bool isControlPressed() const; - // ---------------------- Here are the Mouse controls ---------------------- - virtual void wheelEvent(QWheelEvent *e); - virtual void mouseMoveEvent ( QMouseEvent *e ); - virtual void mousePressEvent ( QMouseEvent * e); - virtual void mouseReleaseEvent ( QMouseEvent * e); - virtual bool mouseEvent(QMouseEvent *e); - - // Overridden from BaseViewer - virtual void configure(sofa::component::setting::ViewerSetting* viewerConf) override; - const std::string screenshotName() override; - void setPrefix(const std::string& prefix, bool prependDirectory = true) override; - virtual void screenshot(const std::string& filename, int compression_level =-1) override; - virtual void setBackgroundImage(std::string imageFileName = std::string("textures/SOFA_logo.bmp")) override; - -protected: - std::unique_ptr m_backend; - - void redraw() override; - - QTimer captureTimer; - - bool m_isControlPressed; - - ColourPickingRenderCallBack colourPickingRenderCallBack; - -signals: - virtual void redrawn() = 0; - virtual void resizeW(int) = 0; - virtual void resizeH(int) = 0; -}; - -} // namespace sofa::gui::qt::viewer diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/viewer/VisualModelPolicy.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/viewer/VisualModelPolicy.cpp deleted file mode 100644 index ae857b1d2b5..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/viewer/VisualModelPolicy.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ - -#include - -namespace sofa::gui::qt::viewer -{ - -VisualModelPolicy::VisualModelPolicy(core::visual::VisualParams* vparams) - :vparams(vparams) -{ -} - -VisualModelPolicy::~VisualModelPolicy() -{ - -} - -} // namespace sofa::gui::qt::viewer diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/viewer/VisualModelPolicy.h b/Sofa/GUI/Qt/src/sofa/gui/qt/viewer/VisualModelPolicy.h deleted file mode 100644 index 65474917fa0..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/viewer/VisualModelPolicy.h +++ /dev/null @@ -1,41 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include -#include - -namespace sofa::gui::qt::viewer -{ - -class SOFA_GUI_QT_API VisualModelPolicy -{ -public: - VisualModelPolicy(core::visual::VisualParams* vparams = core::visual::visualparams::defaultInstance()); - virtual ~VisualModelPolicy(); - virtual void load() = 0; - virtual void unload() = 0; -protected: - sofa::core::visual::VisualParams* vparams; - -}; - -} // namespace sofa::gui::qt::viewer diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/viewer/qgl/QtGLViewer.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/viewer/qgl/QtGLViewer.cpp deleted file mode 100644 index d82f3e63e35..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/viewer/qgl/QtGLViewer.cpp +++ /dev/null @@ -1,1122 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#include "QtGLViewer.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -namespace sofa::gui::qt::viewer::qgl -{ - -using std::endl; -using namespace sofa::type; -using namespace sofa::defaulttype; -using namespace sofa::gl; -using sofa::simulation::getSimulation; -using namespace sofa::simulation; -using namespace sofa::gui::common; - -helper::SofaViewerCreator QtGLViewer_class("qglviewer",false); -// --------------------------------------------------------- -// --- Constructor -// --------------------------------------------------------- - -QtGLViewer::QtGLViewer(QWidget* parent, const char* name) - : QGLViewer(parent, Qt::WindowType::Widget) -{ - this->setObjectName(name); - - m_backend = std::make_unique(); - pick = std::make_unique(); - - groot = nullptr; - initTexturesDone = false; - - backgroundColour[0]=1.0f; - backgroundColour[1]=1.0f; - backgroundColour[2]=1.0f; - - // setup OpenGL mode for the window - timerAnimate = new QTimer(this); - connect( timerAnimate, SIGNAL(timeout()), this, SLOT(animate()) ); - - _video = false; - m_bShowAxis = false; - _background = 0; - _numOBJmodels = 0; - _materialMode = 0; - _facetNormal = GL_FALSE; - _renderingMode = GL_RENDER; - - _waitForRender=false; - - ////////////////////// - - _mouseInteractorMoving = false; - _mouseInteractorSavedPosX = 0; - _mouseInteractorSavedPosY = 0; - m_isControlPressed = false; - - setManipulatedFrame( new qglviewer::ManipulatedFrame() ); - //near and far plane are better placed - camera()->setZNearCoefficient(0.005); - camera()->setZClippingCoefficient(2); - - vparams->zNear() = camera()->zNear(); - vparams->zFar() = camera()->zFar(); - - connect( &captureTimer, SIGNAL(timeout()), this, SLOT(captureEvent()) ); - - //change shortcut (now M key) for camera mode to avoid double effects of shortcut - setShortcut(CAMERA_MODE, Qt::Key_M); -} - - -// --------------------------------------------------------- -// --- Destructor -// --------------------------------------------------------- -QtGLViewer::~QtGLViewer() -{ -} - -// ----------------------------------------------------------------- -// --- OpenGL initialization method - includes light definitions, -// --- color tracking, etc. -// ----------------------------------------------------------------- -void QtGLViewer::init(void) -{ - restoreStateFromFile(); - - - static GLfloat specref[4]; - static GLfloat ambientLight[4]; - static GLfloat diffuseLight[4]; - static GLfloat specular[4]; - static GLfloat lmodel_ambient[] = {0.0f, 0.0f, 0.0f, 0.0f}; - static GLfloat lmodel_twoside[] = {GL_FALSE}; - static GLfloat lmodel_local[] = {GL_FALSE}; - bool initialized = false; - - if (!initialized) - { - _lightPosition[0] = -0.7f; - _lightPosition[1] = 0.3f; - _lightPosition[2] = 0.0f; - _lightPosition[3] = 1.0f; - - ambientLight[0] = 0.5f; - ambientLight[1] = 0.5f; - ambientLight[2] = 0.5f; - ambientLight[3] = 1.0f; - - diffuseLight[0] = 0.9f; - diffuseLight[1] = 0.9f; - diffuseLight[2] = 0.9f; - diffuseLight[3] = 1.0f; - - specular[0] = 1.0f; - specular[1] = 1.0f; - specular[2] = 1.0f; - specular[3] = 1.0f; - - specref[0] = 1.0f; - specref[1] = 1.0f; - specref[2] = 1.0f; - specref[3] = 1.0f; - - // Here we initialize our multi-texturing functions - glewInit(); - - _clearBuffer = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT; - _lightModelTwoSides = false; - - glDepthFunc(GL_LEQUAL); - glClearDepth(1.0); - glEnable(GL_NORMALIZE); - - glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); - - // Set light model - glLightModelfv(GL_LIGHT_MODEL_LOCAL_VIEWER, lmodel_local); - glLightModelfv(GL_LIGHT_MODEL_TWO_SIDE, lmodel_twoside); - glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient); - - // Setup 'light 0' - glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLight); - glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight); - glLightfv(GL_LIGHT0, GL_SPECULAR, specular); - glLightfv(GL_LIGHT0, GL_POSITION, _lightPosition); - - // Enable color tracking - glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); - - // All materials hereafter have full specular reflectivity with a high shine - glMaterialfv(GL_FRONT, GL_SPECULAR, specref); - glMateriali(GL_FRONT, GL_SHININESS, 128); - - glShadeModel(GL_SMOOTH); - - // Define background color - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - - //glBlendFunc(GL_SRC_ALPHA, GL_ONE); - //Load texture for logo - setBackgroundImage(); - - - glEnableClientState(GL_VERTEX_ARRAY); - //glEnableClientState(GL_NORMAL_ARRAY); - - // Turn on our light and enable color along with the light - //glEnable(GL_LIGHTING); - glEnable(GL_LIGHT0); - //glEnable(GL_COLOR_MATERIAL); - - //init Quadrics - _arrow = gluNewQuadric(); - gluQuadricDrawStyle(_arrow, GLU_FILL); - gluQuadricOrientation(_arrow, GLU_OUTSIDE); - gluQuadricNormals(_arrow, GLU_SMOOTH); - - _tube = gluNewQuadric(); - gluQuadricDrawStyle(_tube, GLU_FILL); - gluQuadricOrientation(_tube, GLU_OUTSIDE); - gluQuadricNormals(_tube, GLU_SMOOTH); - - _sphere = gluNewQuadric(); - gluQuadricDrawStyle(_sphere, GLU_FILL); - gluQuadricOrientation(_sphere, GLU_OUTSIDE); - gluQuadricNormals(_sphere, GLU_SMOOTH); - - _disk = gluNewQuadric(); - gluQuadricDrawStyle(_disk, GLU_FILL); - gluQuadricOrientation(_disk, GLU_OUTSIDE); - gluQuadricNormals(_disk, GLU_SMOOTH); - - // change status so we only do this stuff once - initialized = true; - - _beginTime = helper::system::thread::CTime::getTime(); - - printf("\n"); - - - // GL_LIGHT1 follows the camera - // glMatrixMode(GL_MODELVIEW); - // glPushMatrix(); - // glLoadIdentity(); - // glLightfv(GL_LIGHT0, GL_POSITION, _lightPosition); - // glPopMatrix(); - - - - // camera()->setType( qglviewer::Camera::ORTHOGRAPHIC ); - // camera()->setType( qglviewer::Camera::PERSPECTIVE ); - - } - - // switch to preset view - - resetView(); - - // Redefine keyboard events - // The default SAVE_SCREENSHOT shortcut is Ctrl+S and this shortcut is used to - // save x3d file in the MainController. So we need to change it: - setShortcut(QGLViewer::SAVE_SCREENSHOT, Qt::Key_S); - setShortcut(QGLViewer::HELP, Qt::Key_H); - // Disable ESC shortcut - setShortcut(QGLViewer::EXIT_VIEWER, 0); - - - // some useful libQGLViewer's mouse bindings are using Shift and Ctrl keys - // that causes trouble with SOFA bindings - // so let's redefined them - - // change pivot center: right click + double left click - setMouseBinding(Qt::NoModifier, Qt::LeftButton, RAP_FROM_PIXEL, true, Qt::RightButton); - - // Zoom: right click + double middle click - setMouseBinding(Qt::NoModifier, Qt::MiddleButton, ZOOM_ON_PIXEL, true, Qt::RightButton); -} - -// --------------------------------------------------------- -// --- -// --------------------------------------------------------- -void QtGLViewer::PrintString(void* /*font*/, char* string) -{ - gl::GlText::draw(string); - -} - -// --------------------------------------------------------- -// --- -// --------------------------------------------------------- -void QtGLViewer::Display3DText(float x, float y, float z, char* string) -{ - glPushMatrix(); - glTranslatef(x, y, z); - gl::GlText::draw(string); - glPopMatrix(); -} - -// --------------------------------------------------- -// --- -// --- -// --------------------------------------------------- -void QtGLViewer::DrawAxis(double xpos, double ypos, double zpos, - double arrowSize) -{ - glPushMatrix(); - glTranslatef(xpos, ypos,zpos); - QGLViewer::drawAxis(arrowSize); - glPopMatrix(); -} - -// --------------------------------------------------- -// --- -// --- -// --------------------------------------------------- -void QtGLViewer::DrawBox(SReal* minBBox, SReal* maxBBox, SReal r) -{ - if (r==0.0) - r = (Vec3(maxBBox) - Vec3(minBBox)).norm() / 500; -#if 0 - { - Enable depth; - Disable lighting; - glColor3f(0.0, 1.0, 1.0); - glBegin(GL_LINES); - for (int corner=0; corner<4; ++corner) - { - glVertex3d( minBBox[0] , - (corner&1)?minBBox[1]:maxBBox[1], - (corner&2)?minBBox[2]:maxBBox[2]); - glVertex3d( maxBBox[0] , - (corner&1)?minBBox[1]:maxBBox[1], - (corner&2)?minBBox[2]:maxBBox[2]); - } - for (int corner=0; corner<4; ++corner) - { - glVertex3d((corner&1)?minBBox[0]:maxBBox[0], - minBBox[1] , - (corner&2)?minBBox[2]:maxBBox[2]); - glVertex3d((corner&1)?minBBox[0]:maxBBox[0], - maxBBox[1] , - (corner&2)?minBBox[2]:maxBBox[2]); - } - - // --- Draw the Z edges - for (int corner=0; corner<4; ++corner) - { - glVertex3d((corner&1)?minBBox[0]:maxBBox[0], - (corner&2)?minBBox[1]:maxBBox[1], - minBBox[2] ); - glVertex3d((corner&1)?minBBox[0]:maxBBox[0], - (corner&2)?minBBox[1]:maxBBox[1], - maxBBox[2] ); - } - glEnd(); - return; - } -#endif - Enable depth; - Enable lighting; - Enable colorMat; - - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - glShadeModel(GL_SMOOTH); - - // --- Draw the corners - glColor3f(0.0, 1.0, 1.0); - for (int corner=0; corner<8; ++corner) - { - glPushMatrix(); - glTranslated((corner&1)?minBBox[0]:maxBBox[0], - (corner&2)?minBBox[1]:maxBBox[1], - (corner&4)?minBBox[2]:maxBBox[2]); - gluSphere(_sphere,2*r,20,10); - glPopMatrix(); - } - - glColor3f(1.0, 1.0, 0.0); - // --- Draw the X edges - for (int corner=0; corner<4; ++corner) - { - glPushMatrix(); - glTranslated( minBBox[0] , - (corner&1)?minBBox[1]:maxBBox[1], - (corner&2)?minBBox[2]:maxBBox[2]); - glRotatef(90,0,1,0); - gluCylinder(_tube, r, r, maxBBox[0] - minBBox[0], 10, 10); - glPopMatrix(); - } - - // --- Draw the Y edges - for (int corner=0; corner<4; ++corner) - { - glPushMatrix(); - glTranslated((corner&1)?minBBox[0]:maxBBox[0], - minBBox[1] , - (corner&2)?minBBox[2]:maxBBox[2]); - glRotatef(-90,1,0,0); - gluCylinder(_tube, r, r, maxBBox[1] - minBBox[1], 10, 10); - glPopMatrix(); - } - - // --- Draw the Z edges - for (int corner=0; corner<4; ++corner) - { - glPushMatrix(); - glTranslated((corner&1)?minBBox[0]:maxBBox[0], - (corner&2)?minBBox[1]:maxBBox[1], - minBBox[2] ); - gluCylinder(_tube, r, r, maxBBox[2] - minBBox[2], 10, 10); - glPopMatrix(); - } -} - - -// ---------------------------------------------------------------------------------- -// --- Draw a "plane" in wireframe. The "plane" is parallel to the XY axis -// --- of the main coordinate system -// ---------------------------------------------------------------------------------- -void QtGLViewer::DrawXYPlane(double zo, double xmin, double xmax, double ymin, - double ymax, double step) -{ - /*register*/ double x, y; - - Enable depth; - - glBegin(GL_LINES); - for (x = xmin; x <= xmax; x += step) - { - glVertex3d(x, ymin, zo); - glVertex3d(x, ymax, zo); - } - glEnd(); - - glBegin(GL_LINES); - for (y = ymin; y <= ymax; y += step) - { - glVertex3d(xmin, y, zo); - glVertex3d(xmax, y, zo); - } - glEnd(); -} - - -// ---------------------------------------------------------------------------------- -// --- Draw a "plane" in wireframe. The "plane" is parallel to the XY axis -// --- of the main coordinate system -// ---------------------------------------------------------------------------------- -void QtGLViewer::DrawYZPlane(double xo, double ymin, double ymax, double zmin, - double zmax, double step) -{ - /*register*/ double y, z; - Enable depth; - - glBegin(GL_LINES); - for (y = ymin; y <= ymax; y += step) - { - glVertex3d(xo, y, zmin); - glVertex3d(xo, y, zmax); - } - glEnd(); - - glBegin(GL_LINES); - for (z = zmin; z <= zmax; z += step) - { - glVertex3d(xo, ymin, z); - glVertex3d(xo, ymax, z); - } - glEnd(); - -} - - -// ---------------------------------------------------------------------------------- -// --- Draw a "plane" in wireframe. The "plane" is parallel to the XY axis -// --- of the main coordinate system -// ---------------------------------------------------------------------------------- -void QtGLViewer::DrawXZPlane(double yo, double xmin, double xmax, double zmin, - double zmax, double step) -{ - /*register*/ double x, z; - Enable depth; - - glBegin(GL_LINES); - for (x = xmin; x <= xmax; x += step) - { - glVertex3d(x, yo, zmin); - glVertex3d(x, yo, zmax); - } - glEnd(); - - glBegin(GL_LINES); - for (z = zmin; z <= zmax; z += step) - { - glVertex3d(xmin, yo, z); - glVertex3d(xmax, yo, z); - } - glEnd(); -} - -void QtGLViewer::drawColourPicking(ColourPickingVisitor::ColourCode code) -{ - - // Define background color - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - // GL_PROJECTION matrix - camera()->loadProjectionMatrix(); - // GL_MODELVIEW matrix - camera()->loadModelViewMatrix(); - - - - ColourPickingVisitor cpv(sofa::core::visual::visualparams::defaultInstance(), code); - cpv.execute(groot.get()); - - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); -} - -// ------------------------------------------------------------------- -// --- -// ------------------------------------------------------------------- -void QtGLViewer::DrawLogo() -{ - m_backend->drawBackgroundImage(_W, _H); -} - -// ------------------------------------------------------------------- -// --- -// ------------------------------------------------------------------- -void QtGLViewer::DisplayOBJs() -{ - - - if (_background == 0 || _background == 1) - DrawLogo(); - - if (!groot) return; - - // // Initialize lighting - glPushMatrix(); - glLoadIdentity(); - glLightfv(GL_LIGHT0, GL_POSITION, _lightPosition); - glPopMatrix(); - Enable light0; - // - glColor3f(0.5f, 0.5f, 0.6f); - // DrawXZPlane(-4.0, -20.0, 20.0, -20.0, 20.0, 1.0); - // DrawAxis(0.0, 0.0, 0.0, 10.0); - - - if (!groot->f_bbox.getValue().isValid()) viewAll(); - - - sofa::type::BoundingBox& bbox = vparams->sceneBBox(); - bbox = groot->f_bbox.getValue(); - - Enable light; - Enable depth; - - glShadeModel(GL_SMOOTH); - //glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); - glColor4f(1,1,1,1); - glDisable(GL_COLOR_MATERIAL); - - if (!initTexturesDone) - { - // std::cout << "-----------------------------------> initTexturesDone\n"; - //--------------------------------------------------- - sofa::simulation::node::initTextures(groot.get()); - //--------------------------------------------------- - initTexturesDone = true; - } - - - { - this->setSceneBoundingBox(qglviewer::Vec(vparams->sceneBBox().minBBoxPtr()), - qglviewer::Vec(vparams->sceneBBox().maxBBoxPtr())); - - //Draw Debug information of the components - sofa::simulation::node::draw(vparams, groot.get()); - if (m_bShowAxis) - { - //DrawAxis(0.0, 0.0, 0.0, 10.0); - DrawAxis(0.0, 0.0, 0.0, this->sceneRadius()); - - if (vparams->sceneBBox().isValid()) - DrawBox(vparams->sceneBBox().minBBoxPtr(), vparams->sceneBBox().maxBBoxPtr()); - - // 2D Axis: project current world orientation in the lower left part of the screen - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glOrtho(0.0,vparams->viewport()[2],0,vparams->viewport()[3],-30,30); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - const sofa::type::Quat sofaQuat( this->camera()->orientation()[0] - , this->camera()->orientation()[1] - , this->camera()->orientation()[2] - , this->camera()->orientation()[3]); - gl::Axis::draw(sofa::type::Vec3(30.0_sreal,30.0_sreal,0.0_sreal),sofaQuat.inverse(), 25.0, sofa::type::RGBAColor::red(), sofa::type::RGBAColor::green(), sofa::type::RGBAColor::blue()); - - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - - } - } - - // glDisable(GL_COLOR_MATERIAL); -} - -// ------------------------------------------------------- -// --- -// ------------------------------------------------------- -void QtGLViewer::DisplayMenu(void) -{ - Disable light; - - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glOrtho(-0.5, _W, -0.5, _H, -1.0, 1.0); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - - glColor3f(0.3f, 0.7f, 0.95f); - glRasterPos2i(_W / 2 - 5, _H - 15); - //sprintf(buffer,"FPS: %.1f\n", _frameRate.GetFPS()); - - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); -} - -void QtGLViewer::MakeStencilMask() -{ - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - gluOrtho2D(0,_W, 0, _H ); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - - glClear(GL_STENCIL_BUFFER_BIT); - glStencilFunc(GL_ALWAYS, 0x1, 0x1); - glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); - glColor4f(0,0,0,0); - glBegin(GL_LINES); - for (float f=0 ; f< _H ; f+=2.0) - { - glVertex2f(0.0, f); - glVertex2f(_W, f); - } - glEnd(); - - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - -} - -// --------------------------------------------------------- -// --- -// --------------------------------------------------------- -void QtGLViewer::drawScene(void) -{ - - camera()->getProjectionMatrix( lastProjectionMatrix ); - sofa::core::visual::VisualParams::Viewport& viewport = vparams->viewport(); - viewport[0] = 0; - viewport[1] = 0; - viewport[2] = camera()->screenWidth(); - viewport[3] = camera()->screenHeight(); - - vparams->zFar() = camera()->zFar(); - vparams->zNear() = camera()->zNear(); - - camera()->getModelViewMatrix( lastModelviewMatrix ); - vparams->setModelViewMatrix( lastModelviewMatrix ); - - vparams->setProjectionMatrix( lastProjectionMatrix ); - - //update info to SofaCamera - //TODO - - if (_renderingMode == GL_RENDER) - { - DisplayOBJs(); - DisplayMenu(); // always needs to be the last object being drawn - } - -} - -void QtGLViewer::viewAll() -{ - if (!groot) return; - sofa::type::BoundingBox& bbox = vparams->sceneBBox(); - bbox = groot->f_bbox.getValue(); - - - if (bbox.minBBox().x() == bbox.maxBBox().x() || !bbox.isValid()) - { - bbox.minBBox().x() = -1; - bbox.maxBBox().x() = 1; - } - if (bbox.minBBox().y() == bbox.maxBBox().y() || !bbox.isValid()) - { - bbox.minBBox().y() = -1; - bbox.maxBBox().y() = 1; - } - if (bbox.minBBox().z() == bbox.maxBBox().z() || !bbox.isValid()) - { - bbox.minBBox().z() = -1; - bbox.maxBBox().z() = 1; - } - - QGLViewer::setSceneBoundingBox( qglviewer::Vec(bbox.minBBoxPtr()),qglviewer::Vec(bbox.maxBBoxPtr())) ; - - qglviewer::Vec pos; - pos[0] = 0.0; - pos[1] = 0.0; - pos[2] = 75.0; - camera()->setPosition(pos); - camera()->showEntireScene(); -} - - - -// --------------------------------------------------------- -// --- Reshape of the window, reset the projection -// --------------------------------------------------------- -void QtGLViewer::resizeGL(int width, int height) -{ - _W = width; - _H = height; - - - QGLViewer::resizeGL( width, height); - - // TODO: find a better fix -#if not defined(__APPLE__) - this->resize(width, height); -#endif - - emit( resizeW( _W ) ); - emit( resizeH( _H ) ); -} - - -// --------------------------------------------------------- -// --- -// --------------------------------------------------------- -void QtGLViewer::draw() -{ - /// clear buffers (color and depth) - if (_background==0) - glClearColor(0.0f,0.0f,0.0f,1.0f); - else if (_background==1) - glClearColor(0.0f,0.0f,0.0f,1.0f); - else if (_background==2) - glClearColor(0.0f,0.0f,0.0f,0.0f); - else if (_background==3) - glClearColor(backgroundColour[0],backgroundColour[1],backgroundColour[2], 1.0f); - glClearDepth(1.0); - glClear(_clearBuffer); - - /// draw the scene - drawScene(); - - if(!captureTimer.isActive()) - SofaViewer::captureEvent(); - - if (_waitForRender) - { - _waitForRender = false; - } - - emit( redrawn() ); -} - -void QtGLViewer::setCameraMode(core::visual::VisualParams::CameraType mode) -{ - SofaViewer::setCameraMode(mode); - - switch (mode) - { - case core::visual::VisualParams::ORTHOGRAPHIC_TYPE: - camera()->setType( qglviewer::Camera::ORTHOGRAPHIC ); - break; - case core::visual::VisualParams::PERSPECTIVE_TYPE: - camera()->setType( qglviewer::Camera::PERSPECTIVE ); - break; - } -} - - -// ---------------------------------------- -// --- Handle events (mouse, keyboard, ...) -// ---------------------------------------- - - -void QtGLViewer::keyPressEvent ( QKeyEvent * e ) -{ - if (isControlPressed()) - { - if (groot ) - { - sofa::core::objectmodel::KeypressedEvent keyEvent(e->key()); - groot->propagateEvent(core::execparams::defaultInstance(), &keyEvent); - } - } - - switch(e->key()) - { - case Qt::Key_A: // axis - case Qt::Key_H: // help page - case Qt::Key_G: // show grid - { - // these shortcuts are handled by qglviewer - QGLViewer::keyPressEvent(e); - break; - } - case Qt::Key_C: - { - viewAll(); - break; - } - default: - { - SofaViewer::keyPressEvent(e); - } - } - update(); -} - - -void QtGLViewer::screenshot(const std::string& filename, int compression_level) -{ - SOFA_UNUSED(compression_level); - QGLViewer::saveSnapshot(QString::fromStdString(filename), false); -} - - -void QtGLViewer::keyReleaseEvent ( QKeyEvent * e ) -{ - - QGLViewer::keyReleaseEvent(e); - SofaViewer::keyReleaseEvent(e); -} - - -void QtGLViewer::mousePressEvent ( QMouseEvent * e ) -{ - if( ! mouseEvent(e) ) - { - if ( isControlPressed() ) - SofaViewer::mousePressEvent(e); - else - QGLViewer::mousePressEvent(e); - } -} - - -void QtGLViewer::mouseReleaseEvent ( QMouseEvent * e ) -{ - if( ! mouseEvent(e) ) - { - if (isControlPressed()) - SofaViewer::mouseReleaseEvent(e); - else - QGLViewer::mouseReleaseEvent(e); - } -} - - -void QtGLViewer::mouseMoveEvent ( QMouseEvent * e ) -{ - if( ! mouseEvent(e) ) - { - if ( isControlPressed() ) - SofaViewer::mouseMoveEvent(e); - else - QGLViewer::mouseMoveEvent(e); - } -} - - -bool QtGLViewer::mouseEvent(QMouseEvent * e) -{ - if(e->modifiers()&Qt::ShiftModifier) - { - SofaViewer::mouseEvent(e); - return true; - } - - return false; -} - -void QtGLViewer::wheelEvent(QWheelEvent* e) -{ - QGLViewer::wheelEvent(e); - if(isControlPressed()) // pass event to the scene elements - { - if (groot) - { -#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) - sofa::core::objectmodel::MouseEvent me(sofa::core::objectmodel::MouseEvent::Wheel, e->delta()); -#else - sofa::core::objectmodel::MouseEvent me(sofa::core::objectmodel::MouseEvent::Wheel, e->angleDelta().y()); -#endif - groot->propagateEvent(core::execparams::defaultInstance(), &me); - } - } - else - QGLViewer::wheelEvent(e); -} - -void QtGLViewer::moveRayPickInteractor(int eventX, int eventY) -{ - const sofa::core::visual::VisualParams::Viewport& viewport = vparams->viewport(); - Vec3d p0, px, py, pz, px1, py1; - gluUnProject(eventX, viewport[3]-1-(eventY), 0, lastModelviewMatrix, lastProjectionMatrix, viewport.data(), &(p0[0]), &(p0[1]), &(p0[2])); - gluUnProject(eventX+1, viewport[3]-1-(eventY), 0, lastModelviewMatrix, lastProjectionMatrix, viewport.data(), &(px[0]), &(px[1]), &(px[2])); - gluUnProject(eventX, viewport[3]-1-(eventY+1), 0, lastModelviewMatrix, lastProjectionMatrix, viewport.data(), &(py[0]), &(py[1]), &(py[2])); - gluUnProject(eventX, viewport[3]-1-(eventY), 0.1, lastModelviewMatrix, lastProjectionMatrix, viewport.data(), &(pz[0]), &(pz[1]), &(pz[2])); - gluUnProject(eventX+1, viewport[3]-1-(eventY), 0.1, lastModelviewMatrix, lastProjectionMatrix, viewport.data(), &(px1[0]), &(px1[1]), &(px1[2])); - gluUnProject(eventX, viewport[3]-1-(eventY+1), 0, lastModelviewMatrix, lastProjectionMatrix, viewport.data(), &(py1[0]), &(py1[1]), &(py1[2])); - px1 -= pz; - py1 -= pz; - px -= p0; - py -= p0; - pz -= p0; - const double r0 = sqrt(px.norm2() + py.norm2()); - double r1 = sqrt(px1.norm2() + py1.norm2()); - r1 = r0 + (r1-r0) / pz.norm(); - px.normalize(); - py.normalize(); - pz.normalize(); - Mat4x4d transform; - transform.identity(); - transform[0][0] = px[0]; - transform[1][0] = px[1]; - transform[2][0] = px[2]; - transform[0][1] = py[0]; - transform[1][1] = py[1]; - transform[2][1] = py[2]; - transform[0][2] = pz[0]; - transform[1][2] = pz[1]; - transform[2][2] = pz[2]; - transform[0][3] = p0[0]; - transform[1][3] = p0[1]; - transform[2][3] = p0[2]; - Mat3x3d mat; mat = transform; - Quat q; q.fromMatrix(mat); - - - Vec3d position, direction; - position = transform*Vec4d(0,0,0,1); - direction = transform*Vec4d(0,0,1,0); - direction.normalize(); - pick->updateRay(position, direction); - -} - -// ------------------------------------------------------------------- -// --- -// ------------------------------------------------------------------- -void QtGLViewer::resetView() -{ - viewAll(); - - if (!sceneFileName.empty()) - { - //Test if we have a specific view point for the QGLViewer - //That case, the camera will be well placed - std::string viewFileName = sceneFileName+"."+BaseGUI::GetGUIName()+".view"; - std::ifstream in(viewFileName.c_str()); - if (!in.fail()) - { - qglviewer::Vec pos; - in >> pos[0]; - in >> pos[1]; - in >> pos[2]; - - qglviewer::Quaternion q; - in >> q[0]; - in >> q[1]; - in >> q[2]; - in >> q[3]; - q.normalize(); - - camera()->setOrientation(q); - camera()->setPosition(pos); - - in.close(); - update(); - - return; - } - else - { - //If we have the default QtViewer view file, we have to use, showEntireScene - //as the FOV of the QtViewer is not constant, so the parameters are not good - std::string viewFileName = sceneFileName+".view"; - std::ifstream in(viewFileName.c_str()); - if (!in.fail()) - { - qglviewer::Vec pos; - in >> pos[0]; - in >> pos[1]; - in >> pos[2]; - - qglviewer::Quaternion q; - in >> q[0]; - in >> q[1]; - in >> q[2]; - in >> q[3]; - q.normalize(); - - camera()->setOrientation(q); - camera()->setPosition(pos); - camera()->showEntireScene(); - - in.close(); - update(); - - return; - } - } - } - update(); -} - -void QtGLViewer::saveView() -{ - if (!sceneFileName.empty()) - { - const std::string viewFileName = sceneFileName+"."+BaseGUI::GetGUIName()+".view"; - std::ofstream out(viewFileName.c_str()); - if (!out.fail()) - { - out << camera()->position()[0] << " " << camera()->position()[1] << " " << camera()->position()[2] << "\n"; - out << camera()->orientation()[0] << " " << camera()->orientation()[1] << " " << camera()->orientation()[2] << " " << camera()->orientation()[3] << "\n"; - out.close(); - } - std::cout << "View parameters saved in "<& ori) const -{ - qglviewer::Vec position = camera()->position(); - for(int i = 0; i < 3; ++i) pos[i] = position[i]; - qglviewer::Quaternion orientation = camera()->orientation(); - for(int i = 0; i < 4; ++i) ori[i] = orientation[i]; -} - -void QtGLViewer::setView(const Vec3& pos, const Quat &ori) -{ - camera()->setPosition(qglviewer::Vec(pos[0],pos[1],pos[2])); - camera()->setOrientation(qglviewer::Quaternion(ori[0],ori[1],ori[2],ori[3])); -} - -void QtGLViewer::setSizeW( int size ) -{ - resizeGL( size, _H ); - update(); -} - -void QtGLViewer::setSizeH( int size ) -{ - resizeGL( _W, size ); - update(); - -} - -QString QtGLViewer::helpString() const -{ - - static QString text( - (QString)"

    QtGLViewer


    \ -
      \ -
    • Mouse: TO NAVIGATE
    • \ -
    • Shift & Left Button: TO PICK OBJECTS
    • \ -
    • A: TO DRAW AXIS
    • \ -
    • B: TO CHANGE THE BACKGROUND
    • \ -
    • C: TO CENTER THE VIEW
    • \ -
    • H: TO OPEN HELP of QGLViewer
    • \ -
    • O: TO EXPORT TO .OBJ
      \ - The generated files scene-time.obj and scene-time.mtl are saved in the running project directory
    • \ -
    • P: TO SAVE A SEQUENCE OF OBJ
      \ - Each time the frame is updated an obj is exported
    • \ -
    • R: TO DRAW THE SCENE AXIS
    • \ -
    • T: TO CHANGE BETWEEN A PERSPECTIVE OR AN ORTHOGRAPHIC CAMERA
    • \ - The captured images are saved in the running project directory under the name format capturexxxx.bmp
      \ -
    • S: TO SAVE A SCREENSHOT
      \ -
    • V: TO SAVE A VIDEO
      \ - Each time the frame is updated a screenshot is saved
    • \ -
    • Esc: TO QUIT ::sofa::
    " - ); - - return text; -} - -} // namespace sofa::gui::qt::viewer::qgl diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/viewer/qgl/QtGLViewer.h b/Sofa/GUI/Qt/src/sofa/gui/qt/viewer/qgl/QtGLViewer.h deleted file mode 100644 index 4ebce78d423..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/viewer/qgl/QtGLViewer.h +++ /dev/null @@ -1,232 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include - -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - - - -#include -#include - -#define TRACKING_MOUSE - -namespace sofa::gui::qt::viewer::qgl -{ - -class SOFA_GUI_QT_API QtGLViewer :public QGLViewer, public sofa::gui::qt::viewer::OglModelSofaViewer -{ - typedef type::Vec3::value_type Real; - Q_OBJECT -private: - -#ifdef TRACKING_MOUSE - bool m_grabActived; -#endif - - QTimer* timerAnimate; - int _W, _H; - int _clearBuffer; - bool _lightModelTwoSides; - float _lightPosition[4]; - - - double lastProjectionMatrix[16]; - double lastModelviewMatrix[16]; - - GLUquadricObj* _arrow; - GLUquadricObj* _tube; - GLUquadricObj* _sphere; - GLUquadricObj* _disk; - GLuint _numOBJmodels; - GLuint _materialMode; - GLboolean _facetNormal; - - int _renderingMode; - - helper::system::thread::ctime_t _beginTime; - - - bool _waitForRender; - - -public: - - static QtGLViewer* create(QtGLViewer*, sofa::gui::common::BaseViewerArgument& arg) - { - common::BaseViewerArgument* pArg = &arg; - const common::ViewerQtArgument* viewerArg = dynamic_cast(pArg); - return viewerArg ? - new QtGLViewer(viewerArg->getParentWidget(), viewerArg->getName().c_str() ) : - new QtGLViewer(nullptr, pArg->getName().c_str() ) - ; - } - - static const char* viewerName() { return "QGLViewer (QtGLViewer)"; } - - static const char* acceleratedName() { return "&QGLViewer (QtGLViewer)"; } - - virtual void drawColourPicking (common::ColourPickingVisitor::ColourCode code) override; - - QtGLViewer( QWidget* parent, const char* name=""); - ~QtGLViewer() override; - - QWidget* getQWidget() override { return this; } - -protected: - - // void calcProjection(); - void init() override; - /// Overloaded from QGLViewer to render the scene - void draw() override; - /// Overloaded from SofaViewer - virtual void viewAll() override; - void resizeGL( int w, int h ) override; - -public: - - //void reshape(int width, int height); - int getWidth() override - { - return _W; - } - int getHeight() override - { - return _H; - } - bool ready() override {return !_waitForRender;} - void wait() override {_waitForRender = true;} - - void UpdateOBJ(void); - - void moveRayPickInteractor(int eventX, int eventY) override; - - void setCameraMode(core::visual::VisualParams::CameraType mode) override; - - void screenshot(const std::string& filename, int compression_level = -1) override; - - QString helpString() const override; - - -private: - - void InitGFX(void); - void PrintString(void* font, char* string); - void Display3DText(float x, float y, float z, char* string); - void DrawAxis(double xpos, double ypos, double zpos, double arrowSize); - void DrawBox(SReal* minBBox, SReal* maxBBox, SReal r=0.0); - void DrawXYPlane(double zo, double xmin, double xmax, double ymin, double ymax, double step); - void DrawYZPlane(double xo, double ymin, double ymax, double zmin, double zmax, double step); - void DrawXZPlane(double yo, double xmin, double xmax, double zmin, double zmax, double step); - void CreateOBJmodelDisplayList(int material_mode); - //int loadBMP(char *filename, TextureImage *texture); - //void LoadGLTexture(char *Filename); - void DisplayOBJs(); - void DisplayMenu(void); - void MakeStencilMask(); - - //int handle(int event); // required by FLTK - -protected: - //virtual bool event ( QEvent * e ); - - virtual void drawScene() override; - virtual void DrawLogo(void); - - - void keyPressEvent ( QKeyEvent * e ) override; - void keyReleaseEvent ( QKeyEvent * e ) override; - void mousePressEvent ( QMouseEvent * e ) override; - void mouseReleaseEvent ( QMouseEvent * e ) override; - void mouseMoveEvent ( QMouseEvent * e ) override; - void wheelEvent(QWheelEvent* e) override; - bool mouseEvent( QMouseEvent * e ) override; - - - -public slots: - void resetView() override; - void saveView() override; - void setSizeW(int) override; - void setSizeH(int) override; - - virtual void getView(type::Vec3& pos, type::Quat& ori) const override; - virtual void setView(const type::Vec3& pos, const type::Quat &ori) override; - virtual void captureEvent() override { SofaViewer::captureEvent(); } - void fitObjectBBox(sofa::core::objectmodel::BaseObject* object) override - { - if( object->f_bbox.getValue().isValid() && !object->f_bbox.getValue().isFlat() ) - this->camera()->fitBoundingBox( - ::qglviewer::Vec(object->f_bbox.getValue().minBBox()), - ::qglviewer::Vec(object->f_bbox.getValue().maxBBox()) - ); - else - { - if(object->getContext()->f_bbox.getValue().isValid() && !object->getContext()->f_bbox.getValue().isFlat() ) - { - this->camera()->fitBoundingBox( - ::qglviewer::Vec(object->getContext()->f_bbox.getValue().minBBox()), - ::qglviewer::Vec(object->getContext()->f_bbox.getValue().maxBBox()) - ); - } - } - this->update(); - } - - void fitNodeBBox(sofa::core::objectmodel::BaseNode* node) override - { - if( node->f_bbox.getValue().isValid() && !node->f_bbox.getValue().isFlat() ) - this->camera()->fitBoundingBox( - ::qglviewer::Vec(node->f_bbox.getValue().minBBox()), - ::qglviewer::Vec(node->f_bbox.getValue().maxBBox()) - ); - - this->update(); - - } - -signals: - void redrawn() override; - void resizeW( int ) override; - void resizeH( int ) override; - void quit(); -}; - -} // namespace sofa::gui::qt::viewer::qgl diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/viewer/qt/QtViewer.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/viewer/qt/QtViewer.cpp deleted file mode 100644 index fd469cbda79..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/viewer/qt/QtViewer.cpp +++ /dev/null @@ -1,1558 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#ifdef __APPLE__ -#include -#endif - -#include -#include -#include - -#include -#include -#include -#include - -#include - -namespace sofa::gui::qt::viewer::qt -{ -using std::cout; -using std::endl; -using namespace sofa::type; -using namespace sofa::defaulttype; -using namespace sofa::gui::common; - -using sofa::simulation::getSimulation; - -helper::SofaViewerCreator< QtViewer> QtViewer_class("qt",false); -//Q:Why would the QtViewer write its .view file with the qglviewer (a GPL library) extension? -//A:The new QtViewer has the same parameters as QGLViewer. -// Consequently, the old .view file is now totally incorrect. - -///TODO: standardize .view file parameters -//const std::string QtViewer::VIEW_FILE_EXTENSION = "qglviewer.view"; -const std::string QtViewer::VIEW_FILE_EXTENSION = "view"; -// Mouse Interactor -bool QtViewer::_mouseTrans = false; -bool QtViewer::_mouseRotate = false; -Quat QtViewer::_mouseInteractorNewQuat; - -// --------------------------------------------------------- -// --- Constructor -// --------------------------------------------------------- -QtViewer::QtViewer(QWidget* parent, const char* name) - : QOpenGLWidget(parent) -{ - m_backend.reset(new GLBackend()); - pick = std::make_unique(); - - this->setObjectName(name); - - groot = nullptr; - initTexturesDone = false; - backgroundColour[0] = 1.0f; - backgroundColour[1] = 1.0f; - backgroundColour[2] = 1.0f; - - _video = false; - m_bShowAxis = false; - _background = 0; - _numOBJmodels = 0; - _materialMode = 0; - _facetNormal = GL_FALSE; - _renderingMode = GL_RENDER; - _waitForRender = false; - - //////////////// - // Interactor // - //////////////// - m_isControlPressed = false; - _mouseInteractorMoving = false; - _mouseInteractorTranslationMode = false; - _mouseInteractorRotationMode = false; - _mouseInteractorSavedPosX = 0; - _mouseInteractorSavedPosY = 0; - _mouseInteractorTrackball.ComputeQuaternion(0.0, 0.0, 0.0, 0.0); - _mouseInteractorNewQuat = _mouseInteractorTrackball.GetQuaternion(); - - connect( &captureTimer, &QTimer::timeout, this, &QtViewer::captureEvent ); -} - -// --------------------------------------------------------- -// --- Destructor -// --------------------------------------------------------- -QtViewer::~QtViewer() -{ -} - -// ----------------------------------------------------------------- -// --- OpenGL initialization method - includes light definitions, -// --- color tracking, etc. -// ----------------------------------------------------------------- -void QtViewer::initializeGL(void) -{ - msg_info("QtViewer") << "OpenGL version: " << glGetString(GL_VERSION) << " (" << glGetString(GL_VENDOR) << ")"; - if (std::string((const char*)glGetString(GL_VENDOR)).find("Intel") != std::string::npos) - { - const char* mesaEnv = ::getenv("MESA_GL_VERSION_OVERRIDE"); - if ( !mesaEnv || std::string(mesaEnv) != "3.0") - msg_warning("QtViewer") << "QtViewer might not be compatible with Intel drivers on " - "Linux. If you run into rendering issues, try changing " - "the gui to qglviewer (runSofa -g qglviewer) or set the " - "environment variable \"MESA_GL_VERSION_OVERRIDE\" " - "to the value \"3.0\""; - } - - static GLfloat specref[4]; - static GLfloat ambientLight[4]; - static GLfloat diffuseLight[4]; - static GLfloat specular[4]; - static GLfloat lmodel_ambient[] = - { 0.0f, 0.0f, 0.0f, 0.0f }; - static GLfloat lmodel_twoside[] = - { GL_FALSE }; - static GLfloat lmodel_local[] = - { GL_FALSE }; - bool initialized = false; - - if (!initialized) - { - //std::cout << "progname=" << sofa::gui::qt::progname << std::endl; - //sofa::helper::system::SetDirectory cwd(sofa::helper::system::SetDirectory::GetProcessFullPath(sofa::gui::qt::progname)); - - // Define light parameters - //_lightPosition[0] = 0.0f; - //_lightPosition[1] = 10.0f; - //_lightPosition[2] = 0.0f; - //_lightPosition[3] = 1.0f; - - _lightPosition[0] = -0.7f; - _lightPosition[1] = 0.3f; - _lightPosition[2] = 0.0f; - _lightPosition[3] = 1.0f; - - ambientLight[0] = 0.5f; - ambientLight[1] = 0.5f; - ambientLight[2] = 0.5f; - ambientLight[3] = 1.0f; - - diffuseLight[0] = 0.9f; - diffuseLight[1] = 0.9f; - diffuseLight[2] = 0.9f; - diffuseLight[3] = 1.0f; - - specular[0] = 1.0f; - specular[1] = 1.0f; - specular[2] = 1.0f; - specular[3] = 1.0f; - - specref[0] = 1.0f; - specref[1] = 1.0f; - specref[2] = 1.0f; - specref[3] = 1.0f; - - // Here we initialize our multi-texturing functions - glewInit(); - if (!GL_ARB_multitexture) - msg_error("QtViewer") << "GL_ARB_multitexture not supported."; - - _clearBuffer = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT; - _lightModelTwoSides = false; - - glDepthFunc(GL_LEQUAL); - glClearDepth(1.0); - glEnable(GL_NORMALIZE); - - glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); - - // Set light model - glLightModelfv(GL_LIGHT_MODEL_LOCAL_VIEWER, lmodel_local); - glLightModelfv(GL_LIGHT_MODEL_TWO_SIDE, lmodel_twoside); - glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient); - - // Setup 'light 0' - glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLight); - glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight); - glLightfv(GL_LIGHT0, GL_SPECULAR, specular); - glLightfv(GL_LIGHT0, GL_POSITION, _lightPosition); - - // Enable color tracking - glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); - - // All materials hereafter have full specular reflectivity with a high shine - glMaterialfv(GL_FRONT, GL_SPECULAR, specref); - glMateriali(GL_FRONT, GL_SHININESS, 128); - - glShadeModel(GL_SMOOTH); - - // Define background color - // glClearColor(0.0f, 0.0f, 1.0f, 1.0f); - - //glBlendFunc(GL_SRC_ALPHA, GL_ONE); - //Load texture for logo - setBackgroundImage(); - - glEnableClientState(GL_VERTEX_ARRAY); - //glEnableClientState(GL_NORMAL_ARRAY); - - // Turn on our light and enable color along with the light - //glEnable(GL_LIGHTING); - glEnable(GL_LIGHT0); - //glEnable(GL_COLOR_MATERIAL); - - //init Quadrics - _arrow = gluNewQuadric(); - gluQuadricDrawStyle(_arrow, GLU_FILL); - gluQuadricOrientation(_arrow, GLU_OUTSIDE); - gluQuadricNormals(_arrow, GLU_SMOOTH); - - _tube = gluNewQuadric(); - gluQuadricDrawStyle(_tube, GLU_FILL); - gluQuadricOrientation(_tube, GLU_OUTSIDE); - gluQuadricNormals(_tube, GLU_SMOOTH); - - _sphere = gluNewQuadric(); - gluQuadricDrawStyle(_sphere, GLU_FILL); - gluQuadricOrientation(_sphere, GLU_OUTSIDE); - gluQuadricNormals(_sphere, GLU_SMOOTH); - - _disk = gluNewQuadric(); - gluQuadricDrawStyle(_disk, GLU_FILL); - gluQuadricOrientation(_disk, GLU_OUTSIDE); - gluQuadricNormals(_disk, GLU_SMOOTH); - - // change status so we only do this stuff once - initialized = true; - - _beginTime = CTime::getTime(); - - //printf("GL initialized\n"); - } - - // switch to preset view - resetView(); -} - -// --------------------------------------------------------- -// --- -// --------------------------------------------------------- -void QtViewer::PrintString(void* /*font*/, char* string) -{ - gl::GlText::draw(string); -} - -// --------------------------------------------------------- -// --- -// --------------------------------------------------------- -void QtViewer::Display3DText(float x, float y, float z, char* string) -{ - glPushMatrix(); - glTranslatef(x, y, z); - gl::GlText::draw(string); - glPopMatrix(); -} - -// --------------------------------------------------- -// --- -// --- -// --------------------------------------------------- -void QtViewer::DrawAxis(double xpos, double ypos, double zpos, double arrowSize) -{ - const float fontScale = (float) (arrowSize * 0.25); - - Enable depth; - Enable lighting; - Enable colorMat; - - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - glShadeModel(GL_SMOOTH); - - // --- Draw the "X" axis in red - glPushMatrix(); - glColor3f(1.0, 0.0, 0.0); - glTranslated(xpos, ypos, zpos); - glRotatef(90.0f, 0.0, 1.0, 0.0); - gluCylinder(_tube, arrowSize / 50.0, arrowSize / 50.0, arrowSize, 10, 10); - glTranslated(0.0, 0.0, arrowSize); - gluCylinder(_arrow, arrowSize / 15.0, 0.0, arrowSize / 5.0, 10, 10); - // ---- Display a "X" near the tip of the arrow - glTranslated(-0.5 * fontScale, arrowSize / 15.0, arrowSize / 5.0); - - gl::GlText::draw('X', sofa::type::Vec3(0.0_sreal, 0.0_sreal, 0.0_sreal), fontScale); - - // --- Undo transforms - glTranslated(-xpos, -ypos, -zpos); - glPopMatrix(); - - // --- Draw the "Y" axis in green - glPushMatrix(); - glColor3f(0.0, 1.0, 0.0); - glTranslated(xpos, ypos, zpos); - glRotatef(-90.0f, 1.0, 0.0, 0.0); - gluCylinder(_tube, arrowSize / 50.0, arrowSize / 50.0, arrowSize, 10, 10); - glTranslated(0.0, 0.0, arrowSize); - gluCylinder(_arrow, arrowSize / 15.0, 0.0, arrowSize / 5.0, 10, 10); - // ---- Display a "Y" near the tip of the arrow - glTranslated(-0.5 * fontScale, arrowSize / 15.0, arrowSize / 5.0); - gl::GlText::draw('Y', sofa::type::Vec3(0.0, 0.0, 0.0), fontScale); - // --- Undo transforms - glTranslated(-xpos, -ypos, -zpos); - glPopMatrix(); - - // --- Draw the "Z" axis in blue - glPushMatrix(); - glColor3f(0.0, 0.0, 1.0); - glTranslated(xpos, ypos, zpos); - glRotatef(0.0f, 1.0, 0.0, 0.0); - gluCylinder(_tube, arrowSize / 50.0, arrowSize / 50.0, arrowSize, 10, 10); - glTranslated(0.0, 0.0, arrowSize); - gluCylinder(_arrow, arrowSize / 15.0, 0.0, arrowSize / 5.0, 10, 10); - // ---- Display a "Z" near the tip of the arrow - glTranslated(-0.5 * fontScale, arrowSize / 15.0, arrowSize / 5.0); - gl::GlText::draw('Z', sofa::type::Vec3(0.0, 0.0, 0.0), fontScale); - // --- Undo transforms - glTranslated(-xpos, -ypos, -zpos); - glPopMatrix(); -} - -// --------------------------------------------------- -// --- -// --- -// --------------------------------------------------- -void QtViewer::DrawBox(SReal* minBBox, SReal* maxBBox, SReal r) -{ - //std::cout << "box = < " << minBBox[0] << ' ' << minBBox[1] << ' ' << minBBox[2] << " >-< " << maxBBox[0] << ' ' << maxBBox[1] << ' ' << maxBBox[2] << " >"<< std::endl; - if (r == 0.0) - r = (Vec3(maxBBox) - Vec3(minBBox)).norm() / 500; - - Enable depth; - Enable lighting; - Enable colorMat; - - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - glShadeModel(GL_SMOOTH); - - // --- Draw the corners - glColor3f(0.0, 1.0, 1.0); - for (int corner = 0; corner < 8; ++corner) - { - glPushMatrix(); - glTranslated((corner & 1) ? minBBox[0] : maxBBox[0], - (corner & 2) ? minBBox[1] : maxBBox[1], - (corner & 4) ? minBBox[2] : maxBBox[2]); - gluSphere(_sphere, 2 * r, 20, 10); - glPopMatrix(); - } - - glColor3f(1.0, 1.0, 0.0); - // --- Draw the X edges - for (int corner = 0; corner < 4; ++corner) - { - glPushMatrix(); - glTranslated(minBBox[0], (corner & 1) ? minBBox[1] : maxBBox[1], - (corner & 2) ? minBBox[2] : maxBBox[2]); - glRotatef(90, 0, 1, 0); - gluCylinder(_tube, r, r, maxBBox[0] - minBBox[0], 10, 10); - glPopMatrix(); - } - - // --- Draw the Y edges - for (int corner = 0; corner < 4; ++corner) - { - glPushMatrix(); - glTranslated((corner & 1) ? minBBox[0] : maxBBox[0], minBBox[1], - (corner & 2) ? minBBox[2] : maxBBox[2]); - glRotatef(-90, 1, 0, 0); - gluCylinder(_tube, r, r, maxBBox[1] - minBBox[1], 10, 10); - glPopMatrix(); - } - - // --- Draw the Z edges - for (int corner = 0; corner < 4; ++corner) - { - glPushMatrix(); - glTranslated((corner & 1) ? minBBox[0] : maxBBox[0], - (corner & 2) ? minBBox[1] : maxBBox[1], minBBox[2]); - gluCylinder(_tube, r, r, maxBBox[2] - minBBox[2], 10, 10); - glPopMatrix(); - } -} - -// ---------------------------------------------------------------------------------- -// --- Draw a "plane" in wireframe. The "plane" is parallel to the XY axis -// --- of the main coordinate system -// ---------------------------------------------------------------------------------- -void QtViewer::DrawXYPlane(double zo, double xmin, double xmax, double ymin, - double ymax, double step) -{ - double x, y; - - Enable depth; - - glBegin(GL_LINES); - for (x = xmin; x <= xmax; x += step) - { - glVertex3d(x, ymin, zo); - glVertex3d(x, ymax, zo); - } - glEnd(); - - glBegin(GL_LINES); - for (y = ymin; y <= ymax; y += step) - { - glVertex3d(xmin, y, zo); - glVertex3d(xmax, y, zo); - } - glEnd(); -} - -// ---------------------------------------------------------------------------------- -// --- Draw a "plane" in wireframe. The "plane" is parallel to the XY axis -// --- of the main coordinate system -// ---------------------------------------------------------------------------------- -void QtViewer::DrawYZPlane(double xo, double ymin, double ymax, double zmin, - double zmax, double step) -{ - double y, z; - Enable depth; - - glBegin(GL_LINES); - for (y = ymin; y <= ymax; y += step) - { - glVertex3d(xo, y, zmin); - glVertex3d(xo, y, zmax); - } - glEnd(); - - glBegin(GL_LINES); - for (z = zmin; z <= zmax; z += step) - { - glVertex3d(xo, ymin, z); - glVertex3d(xo, ymax, z); - } - glEnd(); - -} - -// ---------------------------------------------------------------------------------- -// --- Draw a "plane" in wireframe. The "plane" is parallel to the XY axis -// --- of the main coordinate system -// ---------------------------------------------------------------------------------- -void QtViewer::DrawXZPlane(double yo, double xmin, double xmax, double zmin, - double zmax, double step) -{ - double x, z; - Enable depth; - - glBegin(GL_LINES); - for (x = xmin; x <= xmax; x += step) - { - glVertex3d(x, yo, zmin); - glVertex3d(x, yo, zmax); - } - glEnd(); - - glBegin(GL_LINES); - for (z = zmin; z <= zmax; z += step) - { - glVertex3d(xmin, yo, z); - glVertex3d(xmax, yo, z); - } - glEnd(); -} - -// ------------------------------------------------------------------- -// --- -// ------------------------------------------------------------------- -void QtViewer::DrawLogo() -{ - m_backend->drawBackgroundImage(_W, _H); -} - - - -// --------------------------------------------------------- -// --- -// --------------------------------------------------------- -void QtViewer::drawColourPicking(ColourPickingVisitor::ColourCode code) -{ - // Define background color - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glMultMatrixd(lastProjectionMatrix); - glMatrixMode(GL_MODELVIEW); - - - ColourPickingVisitor cpv(sofa::core::visual::visualparams::defaultInstance(), code); - cpv.execute( groot.get() ); - - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - -} -// ------------------------------------------------------------------- -// --- -// ------------------------------------------------------------------- -void QtViewer::DisplayOBJs() -{ - - if (_background == 0 || _background == 1) - DrawLogo(); - - if (!groot) - return; - Enable light; - Enable depth; - - - vparams->sceneBBox() = groot->f_bbox.getValue(); - - - glShadeModel(GL_SMOOTH); - //glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); - glColor4f(1, 1, 1, 1); - glDisable(GL_COLOR_MATERIAL); - - if (!initTexturesDone) - { - // std::cout << "-----------------------------------> initTexturesDone\n"; - //--------------------------------------------------- - sofa::simulation::node::initTextures(groot.get()); - //--------------------------------------------------- - initTexturesDone = true; - } - - { - - sofa::simulation::node::draw(vparams, groot.get()); - - if (m_bShowAxis) - { - const SReal* minBBox = vparams->sceneBBox().minBBoxPtr(); - const SReal* maxBBox = vparams->sceneBBox().maxBBoxPtr(); - SReal maxDistance = std::numeric_limits::min(); - - maxDistance = maxBBox[0] - minBBox[0]; - for (int i=1;i<3;i++) - { - if(maxDistance < (maxBBox[i] - minBBox[i])) - maxDistance = (maxBBox[i] - minBBox[i]); - } - - if(maxDistance == 0 ) - maxDistance = 1.0; - - // World Axis: Arrows of axis are defined as 10% of maxBBox - DrawAxis(0.0, 0.0, 0.0,(maxDistance*0.1)); - - if (vparams->sceneBBox().minBBox().x() < vparams->sceneBBox().maxBBox().x()) - DrawBox(vparams->sceneBBox().minBBoxPtr(), vparams->sceneBBox().maxBBoxPtr()); - - // 2D Axis: project current world orientation in the lower left part of the screen - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glOrtho(0.0,vparams->viewport()[2],0,vparams->viewport()[3],-30,30); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - gl::Axis::draw(sofa::type::Vec3(30.0,30.0,0.0),currentCamera->getOrientation().inverse(), 25.0, sofa::type::RGBAColor::red(), sofa::type::RGBAColor::green(), sofa::type::RGBAColor::blue()); - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - - } - } - - // glDisable(GL_COLOR_MATERIAL); -} - -// ------------------------------------------------------- -// --- -// ------------------------------------------------------- -void QtViewer::DisplayMenu(void) -{ - Disable light; - - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glOrtho(-0.5, _W, -0.5, _H, -1.0, 1.0); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - - glColor3f(0.3f, 0.7f, 0.95f); - glRasterPos2i(_W / 2 - 5, _H - 15); - //sprintf(buffer,"FPS: %.1f\n", _frameRate.GetFPS()); - - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); -} - -void QtViewer::MakeStencilMask() -{ - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - gluOrtho2D(0,_W, 0, _H ); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - - glClear(GL_STENCIL_BUFFER_BIT); - glStencilFunc(GL_ALWAYS, 0x1, 0x1); - glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); - glColor4f(0,0,0,0); - glBegin(GL_LINES); - for (float f=0 ; f< _H ; f+=2.0) - { - glVertex2f(0.0, f); - glVertex2f(_W, f); - } - glEnd(); - - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - -} - -// --------------------------------------------------------- -// --- -// --------------------------------------------------------- -void QtViewer::drawScene(void) -{ - if (!groot) return; - - if(!currentCamera) - { - msg_error("QtViewer") << "No camera defined."; - return; - } - - int width = _W; - int height = _H; - const bool stereo = currentCamera->getStereoEnabled(); - bool twopass = stereo; - sofa::component::visual::BaseCamera::StereoMode smode = currentCamera->getStereoMode(); - const sofa::component::visual::BaseCamera::StereoStrategy sStrat = currentCamera->getStereoStrategy(); - const double sShift = currentCamera->getStereoShift(); - bool stencil = false; - bool viewport = false; - const bool supportStereo = currentCamera->isStereo(); - sofa::core::visual::VisualParams::Viewport vpleft, vpright; - if(supportStereo) - { - if(stereo) - { - if (smode == sofa::component::visual::BaseCamera::STEREO_AUTO) - { - // auto-detect stereo mode - static int prevsmode = sofa::component::visual::BaseCamera::STEREO_AUTO; - if ((_W <= 1280 && _H == 1470) || (_W <= 1920 && _H == 2205)) - { - // standard HDMI 1.4 stereo frame packing format - smode = sofa::component::visual::BaseCamera::STEREO_FRAME_PACKING; - if (smode != prevsmode) std::cout << "AUTO Stereo mode: Frame Packing" << std::endl; - } - else if (_W >= 2 * _H) - { - smode = sofa::component::visual::BaseCamera::STEREO_SIDE_BY_SIDE; - if (smode != prevsmode) std::cout << "AUTO Stereo mode: Side by Side" << std::endl; - } - else if (_H > _W) - { - smode = sofa::component::visual::BaseCamera::STEREO_TOP_BOTTOM; - if (smode != prevsmode) std::cout << "AUTO Stereo mode: Top Bottom" << std::endl; - } - else - { - smode = sofa::component::visual::BaseCamera::STEREO_INTERLACED; - if (smode != prevsmode) std::cout << "AUTO Stereo mode: Interlaced" << std::endl; - //smode = STEREO_SYDE_BY_SIDE_HALF; - //if (smode != prevsmode) std::cout << "AUTO Stereo mode: Side by Side Half" << std::endl; - } - prevsmode = smode; - } - switch (smode) - { - case sofa::component::visual::BaseCamera::STEREO_INTERLACED: - { - stencil = true; - glEnable(GL_STENCIL_TEST); - MakeStencilMask(); - break; - } - case sofa::component::visual::BaseCamera::STEREO_SIDE_BY_SIDE: - case sofa::component::visual::BaseCamera::STEREO_SIDE_BY_SIDE_HALF: - { - width /= 2; - viewport = true; - vpleft = sofa::type::make_array(0,0,width,height); - vpright = sofa::type::make_array(_W-width,0,width,height); - if (smode == sofa::component::visual::BaseCamera::STEREO_SIDE_BY_SIDE_HALF) - width = _W; // keep the original ratio for camera - break; - } - case sofa::component::visual::BaseCamera::STEREO_FRAME_PACKING: - case sofa::component::visual::BaseCamera::STEREO_TOP_BOTTOM: - case sofa::component::visual::BaseCamera::STEREO_TOP_BOTTOM_HALF: - { - if (smode == sofa::component::visual::BaseCamera::STEREO_FRAME_PACKING && _H == 1470) // 720p format - height = 720; - else if (smode == sofa::component::visual::BaseCamera::STEREO_FRAME_PACKING && _H == 2205) // 1080p format - height = 1080; - else // other resolutions - height /= 2; - viewport = true; - vpleft = sofa::type::make_array(0,0,width,height); - vpright = sofa::type::make_array(0,_H-height,width,height); - if (smode == sofa::component::visual::BaseCamera::STEREO_TOP_BOTTOM_HALF) - height = _H; // keep the original ratio for camera - break; - } - case sofa::component::visual::BaseCamera::STEREO_AUTO: - case sofa::component::visual::BaseCamera::STEREO_NONE: - default: - twopass = false; - break; - } - } - }else - { - twopass = false; - } - calcProjection(width, height); - - glLoadIdentity(); - - GLdouble mat[16]; - - //std::cout << "Default" << this->defaultFramebufferObject() << std::endl; - currentCamera->getOpenGLModelViewMatrix(mat); - glMultMatrixd(mat); - - glGetDoublev(GL_MODELVIEW_MATRIX, lastModelviewMatrix); - vparams->setModelViewMatrix(lastModelviewMatrix); - - if(supportStereo) - { - if (stereo) - { - //1st pass - if (viewport) - { - sofa::core::visual::VisualParams::Viewport vp = vpleft; - vparams->viewport() = vp; - glViewport(vp[0], vp[1], vp[2], vp[3]); - glScissor(vp[0], vp[1], vp[2], vp[3]); - glEnable(GL_SCISSOR_TEST); - } - if (stencil) - { - glStencilFunc(GL_EQUAL, 0x1, 0x1); - glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - } - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - if(sStrat == sofa::component::visual::BaseCamera::PARALLEL) - { - glTranslated(sShift/2,0,0); - } - else if(sStrat == sofa::component::visual::BaseCamera::TOEDIN) - { - const double distance = currentCamera ? currentCamera->getDistance() : 10*sShift; - const double angle = atan2(sShift,distance)*180.0/M_PI; - glTranslated(0,0,-distance); - glRotated(-angle,0,1,0); - glTranslated(0,0,distance); - } - - - glMultMatrixd(mat); - } - } - - if (_renderingMode == GL_RENDER) - { - currentCamera->setCurrentSide(sofa::component::visual::BaseCamera::LEFT); - DisplayOBJs(); - } - if(supportStereo) - { - if (stereo) - { - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - } - } - //2nd pass - if (twopass) - { - if (viewport) - { - sofa::core::visual::VisualParams::Viewport vp = vpright; - vparams->viewport() = vp; - glViewport(vp[0], vp[1], vp[2], vp[3]); - glScissor(vp[0], vp[1], vp[2], vp[3]); - glEnable(GL_SCISSOR_TEST); - } - if (stencil) - { - glStencilFunc(GL_NOTEQUAL, 0x1, 0x1); - glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - } - - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - - if(sStrat == sofa::component::visual::BaseCamera::PARALLEL) {glTranslated(-sShift/2,0,0);} - - glMultMatrixd(mat); - if (_renderingMode == GL_RENDER) - { - currentCamera->setCurrentSide(sofa::component::visual::BaseCamera::RIGHT); - DisplayOBJs(); - } - if (stereo) - { - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - } - if (viewport) - { - vparams->viewport() = sofa::type::make_array(0,0,_W,_H); - glViewport(0, 0, _W, _H); - glScissor(0, 0, _W, _H); - glDisable(GL_SCISSOR_TEST); - } - if (stencil) - { - glDisable(GL_STENCIL_TEST); - } - } - DisplayMenu(); // always needs to be the last object being drawn - -} - - -// --------------------------------------------------------- -// --- Reshape of the window, reset the projection -// --------------------------------------------------------- -void QtViewer::resizeGL(int width, int height) -{ - - _W = width; - _H = height; - - if(currentCamera) - currentCamera->setViewport(width, height); - - // std::cout << "GL window: " <f_bbox.getValue().isValid() || m_bShowAxis)) - { - vparams->sceneBBox() = groot->f_bbox.getValue(); - currentCamera->setBoundingBox(vparams->sceneBBox().minBBox(), vparams->sceneBBox().maxBBox()); - } - currentCamera->computeZ(); - currentCamera->d_widthViewport.setValue(sofa::Size(width)); - currentCamera->d_heightViewport.setValue(sofa::Size(height)); - - GLdouble projectionMatrix[16]; - currentCamera->getOpenGLProjectionMatrix(projectionMatrix); - - glViewport(0, 0, width * this->devicePixelRatio(), height * this->devicePixelRatio()); // to handle retina displays - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glMultMatrixd(projectionMatrix); - - glMatrixMode(GL_MODELVIEW); - glGetDoublev(GL_PROJECTION_MATRIX, lastProjectionMatrix); - - //Update vparams - vparams->zNear() = currentCamera->getZNear(); - vparams->zFar() = currentCamera->getZFar(); - vparams->viewport() = sofa::type::make_array(0, 0, width, height); - vparams->setProjectionMatrix(projectionMatrix); -} - -// --------------------------------------------------------- -// --- -// --------------------------------------------------------- -void QtViewer::paintGL() -{ - // clear buffers (color and depth) - if (_background == 0) - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - else if (_background == 1) - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - else if (_background == 2) - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - else if (_background == 3) - glClearColor(backgroundColour[0], backgroundColour[1], backgroundColour[2], 1.0f); - - glClearDepth(1.0); - glClear( _clearBuffer); - - // draw the scene - drawScene(); - - if(!captureTimer.isActive()) - { - SofaViewer::captureEvent(); - } - - if (_waitForRender) - { - _waitForRender = false; - } - - emit redrawn(); -} - -void QtViewer::paintEvent(QPaintEvent* qpe) -{ - QOpenGLWidget::paintEvent(qpe ); - /* - QPainter painter(this); - painter.setRenderHint(QPainter::Antialiasing, true); - painter.setRenderHint(QPainter::HighQualityAntialiasing, true); - painter.setRenderHint(QPainter::TextAntialiasing, false); -*/ -} - -// --------------------------------------------------------- -// --- -// --------------------------------------------------------- -void QtViewer::ApplySceneTransformation(int /* x */, int /* y */) -{ - update(); -} - -// --------------------------------------------------------- -// --- -// --------------------------------------------------------- -void QtViewer::ApplyMouseInteractorTransformation(int x, int y) -{ - // Mouse Interaction - double coeffDeplacement = 0.025; - const sofa::type::BoundingBox sceneBBox = vparams->sceneBBox(); - if (sceneBBox.isValid() && ! sceneBBox.isFlat()) - coeffDeplacement *= 0.001 * (sceneBBox.maxBBox() - - sceneBBox.minBBox()).norm(); - Quat conjQuat, resQuat, _newQuatBckUp; - - float x1, x2, y1, y2; - - if (_mouseInteractorMoving) - { - if (_mouseInteractorRotationMode) - { - if ((_mouseInteractorSavedPosX != x) || (_mouseInteractorSavedPosY - != y)) - { - x1 = 0; - y1 = 0; - x2 = (2.0f * (x + (-_mouseInteractorSavedPosX + _W / 2.0f)) - - _W) / _W; - y2 = (_H - 2.0f - * (y + (-_mouseInteractorSavedPosY + _H / 2.0f))) / _H; - - _mouseInteractorTrackball.ComputeQuaternion(x1, y1, x2, y2); - _mouseInteractorCurrentQuat - = _mouseInteractorTrackball.GetQuaternion(); - _mouseInteractorSavedPosX = x; - _mouseInteractorSavedPosY = y; - - _mouseInteractorNewQuat = _mouseInteractorCurrentQuat - + _mouseInteractorNewQuat; - _mouseRotate = true; - } - else - { - _mouseRotate = false; - } - - update(); - } - else if (_mouseInteractorTranslationMode) - { - _mouseInteractorAbsolutePosition = Vec3(0, 0, 0); - - if (_translationMode == XY_TRANSLATION) - { - _mouseInteractorAbsolutePosition[0] = coeffDeplacement * (x - - _mouseInteractorSavedPosX); - _mouseInteractorAbsolutePosition[1] = -coeffDeplacement * (y - - _mouseInteractorSavedPosY); - - _mouseInteractorSavedPosX = x; - _mouseInteractorSavedPosY = y; - } - else if (_translationMode == Z_TRANSLATION) - { - _mouseInteractorAbsolutePosition[2] = coeffDeplacement * (y - - _mouseInteractorSavedPosY); - - _mouseInteractorSavedPosX = x; - _mouseInteractorSavedPosY = y; - } - - _mouseTrans = true; - update(); - } - } -} - -// ---------------------------------------- -// --- Handle events (mouse, keyboard, ...) -// ---------------------------------------- - - -void QtViewer::keyPressEvent(QKeyEvent * e) -{ - if (isControlPressed()) - { - if (groot) - { - sofa::core::objectmodel::KeypressedEvent keyEvent(e->key()); - groot->propagateEvent(core::execparams::defaultInstance(), &keyEvent); - } - } - - // control the GUI - switch (e->key()) - { - - case Qt::Key_C: - // --- switch interaction mode - if (!_mouseInteractorTranslationMode) - { - std::cout << "Interaction Mode ON\n"; - _mouseInteractorTranslationMode = true; - _mouseInteractorRotationMode = false; - } - else - { - std::cout << "Interaction Mode OFF\n"; - _mouseInteractorTranslationMode = false; - _mouseInteractorRotationMode = false; - } - break; - default: - SofaViewer::keyPressEvent(e); - } - update(); -} - -void QtViewer::keyReleaseEvent(QKeyEvent * e) -{ - SofaViewer::keyReleaseEvent(e); -} - -void QtViewer::wheelEvent(QWheelEvent* e) -{ - SofaViewer::wheelEvent(e); -} - -void QtViewer::mousePressEvent(QMouseEvent * e) -{ - mouseEvent(e); - - SofaViewer::mousePressEvent(e); -} - -void QtViewer::mouseReleaseEvent(QMouseEvent * e) -{ - mouseEvent(e); - - SofaViewer::mouseReleaseEvent(e); - -} - -void QtViewer::mouseMoveEvent(QMouseEvent * e) -{ - //if the mouse move is not "interactive", give the event to the camera - if(!mouseEvent(e)) - SofaViewer::mouseMoveEvent(e); -} - -// ---------------------- Here are the mouse controls for the scene ---------------------- -bool QtViewer::mouseEvent(QMouseEvent * e) -{ - bool isInteractive = false; - const int eventX = e->x(); - const int eventY = e->y(); - if (_mouseInteractorRotationMode) - { - switch (e->type()) - { - case QEvent::MouseButtonPress: - // Mouse left button is pushed - if (e->button() == Qt::LeftButton) - { - _mouseInteractorMoving = true; - _mouseInteractorSavedPosX = eventX; - _mouseInteractorSavedPosY = eventY; - } - break; - - case QEvent::MouseMove: - // - break; - - case QEvent::MouseButtonRelease: - // Mouse left button is released - if (e->button() == Qt::LeftButton) - { - if (_mouseInteractorMoving) - { - _mouseInteractorMoving = false; - } - } - break; - - default: - break; - } - ApplyMouseInteractorTransformation(eventX, eventY); - } - else if (_mouseInteractorTranslationMode) - { - switch (e->type()) - { - case QEvent::MouseButtonPress: - // Mouse left button is pushed - if (e->button() == Qt::LeftButton) - { - _translationMode = XY_TRANSLATION; - _mouseInteractorSavedPosX = eventX; - _mouseInteractorSavedPosY = eventY; - _mouseInteractorMoving = true; - } - // Mouse right button is pushed - else if (e->button() == Qt::RightButton) - { - _translationMode = Z_TRANSLATION; - _mouseInteractorSavedPosY = eventY; - _mouseInteractorMoving = true; - } - - break; - - case QEvent::MouseButtonRelease: - // Mouse left button is released - if ((e->button() == Qt::LeftButton) && (_translationMode - == XY_TRANSLATION)) - { - _mouseInteractorMoving = false; - } - // Mouse right button is released - else if ((e->button() == Qt::RightButton) && (_translationMode - == Z_TRANSLATION)) - { - _mouseInteractorMoving = false; - } - break; - - default: - break; - } - - ApplyMouseInteractorTransformation(eventX, eventY); - } - else if (e->modifiers() & Qt::ShiftModifier) - { - isInteractive = true; - SofaViewer::mouseEvent(e); - } - else if (e->modifiers() & Qt::ControlModifier) - { - isInteractive = true; - } - else if (e->modifiers() & Qt::AltModifier) - { - isInteractive = true; - switch (e->type()) - { - case QEvent::MouseButtonPress: - // Mouse left button is pushed - if (e->button() == Qt::LeftButton) - { - _navigationMode = BTLEFT_MODE; - _mouseInteractorMoving = true; - _mouseInteractorSavedPosX = eventX; - _mouseInteractorSavedPosY = eventY; - } - // Mouse right button is pushed - else if (e->button() == Qt::RightButton) - { - _navigationMode = BTRIGHT_MODE; - _mouseInteractorMoving = true; - _mouseInteractorSavedPosX = eventX; - _mouseInteractorSavedPosY = eventY; - } - // Mouse middle button is pushed -#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) - else if (e->button() == Qt::MidButton) -#else - else if (e->button() == Qt::MiddleButton) -#endif - { - _navigationMode = BTMIDDLE_MODE; - _mouseInteractorMoving = true; - _mouseInteractorSavedPosX = eventX; - _mouseInteractorSavedPosY = eventY; - } - break; - - case QEvent::MouseMove: - // - break; - - case QEvent::MouseButtonRelease: - // Mouse left button is released - if (e->button() == Qt::LeftButton) - { - if (_mouseInteractorMoving) - { - _mouseInteractorMoving = false; - } - } - // Mouse right button is released - else if (e->button() == Qt::RightButton) - { - if (_mouseInteractorMoving) - { - _mouseInteractorMoving = false; - } - } - // Mouse middle button is released -#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) - else if (e->button() == Qt::MidButton) -#else - else if (e->button() == Qt::MiddleButton) -#endif - { - if (_mouseInteractorMoving) - { - _mouseInteractorMoving = false; - } - } - break; - - default: - break; - } - if (_mouseInteractorMoving && _navigationMode == BTLEFT_MODE) - { - const int dx = eventX - _mouseInteractorSavedPosX; - const int dy = eventY - _mouseInteractorSavedPosY; - if (dx || dy) - { - _lightPosition[0] -= dx * 0.1; - _lightPosition[1] += dy * 0.1; - std::cout << "Light = " << _lightPosition[0] << " " - << _lightPosition[1] << " " << _lightPosition[2] - << std::endl; - update(); - _mouseInteractorSavedPosX = eventX; - _mouseInteractorSavedPosY = eventY; - } - } - else if (_mouseInteractorMoving && _navigationMode == BTRIGHT_MODE) - { - const int dx = eventX - _mouseInteractorSavedPosX; - const int dy = eventY - _mouseInteractorSavedPosY; - if (dx || dy) - { - //g_DepthBias[0] += dx*0.01; - //g_DepthBias[1] += dy * 0.01; - //std::cout << "Depth bias = " << g_DepthBias[0] << " " - // << g_DepthBias[1] << std::endl; - update(); - _mouseInteractorSavedPosX = eventX; - _mouseInteractorSavedPosY = eventY; - } - } - else if (_mouseInteractorMoving && _navigationMode == BTMIDDLE_MODE) - { - const int dx = eventX - _mouseInteractorSavedPosX; - const int dy = eventY - _mouseInteractorSavedPosY; - if (dx || dy) - { - //g_DepthOffset[0] += dx * 0.01; - //g_DepthOffset[1] += dy * 0.01; - //std::cout << "Depth offset = " << g_DepthOffset[0] << " " - // << g_DepthOffset[1] << std::endl; - update(); - _mouseInteractorSavedPosX = eventX; - _mouseInteractorSavedPosY = eventY; - } - } - } - - return isInteractive; -} - -void QtViewer::moveRayPickInteractor(int eventX, int eventY) -{ - - const sofa::core::visual::VisualParams::Viewport& viewport = vparams->viewport(); - - Vec3d p0, px, py, pz, px1, py1; - gluUnProject(eventX, viewport[3] - 1 - (eventY), 0, - lastModelviewMatrix, lastProjectionMatrix, - viewport.data(), &(p0[0]), &(p0[1]), &(p0[2])); - gluUnProject(eventX + 1, viewport[3] - 1 - (eventY), 0, - lastModelviewMatrix, lastProjectionMatrix, - viewport.data(), &(px[0]), &(px[1]), &(px[2])); - gluUnProject(eventX, viewport[3] - 1 - (eventY + 1), 0, - lastModelviewMatrix, lastProjectionMatrix, - viewport.data(), &(py[0]), &(py[1]), &(py[2])); - gluUnProject(eventX, viewport[3] - 1 - (eventY), 0.1, - lastModelviewMatrix, lastProjectionMatrix, - viewport.data(), &(pz[0]), &(pz[1]), &(pz[2])); - gluUnProject(eventX + 1, viewport[3] - 1 - (eventY), 0.1, - lastModelviewMatrix, lastProjectionMatrix, - viewport.data(), &(px1[0]), &(px1[1]), &(px1[2])); - gluUnProject(eventX, viewport[3] - 1 - (eventY + 1), 0, - lastModelviewMatrix, lastProjectionMatrix, - viewport.data(), &(py1[0]), &(py1[1]), &(py1[2])); - px1 -= pz; - py1 -= pz; - px -= p0; - py -= p0; - pz -= p0; - const double r0 = sqrt(px.norm2() + py.norm2()); - double r1 = sqrt(px1.norm2() + py1.norm2()); - r1 = r0 + (r1 - r0) / pz.norm(); - px.normalize(); - py.normalize(); - pz.normalize(); - Mat4x4d transform; - transform.identity(); - transform[0][0] = px[0]; - transform[1][0] = px[1]; - transform[2][0] = px[2]; - transform[0][1] = py[0]; - transform[1][1] = py[1]; - transform[2][1] = py[2]; - transform[0][2] = pz[0]; - transform[1][2] = pz[1]; - transform[2][2] = pz[2]; - transform[0][3] = p0[0]; - transform[1][3] = p0[1]; - transform[2][3] = p0[2]; - Mat3x3d mat; - mat = transform; - Quat q; - q.fromMatrix(mat); - - Vec3d position, direction; - position = transform * Vec4d(0, 0, 0, 1); - direction = transform * Vec4d(0, 0, 1, 0); - direction.normalize(); - getPickHandler()->updateRay(position, direction); -} - -// ------------------------------------------------------------------- -// --- -// ------------------------------------------------------------------- -void QtViewer::resetView() -{ - - Quat orientation; - bool fileRead = false; - - if (!sceneFileName.empty()) - { - const std::string viewFileName = sceneFileName + "." + VIEW_FILE_EXTENSION; - fileRead = currentCamera->importParametersFromFile(viewFileName); - } - - //if there is no .view file , look at the center of the scene bounding box - // and with a Up vector in the same axis as the gravity - if (!fileRead) - { - newView(); - } - - update(); - //updateGL(); - - //SofaViewer::resetView(); - //ResetScene(); -} - -void QtViewer::newView() -{ - SofaViewer::newView(); -} - -void QtViewer::getView(Vec3& pos, Quat& ori) const -{ - SofaViewer::getView(pos, ori); -} - -void QtViewer::setView(const Vec3& pos, const Quat &ori) -{ - SofaViewer::setView(pos, ori); -} - -void QtViewer::moveView(const Vec3& pos, const Quat &ori) -{ - SofaViewer::moveView(pos, ori); -} - -void QtViewer::saveView() -{ - if (!sceneFileName.empty()) - { - const std::string viewFileName = sceneFileName + "." + VIEW_FILE_EXTENSION; - if(currentCamera->exportParametersInFile(viewFileName)) - std::cout << "View parameters saved in " << viewFileName << std::endl; - else - std::cout << "Error while saving view parameters in " << viewFileName << std::endl; - } -} - -void QtViewer::setSizeW(int size) -{ - resizeGL(size, _H); - update(); -} - -void QtViewer::setSizeH(int size) -{ - resizeGL(_W, size); - update(); -} - -//void QtViewer::setCameraMode(core::visual::VisualParams::CameraType mode) -//{ -// SofaViewer::setCameraMode(mode); - -// switch (mode) -// { -// case core::visual::VisualParams::ORTHOGRAPHIC_TYPE: -// camera()->setType( qglviewer::Camera::ORTHOGRAPHIC ); -// break; -// case core::visual::VisualParams::PERSPECTIVE_TYPE: -// camera()->setType( qglviewer::Camera::PERSPECTIVE ); -// break; -// } -//} - -QString QtViewer::helpString() const -{ - static QString - text( - "

    QtViewer


    \ -
      \ -
    • Mouse: TO NAVIGATE
    • \ -
    • Shift & Left Button: TO PICK OBJECTS
    • \ -
    • B: TO CHANGE THE BACKGROUND
    • \ -
    • C: TO SWITCH INTERACTION MODE: press the KEY C.
      \ - Allow or not the navigation with the mouse.
    • \ -
    • O: TO EXPORT TO .OBJ
      \ - The generated files scene-time.obj and scene-time.mtl are saved in the running project directory
    • \ -
    • P: TO SAVE A SEQUENCE OF OBJ
      \ - Each time the frame is updated an obj is exported
    • \ -
    • R: TO DRAW THE SCENE AXIS
    • \ -
    • S: TO SAVE A SCREENSHOT
      \ - The captured images are saved in the running project directory under the name format capturexxxx.bmp
    • \ -
    • T: TO CHANGE BETWEEN A PERSPECTIVE OR AN ORTHOGRAPHIC CAMERA
    • \ -
    • V: TO SAVE A VIDEO
      \ - Each time the frame is updated a screenshot is saved
    • \ -
    • Esc: TO QUIT ::sofa::
    "); - return text; -} - - -void QtViewer::screenshot(const std::string& filename, int compression_level) -{ - QImage screenshot; - - screenshot = this->grabFramebuffer(); - const bool res = screenshot.save(filename.c_str(), nullptr, (compression_level == -1) ? -1 : compression_level*100); // compression_level is either -1 or [0,100] - if(res) - { - msg_info("QtViewer") << "Saved " << screenshot.width() << "x" << screenshot.height() << " screen image to " << filename; - } - else - { - msg_error("QtViewer") << "Unknown error while saving screen image to " << filename; - } -} - -} // namespace sofa::gui::qt::viewer::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/viewer/qt/QtViewer.h b/Sofa/GUI/Qt/src/sofa/gui/qt/viewer/qt/QtViewer.h deleted file mode 100644 index 9e33cce8cc5..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/viewer/qt/QtViewer.h +++ /dev/null @@ -1,267 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#if defined(QT_VERSION) && QT_VERSION >= 0x050400 -#include -#include -#include -#endif // defined(QT_VERSION) && QT_VERSION >= 0x050400 - -#include - -#include - -#include -#include -#include -#include -#include - -#include -#include - -namespace sofa::gui::qt::viewer::qt -{ - -using sofa::type::Vec3; -using sofa::type::Quat; -using namespace sofa::gl; -using namespace sofa::helper::visual; -using namespace sofa::helper::system::thread; -using namespace sofa::component::collision; - -#if defined(QT_VERSION) && QT_VERSION >= 0x050400 -typedef QOpenGLWidget QOpenGLWidget; -#else -typedef QGLWidget QOpenGLWidget; -#endif // defined(QT_VERSION) && QT_VERSION >= 0x050400 - -class SOFA_GUI_QT_API QtViewer - : public QOpenGLWidget - , public sofa::gui::qt::viewer::OglModelSofaViewer -{ - Q_OBJECT - -private: - // Interaction - enum - { - XY_TRANSLATION = 1, - Z_TRANSLATION = 2, - }; - - enum { MINMOVE = 10 }; - - - QTimer* timerAnimate; - int _W, _H; - int _clearBuffer; - bool _lightModelTwoSides; - float _lightPosition[4]; - - int _mouseX, _mouseY; - int _savedMouseX, _savedMouseY; - - GLUquadricObj* _arrow; - GLUquadricObj* _tube; - GLUquadricObj* _sphere; - GLUquadricObj* _disk; - GLuint _numOBJmodels; - GLuint _materialMode; - GLboolean _facetNormal; - float _zoom; - int _renderingMode; - - bool _waitForRender; - - //GLuint _logoTexture; - - ctime_t _beginTime; - - double lastProjectionMatrix[16]; - double lastModelviewMatrix[16]; - -public: - - static const std::string VIEW_FILE_EXTENSION; - - - static QtViewer* create(QtViewer*, common::BaseViewerArgument& arg) - { - common::BaseViewerArgument* pArg = &arg; - const common::ViewerQtArgument* viewerArg = dynamic_cast(pArg); - return viewerArg ? - new QtViewer(viewerArg->getParentWidget(), viewerArg->getName().c_str() ) : - new QtViewer(nullptr, pArg->getName().c_str() ) - ; - } - - static const char* viewerName() - { - return "OpenGL (QtViewer)"; - } - - static const char* acceleratedName() - { - return "Open&GL (QtViewer)"; - } - - /// Activate this class of viewer. - /// This method is called before the viewer is actually created - /// and can be used to register classes associated with in the ObjectFactory. - static int EnableViewer(); - - /// Disable this class of viewer. - /// This method is called after the viewer is destroyed - /// and can be used to unregister classes associated with in the ObjectFactory. - static int DisableViewer(); - - QtViewer( QWidget* parent, const char* name=""); - ~QtViewer() override; - - QWidget* getQWidget() override { return this; } - - bool ready() override {return !_waitForRender;} - void wait() override {_waitForRender = true;} - -public slots: - void resetView() override; - virtual void saveView() override; - virtual void setSizeW(int) override; - virtual void setSizeH(int) override; - - virtual void getView(type::Vec3& pos, type::Quat& ori) const override; - virtual void setView(const type::Vec3& pos, const type::Quat &ori) override ; - virtual void newView() override ; - virtual void moveView(const type::Vec3& pos, const type::Quat &ori) override ; - virtual void captureEvent() override { SofaViewer::captureEvent(); } - virtual void drawColourPicking (common::ColourPickingVisitor::ColourCode code) override ; - virtual void fitNodeBBox(sofa::core::objectmodel::BaseNode * node ) override { SofaViewer::fitNodeBBox(node); } - virtual void fitObjectBBox(sofa::core::objectmodel::BaseObject * obj) override { SofaViewer::fitObjectBBox(obj); } - -signals: - void redrawn() override ; - void resizeW( int ) override ; - void resizeH( int ) override ; - void quit(); - - -protected: - - void calcProjection( int width = 0, int height = 0 ); - void initializeGL() override; - void paintGL() override; - void paintEvent(QPaintEvent* qpe) override; - void resizeGL( int w, int h ) override; - /// Overloaded from SofaViewer - virtual void viewAll() override {} - -public: - - sofa::simulation::Node* getScene() override - { - return groot.get(); - } - - //void reshape(int width, int height); - int getWidth() override - { - return _W; - } - int getHeight() override - { - return _H; - } - - void UpdateOBJ(void); - void moveRayPickInteractor(int eventX, int eventY) override ; - ///////////////// - // Interaction // - ///////////////// - - bool _mouseInteractorTranslationMode; - bool _mouseInteractorRotationMode; - int _translationMode; - Quat _mouseInteractorCurrentQuat; - Vec3 _mouseInteractorAbsolutePosition; - Trackball _mouseInteractorTrackball; - void ApplyMouseInteractorTransformation(int x, int y); - - static Quat _mouseInteractorNewQuat; - static bool _mouseTrans; - static bool _mouseRotate; - - - QString helpString() const override ; -// void setCameraMode(core::visual::VisualParams::CameraType mode); - void screenshot(const std::string& filename, int compression_level = -1) override; - -private: - - void InitGFX(void); - void PrintString(void* font, char* string); - void Display3DText(float x, float y, float z, char* string); - void DrawAxis(double xpos, double ypos, double zpos, double arrowSize); - void DrawBox(SReal* minBBox, SReal* maxBBox, SReal r=0.0); - void DrawXYPlane(double zo, double xmin, double xmax, double ymin, - double ymax, double step); - void DrawYZPlane(double xo, double ymin, double ymax, double zmin, - double zmax, double step); - void DrawXZPlane(double yo, double xmin, double xmax, double zmin, - double zmax, double step); - void CreateOBJmodelDisplayList(int material_mode); - //int loadBMP(char *filename, TextureImage *texture); - //void LoadGLTexture(char *Filename); - void DrawLogo(void); - void DisplayOBJs(); - void DisplayMenu(void); - virtual void drawScene() override ; - void MakeStencilMask(); - - void ApplySceneTransformation(int x, int y); - //int handle(int event); // required by FLTK - - //virtual bool event ( QEvent * e ); - - void keyPressEvent ( QKeyEvent * e ) override; - void keyReleaseEvent ( QKeyEvent * e ) override; - void mousePressEvent ( QMouseEvent * e ) override; - void mouseReleaseEvent ( QMouseEvent * e ) override; - void mouseMoveEvent ( QMouseEvent * e ) override; - void wheelEvent ( QWheelEvent* e) override; - virtual bool mouseEvent ( QMouseEvent * e ) override; -}; - -} // namespace sofa::gui::qt::viewer::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/viewer/qt/graphicon.ico b/Sofa/GUI/Qt/src/sofa/gui/qt/viewer/qt/graphicon.ico deleted file mode 100644 index adf0a2071a539814323c2b09f3e68c2d70895ce2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1406 zcmeH^&ra%45XQe&ThVI8TB~TSC{?lktG2>yNMJ`uNLUdbfCX%L6kdP@1V|ukcm=j> z*s&ul;0+)okPx`X+=x3jcX|CLGs*8eIcG9+0FH&%3sybI34jJ5$dXy`v;6db6321i zc^-lwKomttk_1_np(qMeRfVQ$&~+V#VZby^Se6Cbw&6GqT-QY;5y| zdcFQ>^xw1J4gA*|phv}X;3p>kfk%0ycj+i_!9Nf^@E3t2>HT)gAv!rDIy)h9wr;P4 z5Rp$K(tJcA;mQ^+pH1PK=vpwJF9n Date: Fri, 20 Dec 2024 11:58:08 +0100 Subject: [PATCH 3/5] Add way to pull and build Sofa.Qt plugin --- applications/plugins/CMakeLists.txt | 1 + .../plugins/Sofa.Qt/ExternalProjectConfig.cmake.in | 14 ++++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 applications/plugins/Sofa.Qt/ExternalProjectConfig.cmake.in diff --git a/applications/plugins/CMakeLists.txt b/applications/plugins/CMakeLists.txt index cfd74c7f771..aceedfd3d66 100644 --- a/applications/plugins/CMakeLists.txt +++ b/applications/plugins/CMakeLists.txt @@ -53,6 +53,7 @@ sofa_add_subdirectory(plugin CSparseSolvers CSparseSolvers EXTERNAL GIT_REF mast sofa_add_subdirectory(plugin ModelOrderReduction ModelOrderReduction EXTERNAL GIT_REF master) sofa_add_subdirectory(plugin Sofa.Metis Sofa.Metis EXTERNAL GIT_REF master) sofa_add_subdirectory(plugin SofaValidation SofaValidation EXTERNAL GIT_REF master) +sofa_add_subdirectory(plugin Sofa.Qt Sofa.Qt EXTERNAL GIT_REF master) diff --git a/applications/plugins/Sofa.Qt/ExternalProjectConfig.cmake.in b/applications/plugins/Sofa.Qt/ExternalProjectConfig.cmake.in new file mode 100644 index 00000000000..7f6c3d1162f --- /dev/null +++ b/applications/plugins/Sofa.Qt/ExternalProjectConfig.cmake.in @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.22) + +include(ExternalProject) +ExternalProject_Add(Sofa.Qt + GIT_REPOSITORY https://github.com/sofa-framework/Sofa.Qt + GIT_TAG origin/@ARG_GIT_REF@ + SOURCE_DIR "${CMAKE_SOURCE_DIR}/applications/plugins/Sofa.Qt" + BINARY_DIR "" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" + GIT_CONFIG "remote.origin.fetch=+refs/pull/*:refs/remotes/origin/pr/*" +) From 6275d11bb8f901c4296c00a111d350d8fc9dbe7b Mon Sep 17 00:00:00 2001 From: Paul Baksic Date: Fri, 20 Dec 2024 13:33:51 +0100 Subject: [PATCH 4/5] Fix compilation --- Sofa/GUI/src/sofa/gui/init.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Sofa/GUI/src/sofa/gui/init.cpp b/Sofa/GUI/src/sofa/gui/init.cpp index 5046b840f07..8083e0fa748 100644 --- a/Sofa/GUI/src/sofa/gui/init.cpp +++ b/Sofa/GUI/src/sofa/gui/init.cpp @@ -24,7 +24,6 @@ #include #include #include -#include namespace sofa::gui { @@ -37,8 +36,7 @@ void init() sofa::gui::component::init(); sofa::gui::common::init(); sofa::gui::batch::init(); - sofa::gui::qt::init(); - + first = false; } } From c3c62ac6ac61c80cb531cf09d4cc865bf2fc1c02 Mon Sep 17 00:00:00 2001 From: Paul Baksic Date: Fri, 20 Dec 2024 13:46:07 +0100 Subject: [PATCH 5/5] Change order so SofaGLFW and SofaIMGUI are already in the target stack when runSofa produce the plugin_list.cong.default --- applications/projects/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/projects/CMakeLists.txt b/applications/projects/CMakeLists.txt index 82a9f133288..d207790f0d1 100644 --- a/applications/projects/CMakeLists.txt +++ b/applications/projects/CMakeLists.txt @@ -10,10 +10,10 @@ sofa_add_subdirectory(application GenerateRigid GenerateRigid) sofa_add_subdirectory(application SofaPhysicsAPI SofaPhysicsAPI) sofa_add_subdirectory(application SofaGuiGlut SofaGuiGlut OFF) +sofa_add_subdirectory(directory SofaGLFW SofaGLFW EXTERNAL GIT_REF master ON) sofa_add_subdirectory(application runSofa runSofa ON) sofa_add_subdirectory(application sofaOPENCL sofaOPENCL OFF) sofa_add_subdirectory(directory Regression Regression EXTERNAL GIT_REF master) -sofa_add_subdirectory(directory SofaGLFW SofaGLFW EXTERNAL GIT_REF master ON) sofa_add_subdirectory(application sofaProjectExample sofaProjectExample) sofa_add_subdirectory(application sofaInfo sofaInfo)

    Z#>VP{?LKfP14`@#Z1W_MERwZSaG6fiZ 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 diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/Doc_Viewer/index_fichiers/Viewer.png b/Sofa/GUI/Qt/src/sofa/gui/qt/Doc_Viewer/index_fichiers/Viewer.png deleted file mode 100644 index 4ffdf78bde31bc6daf359ff97b8050d90b076583..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10961 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 N_`cS?%HJ%){}(FxaG3xA diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/FileManagement.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/FileManagement.cpp deleted file mode 100644 index c6f296bac98..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/FileManagement.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ - -#include "FileManagement.h" -#include - -namespace sofa::gui::qt -{ - -#include - -QString getExistingDirectory ( QWidget* parent, const QString & dir, const char * name, const QString & caption) -{ - QFileDialog::Options options = QFileDialog::ShowDirsOnly; -#if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0)) - options |= QFileDialog::DontUseSheet; -#endif - return QFileDialog::getExistingDirectory ( parent, name?QString(name):caption, dir, options ); -} - -QString getOpenFileName ( QWidget* parent, const QString & startWith, const QString & filter, const char * name, const QString & caption, QString * selectedFilter ) -{ -#if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0)) - QFileDialog::Options options = 0; - options |= QFileDialog::DontUseSheet; -#else - QFileDialog::Options options; -#endif - return QFileDialog::getOpenFileName ( parent, name?QString(name):caption, startWith, filter, selectedFilter, options ); -} - -QString getSaveFileName ( QWidget* parent, const QString & startWith, const QString & filter, const char * name, const QString & caption, QString * selectedFilter ) -{ -#if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0)) - QFileDialog::Options options = 0; - options |= QFileDialog::DontUseSheet; -#else - QFileDialog::Options options; -#endif - return QFileDialog::getSaveFileName ( parent, name?QString(name):caption, startWith, filter, selectedFilter, options ); -} - -void getFilesInDirectory( const QString &p, std::vector< QString > &files, bool recursive, const std::vector< QString > &filter ) -{ - QString path=p; - if (path.endsWith("/")) - { - const int slash=path.indexOf('/',-1); - path.truncate(slash); - } - else if (path.endsWith("\\")) - { - const int slash=path.indexOf('\\',-1); - path.truncate(slash); - } - - QDir d(path); - - d.setFilter( QDir::Dirs | QDir::Hidden | QDir::NoSymLinks ); - - std::vector< QString > subDir; - - const QFileInfoList &listDirectories = d.entryInfoList(); - QStringList filters; - for (unsigned int i=0; i. * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include -#include - -#include - - -namespace sofa::gui::qt -{ - - -QString SOFA_GUI_QT_API getExistingDirectory ( QWidget* parent, const QString & dir = QString(), const char * name = nullptr, const QString & caption = QString() ); - -QString SOFA_GUI_QT_API getOpenFileName ( QWidget* parent, const QString & startWith = QString(), const QString & filter = QString(), const char * name = nullptr, const QString & caption = QString(), QString * selectedFilter = nullptr ); - -QString SOFA_GUI_QT_API getSaveFileName ( QWidget* parent, const QString & startWith = QString(), const QString & filter = QString(), const char * name = nullptr, const QString & caption = QString(), QString * selectedFilter = nullptr ); - - -void SOFA_GUI_QT_API getFilesInDirectory( const QString &path, std::vector< QString > &files, bool recursive=true, const std::vector< QString > &filter=std::vector< QString >() ); - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/GLPickHandler.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/GLPickHandler.cpp deleted file mode 100644 index 3a9c874eea1..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/GLPickHandler.cpp +++ /dev/null @@ -1,138 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#include - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - - -namespace sofa::gui::qt -{ - -using namespace sofa::component::collision::geometry; -using namespace sofa::gui::common; -using namespace sofa::gui::component::performer; - -GLPickHandler::GLPickHandler(double defaultLength) - : Inherit(defaultLength) - , _fboAllocated(false) - , _fbo(true,true,true,false,0) -{ - -} - - -GLPickHandler::~GLPickHandler() -{ - - -} - -void GLPickHandler::allocateSelectionBuffer(int width, int height) -{ - /*called when shift key is pressed */ - assert(_fboAllocated == false ); - - static bool firstTime=true; - if (firstTime) - { - _fboParams.depthInternalformat = GL_DEPTH_COMPONENT24; -#if defined(GL_VERSION_3_0) - if (GLEW_VERSION_3_0) - { - _fboParams.colorInternalformat = GL_RGBA32F; - } - else -#endif // (GL_VERSION_3_0) - { - _fboParams.colorInternalformat = GL_RGBA16; - } - _fboParams.colorFormat = GL_RGBA; - _fboParams.colorType = GL_FLOAT; - - _fbo.setFormat(_fboParams); - firstTime=false; - } - _fbo.init(width,height); - - _fboAllocated = true; -} - -void GLPickHandler::destroySelectionBuffer() -{ - /*called when shift key is released */ - assert(_fboAllocated); - - _fbo.destroy(); - - _fboAllocated = false; -} - -//WARNING: do not use this method with Ogre -BodyPicked GLPickHandler::findCollisionUsingColourCoding(const type::Vec3& origin, - const type::Vec3& direction) -{ - assert(_fboAllocated); - BodyPicked result; - - result.dist = 0; - type::RGBAColor color; - const int x = mousePosition.x; - const int y = mousePosition.screenHeight - mousePosition.y; - TriangleCollisionModel* tmodel; - SphereCollisionModel* smodel; - _fbo.start(); - if(renderCallback) - { - renderCallback->render(ColourPickingVisitor::ENCODE_COLLISIONELEMENT ); - glReadPixels(x,y,1,1,_fboParams.colorFormat,_fboParams.colorType, &color[0]); - decodeCollisionElement(color,result); - renderCallback->render(ColourPickingVisitor::ENCODE_RELATIVEPOSITION ); - glReadPixels(x,y,1,1,_fboParams.colorFormat,_fboParams.colorType, &color[0]); - if( ( tmodel = dynamic_cast*>(result.body) ) != nullptr ) - { - decodePosition(result,color,tmodel,result.indexCollisionElement); - } - if( ( smodel = dynamic_cast*>(result.body)) != nullptr) - { - decodePosition(result, color,smodel,result.indexCollisionElement); - } - result.rayLength = (result.point-origin)*direction; - } - _fbo.stop(); - - return result; -} - -} // namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/GLPickHandler.h b/Sofa/GUI/Qt/src/sofa/gui/qt/GLPickHandler.h deleted file mode 100644 index e4f2f81a8e8..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/GLPickHandler.h +++ /dev/null @@ -1,68 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include -#include - -#include - -#include -#include -#include - -namespace sofa::component::collision::model -{ - class RayCollisionModel; -} // namespace sofa::component::collision::model - -namespace sofa::gui::qt -{ - -class SOFA_GUI_QT_API GLPickHandler : public common::PickHandler -{ - typedef PickHandler Inherit; - typedef sofa::component::collision::model::RayCollisionModel MouseCollisionModel; - typedef sofa::component::statecontainer::MechanicalObject< defaulttype::Vec3Types > MouseContainer; - -public: - enum PickingMethod - { - RAY_CASTING, - SELECTION_BUFFER - }; - - GLPickHandler(double defaultLength = 1000000); - virtual ~GLPickHandler() override; - - void allocateSelectionBuffer(int width, int height) override; - void destroySelectionBuffer() override; - - common::BodyPicked findCollisionUsingColourCoding(const type::Vec3& origin, const type::Vec3& direction) override; - -protected: - bool _fboAllocated; - gl::FrameBufferObject _fbo; - gl::fboParameters _fboParams; - -}; - -} // namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/GUI.ui b/Sofa/GUI/Qt/src/sofa/gui/qt/GUI.ui deleted file mode 100644 index fef9ad0c8de..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/GUI.ui +++ /dev/null @@ -1,1008 +0,0 @@ - - - GUI - - - - 0 - 0 - 800 - 600 - - - - - 0 - 0 - - - - - 210 - 481 - - - - true - - - Sofa - - - - - 0 - 0 - - - - - 0 - 0 - - - - - QLayout::SetNoConstraint - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - - true - - - - 0 - 0 - 800 - 22 - - - - - &File - - - - - - - - - - &Edit - - - - - - - - - - - - &View - - - - - - - - Help - - - - - - - - - - - - - - 0 - 0 - - - - - 200 - 551 - - - - 1 - - - - - 0 - 0 - - - - - 200 - 0 - - - - - - - true - - - - 0 - 0 - - - - - QLayout::SetNoConstraint - - - - - QLayout::SetNoConstraint - - - - - true - - - Set the camera to initial position and orientation - - - Reset &View - - - Alt+V - - - - - - - Launch the Simulation - - - &Animate - - - Alt+A - - - true - - - - - - - true - - - Save the position and orientation of the camera - - - Save Vie&w - - - Alt+W - - - - - - - true - - - Reload the simulation file - - - &Reload Scene - - - Alt+R - - - - - - - Compute the simulation at time t+DT - - - S&tep - - - Alt+T - - - true - - - - - - - Save S&creenshot - - - Alt+C - - - - - - - QLayout::SetNoConstraint - - - - - DT: - - - false - - - - - - - - 0 - 0 - - - - - 20 - 0 - - - - - 80 - 32767 - - - - - - - - Use the duration of the previous simulation step as the next DT - - - Real Time - - - - - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 8 - - - - - - - - - 0 - 0 - - - - - 200 - 0 - - - - 0 - - - - Graph - - - - - - - - - 0 - 0 - - - - Export Graph... - - - - image1image1 - - - Alt+X - - - - - - - Collapse All - - - - - - - - - - - - - Expand All - - - - - - - - - - - - - The scene graph update button has three states -State 0: unlocked (all the changes on the graph are immediately taken into account) -State 1: locked (the changes on the graph are not done but the simulation graph is set to dirty if - there is some changes on the graph. A click on the button unlocks the graph (go to state 0). -State 2: dirty, in that state the button reflect the fact that the scene graph view has changed but not displayed. - A click on the button refreshes the graph view but does not change the Lock/Unlock state - - - - - - - - - - - - - - 0 - 0 - - - - View - - - - - - - - - - Qt::Horizontal - - - QSizePolicy::Ignored - - - - 0 - 16 - - - - - - - - - 0 - 0 - - - - http://sofa-framework.org - - - image0 - - - true - - - false - - - - - - - Qt::Horizontal - - - QSizePolicy::Ignored - - - - 0 - 16 - - - - - - - - - - - - - Visual - - - - - - E&xport Graph... - - - - image1image1 - - - Alt+X - - - - - - - - Stats - - - - - - Record the state at each time step in file "dumpState.data" - - - Dump State - - - - - - - Display information about the time spent at each branch of the graph - - - Log Time - - - - - - - Create gnuplot files for each named element of the simulation - - - Export state as gnuplot files - - - - - - - Open a Dialog showing a trace of the execution and time spent - - - Trace Visitor and Component execution - - - - - - - Display AdvancedTimer profiler - - - - - - - - Viewer - - - - - - 0 - - - - - Qt::Horizontal - - - QSizePolicy::Expanding - - - - 10 - 2 - - - - - - - - 30000 - - - 800 - - - - - - - - 0 - 0 - - - - <p align="center">x</p> - - - false - - - - - - - 30000 - - - 600 - - - - - - - Qt::Horizontal - - - QSizePolicy::Expanding - - - - 10 - 2 - - - - - - - - - - - - - - - - - - - - - - - image3image3 - - - &Open... - - - Open - - - Ctrl+O - - - fileOpenAction - - - - - &Reload - - - Reload - - - Ctrl+R - - - fileReloadAction - - - - - E&xit - - - Exit - - - - - - fileExitAction - - - - - true - - - &About - - - About - - - - - - false - - - helpAboutAction - - - - - Viewer - - - ViewerAction - - - - - Record Directory... - - - Record Directory... - - - editRecordDirectoryAction - - - - - Gnuplot Directory... - - - Gnuplot Directory... - - - editGnuplotDirectoryAction - - - - - Plugin Manager... - - - Plugin Manager... - - - PluginManagerAction - - - - - Mouse Manager... - - - Mouse Manager... - - - MouseManagerAction - - - - - Recently Opened Files... - - - Action - - - - - Test - - - Test - - - TestAction - - - - - Video Recorder Manager... - - - Video Recorder Manager... - - - VideoRecorderManagerAction - - - - - false - - - Show doc browser - - - false - - - - - Data Graph... - - - Data Graph Window - - - - - - sofa/gui/qt/config.h - - - - - fileOpenAction - triggered() - GUI - popupOpenFileSelector() - - - -1 - -1 - - - 20 - 20 - - - - - fileReloadAction - triggered() - GUI - fileReload() - - - -1 - -1 - - - 20 - 20 - - - - - fileExitAction - triggered() - GUI - fileExit() - - - -1 - -1 - - - 20 - 20 - - - - - editRecordDirectoryAction - triggered() - GUI - editRecordDirectory() - - - -1 - -1 - - - 20 - 20 - - - - - PluginManagerAction - triggered() - GUI - showPluginManager() - - - -1 - -1 - - - 20 - 20 - - - - - MouseManagerAction - triggered() - GUI - showMouseManager() - - - -1 - -1 - - - 20 - 20 - - - - - DataGraphAction - triggered() - GUI - showWindowDataGraph() - - - -1 - -1 - - - 20 - 20 - - - - - editGnuplotDirectoryAction - triggered() - GUI - editGnuplotDirectory() - - - -1 - -1 - - - 20 - 20 - - - - - VideoRecorderManagerAction - triggered() - GUI - showVideoRecorderManager() - - - -1 - -1 - - - 20 - 20 - - - - - helpShowDocBrowser - triggered() - GUI - showDocBrowser() - - - -1 - -1 - - - 399 - 299 - - - - - - showDocBrowser() - fileNew() - popupOpenFileSelector() - fileReload() - fileSave() - fileExit() - editRecordDirectory() - showPluginManager() - showMouseManager() - showWindowDataGraph() - editGnuplotDirectory() - showVideoRecorderManager() - - diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/GenGraphForm.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/GenGraphForm.cpp deleted file mode 100644 index 279eca9f45d..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/GenGraphForm.cpp +++ /dev/null @@ -1,526 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#include "GenGraphForm.h" - -#ifdef WIN32 -#include -#include -#endif - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace sofa::gui::qt -{ - -GenGraphForm::GenGraphForm(QWidget *parent) - : QDialog(parent) - , currentTask(nullptr) - , settingFilter(false) -{ - setupUi(this); - // signals and slots connections - connect(browseButton, &QPushButton::clicked, this, &GenGraphForm::doBrowse); - connect(exportButton, &QPushButton::clicked, this, &GenGraphForm::doExport); - connect(displayButton, &QPushButton::clicked, this, &GenGraphForm::doDisplay); - connect(closeButton, &QPushButton::clicked, this, &GenGraphForm::doClose); - connect(filename, &QLineEdit::textChanged, this, &GenGraphForm::change); - connect(presetFilter, QOverload::of(&QComboBox::activated), [=](int){ setFilter(); }); - connect(showNodes, &QCheckBox::toggled, this, &GenGraphForm::changeFilter); - connect(showObjects, &QCheckBox::toggled, this, &GenGraphForm::changeFilter); - connect(showBehaviorModels, &QCheckBox::toggled, this, &GenGraphForm::changeFilter); - connect(showCollisionModels, &QCheckBox::toggled, this, &GenGraphForm::changeFilter); - connect(showVisualModels, &QCheckBox::toggled, this, &GenGraphForm::changeFilter); - connect(showMappings, &QCheckBox::toggled, this, &GenGraphForm::changeFilter); - connect(showContext, &QCheckBox::toggled, this, &GenGraphForm::changeFilter); - connect(showCollisionPipeline, &QCheckBox::toggled, this, &GenGraphForm::changeFilter); - connect(showSolvers, &QCheckBox::toggled, this, &GenGraphForm::changeFilter); - connect(showMechanicalStates, &QCheckBox::toggled, this, &GenGraphForm::changeFilter); - connect(showForceFields, &QCheckBox::toggled, this, &GenGraphForm::changeFilter); - connect(showInteractionForceFields, &QCheckBox::toggled, this, &GenGraphForm::changeFilter); - connect(showConstraints, &QCheckBox::toggled, this, &GenGraphForm::changeFilter); - connect(showMass, &QCheckBox::toggled, this, &GenGraphForm::changeFilter); - connect(showTopology, &QCheckBox::toggled, this, &GenGraphForm::changeFilter); - connect(showMechanicalMappings, &QCheckBox::toggled, this, &GenGraphForm::changeFilter); - - // init preset filters - { - std::set& filt = presetFilters["Full Graph"]; - filt.insert("showNodes"); - filt.insert("showObjects"); - filt.insert("showBehaviorModels"); - filt.insert("showCollisionModels"); - filt.insert("showVisualModels"); - filt.insert("showMappings"); - filt.insert("showContext"); - filt.insert("showCollisionPipeline"); - filt.insert("showSolvers"); - filt.insert("showMechanicalStates"); - filt.insert("showForceFields"); - filt.insert("showInteractionForceFields"); - filt.insert("showConstraints"); - filt.insert("showMass"); - filt.insert("showTopology"); - filt.insert("showMechanicalMappings"); - } - { - std::set& filt = presetFilters["All Objects"]; - filt = presetFilters["Full Graph"]; - filt.erase("showNodes"); - } - { - std::set& filt = presetFilters["All Nodes"]; - filt.insert("showNodes"); - } - { - std::set& filt = presetFilters["Mechanical Graph"]; - filt = presetFilters["Full Graph"]; - filt.erase("showCollisionModels"); - filt.erase("showVisualModels"); - filt.erase("showMappings"); - filt.erase("showCollisionPipeline"); - } - { - std::set& filt = presetFilters["Mechanical Objects"]; - filt = presetFilters["Mechanical Graph"]; - filt.erase("showNodes"); - } - { - std::set& filt = presetFilters["Visual Graph"]; - filt.insert("showNodes"); - filt.insert("showObjects"); - filt.insert("showVisualModels"); - } - { - std::set& filt = presetFilters["Visual Objects"]; - filt = presetFilters["Visual Graph"]; - filt.erase("showNodes"); - } - { - std::set& filt = presetFilters["Collision Graph"]; - filt.insert("showNodes"); - filt.insert("showObjects"); - filt.insert("showCollisionModels"); - filt.insert("showCollisionPipeline"); - } - { - std::set& filt = presetFilters["Collision Objects"]; - filt = presetFilters["Collision Graph"]; - filt.erase("showNodes"); - } - { - std::set& filt = presetFilters["Collision Response Graph"]; - filt.insert("showNodes"); - filt.insert("showObjects"); - //filt.insert("showBehaviorModels"); - filt.insert("showCollisionModels"); - //filt.insert("showVisualModels"); - //filt.insert("showMappings"); - //filt.insert("showContext"); - //filt.insert("showCollisionPipeline"); - filt.insert("showSolvers"); - filt.insert("showMechanicalStates"); - //filt.insert("showForceFields"); - filt.insert("showInteractionForceFields"); - filt.insert("showConstraints"); - //filt.insert("showMass"); - //filt.insert("showTopology"); - filt.insert("showMechanicalMappings"); - } - { - std::set& filt = presetFilters["Collision Response Objects"]; - filt = presetFilters["Collision Response Graph"]; - filt.erase("showNodes"); - } - -} - -void GenGraphForm::change() -{ - //exported = false; - displayButton->setEnabled(false); - if (exportButton->text() != "&Kill") - exportButton->setEnabled(filename->text()!=QString("")); -} - -void GenGraphForm::changeFilter() -{ - displayButton->setEnabled(false); - if (settingFilter) return; - const std::set filt = getCurrentFilter(); - for (std::map >::const_iterator it = presetFilters.begin(); it != presetFilters.end(); ++it) - { - if (it->second == filt) - { - const int index = presetFilter->findText(it->first.c_str()); - presetFilter->setCurrentIndex(index); - return; - } - } - const int index = presetFilter->findText(""); - presetFilter->setCurrentIndex(index); -} - -QString removeFileExt(const QString &s) -{ - const int ext = s.lastIndexOf('.'); - if (ext==-1) return s; - if (s.indexOf('/', ext+1) != -1|| s.indexOf('\\', ext+1) != -1) - return s; - return s.left(ext); -} - -void GenGraphForm::doBrowse() -{ - QString s = QFileDialog::getSaveFileName( - this, - "Choose a file to save", -// "export graph file dialog", - filename->text(), - "DOT Graph (*.dot)" ); - if (s.length()>0) - { - if (removeFileExt(s)==s) // no extension specified - s += ".dot"; - filename->setText(s); - change(); - } -} - -void GenGraphForm::doExport() -{ - if (!tasks.empty()) - { - killAllTasks(); - return; - } - if (filename->text()==QString("")) return; - - filename->setStyleSheet(""); - - QString dotfile = filename->text(); - - QString basefile = removeFileExt(dotfile); - - if (basefile==dotfile) // no extension specified - dotfile += ".dot"; - - std::ofstream fdot; - fdot.open(dotfile.toStdString().c_str(), std::ofstream::out | std::ofstream::trunc); - if (!fdot.is_open()) - { - msg_error("GenGraphForm") << "Output to " << dotfile.toStdString() << " failed."; - filename->clear(); - filename->setFocus(); - filename->setStyleSheet("border: 1px solid red"); - return; - } - { - sofa::simulation::graph::ExportDotVisitor act(sofa::core::execparams::defaultInstance(), &fdot); - act.showNode = this->showNodes->isChecked(); - act.showObject = this->showObjects->isChecked(); - act.showBehaviorModel = this->showBehaviorModels->isChecked(); - act.showCollisionModel = this->showCollisionModels->isChecked(); - act.showVisualModel = this->showVisualModels->isChecked(); - act.showMapping = this->showMappings->isChecked(); - act.showContext = this->showContext->isChecked(); - act.showCollisionPipeline = this->showCollisionPipeline->isChecked(); - act.showSolver = this->showSolvers->isChecked(); - act.showMechanicalState = this->showMechanicalStates->isChecked(); - act.showForceField = this->showForceFields->isChecked(); - act.showInteractionForceField = this->showInteractionForceFields->isChecked(); - act.showConstraint = this->showConstraints->isChecked(); - act.showMass = this->showMass->isChecked(); - act.showTopology = this->showTopology->isChecked(); - act.showMechanicalMapping = this->showMechanicalMappings->isChecked(); - act.labelNodeName = this->labelNodeName->isChecked(); - act.labelNodeClass = this->labelNodeClass->isChecked(); - act.labelObjectName = this->labelObjectName->isChecked(); - act.labelObjectClass = this->labelObjectClass->isChecked(); - graph->executeVisitor(&act); - } - fdot.close(); - msg_info("GenGraphForm") << "File saved in " << dotfile.toStdString(); - - QStringList argv0; - if (layoutDirV->isChecked()) - argv0 << "dot" << "-Grankdir=TB"; - else if (layoutDirH->isChecked()) - argv0 << "dot" << "-Grankdir=LR"; - else if (layoutSpring->isChecked()) - argv0 << "neato"; - else if (layoutRadial->isChecked()) - argv0 << "twopi"; - - bool exp = false; - - exportedFile = dotfile; - - if (genPNG->isChecked()) - { - QStringList argv = argv0; - argv << "-Tpng" << "-o" << basefile+".png"; - argv << dotfile; - addTask(argv); - if (!exp) - { - exp = true; - exportedFile = basefile+".png"; - } - } - - if (genPS->isChecked()) - { - QStringList argv = argv0; - argv << "-Tps" << "-o" << basefile+".ps"; - argv << dotfile; - addTask(argv); - if (!exp) - { - exp = true; - exportedFile = basefile+".ps"; - } - } - - if (genFIG->isChecked()) - { - QStringList argv = argv0; - argv << "-Tfig" << "-o" << basefile+".fig"; - argv << dotfile; - addTask(argv); - if (!exp) - { - exp = true; - exportedFile = basefile+".fig"; - } - } - - if (genSVG->isChecked()) - { - QStringList argv = argv0; - argv << "-Tsvg" << "-o" << basefile+".svg"; - argv << dotfile; - addTask(argv); - if (!exp) - { - exp = true; - exportedFile = basefile+".svg"; - } - } - //exported = true; - //displayButton->setEnabled(true); -} - -void GenGraphForm::doDisplay() -{ - if (exportedFile==QString("")) return; - - msg_info("GenGraphForm") << "OPEN " << exportedFile.toStdString(); - -#ifdef WIN32 - ShellExecuteA(nullptr, "open", exportedFile.toStdString().c_str(), nullptr, nullptr, SW_SHOWNORMAL); -#else - QStringList argv; - argv << "display" << exportedFile; - - QString program = argv.front(); - argv.pop_front(); - //QProcess::startDetached(program, argv); //QString("start \"\"\"")+exportedFile+QString("\"\"\"")); - QProcess::startDetached(program, argv); //QString("start \"\"\"")+exportedFile+QString("\"\"\"")); -#endif -} - -void GenGraphForm::doClose() -{ - killAllTasks(); - this->close(); -} - -void GenGraphForm::setScene(sofa::simulation::Node* scene) -{ - graph = scene; -} - -void GenGraphForm::killAllTasks() -{ - exportButton->setText("&Export"); - if (currentTask) - { - currentTask->kill(); - delete currentTask; - currentTask = nullptr; - } - tasks.clear(); -} - -void GenGraphForm::addTask(QStringList argv) -{ - tasks.push_back(argv); - if (currentTask == nullptr) - runTask(); -} - -void GenGraphForm::taskFinished(int exitCode, QProcess::ExitStatus exitStatus) -{ - static const std::map exitStatusMap { - { QProcess::ExitStatus::NormalExit, "normal exit"}, - { QProcess::ExitStatus::CrashExit, "crash exit"}, - }; - const auto it = exitStatusMap.find(exitStatus); - const auto exitStatusString = it == exitStatusMap.end() ? "unknown" : it->second; - msg_info("GenGraphForm") << "TASK FINISHED (exit code: " << exitCode << ", exit status: " << exitStatusString << ")"; - if (currentTask == nullptr) return; - - delete currentTask; - currentTask = nullptr; - if (tasks.empty()) - { - displayButton->setEnabled(true); - exportButton->setText("&Export"); - } - else - { - runTask(); - } -} - -void GenGraphForm::taskError(QProcess::ProcessError error) -{ - static const std::map errorMap { - { QProcess::ProcessError::FailedToStart, "failed to start"}, - { QProcess::ProcessError::Crashed, "crashed"}, - { QProcess::ProcessError::Timedout, "timed out"}, - { QProcess::ProcessError::ReadError, "read error"}, - { QProcess::ProcessError::WriteError, "write error"}, - { QProcess::ProcessError::UnknownError, "unknown error"}, - }; - const auto it = errorMap.find(error); - const auto errorString = it == errorMap.end() ? "unknown" : it->second; - msg_error("GenGraphForm") << "An error occurred: " << errorString; - - if (error == QProcess::ProcessError::FailedToStart) - { - msg_error("GenGraphForm") << "Check that the required program (Graphviz) is in your PATH environment variable"; - } - - exportButton->setText("&Export"); - if (currentTask) - { - currentTask->kill(); - delete currentTask; - currentTask = nullptr; - } -} - -void GenGraphForm::runTask() -{ - QStringList argv = tasks.front(); - tasks.pop_front(); - exportButton->setText("&Kill"); - const QString cmd = argv.join(QString(" ")); - msg_info("GenGraphForm") << "STARTING TASK " << cmd.toStdString(); - - auto* p = new QProcess(this); - const QString program = argv.front(); - argv.pop_front(); -#if (QT_VERSION < QT_VERSION_CHECK(5, 13, 0)) - p->setReadChannelMode(QProcess::ForwardedChannels); -#else - p->setProcessChannelMode(QProcess::ForwardedChannels); -#endif - connect(p, - QOverload::of(&QProcess::finished), - this,&GenGraphForm::taskFinished); - connect(p,&QProcess::errorOccurred,this,&GenGraphForm::taskError); - p->start(program, argv); - currentTask = p; -} - -void GenGraphForm::setFilter() -{ - const QString fname = presetFilter->currentText(); - if (fname == "") return; - const std::string name = fname.toStdString(); - if (presetFilters.count(name) > 0) - { - const std::set & filt = presetFilters[name]; - settingFilter = true; - this->showNodes->setChecked(filt.find("showNodes")!=filt.end()); - this->showObjects->setChecked(filt.find("showObjects")!=filt.end()); - this->showBehaviorModels->setChecked(filt.find("showBehaviorModels")!=filt.end()); - this->showCollisionModels->setChecked(filt.find("showCollisionModels")!=filt.end()); - this->showVisualModels->setChecked(filt.find("showVisualModels")!=filt.end()); - this->showMappings->setChecked(filt.find("showMappings")!=filt.end()); - this->showContext->setChecked(filt.find("showContext")!=filt.end()); - this->showCollisionPipeline->setChecked(filt.find("showCollisionPipeline")!=filt.end()); - this->showSolvers->setChecked(filt.find("showSolvers")!=filt.end()); - this->showMechanicalStates->setChecked(filt.find("showMechanicalStates")!=filt.end()); - this->showForceFields->setChecked(filt.find("showForceFields")!=filt.end()); - this->showInteractionForceFields->setChecked(filt.find("showInteractionForceFields")!=filt.end()); - this->showConstraints->setChecked(filt.find("showConstraints")!=filt.end()); - this->showMass->setChecked(filt.find("showMass")!=filt.end()); - this->showTopology->setChecked(filt.find("showTopology")!=filt.end()); - this->showMechanicalMappings->setChecked(filt.find("showMechanicalMappings")!=filt.end()); - settingFilter = false; - displayButton->setEnabled(false); - const int index = presetFilter->findText(fname); - presetFilter->setCurrentIndex(index); - } - else - { - //if (QMessage::question(this,"Graph Preset Filters",...) - presetFilters[name] = getCurrentFilter(); - } -} - -std::set GenGraphForm::getCurrentFilter() -{ - std::set filt; - if (this->showNodes->isChecked()) filt.insert("showNodes"); - if (this->showObjects->isChecked()) filt.insert("showObjects"); - if (this->showBehaviorModels->isChecked()) filt.insert("showBehaviorModels"); - if (this->showCollisionModels->isChecked()) filt.insert("showCollisionModels"); - if (this->showVisualModels->isChecked()) filt.insert("showVisualModels"); - if (this->showMappings->isChecked()) filt.insert("showMappings"); - if (this->showContext->isChecked()) filt.insert("showContext"); - if (this->showCollisionPipeline->isChecked()) filt.insert("showCollisionPipeline"); - if (this->showSolvers->isChecked()) filt.insert("showSolvers"); - if (this->showMechanicalStates->isChecked()) filt.insert("showMechanicalStates"); - if (this->showForceFields->isChecked()) filt.insert("showForceFields"); - if (this->showInteractionForceFields->isChecked()) filt.insert("showInteractionForceFields"); - if (this->showConstraints->isChecked()) filt.insert("showConstraints"); - if (this->showMass->isChecked()) filt.insert("showMass"); - if (this->showTopology->isChecked()) filt.insert("showTopology"); - if (this->showMechanicalMappings->isChecked()) filt.insert("showMechanicalMappings"); - return filt; -} - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/GenGraphForm.h b/Sofa/GUI/Qt/src/sofa/gui/qt/GenGraphForm.h deleted file mode 100644 index cba9f697bb9..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/GenGraphForm.h +++ /dev/null @@ -1,70 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include - -#include "QProcess" -#include "QStringList" -#include - -#include -#include -#include - -namespace sofa::gui::qt -{ - -class GenGraphForm : public QDialog, public Ui_BaseGenGraphForm -{ - Q_OBJECT -public: - GenGraphForm(QWidget *parent = Q_NULLPTR); - - void setScene(sofa::simulation::Node* scene); - -public slots: - virtual void change(); - virtual void doBrowse(); - virtual void doExport(); - virtual void doDisplay(); - virtual void doClose(); - virtual void taskFinished(int exitCode ,QProcess::ExitStatus exitStatus); - virtual void taskError(QProcess::ProcessError error); - virtual void changeFilter(); - virtual void setFilter(); - -protected: - QString exportedFile; - sofa::simulation::Node* graph; - std::list tasks; - QProcess* currentTask; - - void addTask(QStringList argv); - void runTask(); - void killAllTasks(); - - std::map > presetFilters; - bool settingFilter; - std::set getCurrentFilter(); -}; - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/GenericWidget.h b/Sofa/GUI/Qt/src/sofa/gui/qt/GenericWidget.h deleted file mode 100644 index 594e6cc21fb..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/GenericWidget.h +++ /dev/null @@ -1,84 +0,0 @@ -/****************************************************************************** - * SOFA, Simulation Open-Framework Architecture * - * (c) 2006 INRIA, USTL, UJF, CNRS, MGH * - * * - * This program is free software; you can redistribute it and/or modify it * - * under the terms of the GNU General Public License as published by the Free * - * Software Foundation; either version 2 of the License, or (at your option) * - * any later version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT * - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * - * more details. * - * * - * You should have received a copy of the GNU General Public License along * - * with this program. If not, see . * - ******************************************************************************* - * Authors: The SOFA Team and external contributors (see Authors.txt) * - * * - * Contact information: contact@sofa-framework.org * - ******************************************************************************/ -#pragma once - -#include - -namespace sofa::gui::qt -{ -template -class GenericDataWidget : public sofa::gui::qt::DataWidget -{ -public: - typedef DATA MyData; - typedef WIDGET MyWidget; - - GenericDataWidget(QWidget* parent, const char* name, MyData* d) : sofa::gui::qt::DataWidget(parent, name, d), m_data(d) {} - - virtual bool createWidgets() - { - m_widget = new MyWidget(this, *m_data); - m_widget->setParent(this); - setLayout(new QVBoxLayout(this)); - layout()->addWidget(m_widget); - m_widget->setVisible(true); - readFromData(); - return true; - } - - template - static RealObject* create(RealObject*, CreatorArgument& arg) - { - typename RealObject::MyData* realData = dynamic_cast(arg.data); - if (!realData) - return nullptr; - else - { - RealObject* obj = new RealObject(arg.parent, arg.name.c_str(), realData); - if (!obj->createWidgets()) - { - delete obj; - obj = nullptr; - } - if (obj) - { - obj->setDataReadOnly(arg.readOnly); - } - return obj; - } - } - - virtual void setDataReadOnly(bool readOnly) { m_widget->setEnabled(!readOnly); } - - virtual void readFromData() { m_widget->readFromData(*m_data); } - - virtual void writeToData() { m_widget->writeToData(*m_data); } - -protected: - MyData* m_data; - MyWidget* m_widget; -}; - -} // namespace sofa::gui::qt - -template -using GenericDataWidget = sofa::gui::qt::GenericDataWidget; diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/GraphDataWidget.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/GraphDataWidget.cpp deleted file mode 100644 index a56e7dd1365..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/GraphDataWidget.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ - -#include -#include -#include -#include -#include - -namespace sofa::gui::qt -{ - -using sofa::helper::Creator; -using sofa::type::fixed_array; -using namespace sofa::type; -using namespace sofa::defaulttype; - -Creator > > > DWClass_mapvectorf("graph",true); -Creator > > > DWClass_mapvectord("graph",true); -Creator > > > DWClass_mapvector2d("graph",true); -Creator > > > DWClass_mapdequef("graph",true); -Creator > > > DWClass_mapdequed("graph",true); -Creator > > > DWClass_mapdeque2d("graph",true); - -Creator > > > DWLClass_mapvectorf("graph_linear",true); -Creator > > > DWLClass_mapvectord("graph_linear",true); -Creator > > > DWLClass_mapvector2d("graph_linear",true); -Creator > > > DWLClass_mapdequef("graph_linear",true); -Creator > > > DWLClass_mapdequed("graph_linear",true); -Creator > > > DWLClass_mapdeque2d("graph_linear",true); - - - -GraphOptionWidget::GraphOptionWidget(const std::string &dataName, GraphSetting *graphConf):graph(graphConf) -{ - QVBoxLayout *generalLayout = new QVBoxLayout(this); - generalLayout->setContentsMargins(0, 0, 0, 0); - generalLayout->setSpacing(0); - QWidget *gnuplotExport=new QWidget(this); - QHBoxLayout* gnuplotLayout = new QHBoxLayout(gnuplotExport); - exportGNUPLOTButton = new QPushButton(QString("GNUPLOT"), gnuplotExport); - - //Create field to enter the base name of the gnuplot files - fileGNUPLOTLineEdit = new QLineEdit(gnuplotExport); - findGNUPLOTFile = new QPushButton(QString("..."), gnuplotExport); - - std::string gnuplotDirectory; //=simulation::getSimulation()->gnuplotDirectory.getValue(); - if (gnuplotDirectory.empty()) - gnuplotDirectory=sofa::helper::system::SetDirectory::GetParentDir(sofa::helper::system::DataRepository.getFirstPath().c_str()) + "/"; - gnuplotDirectory += dataName; - fileGNUPLOTLineEdit->setText(QString(gnuplotDirectory.c_str())); - - gnuplotLayout->addWidget(exportGNUPLOTButton); - gnuplotLayout->addWidget(fileGNUPLOTLineEdit); - gnuplotLayout->addWidget(findGNUPLOTFile); - generalLayout->addWidget(gnuplotExport); - - connect(exportGNUPLOTButton, &QPushButton::clicked, this, &GraphOptionWidget::exportGNUPlot); - connect(findGNUPLOTFile, &QPushButton::clicked, this, &GraphOptionWidget::openFindFileDialog); - - QWidget *imageExport=new QWidget(this); - QHBoxLayout* imageLayout = new QHBoxLayout(imageExport); - - checkBox = new QCheckBox("Check the box to dump the graph at each edit of the data"); - - exportImageButton = new QPushButton(QString("Image"), imageExport); - //Create field to enter the base name of the gnuplot files - fileImageLineEdit = new QLineEdit(imageExport); - findImageFile = new QPushButton(QString("..."), imageExport); - - std::string imageDirectory; //=simulation::getSimulation()->gnuplotDirectory.getValue(); - if (imageDirectory.empty()) - imageDirectory=sofa::helper::system::SetDirectory::GetParentDir(sofa::helper::system::DataRepository.getFirstPath().c_str()) + "/"; - imageDirectory += dataName; - fileImageLineEdit->setText(QString(imageDirectory.c_str())); - - imageLayout->addWidget(exportImageButton); - imageLayout->addWidget(fileImageLineEdit); - imageLayout->addWidget(findImageFile); - generalLayout->addWidget(imageExport); - - connect(exportImageButton, &QPushButton::clicked, this, &GraphOptionWidget::exportImage); - connect(findImageFile, &QPushButton::clicked, this, &GraphOptionWidget::openFindFileDialog); - - generalLayout->addWidget(checkBox); - - idfile = 0; -} - -void GraphOptionWidget::openFindFileDialog() -{ - QLineEdit *fileLineEdit=0; - const QPushButton *button=(QPushButton*)sender(); - if (button == findGNUPLOTFile) fileLineEdit = fileGNUPLOTLineEdit; - - else if (button == findImageFile) fileLineEdit = fileImageLineEdit; - - const std::string filename(sofa::helper::system::SetDirectory::GetParentDir(sofa::helper::system::DataRepository.getFirstPath().c_str())); - std::string directory; - const QString s = getExistingDirectory ( this, filename.empty() ?NULL:filename.c_str(), "open directory dialog", "Choose a directory" ); - if (s.length() > 0) - { - directory = s.toStdString(); - if (directory.at(directory.size()-1) != '/') directory+="/"; - fileLineEdit->setText(directory.c_str()); - } -} -void GraphOptionWidget::exportGNUPlot() -{ - graph->exportGNUPlot(fileGNUPLOTLineEdit->text().toStdString()); -} - -void GraphOptionWidget::exportImage() -{ - const unsigned nbpad=5; - - std::stringstream ss; - ss << idfile; - const std::string idstring = ss.str(); - - const std::string pad(nbpad-idstring.length(),'0'); - idfile++; - if (idfile>99999) idfile = 0; - - std::string filename = fileImageLineEdit->text().toStdString(); - filename.append("_"); - filename.append(pad); - filename.append(idstring); - graph->exportImage(filename); -} - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/GraphDataWidget.h b/Sofa/GUI/Qt/src/sofa/gui/qt/GraphDataWidget.h deleted file mode 100644 index 910a20bdbfb..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/GraphDataWidget.h +++ /dev/null @@ -1,385 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include "TableDataWidget.h" -#include "FileManagement.h" - -#include - -#include - -#include -#include -#include -#include -#include -#include - -#include - - -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) -using namespace QtCharts; -#endif - -namespace sofa::gui::qt -{ - -template -class QDataSeries : public QLineSeries -{ -protected: - const T* data0; -public: - typedef vector_data_trait trait; - typedef typename trait::value_type value_type; - typedef vector_data_trait vtrait; - typedef typename vtrait::value_type real_type; - QDataSeries() : data0(NULL) {} - QDataSeries(const QDataSeries& c) - : QLineSeries() - , data0(c.data0) - { - parseData(); - } - - void setData(const T* p) - { - data0 = p; - parseData(); - } - - virtual QLineSeries* copy() const { return new QDataSeries(*this); } - virtual sofa::Size size() const - { - if (data0 == NULL) - return 0; - else - return trait::size(*(data0)); - } - - virtual void parseData() - { - clear(); - m_xMin = m_yMin = 1000000000.0; - m_xMax = m_yMax = -1000000000.0; - - for (sofa::Index i = 0; i < this->size(); i++) - { - QPointF data = sample(i); - append(data); - - if (data.x() > m_xMax) - m_xMax = data.x(); - if (data.x() < m_xMin) - m_xMin = data.x(); - - if (data.y() > m_yMax) - m_yMax = data.y(); - if (data.y() < m_yMin) - m_yMin = data.y(); - } - } - - virtual QPointF sample (sofa::Index i) const - { - if (i >= size()) - return QPointF(); - else if (vtrait::size(*trait::get(*(data0), i)) < 2) - return QPointF(i, (double)(*vtrait::get(*trait::get(*(data0), i), 0))); - else - return QPointF((double)(*vtrait::get(*trait::get(*(data0), i), 0)), (double)(*vtrait::get(*trait::get(*(data0), i), 1))); - } - - real_type m_xMin, m_yMin; - real_type m_xMax, m_yMax; -}; - -class GraphSetting -{ -public: - virtual ~GraphSetting() {}; - virtual void exportGNUPlot(const std::string &baseFileName) const=0; - virtual void exportImage(const std::string &baseFileName) const=0; -}; - -class GraphOptionWidget: public QWidget -{ - Q_OBJECT -public: - - GraphOptionWidget(const std::string &dataName, GraphSetting *graphConf); -public slots: - - void openFindFileDialog(); - void exportGNUPlot(); - void exportImage(); -//MOC_SKIP_END - - bool isCheckedBox() { - return checkBox->isChecked(); - } - -protected: - unsigned idfile; - QPushButton* exportGNUPLOTButton; - QLineEdit *fileGNUPLOTLineEdit; - QPushButton* findGNUPLOTFile; - - QCheckBox * checkBox; - QPushButton* exportImageButton; - QLineEdit *fileImageLineEdit; - QPushButton* findImageFile; - - GraphSetting *graph; -}; - - - - -template -class GraphWidget: public GraphSetting -{ -public: - typedef DataType data_type; - typedef vector_data_trait trait; - typedef typename trait::value_type curve_type; - typedef QChartView Widget; - typedef QDataSeries CurveData; - - GraphWidget(QWidget *parent) - { - m_chart = new QChart(); - w = new QChartView(m_chart, parent); - w->setMinimumHeight(300); - - m_axisX = new QValueAxis(); - m_axisX->setRange(0, 100); - - m_axisY = new QValueAxis(); - m_chart->addAxis(m_axisX, Qt::AlignBottom); - m_chart->addAxis(m_axisY, Qt::AlignLeft); - m_chart->legend()->setAlignment(Qt::AlignBottom); - } - - virtual ~GraphWidget() {}; - - QWidget *getWidget() {return w;} - - void readFromData(const data_type& d0) - { - double minX = 1000000000.0; - double maxX = -1000000000.0; - double minY = 1000000000.0; - double maxY = -1000000000.0; - currentData=d0; - const data_type& d = currentData; - auto nbData = trait::size(d); - - for (int i = 0; i < nbData; ++i) - { - const curve_type* v = trait::get(d, i); - const char* name = trait::header(d, i); - QString sName; - if (name && *name) - sName = name; - else - sName = "Unknown_" + QString::number(m_curves.size()); - - auto itM = m_curves.find(sName); - CurveData* cdata; - if (itM != m_curves.end()) - { - cdata = itM->second; - } - else { - // new curve data to register and plot - cdata = new CurveData(); - m_curves[sName] = cdata; - - cdata->setName(sName); - cdata->setPen(QColor::fromHsv(255 * i / nbData, 255, 255)); - m_chart->addSeries(cdata); - - cdata->attachAxis(m_axisY); - cdata->attachAxis(m_axisX); - } - - cdata->setData(v); - // gather min and max over all curves - if(cdata->m_xMin < minX) minX = cdata->m_xMin; - if(cdata->m_xMax > maxX) maxX = cdata->m_xMax; - if(cdata->m_yMin < minY) minY = cdata->m_yMin; - if(cdata->m_yMax > maxY) maxY = cdata->m_yMax; - } - - m_axisX->setRange(minX, maxX); - m_axisY->setRange(minY, maxY); - } - - void exportGNUPlot(const std::string &baseFileName) const - { - const int n = trait::size(currentData); - for (int i=0; isize()); - QPainter *paint = new QPainter(&p); - w->render(paint); - - QFile* file = new QFile(QString::fromStdString(filename)); - file->open(QIODevice::WriteOnly); - p.save(file, "PNG"); - - std::cerr << "Export Image: " << filename << std::endl; - } - -protected: - std::string getCurveFilename(unsigned int idx) const - { - std::string name(trait::header(currentData,idx)); - std::replace(name.begin(),name.end(),' ', '_'); - return name; - } - - QChartView* w; - - /// Pointer to the chart Data - QChart *m_chart; - - /// x axis pointer - QValueAxis* m_axisX; - /// y axis pointer - QValueAxis* m_axisY; - - /// vector of series to be plotted - std::map m_curves; - - data_type currentData; -}; - - - -template -class graph_data_widget_container -{ -public: - typedef T data_type; - typedef GraphWidget Widget; - typedef QVBoxLayout Layout; - Widget* w; - GraphOptionWidget *options; - Layout* container_layout; - - - graph_data_widget_container() : w(NULL), options(NULL), container_layout(NULL) {} - - - bool createLayout( DataWidget* parent ) - { - if( parent->layout() != NULL || container_layout != NULL ) - { - return false; - } - container_layout = new Layout(parent); - return true; - } - - bool createLayout( QLayout* layout) - { - if ( container_layout != NULL ) return false; - container_layout = new Layout(); - layout->addItem(container_layout); - return true; - } - - bool createWidgets(DataWidget* parent, const data_type& d, bool /*readOnly*/) - { - w = new Widget(parent); - options = new GraphOptionWidget(parent->getBaseData()->getName(),w); - w->readFromData(d); - return true; - } - void setReadOnly(bool /*readOnly*/) - { - } - void readFromData(const data_type& d0) - { - w->readFromData(d0); - if (options->isCheckedBox()) options->exportImage(); - } - void writeToData(data_type& /*d*/) - { - } - - void insertWidgets() - { - assert(container_layout); - container_layout->addWidget(w->getWidget()); - container_layout->addWidget(options); - } -}; - -template -class GraphDataWidget : public SimpleDataWidget > -{ -public: - typedef SimpleDataWidget > Inherit; - typedef sofa::core::objectmodel::Data MyData; -public: - GraphDataWidget(QWidget* parent,const char* name, MyData* d) : Inherit(parent,name,d) {} - virtual unsigned int sizeWidget() {return 8;} - virtual unsigned int numColumnWidget() {return 1;} -}; - -template -class GraphDataWidget_Linear : public GraphDataWidget< T > -{ -public: - typedef sofa::core::objectmodel::Data MyData; - GraphDataWidget_Linear(QWidget* parent,const char* name, MyData* d) : GraphDataWidget (parent,name,d) { } - virtual bool createWidgets() - { - const bool b = GraphDataWidget::createWidgets(); - //typename GraphWidget::Widget* w = dynamic_cast::Widget*>(this->container.w->getWidget()); - //if (w) - //{ - // w->setAxisScaleEngine(GraphWidget::Widget::yLeft, new QwtLinearScaleEngine); - //} - return b; - } -}; - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/GraphListenerQListView.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/GraphListenerQListView.cpp deleted file mode 100644 index 2b529ba25fa..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/GraphListenerQListView.cpp +++ /dev/null @@ -1,766 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ - -#include "GraphListenerQListView.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -using sofa::component::sceneutility::InfoComponent ; - -#include "resources/icons/iconmultinode.xpm" -#include "resources/icons/iconnode.xpm" -#include "resources/icons/iconinfo.xpm" -#include "resources/icons/iconwarning.xpm" -#include "resources/icons/iconerror.xpm" -#include "resources/icons/icondata.xpm" -#include "resources/icons/iconsleep.xpm" - - -#include -using sofa::helper::logging::Message ; - -using namespace sofa::core::objectmodel; - -namespace sofa::gui::qt -{ - - - -//*********************************************************************************************************** - -static const int iconWidth=8; -static const int iconHeight=16; -static const int iconMargin=6; - -static int hexval(char c) -{ - if (c>='0' && c<='9') return c-'0'; - else if (c>='a' && c<='f') return (c-'a')+10; - else if (c>='A' && c<='F') return (c-'A')+10; - else return 0; -} - -const std::string getClass(core::objectmodel::Base* obj){ - if (obj->toBaseNode()) - { - return "Node"; - } - if (obj->toBaseObject()) - { - if (obj->toContextObject()) - return "Context"; - if (obj->toBehaviorModel()) - return "BehaviorModel"; - if (obj->toCollisionModel()) - return "CollisionModel"; - if (obj->toBaseMechanicalState()) - return "MechanicalModel"; - if (obj->toBaseProjectiveConstraintSet()) - return "ProjectiveConstraintSet"; - if (obj->toBaseConstraintSet()) - return "BaseConstraintSet"; - if (obj->toBaseInteractionForceField() && - obj->toBaseInteractionForceField()->getMechModel1()!=obj->toBaseInteractionForceField()->getMechModel2()) - return "InteractionForceField"; - if (obj->toBaseForceField()) - return "ForceField"; - if (obj->toBaseAnimationLoop() - || obj->toOdeSolver()) - return "Solver"; - if (obj->toPipeline() - || obj->toIntersection() - || obj->toDetection() - || obj->toContactManager() - || obj->toCollisionGroupManager()) - return "Collision"; - if (obj->toBaseMapping()) - return "Mapping"; - if (obj->toBaseMass()) - return "Mass"; - if (obj->toTopology () - || obj->toBaseTopologyObject() ) - return "Topology"; - if (obj->toBaseLoader()) - return "Loader"; - if (obj->toConfigurationSetting()) - return "Configuration"; - if (obj->toVisualModel()) - return "Visual"; - } - return "Other"; -} - - - -QPixmap* getPixmap(core::objectmodel::Base* obj, bool haveInfo, bool haveWarning, bool haveErrors) -{ - static QPixmap pixInfo(reinterpret_cast(iconinfo_xpm)); - static QImage imgInfo8 = pixInfo.scaledToWidth(16).toImage(); - - static QPixmap pixError(reinterpret_cast(iconerror_xpm)); - static QImage imgError8 = pixError.scaledToWidth(16).toImage(); - - static QPixmap pixWarning(reinterpret_cast(iconwarning_xpm)); - static QImage imgWarning8 = pixWarning.scaledToWidth(16).toImage(); - - - using namespace sofa::simulation::Colors; - unsigned int flags = 0; - - if (obj->toBaseNode()) - { - - const char** icon = reinterpret_cast(iconsleep_xpm); - if( !obj->toBaseNode()->getContext()->isSleeping() ){ - icon = reinterpret_cast(iconnode_xpm) ; - flags = 1 ; - } - - if(haveInfo) - flags |= 1 << (2) ; - - if(haveWarning) - flags |= 1 << (3) ; - - if(haveErrors) - flags |= 1 << (4) ; - - - static std::map pixmaps; - if (!pixmaps.count(flags)) - { - /// Create a new image from pixmap - const QImage timg(icon) ; - QImage* img = new QImage(timg.convertToFormat(QImage::Format_ARGB32)) ; - - const QImage* overlaysymbol=nullptr; - if( haveInfo ) - overlaysymbol = &imgInfo8 ; - if( haveWarning ) - overlaysymbol = &imgWarning8 ; - if( haveErrors ) - overlaysymbol = &imgError8 ; - - if(overlaysymbol){ - for (int x=0;x<16;x++) - { - for(int y=0;y<16;y++) - { - if( qAlpha(overlaysymbol->pixel(x,y)) == 255 ){ - img->setPixel(x, y, overlaysymbol->pixel(x,y) ); - } - } - } - } - pixmaps[flags] = new QPixmap(QPixmap::fromImage(*img)); - } - - return pixmaps[flags] ; - } - else if (obj->toBaseObject()) - { - if (obj->toContextObject()) - flags |= 1 << CONTEXT; - if (obj->toBehaviorModel()) - flags |= 1 << BMODEL; - if (obj->toCollisionModel()) - flags |= 1 << CMODEL; - if (obj->toBaseMechanicalState()) - flags |= 1 << MMODEL; - if (obj->toBaseProjectiveConstraintSet()) - flags |= 1 << PROJECTIVECONSTRAINTSET; - if (obj->toBaseConstraintSet()) - flags |= 1 << CONSTRAINTSET; - if (obj->toBaseInteractionForceField() && - obj->toBaseInteractionForceField()->getMechModel1()!=obj->toBaseInteractionForceField()->getMechModel2()) - flags |= 1 << IFFIELD; - else if (obj->toBaseForceField()) - flags |= 1 << FFIELD; - if (obj->toBaseAnimationLoop() - || obj->toOdeSolver()) - flags |= 1 << SOLVER; - if (obj->toPipeline() - || obj->toIntersection() - || obj->toDetection() - || obj->toContactManager() - || obj->toCollisionGroupManager()) - flags |= 1 << COLLISION; - if (obj->toBaseMapping()) - flags |= 1 << ((obj->toBaseMapping())->isMechanical()?MMAPPING:MAPPING); - if (obj->toBaseMass()) - flags |= 1 << MASS; - if (obj->toTopology () - || obj->toBaseTopologyObject() ) - flags |= 1 << TOPOLOGY; - if (obj->toBaseLoader()) - flags |= 1 << LOADER; - if (obj->toConfigurationSetting()) - flags |= 1 << CONFIGURATIONSETTING; - if (obj->toVisualModel() && !flags) - flags |= 1 << VMODEL; - if (!flags) - flags |= 1 << OBJECT; - } - else return nullptr; - - if(haveInfo) - flags |= 1 << (ALLCOLORS+1) ; - - if(haveWarning) - flags |= 1 << (ALLCOLORS+1) ; - - if(haveErrors) - flags |= 1 << (ALLCOLORS+1) ; - - static std::map pixmaps; - if (!pixmaps.count(flags)) - { - int nc = 0; - for (int i=0; isetAlphaBuffer(true); - img->fill(qRgba(0,0,0,0)); - // Workaround for qt 3.x where fill() does not set the alpha channel - for (int y=0 ; y < iconHeight ; y++) - for (int x=0 ; x < nx ; x++) - img->setPixel(x,y,qRgba(0,0,0,0)); - - // left Line - for (int y=iconMargin ; y < iconHeight ; y++) - img->setPixel(0,y,qRgba(0,0,0,255)); - - nc = 0; - for (int i=0; isetPixel(x,iconMargin-1,qRgba(0,0,0,255)); - img->setPixel(x,iconHeight-1,qRgba(0,0,0,255)); - for (int y=iconMargin ; y < iconHeight-1 ; y++) - img->setPixel(x,y,qRgba(r,g,b,a)); - } - ++nc; - } - - // right line Line - for (int y=iconMargin ; y < iconHeight ; y++) - img->setPixel(2+iconWidth*nc-1,y,qRgba(0,0,0,255)); - - const QImage* overlaysymbol=nullptr; - if( haveInfo ) - overlaysymbol = &imgInfo8 ; - if( haveWarning ) - overlaysymbol = &imgWarning8 ; - if( haveErrors ) - overlaysymbol = &imgError8 ; - - if(overlaysymbol){ - for (int x=0;x<16;x++) - { - for(int y=0;y<16;y++) - { - if( qAlpha(overlaysymbol->pixel(x,y)) == 255 ) - img->setPixel(x, y, overlaysymbol->pixel(x,y) ); - } - } - } - - pixmaps[flags] = new QPixmap(QPixmap::fromImage(*img)); - - delete img; - } - return pixmaps[flags]; -} - -void setMessageIconFrom(QTreeWidgetItem* item, Base* object) -{ - const bool haveInfos = object->countLoggedMessages({Message::Info, Message::Deprecated, Message::Advice})!=0; - const bool haveWarnings = object->countLoggedMessages({Message::Warning})!=0; - const bool haveErrors = object->countLoggedMessages({Message::Error, Message::Fatal})!=0; - - const QPixmap* pix = getPixmap(object, haveInfos, haveWarnings, haveErrors); - if (pix) - item->setIcon(0, QIcon(*pix)); -} - -ObjectStateListener::ObjectStateListener( - QTreeWidgetItem* item_, - sofa::core::objectmodel::Base* object_) : item(item_), object(object_) -{ - // We want the view to react to a change in the message log - object->d_messageLogCount.addOutput(this); - - // We want the view to react to a change in the name - object->name.addOutput(this); -} - -ObjectStateListener::~ObjectStateListener() -{ - object->d_messageLogCount.delOutput(this); - object->name.delOutput(this); -} - -void ObjectStateListener::update() {} -void ObjectStateListener::notifyEndEdit() -{ - setMessageIconFrom(item, object.get()); - - const QString oldName = item->text(0); - const QString newName = QString::fromStdString(object->getName()); - if(newName != oldName) - item->setText(0, newName); -} - -GraphListenerQListView::~GraphListenerQListView() -{ - for(auto [key, listener] : listeners) - { - delete listener; - } - listeners.clear(); -} - -/*****************************************************************************************************************/ -QTreeWidgetItem* GraphListenerQListView::createItem(QTreeWidgetItem* parent) -{ - if(parent->childCount() == 0) - return new QTreeWidgetItem(parent); - return new QTreeWidgetItem(parent, parent->child(parent->childCount()-1)); -} - -/*****************************************************************************************************************/ -void GraphListenerQListView::onBeginAddChild(Node* parent, Node* child) -{ - if (widget->isLocked()) - { - widget->setViewToDirty(); - return; - } - if (items.count(child)) - { - QTreeWidgetItem* item = items[child]; - if (item->treeWidget() == nullptr) - { - if (parent == nullptr) - { - dmsg_info("GraphListenerQListView") << "CREATING TOP LEVEL NODE '"<getName()<<"'"; - widget->insertTopLevelItem(0, item); - } - else if (items.count(parent)) - { - items[parent]->insertChild(0, item); - } - else - { - dmsg_error("GraphListenerQListView") << "Unknown parent node '"<getName()<<"'"; - return; - } - } - else - { - static QPixmap pixMultiNode(reinterpret_cast(iconmultinode_xpm)); - - // Node with multiple parents - if (parent && - parent != findObject(item->parent()) ) - { - // check that the multinode have not been added yet - // i.e. verify that all every item equivalent to current 'item' (in nodeWithMultipleParents) do not have the same 'parent' - std::multimap::iterator it=nodeWithMultipleParents.lower_bound(item), itend=nodeWithMultipleParents.upper_bound(item); - for ( ; it!=itend && it->second->parent() != items[parent] ; ++it); - if( it==itend ) - { - QTreeWidgetItem* itemNew = createItem(items[parent]); - //itemNew->setDropEnabled(true); - // QString name=QString("MultiNode ") + QString(child->getName().c_str()); - // itemNew->setText(0, name); - itemNew->setText(0, child->getName().c_str()); - nodeWithMultipleParents.insert(std::make_pair(item, itemNew)); - itemNew->setIcon(0, QIcon(pixMultiNode)); - - // this is one more parent, the first child item must be displayed as a multinode - { - item->setIcon(0, QIcon(pixMultiNode)); - } - } - } - } - } - else - { - QTreeWidgetItem* item; - if (parent == nullptr) - item = new QTreeWidgetItem(widget); - else if (items.count(parent)) - { - item = createItem(items[parent]); - } - else - { - dmsg_error("GraphListenerQListView") << "Unknown parent node '"<getName()<<"'"; - return; - } - - item->setText(0, child->getName().c_str()); - item->setText(1, child->getClassName().c_str()); - item->setForeground(1, nameColor); - QFont font = QApplication::font(); - font.setBold(true); - item->setFont(0, font); - setMessageIconFrom(item, child); - - item->setExpanded(true); - items[child] = item; - - // Add a listener to connect changes on the component state with its graphical view. - listeners[child] = new ObjectStateListener(item, child); - } - - for (BaseObject::SPtr obj : child->object) - onBeginAddObject(child, obj.get()); - for (Node::SPtr node : child->child) - onBeginAddChild(child, node.get()); -} - -/*****************************************************************************************************************/ -void GraphListenerQListView::onBeginRemoveChild(Node* parent, Node* child) -{ - SOFA_UNUSED(parent); - for (Node::ObjectIterator it = child->object.begin(); it != child->object.end(); ++it) - onBeginRemoveObject(child, it->get()); - for (Node::ChildIterator it = child->child.begin(); it != child->child.end(); ++it) - onBeginRemoveChild(child, it->get()); - - if (items.count(child)) - { - delete items[child]; - delete listeners[child]; - items.erase(child); - listeners.erase(child); - } -} - - -/*****************************************************************************************************************/ -void GraphListenerQListView::onBeginAddObject(Node* parent, core::objectmodel::BaseObject* object) -{ - if(widget->isLocked()) - { - widget->setViewToDirty(); - return; - } - if (items.count(object)) - { - QTreeWidgetItem* item = items[object]; - if (item->treeWidget() == nullptr) - { - if (items.count(parent)) - // items[parent]->insertItem(item); - items[parent]->addChild(item); - else - { - dmsg_error("GraphListenerQListView") << "Unknown parent node " << parent->getName()<< "'"; - return; - } - } - } - else - { - QTreeWidgetItem* item; - if (items.count(parent)) - item = createItem(items[parent]); - else - { - dmsg_error("GraphListenerQListView") << "Unknown parent node " << parent->getName()<< "'"; - return; - } - - std::string name; - if(dynamic_cast(object)) - { - name = object->getName() ; - }else if(dynamic_cast(object)){ - name = object->getClassName() ; - }else - { - name = object->getName() ; - item->setText(1, object->getClassName().c_str()); - item->setForeground(1, nameColor); - const QString tooltip( ("Name: " + name + "\nClass Name: " + object->getClassName()).c_str()); - item->setToolTip(0, tooltip); - item->setToolTip(1, tooltip); - } - - item->setText(0, name.c_str()); - - setMessageIconFrom(item, object); - - items[object] = item; - listeners[object] = new ObjectStateListener(item, object); - } - for (BaseObject::SPtr slave : object->getSlaves()) - onBeginAddSlave(object, slave.get()); -} - - -/*****************************************************************************************************************/ -void GraphListenerQListView::onBeginRemoveObject(Node* parent, core::objectmodel::BaseObject* object) -{ - SOFA_UNUSED(parent); - for (BaseObject::SPtr slave : object->getSlaves()) - onBeginRemoveSlave(object, slave.get()); - - if (items.count(object)) - { - delete items[object]; - items.erase(object); - - delete listeners[object]; - listeners.erase(object); - } -} - - -/*****************************************************************************************************************/ -void GraphListenerQListView::onBeginAddSlave(core::objectmodel::BaseObject* master, core::objectmodel::BaseObject* slave) -{ - if(widget->isLocked()) - { - widget->setViewToDirty(); - return; - } - if (items.count(slave)) - { - QTreeWidgetItem* item = items[slave]; - if (item->treeWidget() == nullptr) - { - if (items.count(master)) - // items[master]->insertItem(item); - items[master]->addChild(item); - else - { - dmsg_error("GraphListenerQListView") << "Unknown master node '"<getName()<<"'"; - return; - } - } - } - else - { - QTreeWidgetItem* item; - if (items.count(master)) - item = createItem(items[master]); - else - { - dmsg_error("GraphListenerQListView") << "Unknown master node '"<getName()<<"'"; - return; - } - std::string className = sofa::helper::gettypename(typeid(*slave)); - if (const std::string::size_type pos = className.find('<'); pos != std::string::npos) - className.erase(pos); - if (!slave->toConfigurationSetting()) - { - const auto& name = slave->getName(); - item->setText(0, name.c_str()); - item->setForeground(1, nameColor); - - const QString tooltip( ("Name: " + name + "\nClass Name: " + className).c_str()); - item->setToolTip(0, tooltip); - item->setToolTip(1, tooltip); - } - item->setText(1, className.c_str()); - - setMessageIconFrom(item, slave); - - items[slave] = item; - } - - const core::objectmodel::BaseObject::VecSlaves& slaves = slave->getSlaves(); - for (unsigned int i=0; igetSlaves(); - for (unsigned int i=0; isetIcon(0, QIcon(*pix)); - } -} - -/*****************************************************************************************************************/ -core::objectmodel::Base* GraphListenerQListView::findObject(const QTreeWidgetItem* item) -{ - core::objectmodel::Base* base = nullptr; - - if(item) - { - for ( std::map::iterator it = items.begin() ; it != items.end() ; ++ it ) - { - if ( ( *it ).second == item ) - { - base = (*it).first; - return base; - } - } - } - if (!base) //Can be a multi node - { - std::multimap::iterator it; - for (it=nodeWithMultipleParents.begin(); it!=nodeWithMultipleParents.end(); ++it) - { - if (it->second == item) return findObject(it->first); - } - } - return base; -} - -/*****************************************************************************************************************/ -core::objectmodel::BaseData* GraphListenerQListView::findData(const QTreeWidgetItem* item) -// returns nullptr if nothing is found. -{ - BaseData* data = nullptr; - if(item) - { - std::map::const_iterator it; - for( it = datas.begin(); it != datas.end(); ++it) - { - if((*it).second == item) - { - data = (*it).first; - } - } - } - return data; -} -/*****************************************************************************************************************/ -void GraphListenerQListView::removeDatas(core::objectmodel::BaseObject* parent) -{ - if (widget->isLocked()) - { - widget->setViewToDirty(); - return; - } - - BaseData* data = nullptr; - std::string name; - - if( items.count(parent) ) - { - const sofa::core::objectmodel::Base::VecData& fields = parent->getDataFields(); - for( sofa::core::objectmodel::Base::VecData::const_iterator it = fields.begin(); - it != fields.end(); - ++it) - { - data = (*it); - if(datas.count(data)) - { - delete datas[data]; - datas.erase(data); - } - } - } -} - -/*****************************************************************************************************************/ -void GraphListenerQListView::addDatas(sofa::core::objectmodel::BaseObject *parent) -{ - if (widget->isLocked()) - { - widget->setViewToDirty(); - return; - } - - QTreeWidgetItem* new_item; - std::string name; - BaseData* data = nullptr; - if(items.count(parent)) - { - const sofa::core::objectmodel::Base::VecData& fields = parent->getDataFields(); - for( sofa::core::objectmodel::Base::VecData::const_iterator it = fields.begin(); - it!=fields.end(); - ++it) - { - data = (*it); - if(!datas.count(data)) - { - static QPixmap pixData(reinterpret_cast(icondata_xpm)); - new_item = createItem(items[parent]); - name += " "; - name += data->getName(); - datas.insert(std::pair(data,new_item)); - new_item->setText(0, name.c_str()); - new_item->setIcon(0, QIcon(pixData)); - // widget->ensureItemVisible(new_item); - widget->scrollToItem(new_item); - name.clear(); - } - } - } -} - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/GraphListenerQListView.h b/Sofa/GUI/Qt/src/sofa/gui/qt/GraphListenerQListView.h deleted file mode 100644 index cf6aa376848..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/GraphListenerQListView.h +++ /dev/null @@ -1,96 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ - -#pragma once -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -namespace sofa::gui::qt -{ -using sofa::simulation::Node; -using sofa::simulation::MutationListener; - -QPixmap* getPixmap(core::objectmodel::Base* obj, bool, bool,bool); - - -/// A listener to connect changes on the component state with its graphical view. -/// The listener is added to the ComponentState of an object to track changes to -/// and update the icon/treewidgetitem when this happens. -class ObjectStateListener : public sofa::core::objectmodel::DDGNode -{ -public: - QTreeWidgetItem* item; - - // Use a SPtr here because otherwise sofa may decide to remove the base without notifying the ObjectStateListener - // is going to a segfault the right way. - sofa::core::objectmodel::Base::SPtr object; - - ObjectStateListener(QTreeWidgetItem* item_, sofa::core::objectmodel::Base* object_); - ~ObjectStateListener() override; - void update() override; - void notifyEndEdit() override; -}; - - -class SOFA_GUI_QT_API GraphListenerQListView : public MutationListener -{ -public: - SofaSceneGraphWidget* widget; - std::map listeners; - std::map items; - std::map datas; - std::multimap nodeWithMultipleParents; - - GraphListenerQListView(SofaSceneGraphWidget* w) - : widget(w) - { - } - ~GraphListenerQListView() override; - - /*****************************************************************************************************************/ - QTreeWidgetItem* createItem(QTreeWidgetItem* parent); - virtual void onBeginAddChild(Node* parent, Node* child) override; - virtual void onBeginRemoveChild(Node* parent, Node* child) override; - virtual void onBeginAddObject(Node* parent, core::objectmodel::BaseObject* object) override; - virtual void onBeginRemoveObject(Node* /*parent*/, core::objectmodel::BaseObject* object) override; - virtual void onBeginAddSlave(core::objectmodel::BaseObject* master, core::objectmodel::BaseObject* slave) override; - virtual void onBeginRemoveSlave(core::objectmodel::BaseObject* master, core::objectmodel::BaseObject* slave) override; - - virtual void sleepChanged(Node* node) override; - virtual void addDatas(core::objectmodel::BaseObject* parent); - virtual void removeDatas(core::objectmodel::BaseObject* parent); - core::objectmodel::Base* findObject(const QTreeWidgetItem* item); - core::objectmodel::BaseData* findData(const QTreeWidgetItem* item); - - inline static QColor nameColor { 120, 120, 120}; -}; - -} // namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/GraphVisitor.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/GraphVisitor.cpp deleted file mode 100755 index 83e7b0492c7..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/GraphVisitor.cpp +++ /dev/null @@ -1,362 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#include "GraphVisitor.h" - -#include - -#include - -#include - -#include -#include - - -namespace sofa::gui::qt -{ - -typedef sofa::helper::system::thread::CTime CTime; - -bool cmpTime(const dataTime &a, const dataTime &b) { return a.time > b.time;} -bool GraphVisitor::load(std::string &file) -{ - //Open it using TinyXML - tinyxml2::XMLDocument doc; - doc.Parse(file.c_str()); - - //std::cerr << "GRAPH:"<< std::endl << file << std::endl; - - tinyxml2::XMLHandle hDoc(&doc); - tinyxml2::XMLNode* pElem; - //Getting the root of the file - pElem=hDoc.FirstChildElement().ToElement(); - - // should always have a valid root but handle gracefully if it does - if (!pElem) return false; - - totalTime = getTotalTime(pElem); - - componentsTime.clear(); - visitorsTime.clear(); - - - openNode( pElem, NULL, NULL); - - - std::sort(componentsTime.begin(),componentsTime.end(),cmpTime); - std::sort(visitorsTime.begin(),visitorsTime.end(),cmpTime); - - std::sort(componentsTimeTotal.begin(),componentsTimeTotal.end(),cmpTime); - std::sort(visitorsTimeTotal.begin(),visitorsTimeTotal.end(),cmpTime); - - - if (totalTimeMaxpieChart->setChart(visitorsTime, visitorsTime.size()); - window->setCharts(componentsTime,componentsTimeMax,componentsTimeTotal, - visitorsTime,visitorsTimeMax,visitorsTimeTotal); - - if (!initSize) - { - const int sizeLeft = window->graphView->columnWidth(0)+window->graphView->columnWidth(1)+7; - - QList< int > listSize; - listSize << sizeLeft - << window->statsWidget->width()-(sizeLeft-window->graphView->width()); - window->splitterStats->setSizes(listSize); - initSize=true; - } - return true; -} - - -void GraphVisitor::openAttribute ( tinyxml2::XMLElement* element, QTreeWidgetItem* item) -{ - if (!element) return; - const tinyxml2::XMLAttribute* attribute=element->FirstAttribute(); - while (attribute) - { - std::string nameOfAttribute(attribute->Name()); - std::string valueOfAttribute(attribute->Value()); - addInformation(item, nameOfAttribute, valueOfAttribute); - attribute=attribute->Next(); - } - -} - - - -void GraphVisitor::openTime ( tinyxml2::XMLNode* node, QTreeWidgetItem* item) -{ - tinyxml2::XMLElement* element=node->ToElement(); - if (!element) return; - const tinyxml2::XMLAttribute* attribute=element->FirstAttribute(); - double timeSec= getTime(attribute); - double time = 100.0*timeSec/totalTime; - std::ostringstream s; - s.setf(std::ios::fixed, std::ios::floatfield); - s.precision(3); - - s << time << "%"; - - tinyxml2::XMLNode* parent = node->Parent(); - if (parent) - { - std::string nodeType = parent->Value(); - if (nodeType == "Component") - { - const tinyxml2::XMLAttribute* attribute=parent->ToElement()->FirstAttribute(); - std::string componentName, componentType, componentPtr; - while (attribute) - { - std::string nameOfAttribute(attribute->Name()); - std::string valueOfAttribute(attribute->Value()); - if (nameOfAttribute=="name") - componentName=valueOfAttribute; - else if (nameOfAttribute=="type") - componentType=valueOfAttribute; - else if (nameOfAttribute=="ptr") - componentPtr=valueOfAttribute; - attribute=attribute->Next(); - } - if (std::find(visitedNode.begin(), visitedNode.end(), componentName) == visitedNode.end()) - { - dataTime t(timeSec-timeComponentsBelow.back() - , componentType, componentName, componentPtr); - std::vector< dataTime >::iterator it=std::find(componentsTime.begin(),componentsTime.end(),t); - if (it != componentsTime.end()) it->time += t.time; - else componentsTime.push_back(t); - - - it=std::find(componentsTimeTotal.begin(),componentsTimeTotal.end(),t); - if (it != componentsTimeTotal.end()) it->time += t.time; - else componentsTimeTotal.push_back(t); - - - visitedNode.push_back(componentName); - } - } - else - { - if (nodeType != "Node" && nodeType != "Input" && nodeType != "Output" && nodeType != "Vector" && - std::find(visitedNode.begin(), visitedNode.end(),nodeType) == visitedNode.end()) - { - dataTime t(timeSec, nodeType); - std::vector< dataTime >::iterator it=std::find(visitorsTime.begin(),visitorsTime.end(),t); - if (it != visitorsTime.end()) it->time += timeSec; - else visitorsTime.push_back(t); - - it=std::find(visitorsTimeTotal.begin(),visitorsTimeTotal.end(),t); - if (it != visitorsTimeTotal.end()) it->time += t.time; - else visitorsTimeTotal.push_back(t); - - - visitedNode.push_back(nodeType); - } - } - - if (nodeType == "Node" || nodeType == "Component" || nodeType.rfind("Visitor") == nodeType.size()-7) - timeComponentsBelow.back() = timeSec; - } - - addTime(item, s.str()); -} - -double GraphVisitor::getTime(const tinyxml2::XMLAttribute* attribute) const -{ - static double conversion=1.0/(double)CTime::getTicksPerSec(); - std::string valueOfAttribute(attribute->Value()); - double result=1000.0*atof(valueOfAttribute.c_str())*conversion; - return result; -} - -double GraphVisitor::getTotalTime(tinyxml2::XMLNode* node) const -{ - - for ( tinyxml2::XMLNode* child = node->FirstChild(); child != 0; child = child->NextSibling()) - { - std::string nameOfNode=child->Value(); - if (nameOfNode == "TotalTime") - { - const tinyxml2::XMLAttribute* attribute=child->ToElement()->FirstAttribute(); - double total=getTime(attribute); - std::ostringstream out; out << total; - child->ToElement()->SetAttribute(attribute->Name(),out.str().c_str()); - return total; - } - } - return 1; -} - -QTreeWidgetItem* GraphVisitor::openNode( tinyxml2::XMLNode* node, QTreeWidgetItem* parent, QTreeWidgetItem* elementAbove) -{ - if (!node) return NULL; - - unsigned int sizeVisitedNode=visitedNode.size(); - std::string nameOfNode=node->Value(); - // TinyXml API changed in 2.6.0, ELEMENT was replaced with TINYXML_ELEMENT - // As the version number is not available as a macro, the most portable was is to - // replace these constants with checks of the return value of ToElement(), ... - // -- Jeremie A. 02/07/2011 - //int typeOfNode=node->Type(); - QTreeWidgetItem *graphNode=NULL; - if (node->ToDocument()) // case tinyxml2::XMLNode::DOCUMENT: - { - } - else if (node->ToElement()) // case tinyxml2::XMLNode::ELEMENT: - { - if (nameOfNode == "Time") - { - openTime( node, parent); - } - else - { - graphNode = addNode(parent, elementAbove, nameOfNode); - openAttribute( node->ToElement(), graphNode); - } - } - else if (node->ToComment()) // case tinyxml2::XMLNode::COMMENT: - { - graphNode = addComment(parent, elementAbove, nameOfNode); - } - else if (node->ToText()) // case tinyxml2::XMLNode::TEXT: - { - } - else if (node->ToDeclaration()) // case tinyxml2::XMLNode::DECLARATION: - { - } - else // default: - { - } - - QTreeWidgetItem *element=NULL; - timeComponentsBelow.push_back(0); - - for ( tinyxml2::XMLNode* child = node->FirstChild(); child != 0; child = child->NextSibling()) - { - element = openNode( child, graphNode, element); - } - double t=timeComponentsBelow.back(); - - timeComponentsBelow.pop_back(); - - if (!timeComponentsBelow.empty()) timeComponentsBelow.back() += t; - - if (sizeVisitedNode != visitedNode.size()) visitedNode.resize(sizeVisitedNode); - return graphNode; -} - - -QTreeWidgetItem *GraphVisitor::addNode(QTreeWidgetItem *parent, QTreeWidgetItem *elementAbove, std::string name) -{ - QTreeWidgetItem *item=NULL; - if (!parent) - { - //Add a Root - item=new QTreeWidgetItem(graph); - item->setText(0, QString(name.c_str())); - item->setExpanded(true); - } - else - { - //Add a child to a node - item=new QTreeWidgetItem(parent,elementAbove); - item->setText(0, QString(name.c_str())); - } - QPixmap* icon=WindowVisitor::getPixmap(WindowVisitor::getComponentType(name)); - if (icon) item->setIcon(0,QIcon(*icon)); - //item->setMultiLinesEnabled(true); - return item; -} - -void GraphVisitor::addTime(QTreeWidgetItem *element, std::string info) -{ - if (!element) return; - element->setText(1, QString( info.c_str())); -} - -void GraphVisitor::addInformation(QTreeWidgetItem *element, std::string name, std::string info) -{ - if (!element) return; - if (element->text(0) == QString("Node")) - element->setText(0, QString(info.c_str())); - else if (element->text(0) == QString("Component")) - element->setText(0, QString(info.c_str())); - else - { - - QString nameQt = element->text(2); - QString infoQt = element->text(3); - if (!nameQt.isEmpty()) - { - nameQt += QString("\n"); - infoQt += QString("\n"); - } - - if (element->text(0) == QString("Vector") && name=="value" && !info.empty()) - { - std::istringstream ss(info); - std::ostringstream result; - unsigned int size; ss >> size; - while (!ss.eof()) - { - result << "["; - for (unsigned int i=0; i> v; - result << v; - if (i!=size-1) result << " "; - } - result << "]\n"; - } - info = result.str(); - } - - nameQt += QString(name.c_str()); - infoQt += QString(info.c_str()); - - if (name != "ptr") - { - element->setText(2, nameQt); - element->setText(3, infoQt); - } - } -} - -QTreeWidgetItem *GraphVisitor::addComment(QTreeWidgetItem *element,QTreeWidgetItem *elementAbove, std::string comment) -{ - if (!element) return NULL; - QTreeWidgetItem *result = new QTreeWidgetItem(element, elementAbove); - result->setIcon(0,QIcon(*WindowVisitor::getPixmap(WindowVisitor::COMMENT))); - result->setText(1, QString(comment.c_str())); - //result->setSelectable(false); - result->setFlags(Qt::ItemIsEnabled); - //result->setMultiLinesEnabled(true); - return result; -} - -} // namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/GraphVisitor.h b/Sofa/GUI/Qt/src/sofa/gui/qt/GraphVisitor.h deleted file mode 100755 index 4e056e87a13..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/GraphVisitor.h +++ /dev/null @@ -1,88 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include -#include -#include -#include - -#include -#include - -#include -#include - -#include -//#include - -#include -#include - -namespace sofa::gui::qt -{ - -class GraphVisitor -{ -public: - GraphVisitor(WindowVisitor *w) { window=w; graph=w->graphView; totalTimeMax=-1; initSize=false;} - QTreeWidgetItem *addNode(QTreeWidgetItem *parent,QTreeWidgetItem *elementAbove, std::string info); - QTreeWidgetItem *addComment(QTreeWidgetItem *element, QTreeWidgetItem *elementAbove, std::string comment); - void addInformation(QTreeWidgetItem *element, std::string name, std::string info); - void addTime(QTreeWidgetItem *element, std::string info); - - bool load(std::string &file); - - void setGraph(QTreeWidget* g) {graph = g;} - void clear() {graph->clear();} - - double getTotalTime(tinyxml2::XMLNode* node) const; - inline double getTime(const tinyxml2::XMLAttribute* attribute) const; - -protected: - void openTime ( tinyxml2::XMLNode* element, QTreeWidgetItem* item); - void openAttribute ( tinyxml2::XMLElement* element, QTreeWidgetItem* item); - QTreeWidgetItem* openNode( tinyxml2::XMLNode* node, QTreeWidgetItem* parent, QTreeWidgetItem* elementAbove); - - QTreeWidget *graph; - WindowVisitor *window; - - double totalTime; - double totalTimeMax; - - std::vector timeComponentsBelow; - int level; - - std::vector< dataTime > componentsTime; - std::vector< dataTime > visitorsTime; - - std::vector< dataTime > componentsTimeTotal; - std::vector< dataTime > visitorsTimeTotal; - - std::vector< dataTime > componentsTimeMax; - std::vector< dataTime > visitorsTimeMax; - - std::vector< std::string > visitedNode; - - bool initSize; -}; - -} // namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/LinkWidget.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/LinkWidget.cpp deleted file mode 100644 index bdca3c99bcf..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/LinkWidget.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#define SOFA_GUI_QT_LINKWIDGET_CPP - -#include "LinkWidget.h" -#include "ModifyObject.h" -#include - -#include - -#define SIZE_TEXT 60 - -namespace sofa::gui::qt -{ - -LinkWidget *LinkWidget::CreateLinkWidget(const LinkWidget::CreatorArgument &/*dwarg*/) -{ - return nullptr; // TODO -} - -/*QDisplayLinkInfoWidget definitions */ - -QDisplayLinkInfoWidget::QDisplayLinkInfoWidget(QWidget* parent, const std::string& helper, - core::objectmodel::BaseLink* l, bool /*modifiable*/) - : QWidget(parent), link(l), numLines_(1) -{ - QHBoxLayout* layout = new QHBoxLayout(this); - - layout->setContentsMargins(0,0,0,0); - - std::string final_str; - formatHelperString(helper,final_str); - - const core::objectmodel::BaseClass* ownerClass=link->getOwnerClass(); - std::string ownerClassName; - if (ownerClass) ownerClassName = ownerClass->className; - - /* -#ifndef SOFA_GUI_QT_NO_DATA_HELP - QLabel* helper_label = new QLabel(this); - helper_label->setText(QString(final_str.c_str())); - helper_label->setMinimumWidth(20); - layout->addWidget(helper_label); - if (!ownerClassName.empty()) QToolTip::add(helper_label, ("Link from "+ownerClassName).c_str()); -#else - numLines_ = 0; - if (!final_str.empty() || !ownerClassName.empty()) - { - if (!final_str.empty()) final_str += '\n'; - final_str += "Link from "; - final_str += ownerClassName; - QToolTip::add(parent, final_str.c_str()); - } -#endif - */ -} - -void QDisplayLinkInfoWidget::formatHelperString(const std::string& helper, std::string& final_text) -{ - std::string label_text=helper; - numLines_ = 0; - while (!label_text.empty()) - { - const std::string::size_type pos = label_text.find('\n'); - std::string current_sentence; - if (pos != std::string::npos) - current_sentence = label_text.substr(0,pos+1); - else - current_sentence = label_text; - if (current_sentence.size() > SIZE_TEXT) - { - const std::size_t cut = current_sentence.size()/SIZE_TEXT; - for (std::size_t index_cut=1; index_cut<=cut; index_cut++) - { - const std::string::size_type numero_char=current_sentence.rfind(' ',SIZE_TEXT*index_cut); - current_sentence = current_sentence.insert(numero_char+1,1,'\n'); - numLines_++; - } - } - if (pos != std::string::npos) label_text = label_text.substr(pos+1); - else label_text = ""; - final_text += current_sentence; - numLines_++; - } -} - -unsigned int QDisplayLinkInfoWidget::numLines(const std::string& str) -{ - std::string::size_type newline_pos; - unsigned int numlines = 1; - newline_pos = str.find('\n',0); - while( newline_pos != std::string::npos ) - { - numlines++; - newline_pos = str.find('\n',newline_pos+1); - } - return numlines; -} - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/LinkWidget.h b/Sofa/GUI/Qt/src/sofa/gui/qt/LinkWidget.h deleted file mode 100644 index 0508f9d9469..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/LinkWidget.h +++ /dev/null @@ -1,189 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ - -#pragma once -#include -#include -#include -#include - - -#include -#include -#include -#include -#include -#include -#include - - - -namespace sofa::gui::qt -{ - -/** - *\brief Abstract Interface of a qwidget which allows to edit a link. - */ -class SOFA_GUI_QT_API LinkWidget : public QWidget -{ - Q_OBJECT -public: - // - // Factory related code - // - - struct CreatorArgument - { - std::string name; - core::objectmodel::BaseLink* link; - QWidget* parent; - bool readOnly; - }; - - static LinkWidget *CreateLinkWidget(const LinkWidget::CreatorArgument &dwarg); - - -public slots: - /// Checks that widget has been edited - /// emit LinkOwnerDirty in case the name field has been modified - void updateLinkValue() - { - if(dirty) - { - const bool hasOwner = baseLink->getOwnerBase(); - std::string previousName; - if ( hasOwner ) previousName = baseLink->getOwnerBase()->getName(); - writeToLink(); - updateVisibility(); - if(hasOwner && baseLink->getOwnerBase()->getName() != previousName) - { - emit LinkOwnerDirty(true); - } - } - - dirty = false; - counter = baseLink->getCounter(); - - } - /// First checks that the widget is not currently being edited - /// checks that the link has changed since the last time the widget - /// has read the link value. - /// ultimately read the link value. - void updateWidgetValue() - { - if(!dirty) - { - if(counter != baseLink->getCounter()) - { - readFromLink(); - this->update(); - } - } - - - } - /// You call this slot anytime you want to specify that the widget - /// value is out of sync with the underlying link value. - void setWidgetDirty() - { - setWidgetDirty(true); - } - - void setWidgetDirty(bool b) - { - dirty = b; - emit WidgetDirty(b); - } -signals: - /// Emitted each time setWidgetDirty is called. You can also emit - /// it if you want to tell the widget value is out of sync with - /// the underlying link value. - void WidgetDirty(bool ); - /// Currently this signal is used to reflect the changes of the - /// component name in the sofaListview. - void LinkOwnerDirty(bool ); - - void LinkBeingChanged(); -public: - typedef core::objectmodel::BaseLink MyLink; - - LinkWidget(QWidget* parent,const char* /*name*/, MyLink* l) : - QWidget(parent /*,name */), baseLink(l), dirty(false), counter(-1) - { - } - ~LinkWidget() override {} - - inline virtual void setLink( MyLink* d) - { - baseLink = d; - readFromLink(); - } - - - /// BaseLink pointer accessor function. - const core::objectmodel::BaseLink* getBaseLink() const { return baseLink; } - core::objectmodel::BaseLink* getBaseLink() { return baseLink; } - - void updateVisibility() - { - //parentWidget()->setShown(baseLink->isDisplayed()); - } - bool isDirty() { return dirty; } - - /// The implementation of this method holds the widget creation and the signal / slot - /// connections. - virtual bool createWidgets() = 0; - /// Helper method to give a size. - virtual unsigned int sizeWidget() {return 1;} - /// Helper method for column. - virtual unsigned int numColumnWidget() {return 3;} - -protected: - /// The implementation of this method tells how the widget reads the value of the link. - virtual void readFromLink() = 0; - /// The implementation of this methods needs to tell how the widget can write its value - /// in the link - virtual void writeToLink() = 0; - - core::objectmodel::BaseLink* baseLink; - bool dirty; - int counter; -}; - - - -/// Widget used to display the name of a Link -class SOFA_GUI_QT_API QDisplayLinkInfoWidget: public QWidget -{ - Q_OBJECT -public: - QDisplayLinkInfoWidget(QWidget* parent, const std::string& helper, core::objectmodel::BaseLink* l, bool modifiable); -public slots: - unsigned int getNumLines() const { return numLines_;} -protected: - void formatHelperString(const std::string& helper, std::string& final_text); - static unsigned int numLines(const std::string& str); - core::objectmodel::BaseLink* link; - unsigned int numLines_; -}; - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/MaterialDataWidget.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/MaterialDataWidget.cpp deleted file mode 100644 index f1b497b9973..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/MaterialDataWidget.cpp +++ /dev/null @@ -1,245 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#include "MaterialDataWidget.h" -#include - -namespace sofa::gui::qt::materialdatawidget_h -{ -helper::Creator DWClass_MeshMaterial("default",true); -helper::Creator DWClass_MeshVectorMaterial("default",true); - -bool MaterialDataWidget::createWidgets() -{ - - _nameEdit = new QLineEdit(this); - _ambientPicker = new QRGBAColorPicker(this); - _ambientCheckBox = new QCheckBox(this); - _emissivePicker = new QRGBAColorPicker(this); - _emissiveCheckBox = new QCheckBox(this); - _specularPicker = new QRGBAColorPicker(this); - _specularCheckBox = new QCheckBox(this); - _diffusePicker = new QRGBAColorPicker(this); - _diffuseCheckBox = new QCheckBox(this); - _shininessEdit = new QLineEdit(this); - _shininessEdit->setValidator(new QDoubleValidator(this) ); - _shininessCheckBox = new QCheckBox(this); - QVBoxLayout* layout = new QVBoxLayout(this); - - //QGridLayout* grid = new QGridLayout(5,3); - QGridLayout* grid = new QGridLayout(); - grid->setSpacing(1); - QHBoxLayout* hlayout = new QHBoxLayout(); - hlayout->addWidget(new QLabel("Name",this)); - hlayout->addWidget(_nameEdit); - - grid->addWidget(_ambientCheckBox,0,0,Qt::AlignHCenter); - grid->addWidget(new QLabel("Ambient",this),0,1,Qt::AlignHCenter); - grid->addWidget(_ambientPicker,0,2,Qt::AlignHCenter); - - grid->addWidget(_emissiveCheckBox,1,0,Qt::AlignHCenter); - grid->addWidget(new QLabel("Emissive",this),1,1,Qt::AlignHCenter); - grid->addWidget(_emissivePicker,1,2,Qt::AlignHCenter); - - grid->addWidget(_diffuseCheckBox,2,0,Qt::AlignHCenter); - grid->addWidget(new QLabel("Diffuse",this),2,1,Qt::AlignHCenter); - grid->addWidget(_diffusePicker,2,2,Qt::AlignHCenter); - - grid->addWidget(_specularCheckBox,3,0,Qt::AlignHCenter); - grid->addWidget(new QLabel("Specular",this),3,1,Qt::AlignHCenter); - grid->addWidget(_specularPicker,3,2,Qt::AlignHCenter); - - grid->addWidget(_shininessCheckBox,4,0,Qt::AlignHCenter); - grid->addWidget(new QLabel("Shininess",this),4,1,Qt::AlignHCenter); - grid->addWidget(_shininessEdit,4,2,Qt::AlignHCenter); - - layout->addLayout(hlayout); - layout->addLayout(grid); - - - connect(_nameEdit, &QLineEdit::textChanged, this , [=](const QString &){ setWidgetDirty(true); }); - connect(_shininessEdit, &QLineEdit::textChanged, this , [=](const QString &){ setWidgetDirty(true); }); - - connect(_ambientCheckBox, &QCheckBox::toggled, this , &MaterialDataWidget::setWidgetDirty); - connect(_ambientCheckBox, &QCheckBox::toggled, _ambientPicker , &MaterialDataWidget::setEnabled); - - connect(_emissiveCheckBox, &QCheckBox::toggled, this , &MaterialDataWidget::setWidgetDirty); - connect(_emissiveCheckBox, &QCheckBox::toggled, _emissivePicker, &MaterialDataWidget::setEnabled); - - connect(_specularCheckBox, &QCheckBox::toggled, this , &MaterialDataWidget::setWidgetDirty); - connect(_specularCheckBox, &QCheckBox::toggled, _specularPicker, &MaterialDataWidget::setEnabled); - - connect(_diffuseCheckBox, &QCheckBox::toggled, this , &MaterialDataWidget::setWidgetDirty); - connect(_diffuseCheckBox, &QCheckBox::toggled, _diffusePicker, &MaterialDataWidget::setEnabled); - - connect(_shininessCheckBox, &QCheckBox::toggled, this, &MaterialDataWidget::setWidgetDirty); - connect(_shininessCheckBox, &QCheckBox::toggled, _shininessEdit, &MaterialDataWidget::setEnabled); - - connect(_ambientPicker, &QRGBAColorPicker::hasChanged, this, [=](){ setWidgetDirty(true); }); - connect(_emissivePicker, &QRGBAColorPicker::hasChanged, this, [=](){ setWidgetDirty(true); }); - connect(_specularPicker, &QRGBAColorPicker::hasChanged, this, [=](){ setWidgetDirty(true); }); - connect(_diffusePicker, &QRGBAColorPicker::hasChanged, this, [=](){ setWidgetDirty(true); }); - - readFromData(); - - return true; -} -void MaterialDataWidget::setDataReadOnly(bool readOnly) -{ - _nameEdit->setReadOnly(readOnly); - _nameEdit->setEnabled(!readOnly); - _ambientPicker->setEnabled(!readOnly); - _ambientCheckBox->setEnabled(!readOnly); - _emissivePicker->setEnabled(!readOnly); - _emissiveCheckBox->setEnabled(!readOnly); - _specularPicker->setEnabled(!readOnly); - _specularCheckBox->setEnabled(!readOnly); - _diffusePicker->setEnabled(!readOnly); - _diffuseCheckBox->setEnabled(!readOnly); - _shininessEdit->setReadOnly(readOnly); - _shininessCheckBox->setEnabled(!readOnly); -} -void MaterialDataWidget::readFromData() -{ - const Material& material = getData()->getValue(); - _nameEdit->setText( QString( material.name.c_str() ) ); - _ambientCheckBox->setChecked( material.useAmbient ); - _emissiveCheckBox->setChecked( material.useEmissive ); - _diffuseCheckBox->setChecked( material.useDiffuse ); - _specularCheckBox->setChecked( material.useSpecular ); - _shininessCheckBox->setChecked(material.useShininess); - QString str; - str.setNum(material.shininess); - _shininessEdit->setText(str); - - _ambientPicker->setColor( material.ambient ); - _emissivePicker->setColor( material.emissive ); - _specularPicker->setColor( material.specular ); - _diffusePicker->setColor( material.diffuse ); - - _ambientPicker->setEnabled( _ambientCheckBox->isChecked() ); - _emissivePicker->setEnabled( _emissiveCheckBox->isChecked() ); - _specularPicker->setEnabled( _specularCheckBox->isChecked() ); - _diffusePicker->setEnabled( _diffuseCheckBox->isChecked() ); - - -} -void MaterialDataWidget::writeToData() -{ - Material* material = getData()->beginEdit(); - - material->name = _nameEdit->text().toStdString(); - material->ambient = _ambientPicker->getColor(); - material->diffuse = _diffusePicker->getColor(); - material->emissive = _emissivePicker->getColor(); - material->specular = _specularPicker->getColor(); - material->shininess = _shininessEdit->text().toFloat(); - material->useAmbient = _ambientCheckBox->isChecked(); - material->useDiffuse = _diffuseCheckBox->isChecked(); - material->useShininess = _shininessCheckBox->isChecked(); - material->useEmissive = _emissiveCheckBox->isChecked(); - material->useSpecular = _specularCheckBox->isChecked(); - - - getData()->endEdit(); - -} - - -bool VectorMaterialDataWidget::createWidgets() -{ - if( getData()->getValue().empty() ) - { - return false; - } - _comboBox = new QComboBox(this); - _materialDataWidget = new MaterialDataWidget(this,this->objectName().toStdString().c_str(),&_currentMaterial); - _materialDataWidget->createWidgets(); - QVBoxLayout* layout = new QVBoxLayout(this); - layout->addWidget(_comboBox); - layout->addWidget(_materialDataWidget); - - connect( _comboBox, QOverload::of(&QComboBox::activated), this, &VectorMaterialDataWidget::changeMaterial ); - connect( _materialDataWidget, &MaterialDataWidget::WidgetDirty, this, &VectorMaterialDataWidget::setWidgetDirty ); - - readFromData(); - - return true; -} - -void VectorMaterialDataWidget::setDataReadOnly(bool readOnly) -{ - if (_materialDataWidget) - _materialDataWidget->setDataReadOnly(readOnly); -} - -void VectorMaterialDataWidget::readFromData() -{ - VectorMaterial::const_iterator iter; - const VectorMaterial& vecMaterial = getData()->getValue(); - if( vecMaterial.empty() ) - { - return; - } - _comboBox->clear(); - _vectorEditedMaterial.clear(); - std::copy(vecMaterial.begin(), vecMaterial.end(), std::back_inserter(_vectorEditedMaterial) ); - for( iter = _vectorEditedMaterial.begin(); iter != _vectorEditedMaterial.end(); ++iter ) - { - _comboBox->addItem( QString( (*iter).name.c_str() ) ); - } - _currentMaterialPos = 0; - _comboBox->setCurrentIndex(_currentMaterialPos); - _currentMaterial.setValue(_vectorEditedMaterial[_currentMaterialPos]); - _materialDataWidget->setData(&_currentMaterial); - _materialDataWidget->updateWidgetValue(); -} - -void VectorMaterialDataWidget::changeMaterial( int index ) -{ - //Save previous Material - _materialDataWidget->updateDataValue(); - const Material mat(_currentMaterial.getValue() ); - _vectorEditedMaterial[_currentMaterialPos] = mat; - - //Update current Material - _currentMaterialPos = index; - _currentMaterial.setValue(_vectorEditedMaterial[index]); - - //Update Widget - _materialDataWidget->setData(&_currentMaterial); - _materialDataWidget->updateWidgetValue(); -} - -void VectorMaterialDataWidget::writeToData() -{ - _materialDataWidget->updateDataValue(); - const Material mat(_currentMaterial.getValue() ); - _vectorEditedMaterial[_currentMaterialPos] = mat; - - VectorMaterial* vecMaterial = getData()->beginEdit(); - assert(vecMaterial->size() == _vectorEditedMaterial.size() ); - std::copy(_vectorEditedMaterial.begin(), _vectorEditedMaterial.end(), vecMaterial->begin() ); - - getData()->endEdit(); -} - -} // namespace sofa::gui::qt::materialdatawidget_h diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/MaterialDataWidget.h b/Sofa/GUI/Qt/src/sofa/gui/qt/MaterialDataWidget.h deleted file mode 100644 index 41785c73817..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/MaterialDataWidget.h +++ /dev/null @@ -1,123 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include "DataWidget.h" -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "QRGBAColorPicker.h" - -/// Private namespace -namespace sofa::gui::qt::materialdatawidget_h -{ - -using sofa::gui::qt::QRGBAColorPicker ; -using sofa::type::Material ; -using sofa::core::objectmodel::Data ; - -class MaterialDataWidget : public TDataWidget -{ - Q_OBJECT -public: - MaterialDataWidget(QWidget* parent, - const char* name, - Data* data): - TDataWidget(parent,name,data) - {} - - virtual bool createWidgets(); - virtual void setDataReadOnly(bool readOnly); - virtual unsigned int numColumnWidget() {return 1;} - -protected: - virtual void readFromData(); - virtual void writeToData(); - QLineEdit* _nameEdit; - QRGBAColorPicker* _ambientPicker; - QRGBAColorPicker* _emissivePicker; - QRGBAColorPicker* _specularPicker; - QRGBAColorPicker* _diffusePicker; - QLineEdit* _shininessEdit; - QCheckBox* _ambientCheckBox; - QCheckBox* _emissiveCheckBox; - QCheckBox* _specularCheckBox; - QCheckBox* _diffuseCheckBox; - QCheckBox* _shininessCheckBox; -}; - - -typedef type::vector VectorMaterial; -class VectorMaterialDataWidget : public TDataWidget< VectorMaterial > -{ - Q_OBJECT -public: - VectorMaterialDataWidget(QWidget* parent, - const char* name, - Data< type::vector >* data): - TDataWidget< type::vector >(parent,name,data), - _materialDataWidget(nullptr), - _currentMaterial(0,data->isDisplayed(),data->isReadOnly()), - _comboBox(nullptr) - { - - } - - virtual bool createWidgets(); - virtual void setDataReadOnly(bool readOnly); - virtual unsigned int numColumnWidget() {return 1;} - - -protected: - virtual void readFromData(); - virtual void writeToData(); - - MaterialDataWidget* _materialDataWidget; - VectorMaterial _vectorEditedMaterial; - core::objectmodel::Data _currentMaterial; - QComboBox* _comboBox; - int _currentMaterialPos; - -protected slots: - void changeMaterial( int ); -}; - -} // namespace sofa::gui::qt::materialdatawidget_h - -namespace sofa::gui::qt -{ - using sofa::gui::qt::materialdatawidget_h::MaterialDataWidget; -} // namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/ModifyObject.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/ModifyObject.cpp deleted file mode 100644 index 19a9ed49dc5..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/ModifyObject.cpp +++ /dev/null @@ -1,568 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ - -#include "ModifyObject.h" -#include "DataWidget.h" -#include "QDisplayDataWidget.h" -#include "QDataDescriptionWidget.h" -#include "QTabulationModifyObject.h" -#include -#include -#include -#include -#if SOFA_GUI_QT_HAVE_QT_CHARTS -#include -#include -#endif -#include -using sofa::helper::logging::Message; - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace sofa::gui::qt -{ - -ModifyObject::ModifyObject(void *Id, - QTreeWidgetItem* item_clicked, - QWidget* parent, - const ModifyObjectFlags& dialogFlags, - const char* name, - bool modal, Qt::WindowFlags f ) - :QDialog(parent, f), - Id_(Id), - item_(item_clicked), - basenode(nullptr), - data_(nullptr), - dialogFlags_(dialogFlags), - messageTab(nullptr), - messageEdit(nullptr) - #if SOFA_GUI_QT_HAVE_QT_CHARTS - ,energy(nullptr) - ,momentum(nullptr) - #endif -{ - setWindowTitle(name); - //setObjectName(name); - setModal(modal); -} - -void ModifyObject::createDialog(core::objectmodel::Base* base) -{ - if(base == nullptr) - { - return; - } - emit beginObjectModification(base); - basenode = base; - data_ = nullptr; - - //Layout to organize the whole window - QVBoxLayout *generalLayout = new QVBoxLayout(this); - generalLayout->setObjectName("generalLayout"); - generalLayout->setContentsMargins(0,0,0,0); - generalLayout->setSpacing(1); - - //Tabulation widget - dialogTab = new QTabWidget(this); - - //add a scrollable area for data properties - QScrollArea* m_scrollArea = new QScrollArea(); - - // const int screenHeight = QApplication::desktop()->height(); - QRect geometry = QGuiApplication::primaryScreen()->availableGeometry(); - - m_scrollArea->setMinimumSize(600, geometry.height() * 0.75); - m_scrollArea->setWidgetResizable(true); - dialogTab->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - m_scrollArea->setWidget(dialogTab); - generalLayout->addWidget(m_scrollArea); - - connect(dialogTab, SIGNAL( currentChanged(int)), this, SLOT( updateTables())); - - buttonUpdate = new QPushButton( this ); - buttonUpdate->setObjectName("buttonUpdate"); - buttonUpdate->setText("&Update"); - buttonUpdate->setEnabled(false); - QPushButton *buttonOk = new QPushButton( this ); - buttonOk->setObjectName("buttonOk"); - buttonOk->setText( tr( "&OK" ) ); - - QPushButton *buttonCancel = new QPushButton( this ); - buttonCancel->setObjectName("buttonCancel"); - buttonCancel->setText( tr( "&Cancel" ) ); - - QPushButton *buttonRefresh = new QPushButton( this ); - buttonRefresh->setObjectName("buttonRefresh"); - buttonRefresh->setText( tr( "Refresh" ) ); - - // displayWidget - if (basenode) - { - const sofa::core::objectmodel::Base::VecData& fields = basenode->getDataFields(); - const sofa::core::objectmodel::Base::VecLink& links = basenode->getLinks(); - - std::map< std::string, std::vector > groupTabulation; - - std::vector tabNames; - //Put first the Property Tab - tabNames.push_back("Property"); - - for( sofa::core::objectmodel::Base::VecData::const_iterator it = fields.begin(); it!=fields.end(); ++it) - { - core::objectmodel::BaseData* data=*it; - if (!data) - { - dmsg_error("ModifyObject") << "nullptr Data in '" << basenode->getName() << "'" ; - continue; - } - - if (data->getName().empty()) continue; // ignore unnamed data - - //For each Data of the current Object - //We determine where it belongs: - std::string currentGroup=data->getGroup(); - - if (currentGroup.empty()) currentGroup="Property"; - - // Ignore the data in group "Infos" so they can be putted in the real Infos panel that is - // handled in a different way (see QDataDescriptionWidget) - if (currentGroup == "Infos") - continue; - - QTabulationModifyObject* currentTab=nullptr; - - std::vector &tabs=groupTabulation[currentGroup]; - bool newTab = false; - if (tabs.empty()) tabNames.push_back(currentGroup); - if (tabs.empty() || tabs.back()->isFull()) - { - newTab = true; - m_tabs.push_back(new QTabulationModifyObject(this,basenode, item_,tabs.size()+1)); - tabs.push_back(m_tabs.back()); - } - currentTab = tabs.back(); - currentTab->addData(data, getFlags()); - if (newTab) - { - connect(buttonUpdate, SIGNAL(clicked() ), currentTab, SLOT( updateDataValue() ) ); - connect(buttonOk, SIGNAL(clicked() ), currentTab, SLOT( updateDataValue() ) ); - connect(this, SIGNAL(updateDataWidgets()), currentTab, SLOT( updateWidgetValue()) ); - - /// The timer is deleted when the 'this' object is destroyed. - QTimer *timer = new QTimer(this); - connect(timer, SIGNAL(timeout()), this, SLOT(updateTables())); - connect(timer, SIGNAL(timeout()), currentTab, SLOT(updateDataValue())); - timer->start(10); - - connect(currentTab, SIGNAL( TabDirty(bool) ), buttonUpdate, SLOT( setEnabled(bool) ) ); - connect(currentTab, SIGNAL( TabDirty(bool) ), this, SIGNAL( componentDirty(bool) ) ); - } - } - - for( sofa::core::objectmodel::Base::VecLink::const_iterator it = links.begin(); it!=links.end(); ++it) - { - core::objectmodel::BaseLink* link=*it; - - if (link->getName().empty()) continue; // ignore unnamed links - if (!link->storePath() && link->getSize() == 0) continue; // ignore empty links - - //For each Link of the current Object - //We determine where it belongs: - std::string currentGroup="Links"; - - QTabulationModifyObject* currentTab=nullptr; - - std::vector &tabs=groupTabulation[currentGroup]; - if (tabs.empty()) tabNames.push_back(currentGroup); - if (tabs.empty() || tabs.back()->isFull()) - { - m_tabs.push_back(new QTabulationModifyObject(this,basenode, item_,tabs.size()+1)); - tabs.push_back(m_tabs.back() ); - } - currentTab = tabs.back(); - - currentTab->addLink(link, getFlags()); - connect(buttonUpdate, SIGNAL(clicked() ), currentTab, SLOT( updateDataValue() ) ); - connect(buttonOk, SIGNAL(clicked() ), currentTab, SLOT( updateDataValue() ) ); - connect(this, SIGNAL(updateDataWidgets()), currentTab, SLOT( updateWidgetValue()) ); - - connect(currentTab, SIGNAL( TabDirty(bool) ), buttonUpdate, SLOT( setEnabled(bool) ) ); - connect(currentTab, SIGNAL( TabDirty(bool) ), this, SIGNAL( componentDirty(bool) ) ); - } - - for (std::vector::const_iterator it = tabNames.begin(), itend = tabNames.end(); it != itend; ++it) - { - const std::string& groupName = *it; - std::vector &tabs=groupTabulation[groupName]; - - for (unsigned int i=0; igetIndex()) + "/" + QString::number(tabs.size()); - dialogTab->addTab(tabs[i],nameTab); - tabs[i]->addStretch(); - } - } - -#if SOFA_GUI_QT_HAVE_QT_CHARTS - //Energy Widget - if (simulation::Node* real_node = sofa::core::castTo(basenode)) - { - if (dialogFlags_.REINIT_FLAG) - { - energy = new QEnergyStatWidget(dialogTab, real_node); - dialogTab->addTab(energy, QString("Energy Stats")); - } - } - - //Momentum Widget - if (simulation::Node* real_node = sofa::core::castTo(basenode)) - { - if (dialogFlags_.REINIT_FLAG) - { - momentum = new QMomentumStatWidget(dialogTab, real_node); - dialogTab->addTab(momentum, QString("Momentum Stats")); - } - } -#endif - - - /// Info Widget - { - QDataDescriptionWidget* description=new QDataDescriptionWidget(dialogTab, basenode); - dialogTab->addTab(description, QString("Infos")); - } - - /// Message widget - { - updateConsole(); - if (messageTab) - { - std::stringstream tmp; - int numMessages = basenode->countLoggedMessages({Message::Info, Message::Advice, Message::Deprecated, - Message::Error, Message::Warning, Message::Fatal}); - tmp << "Messages(" << numMessages << ")" ; - dialogTab->addTab(messageTab, QString::fromStdString(tmp.str())); - } - } - - //Adding buttons at the bottom of the dialog - QHBoxLayout *lineLayout = new QHBoxLayout( nullptr); - lineLayout->setContentsMargins(0,0,0,0); - lineLayout->setSpacing(6); - lineLayout->setObjectName("Button Layout"); - lineLayout->addWidget(buttonUpdate); - QSpacerItem *Horizontal_Spacing = new QSpacerItem( 20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum ); - lineLayout->addItem( Horizontal_Spacing ); - - lineLayout->addWidget(buttonOk); - lineLayout->addWidget(buttonCancel); - lineLayout->addWidget(buttonRefresh); - generalLayout->addLayout( lineLayout ); - - //Signals and slots connections - connect( buttonUpdate, SIGNAL( clicked() ), this, SLOT( updateValues() ) ); - connect(buttonRefresh, SIGNAL(clicked() ), this, SLOT( updateTables() )); - connect( buttonOk, SIGNAL( clicked() ), this, SLOT( accept() ) ); - connect( buttonCancel, SIGNAL( clicked() ), this, SLOT( reject() ) ); - - resize( QSize(450, 130).expandedTo(minimumSizeHint()) ); - } -} - -void ModifyObject::clearMessages() -{ - basenode->clearLoggedMessages(); - messageEdit->clear(); - - std::stringstream tmp; - const int numMessages = basenode->countLoggedMessages({Message::Info, Message::Advice, Message::Deprecated, - Message::Error, Message::Warning, Message::Fatal}); - tmp << "Messages(" << numMessages << ")" ; - - dialogTab->setTabText(dialogTab->indexOf(messageTab), QString::fromStdString(tmp.str())); -} - - - -void ModifyObject::createDialog(core::objectmodel::BaseData* data) -{ - data_ = data; - basenode = nullptr; - - emit beginDataModification(data); - - QVBoxLayout *generalLayout = new QVBoxLayout(this); - generalLayout->setContentsMargins(0, 0, 0, 0); - generalLayout->setSpacing(1); - generalLayout->setObjectName("generalLayout"); - QHBoxLayout *lineLayout = new QHBoxLayout( nullptr); - lineLayout->setContentsMargins(0, 0, 0, 0); - lineLayout->setSpacing(6); - lineLayout->setObjectName("Button Layout"); - buttonUpdate = new QPushButton( this ); - buttonUpdate->setObjectName("buttonUpdate"); - buttonUpdate->setText("&Update"); - buttonUpdate->setEnabled(false); - QPushButton *buttonOk = new QPushButton( this ); - buttonOk->setObjectName("buttonOk"); - buttonOk->setText( tr( "&OK" ) ); - QPushButton *buttonCancel = new QPushButton( this ); - buttonCancel->setObjectName("buttonCancel"); - buttonCancel->setText( tr( "&Cancel" ) ); - - QDisplayDataWidget* displaydatawidget = new QDisplayDataWidget(this,data,getFlags()); - generalLayout->addWidget(displaydatawidget); - lineLayout->addWidget(buttonUpdate); - - QSpacerItem *Horizontal_Spacing = new QSpacerItem( 20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum ); - lineLayout->addItem( Horizontal_Spacing ); - - lineLayout->addWidget(buttonOk); - lineLayout->addWidget(buttonCancel); - generalLayout->addLayout( lineLayout ); - connect(buttonUpdate, SIGNAL( clicked() ), displaydatawidget, SLOT( UpdateData() ) ); - connect(displaydatawidget, SIGNAL( WidgetDirty(bool) ), buttonUpdate, SLOT( setEnabled(bool) ) ); - connect(displaydatawidget, SIGNAL( WidgetDirty(bool) ), this, SIGNAL( componentDirty(bool) ) ); - connect(buttonOk, SIGNAL(clicked() ), displaydatawidget, SLOT( UpdateData() ) ); - connect( buttonOk, SIGNAL( clicked() ), this, SLOT( accept() ) ); - connect( buttonCancel, SIGNAL( clicked() ), this, SLOT( reject() ) ); - connect(this, SIGNAL(updateDataWidgets()), displaydatawidget, SLOT(UpdateWidgets()) ); -} - -const std::string toHtmlString(const Message::Type t) -{ - switch(t) - { - case Message::Info: - return "Info"; - case Message::Advice: - return "Advice"; - case Message::Deprecated: - return "Deprecated"; - case Message::Warning: - return "Warning"; - case Message::Error: - return "Error"; - case Message::Fatal: - return "Fatal"; - default: - return "Undefine"; - } - return "Undefine"; -} - -class ClickableTextEdit : public QTextEdit -{ -public: - Q_OBJECT - -public: - ClickableTextEdit(QWidget* w) : QTextEdit(w) {} -}; - -void ModifyObject::openExternalBrowser(const QUrl &link) -{ - QDesktopServices::openUrl(link) ; -} - -//****************************************************************************************** -void ModifyObject::updateConsole() -{ - if (!messageEdit) - { - messageTab = new QWidget(); - QVBoxLayout* tabLayout = new QVBoxLayout( messageTab); - tabLayout->setContentsMargins(0, 0, 0, 0); - tabLayout->setSpacing(1); - tabLayout->setObjectName("tabWarningLayout"); - QPushButton *buttonClearWarnings = new QPushButton(messageTab); - buttonClearWarnings->setObjectName("buttonClearWarnings"); - tabLayout->addWidget(buttonClearWarnings); - buttonClearWarnings->setText( tr("&Clear")); - connect( buttonClearWarnings, SIGNAL( clicked()), this, SLOT( clearMessages())); - - messageEdit = new QTextBrowser(messageTab); - //messageEdit->backwardAvailable(false); - connect(messageEdit, SIGNAL(anchorClicked(const QUrl&)), this, SLOT(openExternalBrowser(const QUrl&))); - messageEdit->setObjectName("WarningEdit"); - messageEdit->setOpenExternalLinks(false); - messageEdit->setOpenLinks(false); - tabLayout->addWidget( messageEdit ); - messageEdit->setReadOnly(true); - } - - if (dialogTab->currentWidget() == messageTab) - { - std::stringstream tmp; - tmp << ""; - tmp << ""; - tmp << "" ; - m_numMessages++; - } - tmp << "
    " ; - m_numMessages = 0 ; - for(const Message& message : basenode->getLoggedMessages()) - { - tmp << "
    ["<" ; - tmp << "" << message.messageAsString() << "
    "; - - messageEdit->setHtml(QString(tmp.str().c_str())); - messageEdit->moveCursor(QTextCursor::End, QTextCursor::MoveAnchor); - messageEdit->ensureCursorVisible(); - } -} - -//******************************************************************************************************************* -void ModifyObject::updateValues() -{ - // this is controlling if we need to re-init (eg: not in the Modeller) - if(!dialogFlags_.REINIT_FLAG) - return; - - if (basenode == nullptr) - return; - - simulation::Node* node = sofa::core::castTo(basenode); - core::objectmodel::BaseObject* object = sofa::core::castTo(basenode); - - // if the selected object is a node - if (node) - { - node->reinit(sofa::core::execparams::defaultInstance()); - } - else if (object) //< if the selected is an object - { - object->reinit(); //< we need to fully re-initialize the object to be sure it is ok. - } - else - { - throw std::runtime_error("Invalid type, only Node and BaseObject are supported. " - "This is a BUG, please report to https://github.com/sofa-framework/sofa/issues"); - } - - // trigger the internal updates (eg: updateDataCallback), - basenode->d_componentState.updateIfDirty(); - - emit objectUpdated(); - emit endObjectModification(basenode); - emit beginObjectModification(basenode); - - if (buttonUpdate) - buttonUpdate->setEnabled(false); -} - -//************************************************************************************************************************************** -//Called each time a new step of the simulation if computed -void ModifyObject::updateTables() -{ - emit updateDataWidgets(); -#if SOFA_GUI_QT_HAVE_QT_CHARTS - if (energy) - { - if (dialogTab->currentWidget() == energy) energy->step(); - } - - if (momentum) - { - if (dialogTab->currentWidget() == momentum) momentum->step(); - } -#endif - - if(basenode) - { - updateConsole(); - } -} - -void ModifyObject::reject () -{ - if (basenode) - { - emit endObjectModification(basenode); - } - - const QString dataModifiedString = parseDataModified(); - if (!dataModifiedString.isEmpty()) - { - emit dataModified( dataModifiedString ); - } - - emit dialogClosed(Id_); - deleteLater(); - QDialog::reject(); -} //When closing a window, inform the parent. - -void ModifyObject::accept () -{ - updateValues(); - - const QString dataModifiedString = parseDataModified(); - if (!dataModifiedString.isEmpty()) - { - emit dataModified( dataModifiedString ); - } - - if (basenode) - { - emit endObjectModification(basenode); - } - emit dialogClosed(Id_); - deleteLater(); - QDialog::accept(); -} //if closing by using Ok button, update the values - -QString ModifyObject::parseDataModified() -{ - QString cat; - - for (std::size_t i = 0; i < m_tabs.size(); ++i) - { - const QString tabString = m_tabs[i]->getDataModifiedString(); - if (!tabString.isEmpty()) - { - cat += tabString; - if (i != (m_tabs.size() - 1)) - { - cat += "\n"; - } - } - } - - return cat; -} - -bool ModifyObject::hideData(core::objectmodel::BaseData* data) { return (!data->isDisplayed()) && dialogFlags_.HIDE_FLAG;} - - -} // namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/ModifyObject.h b/Sofa/GUI/Qt/src/sofa/gui/qt/ModifyObject.h deleted file mode 100644 index 95ad164f984..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/ModifyObject.h +++ /dev/null @@ -1,188 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -namespace sofa::gui::qt -{ - -class QTransformationWidget; -#if SOFA_GUI_QT_HAVE_QT_CHARTS -class QEnergyStatWidget; -class QMomentumStatWidget; -#endif -class QTabulationModifyObject; - -typedef struct ModifyObjectFlags -{ - - bool HIDE_FLAG; //if we allow to hide Datas - bool READONLY_FLAG; //if we allow ReadOnly Datas - bool EMPTY_FLAG;//if we allow empty data - bool RESIZABLE_FLAG; - bool REINIT_FLAG; - bool LINKPATH_MODIFIABLE_FLAG; //if we allow to modify the links of the Data - bool MODELER_FLAG; //if we attempt to create this dialog from the Modeler. - bool PROPERTY_WIDGET_FLAG; //if we attempt to create this dialog in the property widget. - - ModifyObjectFlags(): - HIDE_FLAG(true), - READONLY_FLAG(true), - EMPTY_FLAG(false), - RESIZABLE_FLAG(false), - REINIT_FLAG(true), - LINKPATH_MODIFIABLE_FLAG(false), - MODELER_FLAG(false), - PROPERTY_WIDGET_FLAG(false) - {} - - void setFlagsForSofa() - { - HIDE_FLAG = true; - READONLY_FLAG = true; - EMPTY_FLAG = false; - RESIZABLE_FLAG = true; - REINIT_FLAG = true; - LINKPATH_MODIFIABLE_FLAG = false; - MODELER_FLAG = false; - PROPERTY_WIDGET_FLAG = false; - }; - - void setFlagsForModeler() - { - HIDE_FLAG = false; - READONLY_FLAG=false; //everything will be editable - EMPTY_FLAG = true; - RESIZABLE_FLAG = true; - REINIT_FLAG = false; - LINKPATH_MODIFIABLE_FLAG = true; - MODELER_FLAG = true; - PROPERTY_WIDGET_FLAG = false; - }; -} ModifyObjectFlags; - -class SOFA_GUI_QT_API ModifyObject : public QDialog -{ - Q_OBJECT -public: - - explicit ModifyObject( void *Id, - QTreeWidgetItem* item_clicked, - QWidget* parent, - const ModifyObjectFlags& dialogFlags, - const char* name= nullptr, - bool modal= false, - Qt::WindowFlags f= Qt::WindowType::Widget ); - - ~ModifyObject() override - { - delete buttonUpdate; - } - - const ModifyObjectFlags& getFlags() { return dialogFlags_;} - - void createDialog(core::objectmodel::Base* node); - void createDialog(core::objectmodel::BaseData* data); - bool hideData(core::objectmodel::BaseData* data); - void readOnlyData(QTableWidget *widget, core::objectmodel::BaseData* data); - void readOnlyData(QWidget *widget, core::objectmodel::BaseData* data); - -public slots: - void openExternalBrowser(const QUrl &link); - void reject () override; - void accept () override; - void closeNow () {emit(reject());} //called from outside to close the current widget - void closeEvent ( QCloseEvent * ) override {emit(reject());} - void updateTables(); - virtual void updateValues(); //update the node with the values of the field -signals: - void updateDataWidgets(); // emitted eachtime updateValues is called to propagate the changes to the widgets. - void objectUpdated(); //update done - void dialogClosed(void *); //the current window has been closed: we give the Id of the current window - void nodeNameModification(simulation::Node*); - void componentDirty(bool); - void dataModified( QString ); - - - void beginObjectModification(sofa::core::objectmodel::Base* object); - void endObjectModification(sofa::core::objectmodel::Base* object); - void beginDataModification(sofa::core::objectmodel::BaseData* data); - void endDataModification(sofa::core::objectmodel::BaseData* data); - -protected slots: - //update the tables of value at each step of the simulation - void clearMessages() ; - -protected: - void updateConsole(); //update the console log of warnings and outputs - QString parseDataModified(); - void* Id_; - QTreeWidgetItem* item_; - core::objectmodel::Base* basenode; - core::objectmodel::BaseData* data_; - const ModifyObjectFlags dialogFlags_; - - QWidget *messageTab; - QTextBrowser *messageEdit; - - QTabWidget *dialogTab; - QPushButton *buttonUpdate; - int m_numMessages; - -#if SOFA_GUI_QT_HAVE_QT_CHARTS - //Energy widget: plot the kinetic & potential energy - QEnergyStatWidget* energy; - //Momentum widget: plot the linear & angular momentum - QMomentumStatWidget* momentum; -#endif - //Visual Flags - - std::vector< QTabulationModifyObject* > m_tabs; - -}; - - -} // namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/MouseManager.ui b/Sofa/GUI/Qt/src/sofa/gui/qt/MouseManager.ui deleted file mode 100644 index 7cca58ab10b..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/MouseManager.ui +++ /dev/null @@ -1,104 +0,0 @@ - - - MouseManager - - - - 0 - 0 - 295 - 612 - - - - MouseManager - - - - - - Left Button - - - - - - Operation: - - - false - - - - - - - - - - - - - Middle Button - - - - - - Operation: - - - false - - - - - - - - - - - - - Right Button - - - - - - Operation: - - - false - - - - - - - - - - - - - Qt::Vertical - - - QSizePolicy::Expanding - - - - 20 - 40 - - - - - - - - - - diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/PickHandlerCallBacks.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/PickHandlerCallBacks.cpp deleted file mode 100644 index a2149cc4daa..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/PickHandlerCallBacks.cpp +++ /dev/null @@ -1,107 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#include "PickHandlerCallBacks.h" -#include "RealGUI.h" -#include "viewer/SofaViewer.h" -#include "QSofaListView.h" -#include -#include - -#include - -using namespace sofa::gui::common; - -namespace sofa::gui::qt -{ - -InformationOnPickCallBack::InformationOnPickCallBack() - :gui(nullptr) -{ -} - -InformationOnPickCallBack::InformationOnPickCallBack(RealGUI *g) - :gui(g) -{ -} - -void InformationOnPickCallBack::execute(const BodyPicked &body) -{ - if(!gui) return; - core::objectmodel::BaseObject *objectPicked=nullptr; - if (body.body) - { - QTreeWidgetItem* item=gui->simulationGraph->getListener()->items[body.body]; -// gui->simulationGraph->ensureItemVisible(item); - gui->simulationGraph->scrollToItem(item); - gui->simulationGraph->clearSelection(); -// gui->simulationGraph->setSelected(item,true); - gui->simulationGraph->setCurrentItem(item); - objectPicked=body.body; - } - else if (body.mstate) - { - QTreeWidgetItem* item=gui->simulationGraph->getListener()->items[body.mstate]; -// gui->simulationGraph->ensureItemVisible(item); - gui->simulationGraph->scrollToItem(item); - gui->simulationGraph->clearSelection(); -// gui->simulationGraph->setSelected(item,true); - gui->simulationGraph->setCurrentItem(item); - objectPicked=body.mstate; - } - else - gui->simulationGraph->clearSelection(); - - if (objectPicked) - { - QString messagePicking; - const simulation::Node *n=static_cast(objectPicked->getContext()); - messagePicking=QString("Index ") + QString::number(body.indexCollisionElement) - + QString(" of ") - + QString(n->getPathName().c_str()) - + QString("/") + QString(objectPicked->getName().c_str()) - + QString(" : ") + QString(objectPicked->getClassName().c_str()); - if (!objectPicked->getTemplateName().empty()) - messagePicking += QString("<") + QString(objectPicked->getTemplateName().c_str()) + QString(">"); - gui->statusBar()->showMessage(messagePicking,3000); //display message during 3 seconds - } -} - -ColourPickingRenderCallBack::ColourPickingRenderCallBack() - :_viewer(nullptr) -{ -} - -ColourPickingRenderCallBack::ColourPickingRenderCallBack(viewer::SofaViewer* viewer) - :_viewer(viewer) -{ -} - -void ColourPickingRenderCallBack::render(ColourPickingVisitor::ColourCode code) -{ - if(_viewer) - { - _viewer->drawColourPicking(code); - } - -} - -} // namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/PickHandlerCallBacks.h b/Sofa/GUI/Qt/src/sofa/gui/qt/PickHandlerCallBacks.h deleted file mode 100644 index 5d9729a46fd..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/PickHandlerCallBacks.h +++ /dev/null @@ -1,64 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include -#include -#include - -namespace sofa::component::collision -{ - struct BodyPicked; -} // namespace sofa::component::collision - -namespace sofa::gui::qt::viewer -{ - class SofaViewer; -} // namespace sofa::gui::qt::viewer - -namespace sofa::gui::qt -{ -class RealGUI; - -class InformationOnPickCallBack: public common::CallBackPicker -{ -public: - using BodyPicked = sofa::gui::component::performer::BodyPicked; - InformationOnPickCallBack(); - InformationOnPickCallBack(RealGUI *g); - void execute(const BodyPicked &body) override; -protected: - RealGUI *gui; -}; - - -class ColourPickingRenderCallBack : public sofa::gui::common::CallBackRender -{ -public: - ColourPickingRenderCallBack(); - ColourPickingRenderCallBack(viewer::SofaViewer* viewer); - void render(common::ColourPickingVisitor::ColourCode code) override; -protected: - viewer::SofaViewer* _viewer; - -}; - -} // namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/PieWidget.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/PieWidget.cpp deleted file mode 100644 index 3f928550623..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/PieWidget.cpp +++ /dev/null @@ -1,173 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#include - -#include -#include -#include -#include - -namespace sofa::gui::qt -{ - -std::vector< type::Vec<3,int> > PieWidget::colorArray; - -type::Vec<3,int> PieWidget::getColor(int i) -{ - type::Vec<3,int> res=PieWidget::colorArray[i%PieWidget::colorArray.size()]; - float factor=1.0/(1.0+(0.3*(i/PieWidget::colorArray.size()))); - res[0] = (int)(res[0]*factor); - res[1] = (int)(res[1]*factor); - res[2] = (int)(res[2]*factor); - return res; -} - -PieWidget::PieWidget(QWidget *parent): QWidget(parent) -{ - if (PieWidget::colorArray.empty()) - { - colorArray.push_back( type::Vec<3,int>(250,125,70) ); - colorArray.push_back( type::Vec<3,int>(120,220,110) ); - colorArray.push_back( type::Vec<3,int>(215,90,215) ); - colorArray.push_back( type::Vec<3,int>(255,210,40) ); - colorArray.push_back( type::Vec<3,int>(75,210,210) ); - } -} -void PieWidget::paintEvent( QPaintEvent* ) -{ - sizePie = (int)(std::min(this->width(),this->height())*0.95); - if (data.empty()) return; - - QPainter p( this ); - - int initDraw=0; - int startAngle=0; - - p.setBrush(Qt::SolidPattern); - - for (unsigned int i=0; i c=PieWidget::getColor(i); - QColor color(c[0],c[1],c[2]); - p.setBrush(color); - int spanAngle=(int)(16*360*data[i].time/totalTime); - p.drawPie(initDraw,initDraw,sizePie,sizePie,startAngle,spanAngle); - startAngle+= spanAngle; - } -} - -void PieWidget::setChart( std::vector< dataTime >& value, unsigned int s) -{ - data=value; - selection=s; - totalTime=0; - for (unsigned int i=0; isetOrientation(Qt::Horizontal); - QGridLayout *grid = new QGridLayout(this); - pie = new PieWidget(splitter); - - table = new QTableWidget(0,3,splitter); - -#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) - table->horizontalHeader()->setResizeMode(0,QHeaderView::Fixed); - table->horizontalHeader()->setResizeMode(1,QHeaderView::ResizeToContents); - table->horizontalHeader()->setResizeMode(2,QHeaderView::ResizeToContents); -#else - table->horizontalHeader()->setSectionResizeMode(0,QHeaderView::Fixed); - table->horizontalHeader()->setSectionResizeMode(1,QHeaderView::ResizeToContents); - table->horizontalHeader()->setSectionResizeMode(2,QHeaderView::ResizeToContents); -#endif // QT_VERSION < QT_VERSION_CHECK(5, 0, 0) - - table->horizontalHeader()->resizeSection(0,30); - - QStringList list; list<<"Id" << name.c_str() << "Time"; - table->setHorizontalHeaderLabels(list); - - grid->addWidget(splitter,0,0); - -} - - -void ChartsWidget::clear() -{ - int rows=table->rowCount(); - - for (int i=0; iremoveRow(0); - pie->clear(); -} - -void ChartsWidget::setChart( std::vector< dataTime >& value, unsigned int s) -{ - clear(); - pie->setChart(value,s); - selection=s; - for (unsigned int i=0; iinsertRow(i); - - type::Vec<3,int> c=PieWidget::getColor(i); - QColor color(c[0],c[1],c[2]); - - QString text(value[i].name.c_str()); - QString time= QString::number(value[i].time); - time += QString(" ms"); - if (!value[i].type.empty()) - { - text+="("; - text+= QString(value[i].type.c_str()); - text+=")"; - } - - QTableWidgetItem *itemColor = new QTableWidgetItem(); - itemColor->setBackground(color); - QTableWidgetItem *item = new QTableWidgetItem(); - QTableWidgetItem *itemTime = new QTableWidgetItem(); - table->setItem(i,0, itemColor); - item->setText(text); - table->setItem(i,1, item); - itemTime->setText(time); - table->setItem(i,2, itemTime); - table->resizeColumnToContents(1); - itemColor->setFlags(QFlag(0)); - item->setFlags(QFlag(0)); - itemTime->setFlags(QFlag(0)); - - } - pie->repaint(); - -} - -} // namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/PieWidget.h b/Sofa/GUI/Qt/src/sofa/gui/qt/PieWidget.h deleted file mode 100644 index 194b83d3a60..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/PieWidget.h +++ /dev/null @@ -1,88 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ - -#pragma once -#include -#include -#include - - -#include -#include - -namespace sofa::gui::qt -{ - -struct dataTime -{ - dataTime(double t, - std::string n, - std::string ty=std::string(),std::string address=std::string()):time(t), name(n), type(ty), ptr(address) {} - bool operator== (const dataTime& other) - { - if (ptr.empty()) return name == other.name; - else return ptr == other.ptr; - } - double time; - std::string name; - std::string type; - std::string ptr; -}; - - -class PieWidget: public QWidget -{ -public: - - PieWidget(QWidget *parent); - - void paintEvent( QPaintEvent* ); - - void setChart( std::vector< dataTime >& value, unsigned int s); - void clear(); - static type::Vec<3,int> getColor(int i); - static std::vector< type::Vec<3,int> > colorArray; -protected: - std::vector< dataTime > data; - - unsigned int selection; - double totalTime; - int sizePie; -}; - -class ChartsWidget: public QWidget -{ -public: - ChartsWidget(const std::string &name, QWidget *parent); - - void setChart( std::vector< dataTime >& value, unsigned int s); - void clear(); -protected: - - unsigned int selection; - - PieWidget* pie; - QTableWidget *table; -}; - - -} // namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/PluginManager.ui b/Sofa/GUI/Qt/src/sofa/gui/qt/PluginManager.ui deleted file mode 100644 index 6c5c28dc02d..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/PluginManager.ui +++ /dev/null @@ -1,186 +0,0 @@ - - - PluginManager - - - true - - - - 0 - 0 - 800 - 400 - - - - Plugin Manager - - - false - - - - - - - - Qt::Vertical - - - - - 416 - 0 - - - - 1 - - - true - - - true - - - 4 - - - - 1 - - - - - - - - - - - Description - - - false - - - - - - - true - - - false - - - true - - - - - - - - - true - - - - 1 - - - - - - - - - - - - - - - - &Add... - - - Alt+A - - - true - - - true - - - - - - - &Remove - - - Alt+R - - - true - - - - - - - Qt::Horizontal - - - QSizePolicy::Expanding - - - - 237 - 20 - - - - - - - - &Close - - - Alt+C - - - true - - - - - - - - - - - - buttonClose - clicked() - PluginManager - close() - - - 20 - 20 - - - 20 - 20 - - - - - diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/QDataDescriptionWidget.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/QDataDescriptionWidget.cpp deleted file mode 100644 index 491de6d3661..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/QDataDescriptionWidget.cpp +++ /dev/null @@ -1,193 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#include "QDataDescriptionWidget.h" - -#include -#include - -#include -#include -#include - - - -namespace sofa::gui::qt -{ -void QDataDescriptionWidget::addRow(QGridLayout* grid, const std::string& title, - const std::string& value, unsigned int row, - unsigned int /*minimumWidth*/) -{ - QLabel* titlew = new QLabel(QString(title.c_str())); - grid->addWidget(titlew, row, 0, Qt::AlignTop); - - QLabel* tmplabel = (new QLabel(QString(value.c_str()))); - tmplabel->setMinimumWidth(20); - tmplabel->setWordWrap(true); - tmplabel->setAlignment(Qt::AlignTop); - tmplabel->setSizePolicy(QSizePolicy::MinimumExpanding, - QSizePolicy::MinimumExpanding); - grid->addWidget(tmplabel, row, 1, Qt::AlignTop); -} - -void QDataDescriptionWidget::addRowHyperLink(QGridLayout* grid, - const std::string& title, const std::string& value, unsigned int row, - unsigned int minimumWidth) -{ - SOFA_UNUSED(minimumWidth); - QLabel* titlew = new QLabel(QString(title.c_str())); - grid->addWidget(titlew, row, 0, Qt::AlignTop); - - QLabel* tmplabel = (new QLabel(QString(value.c_str()))); - tmplabel->setMinimumWidth(20); - tmplabel->setWordWrap(true); - tmplabel->setAlignment(Qt::AlignTop); - tmplabel->setSizePolicy(QSizePolicy::MinimumExpanding, - QSizePolicy::MinimumExpanding); - tmplabel->setTextFormat(Qt::RichText); - tmplabel->setTextInteractionFlags(Qt::TextBrowserInteraction); - tmplabel->setOpenExternalLinks(true); - grid->addWidget(tmplabel, row, 1, Qt::AlignTop); -} - -QDataDescriptionWidget::QDataDescriptionWidget(QWidget* parent, core::objectmodel::Base* object) - :QWidget(parent) -{ - - QVBoxLayout* tabLayout = new QVBoxLayout(this); - tabLayout->setContentsMargins(0,0,0,0); - tabLayout->setSpacing(1); - tabLayout->setObjectName("tabInfoLayout"); - - //Instance - { - QGroupBox *box = new QGroupBox(this); - tabLayout->addWidget(box); - QGridLayout* boxLayout = new QGridLayout(); - box->setLayout(boxLayout); - - box->setTitle(QString("Instance")); - - addRow(boxLayout, "Name", object->getName(), 0); - addRow(boxLayout, "Class", object->getClassName(), 1); - - const std::string namespacename = sofa::helper::NameDecoder::decodeNamespaceName(typeid(*object)); - - int nextRow = 2; - if (!namespacename.empty()) - { - addRow(boxLayout, "Namespace", namespacename, nextRow, 20); - nextRow++; - } - if (!object->getTemplateName().empty()) - { - addRow(boxLayout, "Template", object->getTemplateName(), nextRow, 20); - nextRow++; - } - - const core::objectmodel::BaseNode* node = object->toBaseNode(); // Node - if (node && node->getNbParents()>1) // MultiNode - { - addRow(boxLayout, "Path", node->getPathName(), nextRow, 20); - nextRow++; - } - - tabLayout->addWidget( box ); - } - - - //Class description - core::ObjectFactory::ClassEntry entry = core::ObjectFactory::getInstance()->getEntry(object->getClassName()); - if (! entry.creatorMap.empty()) - { - QGroupBox *box = new QGroupBox(this); - tabLayout->addWidget(box); - QGridLayout* boxLayout = new QGridLayout(); - box->setLayout(boxLayout); - box->setTitle(QString("Class")); - - int nextRow = 0; - if (!entry.description.empty() && entry.description != std::string("TODO")) - { - addRow(boxLayout, "Description", entry.description, nextRow, 20); - nextRow++; - } - if (!entry.documentationURL.empty() && entry.documentationURL != std::string("TODO")) - { - const std::string textURL = "
    " + entry.documentationURL + ""; - addRowHyperLink(boxLayout, "Documentation URL", textURL, nextRow, 20); - nextRow++; - } - const core::ObjectFactory::CreatorMap::iterator it = entry.creatorMap.find(object->getTemplateName()); - if (it != entry.creatorMap.end() && *it->second->getTarget()) - { - addRow(boxLayout, "Provided by",it->second->getTarget(), nextRow, 20); - nextRow++; - } - - if (!entry.authors.empty() && entry.authors != std::string("TODO")) - { - addRow(boxLayout, "Authors", entry.authors, nextRow, 20); - nextRow++; - } - if (!entry.license.empty() && entry.license != std::string("TODO")) - { - addRow(boxLayout, "License", entry.license, nextRow, 20); - nextRow++; - } - tabLayout->addWidget( box ); - } - - - - //Extra description - std::vector selecteddatum ; - for(sofa::core::objectmodel::BaseData* datafield : object->getDataFields()) - { - if( datafield->getGroup() == "Infos" ) - selecteddatum.push_back(datafield) ; - } - - if(!selecteddatum.empty()) - { - QGroupBox *box = new QGroupBox(this); - tabLayout->addWidget(box); - QGridLayout* boxLayout = new QGridLayout(); - - box->setLayout(boxLayout); - - box->setTitle(QString("Extra information")); - - unsigned int row = 0; - for(const auto& data : selecteddatum) - { - addRow(boxLayout, data->getName(), data->getValueString(), row++); - } - } - - - tabLayout->addStretch(); -} - - - - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/QDataDescriptionWidget.h b/Sofa/GUI/Qt/src/sofa/gui/qt/QDataDescriptionWidget.h deleted file mode 100644 index cd4f4669897..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/QDataDescriptionWidget.h +++ /dev/null @@ -1,51 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include -#include - -#include -#include -#include -#include - - - -namespace sofa::gui::qt -{ - -struct ModifyObjectFlags; -class SOFA_GUI_QT_API QDataDescriptionWidget : public QWidget -{ - Q_OBJECT -public: - QDataDescriptionWidget(QWidget* parent, core::objectmodel::Base* object); - - void addRow(QGridLayout* grid, const std::string& title, - const std::string& value, unsigned int row, unsigned int minimumWidth =0); - - void addRowHyperLink(QGridLayout* grid, const std::string& title, - const std::string& value, unsigned int row, unsigned int minimumWidth =0); -}; - - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/QDisplayDataWidget.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/QDisplayDataWidget.cpp deleted file mode 100644 index f34018ffb5e..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/QDisplayDataWidget.cpp +++ /dev/null @@ -1,351 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#include "QDisplayDataWidget.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#define TEXTSIZE_THRESHOLD 45 - -using namespace sofa::core::objectmodel; - -namespace sofa::gui::qt -{ - -QDisplayDataWidget::QDisplayDataWidget(QWidget* parent, - BaseData* data, - const ModifyObjectFlags& flags):QGroupBox(), - data_(data), - flags_(flags), - datainfowidget_(nullptr), - datawidget_(nullptr), - numWidgets_(0) - -{ - gridLayout_ = new QGridLayout(); - this->setLayout(gridLayout_); - - parent->layout()->addWidget(this); - - setAutoFillBackground(true); - - if(data_ == nullptr) - return; - - const std::string& help = data_->getHelp().c_str(); - const std::string valuetype = data_->getValueTypeString(); - const std::string& ownerClass = data_->getOwner()->getClassName(); - const std::string defaultValue = data_->getDefaultValueString(); - std::stringstream s; - - s << (!help.empty() ? help : "< No help found >") - << "\nData type: " << valuetype - << "\nOwner: " << (!ownerClass.empty() ? ownerClass : "< No owner found >"); - - if (!defaultValue.empty()) - { - s << "\nDefault value: " << defaultValue; - } - - const std::string fullHelpText = s.str(); - setToolTip(fullHelpText.c_str()); - datainfowidget_ = new QDisplayDataInfoWidget(this,fullHelpText,data_, - flags.LINKPATH_MODIFIABLE_FLAG, flags_); - datainfowidget_->setContentsMargins(0, 0, 0, 0); - datainfowidget_->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - gridLayout_->addWidget(datainfowidget_, 0,0); - numWidgets_ += 1; - - - DataWidget::CreatorArgument dwarg; - dwarg.name = data_->getName(); - dwarg.data = data_; - dwarg.parent = this; - dwarg.readOnly = (data_->isReadOnly() && flags.READONLY_FLAG); - - if( dynamic_cast(data_) != nullptr ) - { - // a bit of a hack for DataFileName widgets. - // A custom widget is used by default if we run this code from the Modeler - - std::string widgetName=data_->getWidget(); - if( widgetName.empty() && flags.MODELER_FLAG ) - { - data_->setWidget("widget_filename"); - } - } - - datawidget_= DataWidget::CreateDataWidget(dwarg); - - if (datawidget_ == nullptr) - { - datawidget_ = new QDataSimpleEdit(this,dwarg.data->getName().c_str(), dwarg.data); - datawidget_->createWidgets(); - datawidget_->setDataReadOnly(dwarg.readOnly); - assert(datawidget_ != nullptr); - } - - if(datawidget_->layout()) - { - datawidget_->layout()->setAlignment(Qt::AlignCenter); - datawidget_->layout()->setContentsMargins(0, 0, 0, 0); - } - - datawidget_->setContentsMargins(0, 16, 0, 0); - datawidget_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); - this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); - - datawidget_->setToolTip(s.str().c_str()); - - numWidgets_ += datawidget_->sizeWidget(); - connect(datawidget_,SIGNAL(WidgetDirty(bool)), this, SIGNAL ( WidgetDirty(bool) ) ); - connect(this, SIGNAL( WidgetUpdate() ), datawidget_, SLOT( updateWidgetValue() ) ); - connect(this, SIGNAL( DataUpdate() ), datawidget_, SLOT(updateDataValue() ) ); - connect(datawidget_,SIGNAL(DataOwnerDirty(bool)),this,SIGNAL(DataOwnerDirty(bool)) ); - connect(datawidget_,SIGNAL(dataValueChanged(QString)),this,SIGNAL(dataValueChanged(QString)) ); - - if(flags.PROPERTY_WIDGET_FLAG) - { - QWidget* refreshWidget = new QWidget(this); - refreshWidget->setFixedSize(QSize(16, 16)); - QPushButton *refresh = new QPushButton(RefreshIcon(), "", refreshWidget); - refresh->setHidden(true); - refresh->setFixedSize(QSize(16, 16)); - refresh->setContentsMargins(0, 0, 0, 0); - - ++numWidgets_; - - { - connect(datawidget_,SIGNAL(WidgetDirty(bool)), refresh, SLOT ( setVisible(bool) ) ); - - if(datainfowidget_ && flags.LINKPATH_MODIFIABLE_FLAG) - { - connect(datainfowidget_,SIGNAL(WidgetDirty()), refresh, SLOT ( show() ) ); - connect(refresh, SIGNAL(clicked()), datainfowidget_, SLOT(linkEdited())); - } - - connect(refresh, SIGNAL(clicked()), this, SLOT(UpdateData())); - connect(refresh, SIGNAL(clicked(bool)), refresh, SLOT(setVisible(bool))); - } - - setStyleSheet("QGroupBox{border:0;}"); - setContentsMargins(0, 0, 0, 0); - } - else - { - setTitle(data_->getName().c_str()); - setContentsMargins(0,0,0,0); - } - gridLayout_->setContentsMargins(10,10,10,10); - gridLayout_->addWidget(datawidget_, 0, 1); - gridLayout_->setAlignment(datawidget_, Qt::AlignVCenter); -} - -void QDisplayDataWidget::UpdateData() -{ - emit DataUpdate(); -} - -void QDisplayDataWidget::UpdateWidgets() -{ - emit WidgetUpdate(); -} - -QDataSimpleEdit::QDataSimpleEdit(QWidget* parent, const char* name, BaseData* data): - DataWidget(parent,name,data) -{ -} -bool QDataSimpleEdit::createWidgets() -{ - const QString str = QString( getBaseData()->getValueString().c_str() ); - QLayout* layout = new QHBoxLayout(this); - if( str.length() > TEXTSIZE_THRESHOLD ) - { - innerWidget_.type = TEXTEDIT; - innerWidget_.widget.textEdit = new QTextEdit(this); innerWidget_.widget.textEdit->setText(str); - connect(innerWidget_.widget.textEdit , SIGNAL( textChanged() ), this, SLOT ( setWidgetDirty() ) ); - layout->addWidget(innerWidget_.widget.textEdit); - } - else - { - innerWidget_.type = LINEEDIT; - innerWidget_.widget.lineEdit = new QLineEdit(this); - innerWidget_.widget.lineEdit->setText(str); - connect( innerWidget_.widget.lineEdit, SIGNAL(textChanged(const QString&)), this, SLOT( setWidgetDirty() ) ); - layout->addWidget(innerWidget_.widget.lineEdit); - } - - - - - return true; -} - -void QDataSimpleEdit::setDataReadOnly(bool readOnly) -{ - if(innerWidget_.type == TEXTEDIT) - { - innerWidget_.widget.textEdit->setReadOnly(readOnly); - } - else if(innerWidget_.type == LINEEDIT) - { - innerWidget_.widget.lineEdit->setReadOnly(readOnly); - } -} - -void QDataSimpleEdit::readFromData() -{ - const QString str = QString( getBaseData()->getValueString().c_str() ); - if(innerWidget_.type == TEXTEDIT) - { - innerWidget_.widget.textEdit->setText(str); - } - else if(innerWidget_.type == LINEEDIT) - { - innerWidget_.widget.lineEdit->setText(str); - } -} - -void QDataSimpleEdit::writeToData() -{ - if(!getBaseData()) - return ; - - if(getBaseData()->isReadOnly()) - return ; - std::string value; - if( innerWidget_.type == TEXTEDIT) - { - value = innerWidget_.widget.textEdit->toPlainText().toStdString(); - } - else if( innerWidget_.type == LINEEDIT) - { - value = innerWidget_.widget.lineEdit->text().toStdString(); - } -} - -/* QPoissonRatioWidget */ -QPoissonRatioWidget::QPoissonRatioWidget(QWidget * parent, const char * name, sofa::core::objectmodel::Data *data) - :TDataWidget(parent,name,data) -{ - -} - - -bool QPoissonRatioWidget::createWidgets() -{ - QGridLayout* layout = new QGridLayout(this /*,2,3 */); - - lineEdit = new QLineEdit(this); - lineEdit->setText(QString("-1.0")); - lineEdit->setMaximumSize(lineEdit->size()); - lineEdit->setAlignment(Qt::AlignHCenter); - - lineEdit->setValidator(new QDoubleValidator(0.0,0.5,2,this)); - - layout->addWidget(lineEdit,0,1,Qt::AlignHCenter); - QLabel* min = new QLabel(this); - min->setText(QString("0.0")); - min->setMaximumSize( min->sizeHint() ); - layout->addWidget(min,1,0,Qt::AlignHCenter); - - slider = new QSlider(Qt::Horizontal, this); - slider->setRange(0,50); //max times 10 at the power 2 (2 digits after ".") - slider->setTickPosition(QSlider::TicksBelow); - slider->setTickInterval(5); - layout->addWidget(slider,1,1,Qt::AlignHCenter); - - QLabel* max = new QLabel(this); - max->setText(QString("0.5")); - max->setMaximumSize ( max->sizeHint() ); - - layout->addWidget(max,1,2,Qt::AlignHCenter); - - // synchronization between qslider and qlineedit - connect(slider, SIGNAL( valueChanged(int) ), this, SLOT ( changeLineEditValue() ) ); - connect(slider, SIGNAL( sliderReleased() ), this, SLOT ( changeLineEditValue() ) ); - connect(lineEdit, SIGNAL( textChanged(const QString&) ), this, SLOT (changeSliderValue() ) ); - - // synchronization between the widgets and the modify object dialog box - connect(lineEdit, SIGNAL( textChanged(const QString&) ), this, SLOT( setWidgetDirty() ) ); - connect(slider, SIGNAL( sliderReleased() ), this, SLOT ( setWidgetDirty() ) ); - connect(slider, SIGNAL( valueChanged(int) ), this, SLOT ( setWidgetDirty() ) ); - - - return true; -} - -void QPoissonRatioWidget::setDataReadOnly(bool readOnly) -{ - lineEdit->setReadOnly(readOnly); - slider->setEnabled(!readOnly); -} - -void QPoissonRatioWidget::readFromData() -{ - const double value = this->getData()->getValue(); - QString str; - str.setNum(value); - lineEdit->setText(str); - changeSliderValue(); -} - -void QPoissonRatioWidget::writeToData() -{ - bool ok; - const double d = lineEdit->text().toDouble(&ok); - if(ok) - { - this->getData()->setValue(d); - } -} - -void QPoissonRatioWidget::changeLineEditValue() -{ - const int v = slider->value(); - const double db = (double)v / 100.; - QString str; - str.setNum(db); - lineEdit->setText(str); -} - -void QPoissonRatioWidget::changeSliderValue() -{ - bool ok; - const double v = lineEdit->text().toDouble(&ok); - if(ok) - { - slider->setValue( (int)(v*100.) ); - } -} - -helper::Creator DWClass_Poissonratio("poissonRatio",false); - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/QDisplayDataWidget.h b/Sofa/GUI/Qt/src/sofa/gui/qt/QDisplayDataWidget.h deleted file mode 100644 index 4b47632a40b..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/QDisplayDataWidget.h +++ /dev/null @@ -1,131 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include "DataWidget.h" -#include "ModifyObject.h" - -#include -#include -#include -#include -#include - -namespace sofa::gui::qt -{ - -class DataWidget; -class QDisplayDataInfoWidget; -struct ModifyObjectFlags; - -class SOFA_GUI_QT_API QDisplayDataWidget : public QGroupBox -{ - Q_OBJECT -public: - QDisplayDataWidget(QWidget* parent, - core::objectmodel::BaseData* data, - const ModifyObjectFlags& flags); - unsigned int getNumWidgets() const { return numWidgets_;} - - ModifyObjectFlags flag() {return flags_;} - -public slots: - void UpdateData(); //QWidgets ---> BaseData - void UpdateWidgets(); //BaseData ---> QWidget -signals: - void WidgetDirty(bool); - void WidgetUpdate(); - void DataUpdate(); - void DataOwnerDirty(bool); - void dataValueChanged(QString); - -protected: - static QIcon& RefreshIcon() - { - static QIcon icon; - if(icon.isNull()) - { - std::string filename = "textures/refresh.png"; - sofa::helper::system::DataRepository.findFile(filename); - icon = QIcon(filename.c_str()); - } - return icon; - } - -protected: - core::objectmodel::BaseData* data_; - ModifyObjectFlags flags_; - QDisplayDataInfoWidget* datainfowidget_; - DataWidget* datawidget_; - unsigned int numWidgets_; - QGridLayout* gridLayout_; -}; - - - -class SOFA_GUI_QT_API QDataSimpleEdit : public DataWidget -{ - Q_OBJECT - typedef enum QEditType { TEXTEDIT, LINEEDIT } QEditType; - typedef union QEditWidgetPtr - { - QLineEdit* lineEdit; - QTextEdit* textEdit; - } QEditWidgetPtr; - - typedef struct QSimpleEdit - { - QEditType type; - QEditWidgetPtr widget; - } QSimpleEdit; -public : - QDataSimpleEdit(QWidget*, const char* name, core::objectmodel::BaseData*); - unsigned int numColumnWidget() override {return 3;} - unsigned int sizeWidget() override {return 6;} - bool createWidgets() override; - void setDataReadOnly(bool readOnly) override; -protected: - void readFromData() override; - void writeToData() override; - QSimpleEdit innerWidget_; -}; - -class SOFA_GUI_QT_API QPoissonRatioWidget : public TDataWidget -{ - Q_OBJECT -public : - QPoissonRatioWidget(QWidget*, const char*, core::objectmodel::Data*); - virtual bool createWidgets(); - virtual void setDataReadOnly(bool readOnly); - -protected slots : - void changeLineEditValue(); - void changeSliderValue(); - -protected: - virtual void readFromData(); - virtual void writeToData(); - QSlider* slider; - QLineEdit* lineEdit; - -}; - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/QDisplayLinkWidget.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/QDisplayLinkWidget.cpp deleted file mode 100644 index 8eab2ab86cf..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/QDisplayLinkWidget.cpp +++ /dev/null @@ -1,204 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#include "QDisplayLinkWidget.h" -#include "ModifyObject.h" - -#include -#include -#include -#include -#include - -#define TEXTSIZE_THRESHOLD 45 - -namespace sofa::gui::qt -{ - -QDisplayLinkWidget::QDisplayLinkWidget(QWidget* parent, - BaseLink* link, - const ModifyObjectFlags& flags) - : QGroupBox(parent), - link_(link), - linkinfowidget_(nullptr), - linkwidget_(nullptr), - numWidgets_(0) -{ - if(link_ == nullptr) - { - return; - } - - gridLayout_ = new QHBoxLayout(); - this->setLayout(gridLayout_); - - parent->layout()->addWidget(this); - parent->setContentsMargins(0,0,0,0); - - setToolTip(QString::fromStdString(link->getHelp())); - - LinkWidget::CreatorArgument dwarg; - dwarg.name = link_->getName(); - dwarg.link = link_; - dwarg.parent = this; - dwarg.readOnly = (!link_->storePath() && flags.READONLY_FLAG); - - linkwidget_= LinkWidget::CreateLinkWidget(dwarg); - - if (linkwidget_ == nullptr) - { - linkwidget_ = new QLinkSimpleEdit(this,dwarg.link->getName().c_str(), dwarg.link); - linkwidget_->createWidgets(); - linkwidget_->setEnabled(!(dwarg.readOnly)); - assert(linkwidget_ != nullptr); - } - - if(linkwidget_->layout()) - { - linkwidget_->layout()->setAlignment(Qt::AlignCenter); - linkwidget_->layout()->setContentsMargins(2, 2, 2, 2); - } - - linkwidget_->setContentsMargins(0, 10, 0, 0); - linkwidget_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); - - const std::string valuetype = link_->getValueTypeString(); - if (!valuetype.empty()) - linkwidget_->setToolTip(QString::fromStdString(valuetype)); - - numWidgets_ += linkwidget_->sizeWidget(); - connect(linkwidget_,SIGNAL(WidgetDirty(bool)), this, SIGNAL ( WidgetDirty(bool) ) ); - connect(this, SIGNAL( WidgetUpdate() ), linkwidget_, SLOT( updateWidgetValue() ) ); - connect(this, SIGNAL( LinkUpdate() ), linkwidget_, SLOT(setWidgetDirty() ) ); - connect(this, SIGNAL( LinkUpdate() ), linkwidget_, SLOT(updateLinkValue() ) ); - connect(linkwidget_,SIGNAL(LinkOwnerDirty(bool)),this,SIGNAL(LinkOwnerDirty(bool)) ); - - if(flags.PROPERTY_WIDGET_FLAG) - { - if(!dwarg.readOnly) - { - QWidget* refreshWidget = new QWidget(this); - refreshWidget->setFixedSize(QSize(16, 16)); - QPushButton *refresh = new QPushButton(RefreshIcon(), "", refreshWidget); - refresh->setHidden(true); - refresh->setFixedSize(QSize(16, 16)); - refresh->setContentsMargins(0, 0, 0, 0); - - ++numWidgets_; - - connect(linkwidget_,SIGNAL(LinkBeingChanged()), refresh, SLOT ( show() ) ); - connect(refresh, SIGNAL(clicked()), this, SLOT(UpdateLink())); - connect(refresh, SIGNAL(clicked(bool)), refresh, SLOT(setVisible(bool))); - } - - setStyleSheet("QGroupBox{border:0;}"); - setContentsMargins(0, 0, 0, 0); - } - else - { - setTitle(QString::fromStdString(link_->getName())); - setContentsMargins(2,2,4,4); - } - gridLayout_->addWidget(linkwidget_); -} - -void QDisplayLinkWidget::UpdateLink() -{ - emit LinkUpdate(); -} - -void QDisplayLinkWidget::UpdateWidgets() -{ - emit WidgetUpdate(); -} - -QLinkSimpleEdit::QLinkSimpleEdit(QWidget* parent, const char* name, BaseLink* link) - : LinkWidget(parent,name,link) -{ -} - -bool QLinkSimpleEdit::createWidgets() -{ - const QString str = QString::fromStdString(getBaseLink()->getValueString()); - QLayout* layout = new QHBoxLayout(this); - if( str.length() > TEXTSIZE_THRESHOLD ) - { - innerWidget_.type = TEXTEDIT; - innerWidget_.widget.textEdit = new QTextEdit(this); - innerWidget_.widget.textEdit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); - innerWidget_.widget.textEdit->setContentsMargins(0, 0, 0, 0); - innerWidget_.widget.textEdit->setText(str); - innerWidget_.widget.textEdit->setFixedHeight(60); - connect(innerWidget_.widget.textEdit , SIGNAL( textChanged() ), this, SIGNAL(LinkBeingChanged())); - connect(innerWidget_.widget.textEdit , SIGNAL( textChanged() ), this, SLOT(update())); - layout->addWidget(innerWidget_.widget.textEdit); - } - else - { - innerWidget_.type = LINEEDIT; - innerWidget_.widget.lineEdit = new QLineEdit(this); - innerWidget_.widget.lineEdit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); - innerWidget_.widget.lineEdit->setContentsMargins(0, 0, 0, 0); - innerWidget_.widget.lineEdit->setText(str); - connect( innerWidget_.widget.lineEdit, SIGNAL(textChanged(const QString&)), this, SIGNAL(LinkBeingChanged())); - connect( innerWidget_.widget.lineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(update())); - layout->addWidget(innerWidget_.widget.lineEdit); - } - layout->setContentsMargins(0, 0, 0, 0); - layout->setSpacing(0); - - return true; -} - -void QLinkSimpleEdit::readFromLink() -{ - const QString str = QString::fromStdString(getBaseLink()->getValueString()); - if(innerWidget_.type == TEXTEDIT) - { - innerWidget_.widget.textEdit->setText(str); - } - else if(innerWidget_.type == LINEEDIT) - { - innerWidget_.widget.lineEdit->setText(str); - } -} - -void QLinkSimpleEdit::writeToLink() -{ - if(!getBaseLink()) - return ; - - if(getBaseLink()->isReadOnly()) - return; - - std::string value; - if( innerWidget_.type == TEXTEDIT) - { - value = innerWidget_.widget.textEdit->toPlainText().toStdString(); - } - else if( innerWidget_.type == LINEEDIT) - { - value = innerWidget_.widget.lineEdit->text().toStdString(); - } - getBaseLink()->read(value); -} - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/QDisplayLinkWidget.h b/Sofa/GUI/Qt/src/sofa/gui/qt/QDisplayLinkWidget.h deleted file mode 100644 index 9b24fcb98d8..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/QDisplayLinkWidget.h +++ /dev/null @@ -1,108 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include "LinkWidget.h" -#include "ModifyObject.h" - -#include -#include -#include -#include -#include -#include - -namespace sofa::gui::qt -{ - -class LinkWidget; -class QDisplayLinkInfoWidget; -struct ModifyObjectFlags; - -class SOFA_GUI_QT_API QDisplayLinkWidget : public QGroupBox -{ - Q_OBJECT -public: - QDisplayLinkWidget(QWidget* parent, - core::objectmodel::BaseLink* link, - const ModifyObjectFlags& flags); - unsigned int getNumWidgets() const { return numWidgets_;}; - -public slots: - void UpdateLink(); //QWidgets ---> BaseLink - void UpdateWidgets(); //BaseLink ---> QWidget -signals: - void WidgetDirty(bool); - void WidgetUpdate(); - void LinkUpdate(); - void LinkOwnerDirty(bool); - -protected: - static QIcon& RefreshIcon() - { - static QIcon icon; - if(icon.isNull()) - { - std::string filename = "textures/refresh.png"; - sofa::helper::system::DataRepository.findFile(filename); - icon = QIcon(filename.c_str()); - } - return icon; - } - -protected: - core::objectmodel::BaseLink* link_; - QDisplayLinkInfoWidget* linkinfowidget_; - LinkWidget* linkwidget_; - unsigned int numWidgets_; - QHBoxLayout* gridLayout_; - -}; - - - -class SOFA_GUI_QT_API QLinkSimpleEdit : public LinkWidget -{ - Q_OBJECT - typedef enum QEditType { TEXTEDIT, LINEEDIT } QEditType; - typedef union QEditWidgetPtr - { - QLineEdit* lineEdit; - QTextEdit* textEdit; - } QEditWidgetPtr; - - typedef struct QSimpleEdit - { - QEditType type; - QEditWidgetPtr widget; - } QSimpleEdit; -public : - QLinkSimpleEdit(QWidget*, const char* name, core::objectmodel::BaseLink*); - unsigned int numColumnWidget() override {return 3;} - unsigned int sizeWidget() override {return 1;} - bool createWidgets() override; -protected: - void readFromLink() override; - void writeToLink() override; - QSimpleEdit innerWidget_; -}; - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/QDisplayPropertyWidget.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/QDisplayPropertyWidget.cpp deleted file mode 100644 index 5caa0d355fc..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/QDisplayPropertyWidget.cpp +++ /dev/null @@ -1,668 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ - -#include "QDisplayPropertyWidget.h" -#include "ModifyObject.h" -#include "DataWidget.h" -#include "QDisplayDataWidget.h" -#include "QDisplayLinkWidget.h" -#include "QDataDescriptionWidget.h" -#include "QTabulationModifyObject.h" -#include - -#include -using sofa::helper::logging::Message ; - -namespace sofa::gui::qt -{ - -QDisplayTreeItemWidget::QDisplayTreeItemWidget(QWidget* parent, QTreeWidgetItem* item) : QWidget(parent) - , treeWidgetItem(item) -{ - -} - -QDisplayTreeItemWidget::~QDisplayTreeItemWidget() -{ - -} - -void QDisplayTreeItemWidget::updateDirtyWidget() -{ - const QObjectList& parentList = children(); - for(QObjectList::const_iterator itParent = parentList.begin(); itParent != parentList.end(); ++itParent) - { - QWidget* parentWidget = dynamic_cast(*itParent); - if(parentWidget) - { - const QObjectList& childList = parentWidget->children(); - for(QObjectList::const_iterator itChild = childList.begin(); itChild != childList.end(); ++itChild) - { - QWidget* childWidget = dynamic_cast(*itChild); - if(childWidget) - { - childWidget->adjustSize(); - childWidget->setHidden(false); - } - } - parentWidget->adjustSize(); - parentWidget->setHidden(false); - } - } - - adjustSize(); - setHidden(false); - treeWidgetItem->setHidden(false); -} - -QDisplayPropertyWidget::QDisplayPropertyWidget(const ModifyObjectFlags& modifyFlags, QWidget* parent) : QTreeWidget(parent) - , objects() - , pinIcon() - , modifyObjectFlags(modifyFlags) -{ - modifyObjectFlags.PROPERTY_WIDGET_FLAG = true; - - std::string filename = "textures/pin.png"; - sofa::helper::system::DataRepository.findFile(filename); - pinIcon = QIcon(QString::fromStdString(filename)); - - setColumnCount(2); - //setIndentation(10); - - headerItem()->setText(0, QString("Property")); - headerItem()->setText(1, QString("Value")); - setSelectionMode(QAbstractItemView::NoSelection); - //setSelectionBehavior(QAbstractItemView::SelectItems); - setDragEnabled(false); - setAcceptDrops(false); - setDropIndicatorShown(false); - //setDragDropMode(QAbstractItemView::InternalMove); - setIndentation(0); - - setFocusPolicy(Qt::NoFocus); - setAutoFillBackground(false); -} - -QDisplayPropertyWidget::~QDisplayPropertyWidget() -{ - -} - -void QDisplayPropertyWidget::addComponent(const QString& component, core::objectmodel::Base* base, QTreeWidgetItem* listItem, bool clear) -{ - if(clear) - this->clear(); - - // return now if the component to add is empty or if it is already in the tree - if(component.isEmpty() || !base || findComponent(component)) - return; - - const sofa::core::objectmodel::Base::VecData& fields = base->getDataFields(); - - // return now if the component has no field - if(fields.empty()) - return; - - // finally, add the group - QTreeWidgetItem *componentItem = new QTreeWidgetItem(this); - QFont *font = new QFont(); - font->setBold(true); - componentItem->setFont(0, *font); - componentItem->setText(0, component); - QPushButton* pin = new QPushButton(this); - pin->setFixedSize(QSize(18, 18)); - pin->setCheckable(true); - pin->setIcon(pinIcon); - setItemWidget(componentItem, 1, pin); - componentItem->setExpanded(true); - const QBrush *backgroundBrush = new QBrush(QColor(20, 20, 20)); - const QBrush *foregroundBrush = new QBrush(QColor(255, 255, 255)); - componentItem->setBackground(0, *backgroundBrush); - componentItem->setForeground(0, *foregroundBrush); - componentItem->setTextAlignment(0, Qt::AlignLeft); - componentItem->setBackground(1, *backgroundBrush); - componentItem->setForeground(1, *foregroundBrush); - componentItem->setTextAlignment(1, Qt::AlignRight); - - objects[componentItem] = std::pair(base, listItem); - - // add data - for(sofa::core::objectmodel::Base::VecData::const_iterator it = fields.begin(); it != fields.end(); ++it) - { - core::objectmodel::BaseData *data = *it; - - // ignore unnamed data - if(data->getName().empty()) - continue; - - // for each data of the current object we determine where it belongs - QString group = QString::fromStdString(data->getGroup()); - - // use the default group if data does not belong to any group - if(group.isEmpty()) - group = DefaultDataGroup(); - - // finally, add the data - addData(component, group, data); - } - - - // add links - const sofa::core::objectmodel::Base::VecLink& links = base->getLinks(); - for(sofa::core::objectmodel::Base::VecLink::const_iterator it = links.begin(); it != links.end(); ++it) - { - core::objectmodel::BaseLink *link = *it; - - // ignore unnamed link - if(link->getName().empty()) - continue; - - if(!link->storePath() && 0 == link->getSize()) - continue; - - // use the default link group - QString group = DefaultLinkGroup(); - - // finally, add the data - addLink(component, group, link); - } - - // add info - { - // use the default info group - const QString group = DefaultInfoGroup(); - - setDescription(component, group, base); - } - - const bool notImplementedYet = false; - if(!notImplementedYet) - // add console - { - // use the default info group - const QString group = DefaultLogGroup(); - - setConsoleOutput(component, group, base); - } -} - -void QDisplayPropertyWidget::addGroup(const QString& component, const QString& group) -{ - // return now if the component does not exist - QTreeWidgetItem *componentItem = nullptr; - componentItem = findComponent(component); - if(!componentItem) - return; - - // return now if the group component already exist - QTreeWidgetItem *groupItem = nullptr; - groupItem = findGroup(component, group); - if(groupItem) - return; - - // assign the default label if group is an empty string - QString groupLabel = group; - if(group.isEmpty()) - groupLabel = DefaultDataGroup(); - - // finally, add the group - groupItem = new QTreeWidgetItem(componentItem); - QFont *font = new QFont(); - font->setBold(true); - groupItem->setFont(0, *font); - groupItem->setText(0, groupLabel); - groupItem->setExpanded(true); - const QBrush *backgroundBrush = new QBrush(QColor(160, 160, 160)); - const QBrush *foregroundBrush = new QBrush(QColor(255, 255, 255)); - groupItem->setBackground(0, *backgroundBrush); - groupItem->setForeground(0, *foregroundBrush); - groupItem->setBackground(1, *backgroundBrush); - groupItem->setForeground(1, *foregroundBrush); -} - -void QDisplayPropertyWidget::addData(const QString& component, const QString& group, sofa::core::objectmodel::BaseData *data) -{ - if(!data || !data->isDisplayed()) - return; - - addGroup(component, group); - QTreeWidgetItem *groupItem = nullptr; - groupItem = findGroup(component, group); - - if(!groupItem) - return; - - QTreeWidgetItem *dataItem = new QTreeWidgetItem(groupItem); - const QBrush *brush = nullptr; - if(groupItem->childCount() % 2 == 0) - brush = new QBrush(QColor(255, 255, 191)); - else - brush = new QBrush(QColor(255, 255, 222)); - dataItem->setBackground(0, *brush); - dataItem->setBackground(1, *brush); - - data->setDisplayed(true); - - QDisplayTreeItemWidget *widget = new QDisplayTreeItemWidget(this, dataItem); - QHBoxLayout *layout = new QHBoxLayout(widget); - - dataItem->setText(0, QString::fromStdString(data->getName())); - dataItem->setToolTip(0, QString::fromStdString(data->getHelp())); - QDisplayDataWidget *displayDataWidget = new QDisplayDataWidget(widget, data, modifyObjectFlags); - layout->addWidget(displayDataWidget); - - connect(displayDataWidget, SIGNAL(WidgetDirty(bool)), widget, SLOT(updateDirtyWidget())); - - widget->setContentsMargins(0, 0, 0, 0); - if(widget->layout()) - { - widget->layout()->setContentsMargins(0, 0, 0, 0); - widget->layout()->setSpacing(0); - } - setItemWidget(dataItem, 1, widget); - dataItem->setToolTip(1, QString::fromStdString(data->getHelp())); -} - -void QDisplayPropertyWidget::addLink(const QString& component, const QString& group, sofa::core::objectmodel::BaseLink *link) -{ - if(!link) - return; - - addGroup(component, group); - QTreeWidgetItem *groupItem = nullptr; - groupItem = findGroup(component, group); - - if(!groupItem) - return; - - QTreeWidgetItem *linkItem = new QTreeWidgetItem(groupItem); - const QBrush *brush = nullptr; - if(groupItem->childCount() % 2 == 0) - brush = new QBrush(QColor(255, 255, 191)); - else - brush = new QBrush(QColor(255, 255, 222)); - linkItem->setBackground(0, *brush); - linkItem->setBackground(1, *brush); - - QDisplayTreeItemWidget *widget = new QDisplayTreeItemWidget(this, linkItem); - QHBoxLayout *layout = new QHBoxLayout(widget); - - ModifyObjectFlags linkFlags = modifyObjectFlags; - linkFlags.READONLY_FLAG = true; - - linkItem->setText(0, QString::fromStdString(link->getName())); - linkItem->setToolTip(0, QString::fromStdString(link->getHelp())); - QDisplayLinkWidget *displayLinkWidget = new QDisplayLinkWidget(widget, link, linkFlags); - layout->addWidget(displayLinkWidget); - - connect(displayLinkWidget, SIGNAL(WidgetDirty(bool)), widget, SLOT(updateDirtyWidget())); - - widget->setContentsMargins(0, 0, 0, 0); - if(widget->layout()) - { - widget->layout()->setContentsMargins(0, 0, 0, 0); - widget->layout()->setSpacing(0); - } - setItemWidget(linkItem, 1, widget); - linkItem->setToolTip(1, QString::fromStdString(link->getHelp())); -} - -void QDisplayPropertyWidget::setDescription(const QString& component, const QString& group, sofa::core::objectmodel::Base *base) -{ - if(!base) - return; - - addGroup(component, group); - QTreeWidgetItem *groupItem = nullptr; - groupItem = findGroup(component, group); - - if(!groupItem) - return; - - const QBrush *brush = nullptr; - QFont categoryFont; - categoryFont.setBold(true); - - // Instance - QTreeWidgetItem *instanceItem = new QTreeWidgetItem(groupItem); - if(groupItem->childCount() % 2 == 0) - brush = new QBrush(QColor(255, 255, 191)); - else - brush = new QBrush(QColor(255, 255, 222)); - - instanceItem->setBackground(0, *brush); - instanceItem->setBackground(1, *brush); - - instanceItem->setText(0, "Instance"); - instanceItem->setFont(0, categoryFont); - - { - addDescriptionItem(groupItem, "Name", QString::fromStdString(base->getName())); - - addDescriptionItem(groupItem, "Class", QString::fromStdString(base->getClassName())); - - const std::string namespacename = sofa::helper::NameDecoder::decodeNamespaceName(typeid(*base)); - if (!namespacename.empty()) - addDescriptionItem(groupItem, "Namespace", QString::fromStdString(namespacename)); - - if (!base->getTemplateName().empty()) - addDescriptionItem(groupItem, "Template", QString::fromStdString(base->getTemplateName())); - } - - // Class - core::ObjectFactory::ClassEntry entry = core::ObjectFactory::getInstance()->getEntry(base->getClassName()); - if(!entry.creatorMap.empty()) - { - QTreeWidgetItem *classItem = new QTreeWidgetItem(groupItem); - if(groupItem->childCount() % 2 == 0) - brush = new QBrush(QColor(255, 255, 191)); - else - brush = new QBrush(QColor(255, 255, 222)); - - classItem->setBackground(0, *brush); - classItem->setBackground(1, *brush); - - classItem->setText(0, "Class"); - classItem->setFont(0, categoryFont); - - if(!entry.description.empty() && std::string("TODO") != entry.description) - addDescriptionItem(groupItem, "Description", QString::fromStdString(entry.description)); - - const core::ObjectFactory::CreatorMap::iterator it = entry.creatorMap.find(base->getTemplateName()); - if(entry.creatorMap.end() != it && *it->second->getTarget()) - addDescriptionItem(groupItem, "Provided by", QString(it->second->getTarget())); - - if(!entry.authors.empty() && std::string("TODO") != entry.authors) - addDescriptionItem(groupItem, "Authors", QString::fromStdString(entry.authors)); - - if(!entry.license.empty() && std::string("TODO") != entry.license) - addDescriptionItem(groupItem, "License", QString::fromStdString(entry.license)); - } -} - -void QDisplayPropertyWidget::addDescriptionItem(QTreeWidgetItem *groupItem, const QString& name, const QString& description) -{ - QTreeWidgetItem *descriptionItem = new QTreeWidgetItem(groupItem); - - const QBrush *brush = nullptr; - if(groupItem->childCount() % 2 == 0) - brush = new QBrush(QColor(255, 255, 191)); - else - brush = new QBrush(QColor(255, 255, 222)); - - descriptionItem->setBackground(0, *brush); - descriptionItem->setBackground(1, *brush); - - descriptionItem->setText(0, name); - - QDisplayTreeItemWidget *widget = new QDisplayTreeItemWidget(this, descriptionItem); - new QLabel(description, widget); - setItemWidget(descriptionItem, 1, widget); -} - -void QDisplayPropertyWidget::setConsoleOutput(const QString& component, const QString& group, sofa::core::objectmodel::Base *base) -{ - if(base==nullptr) - return; - - const std::string warnings = base->getLoggedMessagesAsString( { Message::Warning, - Message::Error, - Message::Fatal } ); - - const std::string infos = base->getLoggedMessagesAsString( { Message::Info, - Message::Advice, - Message::Deprecated } ); - - if((infos.empty() && warnings.empty())) - return; - - addGroup(component, group); - QTreeWidgetItem *groupItem = nullptr; - groupItem = findGroup(component, group); - - if(!groupItem) - return; - - // log outputs - if(!infos.empty()) - { - QTreeWidgetItem *consoleItem = new QTreeWidgetItem(groupItem); - const QBrush *brush = nullptr; - if(groupItem->childCount() % 2 == 0) - brush = new QBrush(QColor(255, 255, 191)); - else - brush = new QBrush(QColor(255, 255, 222)); - consoleItem->setBackground(0, *brush); - consoleItem->setBackground(1, *brush); - - QDisplayTreeItemWidget *clearWidget = new QDisplayTreeItemWidget(this, consoleItem); - QVBoxLayout *clearLayout = new QVBoxLayout(clearWidget); - - QPushButton* clearButton = new QPushButton("Clear output", clearWidget); - clearButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); - clearButton->setFixedHeight(200); -#if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0)) - clearButton->setProperty("base", qVariantFromValue((void*) base)); -#else - clearButton->setProperty("base", QVariant::fromValue((void*) base)); -#endif - clearLayout->addWidget(clearButton); - - clearWidget->setContentsMargins(0, 0, 0, 0); - clearLayout->setContentsMargins(0, 0, 0, 0); - clearLayout->setSpacing(0); - - QDisplayTreeItemWidget *logWidget = new QDisplayTreeItemWidget(this, consoleItem); - QVBoxLayout *logLayout = new QVBoxLayout(logWidget); - - QTextEdit* textEdit = new QTextEdit(QString::fromStdString(infos), logWidget); - textEdit->setText(QString::fromStdString(infos)); - textEdit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); - textEdit->setFixedHeight(200); - textEdit->moveCursor(QTextCursor::End, QTextCursor::MoveAnchor); - textEdit->ensureCursorVisible(); - logLayout->addWidget(textEdit); - - logWidget->setContentsMargins(0, 0, 0, 0); - logLayout->setContentsMargins(0, 0, 0, 0); - logLayout->setSpacing(0); - - connect(clearButton, SIGNAL(clicked()), textEdit, SLOT(clear())); - connect(clearButton, SIGNAL(clicked()), this, SLOT(clearComponentOutput())); - - setItemWidget(consoleItem, 0, clearWidget); - setItemWidget(consoleItem, 1, logWidget); - } - - // warnings output - if(!warnings.empty()) - { - QTreeWidgetItem *consoleItem = new QTreeWidgetItem(groupItem); - const QBrush *brush = nullptr; - if(groupItem->childCount() % 2 == 0) - brush = new QBrush(QColor(255, 255, 191)); - else - brush = new QBrush(QColor(255, 255, 222)); - consoleItem->setBackground(0, *brush); - consoleItem->setBackground(1, *brush); - - QDisplayTreeItemWidget *clearWidget = new QDisplayTreeItemWidget(this, consoleItem); - QVBoxLayout *clearLayout = new QVBoxLayout(clearWidget); - - QPushButton* clearButton = new QPushButton("Clear warning", clearWidget); - clearButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); - clearButton->setFixedHeight(200); -#if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0)) - clearButton->setProperty("base", qVariantFromValue((void*) base)); -#else - clearButton->setProperty("base", QVariant::fromValue((void*) base)); -#endif - clearLayout->addWidget(clearButton); - - clearWidget->setContentsMargins(0, 0, 0, 0); - clearLayout->setContentsMargins(0, 0, 0, 0); - clearLayout->setSpacing(0); - - QDisplayTreeItemWidget *logWidget = new QDisplayTreeItemWidget(this, consoleItem); - QVBoxLayout *logLayout = new QVBoxLayout(logWidget); - - QTextEdit* textEdit = new QTextEdit(logWidget); - textEdit->setText(QString::fromStdString(warnings)); - textEdit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); - textEdit->setFixedHeight(200); - textEdit->moveCursor(QTextCursor::End, QTextCursor::MoveAnchor); - textEdit->ensureCursorVisible(); - logLayout->addWidget(textEdit); - - logWidget->setContentsMargins(0, 0, 0, 0); - logLayout->setContentsMargins(0, 0, 0, 0); - logLayout->setSpacing(0); - - connect(clearButton, SIGNAL(clicked()), textEdit, SLOT(clear())); - connect(clearButton, SIGNAL(clicked()), this, SLOT(clearComponentWarning())); - - setItemWidget(consoleItem, 0, clearWidget); - setItemWidget(consoleItem, 1, logWidget); - } -} - -void QDisplayPropertyWidget::clear() -{ - QTreeWidgetItem *item = nullptr; - const QPushButton* pin = nullptr; - for(unsigned int i = 0; (item = topLevelItem(i));) - { - pin = static_cast(itemWidget(item, 1)); - if(pin && !pin->isChecked()) - { - objects.erase(objects.find(item)); - takeTopLevelItem(i); - } - else - ++i; - } -} - -void QDisplayPropertyWidget::clearAll() -{ - QTreeWidget::clear(); -} - -void QDisplayPropertyWidget::clearComponentOutput() -{ - const QObject* signalEmitter = sender(); - if(nullptr == signalEmitter) - return; - - const QVariant variant = signalEmitter->property("base"); - const sofa::core::objectmodel::Base* base = static_cast(variant.value()); - if(base) - base->clearLoggedMessages(); -} - -void QDisplayPropertyWidget::clearComponentWarning() -{ - const QObject* signalEmitter = sender(); - if(nullptr == signalEmitter) - return; - - const QVariant variant = signalEmitter->property("base"); - const sofa::core::objectmodel::Base* base = static_cast(variant.value()); - if(base) - base->clearLoggedMessages(); -} - -QTreeWidgetItem* QDisplayPropertyWidget::findComponent(const QString& component) const -{ - QTreeWidgetItem *componentItem = nullptr; - for(unsigned int i = 0; (componentItem = topLevelItem(i)); ++i) - if(componentItem->text(0) == component) - break; - - return componentItem; -} - -QTreeWidgetItem* QDisplayPropertyWidget::findGroup(const QString& component, const QString& group) const -{ - const QTreeWidgetItem *componentItem = nullptr; - componentItem = findComponent(component); - if(!componentItem) - return nullptr; - - QTreeWidgetItem *groupItem = nullptr; - for(unsigned int i = 0; (groupItem = componentItem->child(i)); ++i) - if(groupItem->text(0) == group) - break; - - return groupItem; -} - -/*void QDisplayPropertyWidget::dragEnterEvent(QDragEnterEvent *event) -{ - QModelIndex index = indexAt(event->pos()); - if(!index.isValid() && !index.parent().isValid()) - return; - - std::cout << index.row() << " - " << index.column() << std::endl; - QTreeWidgetItem* source = itemFromIndex(index); - if(source->checkState(0) == Qt::Unchecked) - return; - - QTreeWidget::dragEnterEvent(event); -} - -void QDisplayPropertyWidget::dropEvent(QDropEvent *event) -{ - QModelIndex index = indexAt(event->pos()); - if(!index.isValid() && !index.parent().isValid()) - return; - - std::cout << index.row() << " - " << index.column() << std::endl; - QTreeWidgetItem* target = itemFromIndex(index); - if(target->checkState(0) == Qt::Unchecked) - return; - - QTreeWidget::dropEvent(event); -}*/ - -/*void QDisplayPropertyWidget::dragMoveEvent(QDragMoveEvent *event) -{ - QModelIndex index = indexAt(event->pos()); - QTreeWidgetItem* source = itemFromIndex(index); - if(source->checkState(0) == Qt::Checked) - event->accept(); -} - -void QDisplayPropertyWidget::dragLeaveEvent(QDragLeaveEvent *event) -{ -// QModelIndex index = indexAt(event->pos()); -// QTreeWidgetItem* source = itemFromIndex(index); -// if(source->checkState(0) == Qt::Checked) -// event->accept(); -} - -Qt::DropActions QDisplayPropertyWidget::supportedDropActions() const -{ - return Qt::CopyAction | Qt::MoveAction; -}*/ - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/QDisplayPropertyWidget.h b/Sofa/GUI/Qt/src/sofa/gui/qt/QDisplayPropertyWidget.h deleted file mode 100644 index 7ed192622a6..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/QDisplayPropertyWidget.h +++ /dev/null @@ -1,155 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include -#include -#include - -#include -#include -#include -#include -#if SOFA_GUI_QT_HAVE_QT_CHARTS -#include -#endif -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -#include -#include -#include - -namespace sofa::gui::qt -{ - -class SOFA_GUI_QT_API QDisplayTreeItemWidget : public QWidget -{ - Q_OBJECT - -public: - // constructor / destructor - QDisplayTreeItemWidget(QWidget* parent=nullptr, QTreeWidgetItem* item=nullptr); - ~QDisplayTreeItemWidget() override; - -protected slots: - // resize the corresponding TreeItem when the Widget size changes - void updateDirtyWidget(); - -private: - QTreeWidgetItem* treeWidgetItem; -}; - -// QDisplayPropertyWidget describe a widget where you can view and edit every properties of Sofa components -class SOFA_GUI_QT_API QDisplayPropertyWidget : public QTreeWidget -{ - Q_OBJECT - - friend class GraphHistoryManager; - friend class LinkComponent; - -public: - // constructor / destructor - QDisplayPropertyWidget(const ModifyObjectFlags& modifyFlags, QWidget* parent=nullptr); - ~QDisplayPropertyWidget() override; - - // add a component in the tree in order to show / change its data and compare them with other component data - void addComponent(const QString& component, core::objectmodel::Base* base, QTreeWidgetItem* listItem, bool clear = true); - - // add a data / link group - void addGroup(const QString& component, const QString& group); - - // add a component data to show / change its value - void addData(const QString& component, const QString& group, sofa::core::objectmodel::BaseData *data); - - // add a component link to show / change its value - void addLink(const QString& component, const QString& group, sofa::core::objectmodel::BaseLink *link); - - // set a component description - void setDescription(const QString& component, const QString& group, sofa::core::objectmodel::Base *base); - - // set a component console output - void setConsoleOutput(const QString& component, const QString& group, sofa::core::objectmodel::Base *base); - -protected: - // add a description item - void addDescriptionItem(QTreeWidgetItem *groupItem, const QString& name, const QString& description); - -public: - // clear non-pinned components, theirs groups, data and links - void clear(); - - // clear everything, even pinned components - void clearAll(); - - // name of the default property group - static QString DefaultDataGroup() {return "Property";} - static QString DefaultLinkGroup() {return "Link";} - static QString DefaultInfoGroup() {return "Info";} - static QString DefaultLogGroup() {return "Log";} - -protected slots: - // retrieve the component stored as a property in the signal emitter and clear its output messages - void clearComponentOutput(); - - // retrieve the component stored as a property in the signal emitter and clear its warning messages - void clearComponentWarning(); - -protected: - // find a component by name - QTreeWidgetItem* findComponent(const QString& component) const; - - // find a group by name - QTreeWidgetItem* findGroup(const QString& component, const QString& group) const; - - /*void dragEnterEvent(QDragEnterEvent *event); - //void dragMoveEvent(QDragMoveEvent *event); - //void dragLeaveEvent(QDragLeaveEvent *event); - void dropEvent(QDropEvent *event); - Qt::DropActions supportedDropActions() const;*/ - -private: - - // remember the Base Object and its item in the scene graph list view for each component registered in this property view - std::map > objects; - QIcon pinIcon; - ModifyObjectFlags modifyObjectFlags; - -}; - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/QEnergyStatWidget.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/QEnergyStatWidget.cpp deleted file mode 100644 index a22f1f68a9e..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/QEnergyStatWidget.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#include -#include -#include -#include -#include - -#include - -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) -using namespace QtCharts; -#endif - -namespace sofa::gui::qt -{ - -QEnergyStatWidget::QEnergyStatWidget( QWidget* parent, simulation::Node* node ) - : QGraphStatWidget( parent, node, "Energy", 3, 500 ) -{ - setCurve( 0, "Kinetic", Qt::red ); - setCurve( 1, "Potential", Qt::green ); - setCurve( 2, "Mechanical", Qt::blue ); - - m_energyVisitor = new sofa::simulation::mechanicalvisitor::MechanicalComputeEnergyVisitor(core::mechanicalparams::defaultInstance()); -} - -QEnergyStatWidget::~QEnergyStatWidget() -{ - delete m_energyVisitor; -} - -void QEnergyStatWidget::stepImpl() -{ - if (m_curves.size() != 3) { - msg_warning("QEnergyStatWidget") << "Wrong number of curves: " << m_curves.size() << ", should be 3."; - return; - } - m_energyVisitor->execute(m_node->getContext() ); - - // Update series - const SReal time = m_node->getTime(); - const SReal kinectic = m_energyVisitor->getKineticEnergy(); - const SReal potential = m_energyVisitor->getPotentialEnergy(); - - m_curves[0]->append(time, kinectic); - m_curves[1]->append(time, potential); - //Add Mechanical Energy - m_curves[2]->append(time, kinectic + potential); - - // update maxY - updateYAxisBounds(kinectic + potential); - - // update minY - if (kinectic < potential) - updateYAxisBounds(kinectic); - else - updateYAxisBounds(potential); -} - - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/QEnergyStatWidget.h b/Sofa/GUI/Qt/src/sofa/gui/qt/QEnergyStatWidget.h deleted file mode 100644 index 94cada38460..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/QEnergyStatWidget.h +++ /dev/null @@ -1,53 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include -#include - -namespace sofa::simulation::mechanicalvisitor -{ - class MechanicalComputeEnergyVisitor; -} - -namespace sofa::gui::qt -{ - -class SOFA_GUI_QT_API QEnergyStatWidget : public QGraphStatWidget -{ - - Q_OBJECT - - sofa::simulation::mechanicalvisitor::MechanicalComputeEnergyVisitor *m_energyVisitor { nullptr }; - - -public: - - QEnergyStatWidget( QWidget* parent, simulation::Node* node ); - - ~QEnergyStatWidget(); - - void stepImpl() override; - -}; - - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/QGraphStatWidget.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/QGraphStatWidget.cpp deleted file mode 100644 index b806ba88793..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/QGraphStatWidget.cpp +++ /dev/null @@ -1,163 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ - -#include -#include - -namespace sofa::gui::qt -{ - -QGraphStatWidget::QGraphStatWidget( QWidget* parent, simulation::Node* node, const QString& title, unsigned numberOfCurves, int bufferSize) - : QWidget( parent ) - , m_node( node ) - , m_bufferSize(bufferSize) - , m_yMin(10000) - , m_yMax(-10000) - , m_lastTime(0.0) - , m_cptStep(0) -{ - QVBoxLayout *layout = new QVBoxLayout(this); - layout->setContentsMargins(0, 0, 0, 0); - layout->setSpacing(1); - layout->setObjectName(QString( "tabStats" ) + title); - - m_chart = new QChart(); - QFont font; - font.setPixelSize(18); - m_chart->setTitleFont(font); - m_chart->setTitle(title); - - m_axisX = new QValueAxis(); - m_axisX->setRange(0, m_node->getDt()*m_bufferSize); - m_axisX->setTitleText("Time (ms)"); - - m_axisY = new QValueAxis(); - m_axisY->setTitleText("Value"); - - m_chart->addAxis(m_axisX, Qt::AlignBottom); - m_chart->addAxis(m_axisY, Qt::AlignLeft); - m_chart->legend()->setAlignment(Qt::AlignBottom); - - QChartView* chartView = new QChartView(m_chart, this); - layout->addWidget(chartView); - - m_curves.resize(numberOfCurves); - -} - -QGraphStatWidget::~QGraphStatWidget() -{ - -} - -void QGraphStatWidget::step() -{ - const SReal time = m_node->getTime(); - if (time <= m_lastTime) - return; - - // call internal method to add Data into series - stepImpl(); - - if (m_cptStep > m_bufferSize) // start swipping the Xaxis - { - const qreal min = m_axisX->min() + m_node->getDt(); - m_axisX->setRange(min, time); - - // flush series data not anymore display for memory storage - if ((m_cptStep% m_bufferSize * 2) == 0) - { - flushSeries(); - } - } - - if ((m_cptStep% m_bufferSize) == 0) - { - if (m_axisY->max() > m_yMax) - m_axisY->setMax(m_yMax*1.01); - - if (m_axisY->min() < m_yMin) - m_axisY->setMin(m_yMin*0.99); - - m_yMax = -10000; - m_yMin = 10000; - } - - m_lastTime = time; - m_cptStep++; -} - - -void QGraphStatWidget::flushSeries() -{ - for (const auto serie : m_curves) - { - if (serie->count() >= m_bufferSize) { - serie->removePoints(0, m_bufferSize); - } - } -} - - -void QGraphStatWidget::setCurve( unsigned index, const QString& name, const QColor& color ) -{ - if (index >= m_curves.size()) - { - m_curves.resize(index+1); - } - - m_curves[index] = new QLineSeries(); - m_curves[index]->setName(name); - m_curves[index]->setPen(QPen(color)); - - m_chart->addSeries(m_curves[index]); - - m_curves[index]->attachAxis(m_axisY); - m_curves[index]->attachAxis(m_axisX); -} - - -void QGraphStatWidget::updateYAxisBounds(SReal value) -{ - if (value > m_axisY->max()) - { - m_axisY->setMax(value*1.01); - } - if (value < m_axisY->min()) - { - m_axisY->setMin(value*0.99); - } - - // for record - if (value > m_yMax) - { - m_yMax = value; - } - - if (value < m_yMin) - { - m_yMin = value; - } -} - - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/QGraphStatWidget.h b/Sofa/GUI/Qt/src/sofa/gui/qt/QGraphStatWidget.h deleted file mode 100644 index 9026a1e58a2..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/QGraphStatWidget.h +++ /dev/null @@ -1,99 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once - -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) -using namespace QtCharts; -#endif - -namespace sofa::gui::qt -{ - -/// Base class to make graphes in the stat tab of the node widget -/// TODO add gnuplot export -class QGraphStatWidget : public QWidget -{ - Q_OBJECT - -public: - QGraphStatWidget( QWidget* parent, simulation::Node* node, const QString& title, unsigned numberOfCurves, int bufferSize ); - virtual ~QGraphStatWidget(); - - /// Main method called to update the graph - virtual void step() final; - - /// the only function that should be overloaded - virtual void stepImpl() = 0; - -protected: - /// set the index-th curve (index must be < _numberOfCurves) - void setCurve( unsigned index, const QString& name, const QColor& color ); - - /// Method to update Y axis scale - void updateYAxisBounds(SReal value); - - /// flush data from series not anymore displayed - void flushSeries(); - - /// pointer to the node monitored - simulation::Node *m_node; - - /// size of the buffers to stored - int m_bufferSize; - - /// Pointer to the chart Data - QChart *m_chart; - - /// vector of series to be plotted - std::vector< QLineSeries *> m_curves; - - /// x axis pointer - QValueAxis* m_axisX; - /// y axis pointer - QValueAxis* m_axisY; - - /// min y axis value stored - SReal m_yMin; - /// max y axis value stored - SReal m_yMax; - /// last timestep monitored - SReal m_lastTime; - /// step counter monitored - int m_cptStep; -}; - - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/QMenuFilesRecentlyOpened.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/QMenuFilesRecentlyOpened.cpp deleted file mode 100644 index b587aeeec0e..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/QMenuFilesRecentlyOpened.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ - -#include "QMenuFilesRecentlyOpened.h" - -#include -#include -#include - - -namespace sofa::gui::qt -{ - -void QMenuFilesRecentlyOpened::updateWidget() -{ - //Clear the current widget - menuRecentlyOpenedFiles->clear(); - //while (menuRecentlyOpenedFiles->actions().count()) - // menuRecentlyOpenedFiles->removeAction(menuRecentlyOpenedFiles->actions()[0]); - //Add the content of files - for (unsigned int i=0; iaddAction(QString(files[i].c_str())); -} - -QMenu *QMenuFilesRecentlyOpened::createWidget(QWidget *parent, const std::string &name) -{ - menuRecentlyOpenedFiles = new QMenu(QString(name.c_str()), parent); - menuRecentlyOpenedFiles->setTearOffEnabled(true); - - updateWidget(); - return menuRecentlyOpenedFiles; -} - -void QMenuFilesRecentlyOpened::openFile(const std::string &file) -{ - FilesRecentlyOpenedManager::openFile(file); - updateWidget(); - writeFiles(); -} - - -} // namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/QMenuFilesRecentlyOpened.h b/Sofa/GUI/Qt/src/sofa/gui/qt/QMenuFilesRecentlyOpened.h deleted file mode 100644 index 127fc981f1b..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/QMenuFilesRecentlyOpened.h +++ /dev/null @@ -1,49 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include -#include - -#include - -namespace sofa::gui::qt -{ - -class SOFA_GUI_QT_API QMenuFilesRecentlyOpened: public common::FilesRecentlyOpenedManager -{ -public: - QMenuFilesRecentlyOpened(const std::string &configFile):common::FilesRecentlyOpenedManager(configFile),menuRecentlyOpenedFiles(nullptr) {} - ~QMenuFilesRecentlyOpened() override {if (menuRecentlyOpenedFiles) delete menuRecentlyOpenedFiles;} - void openFile(const std::string &file) override; - - QMenu *createWidget(QWidget *parent, const std::string& =std::string("Recently Opened Files ...")); - QMenu *getMenu() {return menuRecentlyOpenedFiles;} - -protected: - void updateWidget(); - QMenu *menuRecentlyOpenedFiles; - - -}; - - -} // namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/QModelViewTableDataContainer.h b/Sofa/GUI/Qt/src/sofa/gui/qt/QModelViewTableDataContainer.h deleted file mode 100644 index 3745cdc13e7..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/QModelViewTableDataContainer.h +++ /dev/null @@ -1,751 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include "SimpleDataWidget.h" -#include "StructDataWidget.h" -#include - -#include "QMouseWheelAdjustementGuard.h" -#include "QModelViewTableUpdater.h" -#include "QDisplayDataWidget.h" - -#include -#include -#include - -#include - -namespace sofa::gui::qt -{ - -enum -{ - TYPE_SINGLE = 0, - TYPE_VECTOR = 1, - TYPE_STRUCT = 2, -}; - -template -class flat_data_trait; - -template -class default_flat_data_trait : public flat_data_trait< T, ( (struct_data_trait::NVAR >1 ) ? TYPE_STRUCT : ( (vector_data_trait::NDIM > 0) ? TYPE_VECTOR : TYPE_SINGLE ) ) > {}; - - - -template inline std::string toString(const T& v) -{ - std::ostringstream o; - o << v; - return o.str(); -} - -inline std::string toString(const std::string& s) -{ - return s; -} - -template inline void fromString(const std::string& s, T& v) -{ - std::istringstream i(s); - i >> v; -} - -inline void fromString(const std::string& s, std::string& v) -{ - v = s; -} - -template -class flat_data_trait -{ -public: - enum { is_struct = 0 }; - enum { is_vector = 0 }; - enum { is_single = 1 }; - typedef T data_type; - typedef T value_type; - static int size() { return 1; } - static int size(const data_type&) { return size(); } - static const char* header(const data_type& /*d*/, int /*i*/ = 0) - { - return nullptr; - } - static const value_type* get(const data_type& d, int /*i*/ = 0) { return &d; } - static void set(const value_type& v, data_type& d, int /*i*/ = 0) { d = v; } - static void setS(const std::string& v, data_type& d, int /*i*/ = 0) - { - fromString(v, d); - } -}; - -template::NVAR> -class flat_struct_data_trait -{ -public: - enum { is_struct = 1 }; - enum { is_vector = 0 }; - enum { is_single = 0 }; - enum { is_default = ((struct_data_trait::NVAR > 1) ? 1 : 0) }; - typedef T data_type; - typedef std::string value_type; - typedef struct_data_trait shelper; - typedef struct_data_trait_var vhelper; - typedef typename vhelper::value_type vtype; - typedef default_flat_data_trait vtrait; - typedef typename vtrait::value_type iotype; - typedef flat_struct_data_trait prev; - static int size() { return prev::size() + vtrait::size(); } - static int size(const data_type&) { return size(); } - static const char* header(const data_type& d, int i = 0) - { - const int s = prev::size(); - if (i < s) - return prev::header(d, i); - else - { - const char* h1 = vhelper::shortname(); - const char* h2 = vtrait::header(*vhelper::get(d), i-s); - if (h2 == nullptr) return h1; - else if (h1 == nullptr) return h2; - else - { - static std::string t; - t = h1; - t += " "; - t += h2; - return t.c_str(); - } - } - } - static value_type* get(const data_type& d, int i = 0) - { - const int s = prev::size(); - if (i < s) - return prev::get(d, i); - else - { - static std::string t; - t = toString(*vtrait::get(*vhelper::get(d), i-s)); - return &t; - } - } - static void setS(const std::string& v, data_type& d, int i = 0) - { - const int s = prev::size(); - if (i < s) - prev::setS(v, d, i); - else - { - vtype var = *vhelper::get(d); - vtrait::setS(v, var,i-s); - vhelper::set(var, d); - } - } - static void set(const value_type& v, data_type& d, int i = 0) - { - setS(v, d, i); - } -}; - -template -class flat_struct_data_trait -{ -public: - enum { is_struct = 1 }; - enum { is_vector = 0 }; - enum { is_single = 0 }; - enum { is_default = ((struct_data_trait::NVAR > 1) ? 1 : 0) }; - typedef T data_type; - typedef std::string value_type; - typedef struct_data_trait shelper; - static int size() { return 0; } - static int size(const data_type&) { return size(); } - static const char* header(const data_type& /*d*/, int /*i*/ = 0) - { - return nullptr; - } - static value_type* get(const data_type& /*d*/, int /*i*/ = 0) - { - return nullptr; - } - static void setS(const std::string& /*v*/, data_type& /*d*/, int /*i*/ = 0) - { - } - static void set(const value_type& /*v*/, data_type& /*d*/, int /*i*/ = 0) - { - } -}; - -template -class flat_data_trait : public flat_struct_data_trait -{ -}; - -template -class flat_vector_data_trait -{ -public: - enum { is_struct = 0 }; - enum { is_vector = 1 }; - enum { is_single = 0 }; - enum { is_default = ((struct_data_trait::NVAR == 1 && vector_data_trait::NDIM >= 1) ? 1 : 0) }; - typedef T data_type; - typedef vector_data_trait vhelper; - typedef typename vhelper::value_type vtype; - typedef default_flat_data_trait vtrait; - typedef typename vtrait::value_type value_type; - static int size() { return vhelper::SIZE * vtrait::size(); } - static int size(const data_type&) { return size(); } - static const char* header(const data_type& d, int i = 0) - { - const int s = vtrait::size(); - int j = i / s; - i = i % s; - const char* h1 = vhelper::header(d, j); - const char* h2 = vtrait::header(*vhelper::get(d, j), i); - if (h2 == nullptr) return h1; - else if (h1 == nullptr) return h2; - else - { - static std::string t; - t = h1; - t += " "; - t += h2; - return t.c_str(); - } - } - static const value_type* get(const data_type& d, int i = 0) - { - const int s = vtrait::size(); - int j = i / s; - i = i % s; - return vtrait::get(*vhelper::get(d, j), i); - } - static void set(const value_type& v, data_type& d, int i = 0) - { - const int s = vtrait::size(); - int j = i / s; - i = i % s; - vtype t = *vhelper::get(d, j); - vtrait::set(v, t, i); - vhelper::set(t, d, j); - } - static void setS(const std::string& v, data_type& d, int i = 0) - { - const int s = vtrait::size(); - int j = i / s; - i = i % s; - vtype t = *vhelper::get(d, j); - vtrait::setS(v, t, i); - vhelper::set(t, d, j); - } -}; - -template -class flat_data_trait : public flat_vector_data_trait -{ -}; - -enum -{ - TABLE_NORMAL = 0, - TABLE_HORIZONTAL = 1 << 0, - TABLE_FIXEDSIZE = 1 << 1, -}; - - - -template -class table_data_widget_container -{ -public: - typedef T data_type; - typedef vector_data_trait rhelper; - typedef typename rhelper::value_type row_type; - typedef default_flat_data_trait vhelper; - typedef typename vhelper::value_type value_type; - typedef QVBoxLayout Layout; - - QSpinBox* wSize; - QTableViewUpdater* wTableView; - QTableModelUpdater* wTableModel; - QPushButtonUpdater* wDisplay; - DataWidget * widget; - Layout* container_layout; - - int rows; - int cols; - - table_data_widget_container() : wSize(nullptr), wTableView(nullptr), wDisplay(nullptr), widget(nullptr), container_layout(nullptr) {} - - bool createLayout( DataWidget* parent ) - { - if( parent->layout() != nullptr || container_layout != nullptr) return false; - container_layout = new Layout(parent); - return true; - } - - bool createLayout( QLayout* layout) - { - if ( container_layout != nullptr ) return false; - container_layout = new Layout(); - layout->addItem(container_layout); - return true; - } - - void setCellText(int r, int c, const std::string& s) - { - if (FLAGS & TABLE_HORIZONTAL) - { - QStandardItem* item= wTableModel->item(c,r); - if (!item) wTableModel->setItem(c, r, new QStandardItem(QString(s.c_str()))); - else item->setText(QString(s.c_str())); - } - else - { - QStandardItem* item= wTableModel->item(r,c); - if (!item) wTableModel->setItem(r, c, new QStandardItem(QString(s.c_str()))); - else item->setText(QString(s.c_str())); - } - } - std::string getCellText(int r, int c) - { - QStandardItem *item; - QString text; - if (FLAGS & TABLE_HORIZONTAL) - item=wTableModel->item(c,r); - else - item=wTableModel->item(r,c); - - if (item) - { - text=item->text(); - - if (!text.isNull()) - return std::string(text.toStdString()); - } - return std::string(""); - } - - template - void setCell(int r, int c, const V& v) - { - std::ostringstream o; - o << v; - setCellText(r,c, o.str()); - } - void setCell(int r, int c, const std::string& s) - { - setCellText(r, c, s); - } - template - void getCell(int r, int c, V& v) - { - std::istringstream i(getCellText(r,c)); - i >> v; - } - void getCell(int r, int c, const std::string& s) - { - s = getCellText(r,c); - } - - bool createWidgets(DataWidget* parent, const data_type& d, bool readOnly) - { - rows = 0; - const int dataRows = rhelper::size(d); - - wSize = new QSpinBox(parent); - wSize->setMinimum(0); - wSize->setMaximum(INT_MAX); - wSize->setSingleStep(1); - wSize->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); - - /// Activates the scrolling only when the widget is selected and not when it is - /// hovering. - wSize->setFocusPolicy(Qt::StrongFocus); - - wSize->installEventFilter(new QMouseWheelAdjustmentGuard(wSize)); - - wDisplay = new QPushButtonUpdater( QString("Display the values"), parent); - wDisplay->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); - - if (dataRows > 0) - cols = vhelper::size(*rhelper::get(d,0)); - else - cols = vhelper::size(row_type()); - - if (FLAGS & TABLE_HORIZONTAL) - wTableModel = new QTableModelUpdater(cols,dataRows,parent); - else - wTableModel = new QTableModelUpdater(dataRows,cols,parent); - - wTableView = new QTableViewUpdater(parent); - wTableView->setModel(wTableModel); - - widget=parent; - - wDisplay->setCheckable(true); - QWidget* parentWidget = parent; - if(parentWidget) - parentWidget = static_cast(parentWidget->parent()); - bool propertyWidgetFlagOn = false; - QDisplayDataWidget* displayDataWidget = dynamic_cast(parentWidget); - if(displayDataWidget) - propertyWidgetFlagOn = displayDataWidget->flag().PROPERTY_WIDGET_FLAG; - - parent->setFilled(false); - rows = dataRows; - - wSize->setValue(dataRows); - - wTableView->setDisplayed(isDisplayed()); - - if (readOnly) - { - wSize->setEnabled(false); - wTableView->setEditTriggers(QAbstractItemView::NoEditTriggers); - } - else - { - if (!(FLAGS & TABLE_FIXEDSIZE)) - { - parent->connect(wSize, SIGNAL( valueChanged(int) ), parent, SLOT( setWidgetDirty() )); - - if( FLAGS & TABLE_HORIZONTAL) - parent->connect(wSize, SIGNAL( valueChanged(int) ), wTableModel, SLOT(resizeTableH(int) ) ); - else - parent->connect(wSize, SIGNAL( valueChanged(int) ), wTableModel, SLOT(resizeTableV(int) ) ); - } - else - { - wSize->setEnabled(false); - } - } - parent->connect(wDisplay, SIGNAL( toggled(bool) ), wTableView, SLOT(setDisplayed(bool))); - parent->connect(wDisplay, SIGNAL( toggled(bool) ), wDisplay, SLOT(setDisplayed(bool))); - parent->connect(wDisplay, SIGNAL( toggled(bool) ), parent, SLOT(fillFromData() )); - - if (!propertyWidgetFlagOn) - wDisplay->setChecked(dataRows < MAX_NUM_ELEM && dataRows != 0); - else - wDisplay->setChecked(false); - - wDisplay->setAutoDefault(false); - - return true; - } - - - void setReadOnly(bool readOnly) - { - wSize->setEnabled(!readOnly); - if (readOnly) - { - wTableModel->setReadOnly(true); - wTableView->setEditTriggers(QAbstractItemView::NoEditTriggers); - QObject::disconnect(wTableView, SIGNAL( activated(QModelIndex) ) , widget, SLOT(setWidgetDirty()) ); - QObject::disconnect(wTableView, SIGNAL( pressed(QModelIndex) ) , widget, SLOT(setWidgetDirty()) ); - QObject::disconnect(wTableView, SIGNAL( clicked(QModelIndex) ) , widget, SLOT(setWidgetDirty()) ); - }else{ - QObject::connect(wTableView, SIGNAL( activated(QModelIndex) ) , widget, SLOT(setWidgetDirty()) ); - QObject::connect(wTableView, SIGNAL( pressed(QModelIndex) ) , widget, SLOT(setWidgetDirty()) ); - QObject::connect(wTableView, SIGNAL( clicked(QModelIndex) ) , widget, SLOT(setWidgetDirty()) ); - } - } - - bool isDisplayed() - { - return (wDisplay->isChecked()); - } - - - void readFromData(const data_type& d) - { - if (isDisplayed()) - { - processTableModifications(d); - fillTable(d); - } - } - void writeToData(data_type& d) - { - rows = wSize->value(); - if (!(FLAGS & TABLE_FIXEDSIZE)) - { - const int oldrows = rhelper::size(d); - if( rows != oldrows) - { - rhelper::resize(rows,d); - } - const int newrows = rhelper::size(d); - if( rows != newrows) - { - wSize->setValue(newrows); - rows = newrows; - if (FLAGS & TABLE_HORIZONTAL) - wTableModel->setColumnCount(newrows); - else - wTableModel->setRowCount(newrows); - } - } - - processTableModifications(d); - - if (isDisplayed()) - { - for (int y=0; ycolumnCount(); - else - currentTableNumRows=wTableModel->rowCount(); - const int rowSize = wSize->value(); - - QStringList horizontalHeaders; - QStringList verticalHeaders; - - for (int x=0; x 0) ? vhelper::header(*rhelper::get(d,0),x) : vhelper::header(row_type(),x); - - if (h && *h) - horizontalHeaders << h; - else - horizontalHeaders << QString::number(x); - } - - if (FLAGS & TABLE_HORIZONTAL) - wTableModel->setVerticalHeaderLabels(horizontalHeaders); - else - wTableModel->setHorizontalHeaderLabels(horizontalHeaders); - - - for (int y=0; ysetHorizontalHeaderLabels(verticalHeaders); - else - wTableModel->setVerticalHeaderLabels(verticalHeaders); - - if (rowSize == currentTableNumRows) - { - return; - } - - - if (FLAGS & TABLE_HORIZONTAL) - wTableModel->setColumnCount(rowSize); - else - wTableModel->setRowCount(rowSize); - - for (int y=currentTableNumRows; ysetHorizontalHeaderLabels(verticalHeaders); - else - wTableModel->setVerticalHeaderLabels(verticalHeaders); - - } - - void fillTable(const data_type &d) - { - int currentNum; - const int dsize = (int)d.size(); - if (FLAGS & TABLE_HORIZONTAL) - currentNum=wTableModel->columnCount() > dsize ? dsize : wTableModel->columnCount(); - else - currentNum=wTableModel->rowCount() > dsize ? dsize : wTableModel->rowCount(); - - for (int y=0; yaddWidget(wSize); - container_layout->addWidget(wTableView); - container_layout->addWidget(wDisplay); - } -}; - -//////////////////////////////////////////////////////////////// -/// variable-sized vectors support -//////////////////////////////////////////////////////////////// - -template -class vector_data_trait < std::vector > -{ -public: - typedef std::vector data_type; - typedef T value_type; - enum { NDIM = 1 }; - static int size(const data_type& d) { return d.size(); } - static const char* header(const data_type& /*d*/, int /*i*/ = 0) - { - return nullptr; - } - static const value_type* get(const data_type& d, int i = 0) - { - return ((unsigned)i < (unsigned)size(d)) ? &(d[i]) : nullptr; - } - static void set( const value_type& v, data_type& d, int i = 0) - { - if ((unsigned)i < (unsigned)size(d)) - d[i] = v; - } - static void resize( int s, data_type& d) - { - d.resize(s); - } -}; - - - - -template -class vector_data_trait < sofa::type::vector > : public vector_data_trait< std::vector > -{ -}; - - -//////////////////////////////////////////////////////////////// -/// std::map from strings support -//////////////////////////////////////////////////////////////// - -template -class vector_data_trait < std::map > -{ -public: - typedef std::map data_type; - typedef T value_type; - enum { NDIM = 1 }; - static int size(const data_type& d) { return d.size(); } - static const char* header(const data_type& d, int i = 0) - { - typename data_type::const_iterator it = d.begin(); - while (i > 0 && it != d.end()) - { - ++it; - --i; - } - if (i == 0) return it->first.c_str(); - else return nullptr; - } - static const value_type* get(const data_type& d, int i = 0) - { - typename data_type::const_iterator it = d.begin(); - while (i > 0 && it != d.end()) - { - ++it; - --i; - } - if (i == 0) return &(it->second); - else return nullptr; - } - static void set( const value_type& v, data_type& d, int i = 0) - { - typename data_type::iterator it = d.begin(); - while (i > 0 && it != d.end()) - { - ++it; - --i; - } - if (i == 0) it->second = v; - } - static void resize( int /*s*/, data_type& /*d*/) - { - //d.resize(s); - } -}; - - - -//////////////////////////////////////////////////////////////// -/// dequeues support -//////////////////////////////////////////////////////////////// - -template -class vector_data_trait < std::deque > -{ -public: - typedef std::deque data_type; - typedef T value_type; - enum { NDIM = 1 }; - static int size(const data_type& d) { return d.size(); } - static const char* header(const data_type& /*d*/, int /*i*/ = 0) - { - return nullptr; - } - static const value_type* get(const data_type& d, int i = 0) - { - return ((unsigned)i < (unsigned)size(d)) ? &(d[i]) : nullptr; - } - static void set( const value_type& v, data_type& d, int i = 0) - { - if ((unsigned)i < (unsigned)size(d)) - d[i] = v; - } - static void resize(int s, data_type& d) - { - d.resize(s); - } -}; - - - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/QModelViewTableUpdater.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/QModelViewTableUpdater.cpp deleted file mode 100644 index be4678c7edf..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/QModelViewTableUpdater.cpp +++ /dev/null @@ -1,127 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ - -#include "QModelViewTableUpdater.h" - -#include -#include -#include -#include -#include - -namespace sofa::gui::qt -{ - -QTableViewUpdater::QTableViewUpdater(QWidget * parent ) - : QTableView(parent) -{ - setAutoFillBackground(true); -} - -void QTableViewUpdater::setDisplayed(bool b) -{ - this->setVisible(b); -} - - -QTableModelUpdater::QTableModelUpdater ( int numRows, int numCols, QWidget * parent , const char * /*name*/ ): - QStandardItemModel(numRows, numCols, parent) -{} - -Qt::ItemFlags QTableModelUpdater::flags(const QModelIndex&) const -{ - if(m_isReadOnly) - return (Qt::ItemIsSelectable) ; - return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled ; -} - -QVariant QTableModelUpdater::data(const QModelIndex &index, int role) const -{ - if(m_isReadOnly){ - switch(role){ - case Qt::BackgroundRole: -#if (QT_VERSION < QT_VERSION_CHECK(5, 13, 0)) - return QApplication::palette().color(QPalette::Disabled, QPalette::Background) ; -#else - return QApplication::palette().color(QPalette::Disabled, QPalette::Window) ; -#endif - case Qt::ForegroundRole: - return QApplication::palette().color(QPalette::Disabled, QPalette::Text); - } - } - return QStandardItemModel::data(index,role) ; -} - -void QTableModelUpdater::setReadOnly(const bool isReadOnly) -{ - m_isReadOnly=isReadOnly; -} - -void QTableModelUpdater::resizeTableV( int number ) -{ - const QSpinBox *spinBox = (QSpinBox *) sender(); - QString header; - if( spinBox == nullptr) - { - return; - } - if (number != rowCount()) - { - const int previousRows=rowCount(); - setRowCount(number); - if (number > previousRows) - { - for (int i=previousRows; isetText(QString::number(i)); - } - } - } -} - -void QTableModelUpdater::resizeTableH( int number ) -{ - const QSpinBox *spinBox = (QSpinBox *) sender(); - QString header; - if( spinBox == nullptr) - { - return; - } - if (number != columnCount()) - { - const int previousColumns=columnCount(); - setColumnCount(number); - if (number > previousColumns) - { - for (int i=previousColumns; isetText(QString::number(i)); - } - } - } -} - -} // namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/QModelViewTableUpdater.h b/Sofa/GUI/Qt/src/sofa/gui/qt/QModelViewTableUpdater.h deleted file mode 100644 index 6bf003e9c51..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/QModelViewTableUpdater.h +++ /dev/null @@ -1,65 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ - -#pragma once -#include - -#include -#include -#include -#include - - -namespace sofa::gui::qt -{ - -class SOFA_GUI_QT_API QTableViewUpdater : public QTableView -{ - Q_OBJECT - -public: - QTableViewUpdater (QWidget * parent = nullptr); - -public slots: - void setDisplayed(bool b); - -}; - -class SOFA_GUI_QT_API QTableModelUpdater : public QStandardItemModel -{ - Q_OBJECT - bool m_isReadOnly ; -public: - QTableModelUpdater ( int numRows, int numCols, QWidget * parent = nullptr, const char * /*name*/ = nullptr ); - - Qt::ItemFlags flags(const QModelIndex&) const override ; - QVariant data(const QModelIndex &index, int role) const override; - - void setReadOnly(const bool isReadOnly) ; - -public slots: - void resizeTableV( int number ); - - void resizeTableH( int number ); -}; - -} // namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/QMomentumStatWidget.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/QMomentumStatWidget.cpp deleted file mode 100644 index 690122b4d05..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/QMomentumStatWidget.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ - -#include -#include -#include -#include -#include - -namespace sofa::gui::qt -{ - -QMomentumStatWidget::QMomentumStatWidget( QWidget* parent, simulation::Node* node ) : QGraphStatWidget( parent, node, "Momenta", 6, 500 ) -{ - setCurve( 0, "Linear X", Qt::red ); - setCurve( 1, "Linear Y", Qt::green ); - setCurve( 2, "Linear Z", Qt::blue ); - setCurve( 3, "Angular X", Qt::cyan ); - setCurve( 4, "Angular Y", Qt::magenta ); - setCurve( 5, "Angular Z", Qt::yellow ); - - m_momentumVisitor = new simulation::mechanicalvisitor::MechanicalGetMomentumVisitor(core::mechanicalparams::defaultInstance()); -} - -QMomentumStatWidget::~QMomentumStatWidget() -{ - delete m_momentumVisitor; -} - -void QMomentumStatWidget::stepImpl() -{ - if (m_curves.size() != 6) { - msg_warning("QMomentumStatWidget") << "Wrong number of curves: " << m_curves.size() << ", should be 3."; - return; - } - - m_momentumVisitor->execute( m_node->getContext() ); - - const type::Vec6& momenta = m_momentumVisitor->getMomentum(); - - // Update series - const SReal time = m_node->getTime(); - SReal min = 100000; - SReal max = -100000; - for (unsigned i = 0; i < 6; ++i) - { - m_curves[i]->append(time, momenta[i]); - if (momenta[i] < min) - min = momenta[i]; - if (momenta[i] > max) - max = momenta[i]; - } - - // update minY - updateYAxisBounds(min); - // update maxY - updateYAxisBounds(max); -} - - - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/QMomentumStatWidget.h b/Sofa/GUI/Qt/src/sofa/gui/qt/QMomentumStatWidget.h deleted file mode 100644 index df22b6f3c88..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/QMomentumStatWidget.h +++ /dev/null @@ -1,52 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include -#include - -namespace sofa::simulation::mechanicalvisitor -{ -class MechanicalGetMomentumVisitor; -} - -namespace sofa::gui::qt -{ - -class SOFA_GUI_QT_API QMomentumStatWidget : public QGraphStatWidget -{ - - Q_OBJECT - - - simulation::mechanicalvisitor::MechanicalGetMomentumVisitor *m_momentumVisitor; - -public: - - QMomentumStatWidget( QWidget* parent, simulation::Node* node ); - - virtual ~QMomentumStatWidget(); - - void stepImpl() override; -}; - - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/QMouseOperations.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/QMouseOperations.cpp deleted file mode 100644 index 8d54e1f7244..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/QMouseOperations.cpp +++ /dev/null @@ -1,426 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ - -#include "QMouseOperations.h" -#include "QDisplayDataWidget.h" -#include "DataWidget.h" - -#include -#include - -#include -#include -#include -#include - -using namespace sofa::gui::common; - -namespace sofa::gui::qt -{ - -DataWidget *QMouseOperation::createWidgetFromData(sofa::core::objectmodel::BaseData* data) -{ - DataWidget::CreatorArgument arg; - arg.data = data; - arg.name = arg.data->getName(); - arg.parent = this; - arg.readOnly = arg.data->isReadOnly(); - DataWidget *widget = DataWidget::CreateDataWidget(arg); - connect(widget, SIGNAL(WidgetDirty(bool)), this, SLOT(WidgetDirty(bool))); - return widget; -} - -void QMouseOperation::WidgetDirty(bool b) -{ - if (b) - { - DataWidget *dataW=(DataWidget*) sender(); - - dataW->updateDataValue(); - } -} - - -//******************************************************************************************* -QAttachOperation::QAttachOperation() -{ - //Building the GUI for the Attach Operation - - QHBoxLayout *layout=new QHBoxLayout(this); - QLabel *label=new QLabel(QString("Stiffness"), this); - stiffnessWidget = createWidgetFromData(&(setting->d_stiffness)); - - QLabel *labelSize=new QLabel(QString("Arrow Size"), this); - arrowSizeWidget = createWidgetFromData(&(setting->d_arrowSize)); - - QLabel *labelShowFactor=new QLabel(QString("Show Factor Size"), this); - showSizeFactorWidget = createWidgetFromData(&(setting->d_showFactorSize)); - - layout->addWidget(label); - layout->addWidget(stiffnessWidget); - - layout->addWidget(labelSize); - layout->addWidget(arrowSizeWidget); - - layout->addWidget(labelShowFactor); - layout->addWidget(showSizeFactorWidget); -} - - -void QAttachOperation::configure(PickHandler *picker, sofa::component::setting::MouseButtonSetting* button) -{ - if (const sofa::gui::component::AttachBodyButtonSetting* attachSetting=dynamic_cast(button)) - { - AttachOperation::configure(picker,GetMouseId(button->d_button.getValue().getSelectedId())); - setting->d_stiffness.copyValueFrom(&(attachSetting->d_stiffness)); - setting->d_arrowSize.copyValueFrom(&(attachSetting->d_arrowSize) ); - setting->d_showFactorSize.copyValueFrom(&( attachSetting->d_showFactorSize) ) ; - - stiffnessWidget->updateWidgetValue(); - arrowSizeWidget->updateWidgetValue(); - } - else AttachOperation::configure(picker,GetMouseId(button->d_button.getValue().getSelectedId())); -} -//******************************************************************************************* - - -//******************************************************************************************* -QAddRecordedCameraOperation::QAddRecordedCameraOperation() -{} - -void QAddRecordedCameraOperation::configure(PickHandler *picker, sofa::component::setting::MouseButtonSetting* button) -{ - if (dynamic_cast(button)) - { - AddRecordedCameraOperation::configure(picker,GetMouseId(button->d_button.getValue().getSelectedId())); - } - else AddRecordedCameraOperation::configure(picker,GetMouseId(button->d_button.getValue().getSelectedId())); -} - -//******************************************************************************************* - - -//******************************************************************************************* -QStartNavigationOperation::QStartNavigationOperation() -{} - -void QStartNavigationOperation::configure(PickHandler *picker, sofa::component::setting::MouseButtonSetting* button) -{ - if (dynamic_cast(button)) - { - StartNavigationOperation::configure(picker,GetMouseId(button->d_button.getValue().getSelectedId())); - } - else StartNavigationOperation::configure(picker,GetMouseId(button->d_button.getValue().getSelectedId())); -} - -//******************************************************************************************* - - -//******************************************************************************************* -QFixOperation::QFixOperation() -{ - //Building the GUI for the Fix Operation - QHBoxLayout *layout=new QHBoxLayout(this); - QLabel *label=new QLabel(QString("Fixation"), this); - stiffnessWidget = createWidgetFromData(&setting->stiffness); - - layout->addWidget(label); - layout->addWidget(stiffnessWidget); -} - -void QFixOperation::configure(PickHandler *picker, sofa::component::setting::MouseButtonSetting* button) -{ - if (const auto* fixSetting=dynamic_cast(button)) - { - FixOperation::configure(picker,GetMouseId(button->d_button.getValue().getSelectedId() )) ; - setting->stiffness.setValue(fixSetting->stiffness.getValue()); - - stiffnessWidget->updateWidgetValue(); - } - else FixOperation::configure(picker,GetMouseId(button->d_button.getValue().getSelectedId())); -} - -//******************************************************************************************* - -//******************************************************************************************* -QInciseOperation::QInciseOperation() - : finishIncision(false) - , keepPoint(false) -{ - //Building the GUI for the Injection Operation - QVBoxLayout *layout=new QVBoxLayout(this); - - // First group box for incision method choice - incisionMethodChoiceGroup = new QGroupBox(tr("Incision method choice"),this); - QVBoxLayout *vbox1 = new QVBoxLayout(incisionMethodChoiceGroup); - - method1 = new QRadioButton(tr("&Through segment: Incise from click to click."), incisionMethodChoiceGroup); - method2 = new QRadioButton(tr("&Continually: Incise continually from first click localization."), incisionMethodChoiceGroup); - method1->setChecked (true); - - vbox1->addWidget(method1); - vbox1->addWidget(method2); - - // Second group box for easy use - advancedOperations = new QGroupBox(tr("Advanced operations"),this); - QVBoxLayout *vbox2 = new QVBoxLayout(advancedOperations); - - // on push button and one check box with labels - finishCut = new QCheckBox(QString("Complete incision"),advancedOperations); - storeLastPoint = new QCheckBox (QString("Keep in memory last incision point."),advancedOperations); - - vbox2->addWidget(finishCut); - vbox2->addWidget(storeLastPoint); - - - // Third group box for advanced settings (only snping % value for the moment) - advancedOptions = new QGroupBox(tr("Advanced settings"),this); - QVBoxLayout *vbox3 = new QVBoxLayout(advancedOptions); - - // first slider for border snapping - QHBoxLayout *slider1=new QHBoxLayout(); - QLabel *label1=new QLabel(QString("Distance to snap from border (in %)"), this); - snapingBorderSlider=new QSlider(Qt::Horizontal, this); - snapingBorderValue=new QSpinBox(this); - snapingBorderValue->setMinimum(0); - snapingBorderValue->setMaximum(100); - snapingBorderValue->setSingleStep(1); - snapingBorderValue->setEnabled(true); - - slider1->addWidget (label1); - slider1->addWidget (snapingBorderSlider); - slider1->addWidget (snapingBorderValue); - vbox3->addLayout (slider1); - - // second slider for along path snapping - QHBoxLayout *slider2=new QHBoxLayout(); - QLabel *label2=new QLabel(QString("Distance to snap along path (in %)"), this); - snapingSlider=new QSlider(Qt::Horizontal, this); - snapingValue=new QSpinBox(this); - snapingBorderValue->setMinimum(0); - snapingBorderValue->setMaximum(100); - snapingBorderValue->setSingleStep(1); - snapingValue->setEnabled(true); - snapingBorderValue->setValue(0); - - slider2->addWidget (label2); - slider2->addWidget (snapingSlider); - slider2->addWidget (snapingValue); - vbox3->addLayout (slider2); - - // Creating UI - layout->addWidget(incisionMethodChoiceGroup); - layout->addWidget(advancedOperations); - layout->addWidget(advancedOptions); - - connect(finishCut, SIGNAL(toggled(bool)), this, SLOT(setFinishIncision(bool))); - connect(storeLastPoint, SIGNAL(toggled(bool)), this, SLOT(setkeepPoint(bool))); - - connect(snapingBorderSlider,SIGNAL(valueChanged(int)), snapingBorderValue, SLOT(setValue(int))); - connect(snapingBorderValue,SIGNAL(valueChanged(int)), snapingBorderSlider, SLOT(setValue(int))); - - connect(snapingSlider,SIGNAL(valueChanged(int)), snapingValue, SLOT(setValue(int))); - connect(snapingValue,SIGNAL(valueChanged(int)), snapingSlider, SLOT(setValue(int))); - - connect(method1, SIGNAL(toggled(bool)), this, SLOT(setEnableBox(bool))); - - if ( method1->isChecked()) - snapingBorderValue->setValue(50); - - advancedOptions->setHidden(false); -} - -void QInciseOperation::setEnableBox(bool i) -{ - advancedOptions->setVisible(i); -} - -void QInciseOperation::setFinishIncision(bool i) -{ - finishIncision = i; -} - -void QInciseOperation::setkeepPoint(bool i) -{ - keepPoint = i; -} - - - -int QInciseOperation::getIncisionMethod() const -{ - if (method2->isChecked()) - return 1; - else - return 0; -} - -int QInciseOperation::getSnapingBorderValue() const -{ - return snapingBorderValue->value(); -} - -int QInciseOperation::getSnapingValue() const -{ - return snapingValue->value(); -} - -//******************************************************************************************* - - - -//******************************************************************************************* -QTopologyOperation::QTopologyOperation() -{ - //Building the GUI for Topological Operation - - QVBoxLayout *layout=new QVBoxLayout(this); - - // First part: selection of topological operation: - QHBoxLayout *HLayout1 = new QHBoxLayout(); - QLabel *label1=new QLabel(QString("Topological operation: "), this); - operationChoice = new QComboBox(this); - operationChoice->addItem("Remove one element"); - operationChoice->addItem("Remove a zone of elements"); - - HLayout1->addWidget (label1); - HLayout1->addWidget (operationChoice); - - - // Second part: advanced settings - advancedOptions = new QGroupBox(tr("Advanced settings"),this); - QVBoxLayout *VLayout1 = new QVBoxLayout(advancedOptions); - - // First setting: type of mesh, either surface or volume - QHBoxLayout *HLayout2 = new QHBoxLayout(); - - QLabel *label2 = new QLabel(QString("Remove area type: "), this); - meshType1 = new QRadioButton(tr("&Surface"), advancedOptions); - meshType2 = new QRadioButton(tr("&Volume"), advancedOptions); - meshType1->setChecked (true); - - HLayout2->addWidget (label2); - HLayout2->addWidget (meshType1); - HLayout2->addWidget (meshType2); - VLayout1->addLayout (HLayout2); - - // Second setting: selector scale - QHBoxLayout *HLayout3 = new QHBoxLayout(); - - QLabel *label3=new QLabel(QString("Selector scale: "), this); - scaleSlider = new QSlider (Qt::Horizontal, this); - scaleValue = new QSpinBox(this); - scaleValue->setMinimum(0); - scaleValue->setMaximum(100); - scaleValue->setSingleStep(1); - scaleValue->setEnabled(true); - - HLayout3->addWidget (label3); - HLayout3->addWidget (scaleSlider); - HLayout3->addWidget (scaleValue); - VLayout1->addLayout (HLayout3); - - - // Creating UI - layout->addLayout (HLayout1); - layout->addWidget(advancedOptions); - - connect(scaleSlider,SIGNAL(valueChanged(int)), scaleValue, SLOT(setValue(int))); - connect(scaleValue,SIGNAL(valueChanged(int)), scaleSlider, SLOT(setValue(int))); - connect(operationChoice, SIGNAL(activated(int)), this, SLOT(setEnableBox(int))); - - scaleValue->setValue(0); - advancedOptions->setHidden(true); - -} - - -double QTopologyOperation::getScale() const -{ - return scaleValue->value(); -} - -int QTopologyOperation::getTopologicalOperation() const -{ - return operationChoice->currentIndex(); -} - -bool QTopologyOperation::getVolumicMesh() const -{ - return meshType2->isChecked(); -} - -void QTopologyOperation::setEnableBox(int i) -{ - switch (i) - { - case 0: - advancedOptions->setHidden(true); - break; - case 1: - advancedOptions->setHidden(false); - break; - default: - break; - } -} - -//******************************************************************************************* - - -//******************************************************************************************* -QAddSutureOperation::QAddSutureOperation() -{ - //Building the GUI for the Suture Operation - QVBoxLayout *layout=new QVBoxLayout(this); - - QHBoxLayout *option1=new QHBoxLayout(); - QLabel *label1=new QLabel(QString("Spring stiffness"), this); - stiffness = new QLineEdit(QString("10.0"), this); - option1->addWidget(label1); - option1->addWidget(stiffness); - - QHBoxLayout *option2=new QHBoxLayout(); - QLabel *label2=new QLabel(QString("Spring damping"), this); - damping = new QLineEdit(QString("1.0"), this); - option1->addWidget(label2); - option1->addWidget(damping); - - layout->addLayout(option1); - layout->addLayout(option2); -} - -double QAddSutureOperation::getStiffness() const -{ - return stiffness->displayText().toDouble(); - //return atof(stiffness->displayText().toStdString().c_str()); -} - -double QAddSutureOperation::getDamping() const -{ - return damping->displayText().toDouble(); - //return atof(damping->displayText().toStdString()); -} - -//******************************************************************************************* - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/QMouseOperations.h b/Sofa/GUI/Qt/src/sofa/gui/qt/QMouseOperations.h deleted file mode 100644 index 303c9693b94..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/QMouseOperations.h +++ /dev/null @@ -1,190 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include -#include -#include "SofaMouseManager.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -namespace sofa::gui::qt -{ -class DataWidget; - -class SOFA_GUI_QT_API QMouseOperation : public QWidget -{ - Q_OBJECT -public: - DataWidget *createWidgetFromData(sofa::core::objectmodel::BaseData* data); - -public slots: - void WidgetDirty(bool); -}; - -class QAttachOperation : public QMouseOperation, public common::AttachOperation -{ - Q_OBJECT -public: - QAttachOperation(); - void configure(common::PickHandler *picker, sofa::component::setting::MouseButtonSetting* button) override; - -protected: - DataWidget *stiffnessWidget; - DataWidget *arrowSizeWidget; - DataWidget *showSizeFactorWidget; -}; - -class QAddRecordedCameraOperation : public QMouseOperation, public common::AddRecordedCameraOperation -{ - Q_OBJECT -public: - QAddRecordedCameraOperation(); - void configure(common::PickHandler *picker, sofa::component::setting::MouseButtonSetting* button) override; -}; - -class QStartNavigationOperation : public QMouseOperation, public common::StartNavigationOperation -{ - Q_OBJECT -public: - QStartNavigationOperation(); - void configure(common::PickHandler *picker, sofa::component::setting::MouseButtonSetting* button) override; -}; - -class QFixOperation : public QMouseOperation, public common::FixOperation -{ - Q_OBJECT -public: - QFixOperation(); - void configure(common::PickHandler *picker, sofa::component::setting::MouseButtonSetting* button) override; - -protected: - DataWidget *stiffnessWidget; -}; - - - - -class QInciseOperation : public QWidget, public common::InciseOperation -{ - Q_OBJECT -public: - QInciseOperation(); - int getIncisionMethod() const override; - int getSnapingBorderValue() const override; - int getSnapingValue() const override; - - bool getCompleteIncision () override {return finishIncision;} - bool getKeepPoint () override {return keepPoint;} - - void configure(common::PickHandler *picker, common::MOUSE_BUTTON b) override - { - InciseOperation::configure(picker, b); - } - - bool finishIncision; - bool keepPoint; - -public slots: - void setEnableBox (bool i); - void setFinishIncision (bool i); - void setkeepPoint (bool i); - - -protected: - QGroupBox* incisionMethodChoiceGroup; - QRadioButton* method1; - QRadioButton* method2; - - QGroupBox *advancedOperations; - QCheckBox *finishCut; - QCheckBox *storeLastPoint; - - QGroupBox* advancedOptions; - QSlider *snapingBorderSlider; - QSpinBox *snapingBorderValue; - QSlider *snapingSlider; - QSpinBox *snapingValue; -}; - - - - -class QTopologyOperation : public QWidget, public common::TopologyOperation -{ - Q_OBJECT -public: - QTopologyOperation(); - double getScale() const override; - int getTopologicalOperation() const override; - bool getVolumicMesh() const override; - - - - void configure(common::PickHandler *picker, common::MOUSE_BUTTON b) override - { - common::TopologyOperation::configure(picker, b); - } - -public slots: - void setEnableBox (int i); - -protected: - - QComboBox *operationChoice; - QRadioButton *meshType1; - QRadioButton *meshType2; - - QGroupBox *advancedOptions; - QSlider *scaleSlider; - QSpinBox *scaleValue; -}; - - -class QAddSutureOperation : public QWidget, public common::AddSutureOperation -{ - Q_OBJECT -public: - QAddSutureOperation(); - double getStiffness() const override; - double getDamping() const override; - - void configure(common::PickHandler *picker, common::MOUSE_BUTTON b) override - { - common::AddSutureOperation::configure(picker, b); - } - -protected: - QLineEdit *stiffness; - QLineEdit *damping; -}; - -} // namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/QMouseWheelAdjustementGuard.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/QMouseWheelAdjustementGuard.cpp deleted file mode 100644 index 7e906e12302..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/QMouseWheelAdjustementGuard.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#include -#include -#include "QMouseWheelAdjustementGuard.h" - -namespace sofa::gui::qt -{ - -QMouseWheelAdjustmentGuard::QMouseWheelAdjustmentGuard(QObject *parent) : QObject(parent) -{ -} - -bool QMouseWheelAdjustmentGuard::eventFilter(QObject *o, QEvent *e) -{ - const QWidget* widget = static_cast(o); - if (e->type() == QEvent::Wheel && widget && !widget->hasFocus()) - { - e->ignore(); - return true; - } - - return QObject::eventFilter(o, e); -} - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/QMouseWheelAdjustementGuard.h b/Sofa/GUI/Qt/src/sofa/gui/qt/QMouseWheelAdjustementGuard.h deleted file mode 100644 index 72252dbaf9b..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/QMouseWheelAdjustementGuard.h +++ /dev/null @@ -1,46 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include -#include - -namespace sofa::gui::qt -{ - -/// @brief Filter qt events to allows wheel event to only be accepted when the widget has focus. -/// -/// To use it you need to do: -/// myWidget->setFocusPolicy(Qt::StrongFocus); -/// myWidget->installEventFilter(new MouseWheelWidgetAdjustmentGuard(ui.comboBox)); -/// -/// This code is grabbed from: -/// https://stackoverflow.com/questions/5821802/qspinbox-inside-a-qscrollarea-how-to-prevent-spin-box-from-stealing-focus-when -class SOFA_GUI_QT_API QMouseWheelAdjustmentGuard : public QObject -{ -public: - explicit QMouseWheelAdjustmentGuard(QObject *parent); - -protected: - bool eventFilter(QObject* o, QEvent* e) override; -}; - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/QRGBAColorPicker.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/QRGBAColorPicker.cpp deleted file mode 100644 index 8148021a158..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/QRGBAColorPicker.cpp +++ /dev/null @@ -1,168 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#include -#include "QRGBAColorPicker.h" - -namespace sofa::gui::qt::qrgbacolorpicker_h -{ - -QRGBAColorPicker::QRGBAColorPicker(QWidget* parent) : QWidget(parent) -{ - _r = new QLineEdit(this); - _r->setValidator(new QIntValidator(0,255,this)); - _g = new QLineEdit(this); - _g->setValidator(new QIntValidator(0,255,this)); - _b = new QLineEdit(this); - _b->setValidator(new QIntValidator(0,255,this)); - _a = new QLineEdit(this); - _a->setValidator(new QIntValidator(0,255,this)); - _colorButton = new QPushButton(this); - QHBoxLayout* layout = new QHBoxLayout(this); - layout->addWidget(_colorButton); - layout->addWidget(new QLabel("r",this)); - layout->addWidget(_r); - layout->addWidget(new QLabel("g",this)); - layout->addWidget(_g); - layout->addWidget(new QLabel("b",this)); - layout->addWidget(_b); - layout->addWidget(new QLabel("a",this)); - layout->addWidget(_a); - connect( _r , SIGNAL( textChanged(const QString & ) ) ,this , SIGNAL( hasChanged() ) ); - connect( _g , SIGNAL( textChanged(const QString & ) ) ,this , SIGNAL( hasChanged() ) ); - connect( _b , SIGNAL( textChanged(const QString & ) ) ,this , SIGNAL( hasChanged() ) ); - connect( _a , SIGNAL( textChanged(const QString & ) ) ,this , SIGNAL( hasChanged() ) ); - connect( _r , SIGNAL( returnPressed() ) ,this , SLOT( updateRGBAColor() ) ); - connect( _g , SIGNAL( returnPressed() ) ,this , SLOT( updateRGBAColor() ) ); - connect( _b , SIGNAL( returnPressed() ) ,this , SLOT( updateRGBAColor() ) ); - connect( _a , SIGNAL( returnPressed() ) ,this , SLOT( updateRGBAColor() ) ); - connect( _r , SIGNAL( editingFinished() ) ,this , SLOT( updateRGBAColor() ) ); - connect( _g , SIGNAL( editingFinished() ) ,this , SLOT( updateRGBAColor() ) ); - connect( _b , SIGNAL( editingFinished() ) ,this , SLOT( updateRGBAColor() ) ); - connect( _a , SIGNAL( editingFinished() ) ,this , SLOT( updateRGBAColor() ) ); - connect( _colorButton, SIGNAL( clicked() ), this, SLOT( raiseQColorDialog() ) ); -} - -type::RGBAColor QRGBAColorPicker::getColor() const -{ - typedef unsigned char uchar; - constexpr uchar max = std::numeric_limits::max(); - type::RGBAColor color; - float r = _r->text().toFloat(); - float g = _g->text().toFloat(); - float b = _b->text().toFloat(); - float a = _a->text().toFloat(); - r /= max; - g /= max; - b /= max; - a /= max; - - color[0] = r; - color[1] = g; - color[2] = b; - color[3] = a; - return color; -} - -void QRGBAColorPicker::updateRGBAColor() -{ - typedef unsigned char uchar; - - const uchar r = _r->text().toInt(); - const uchar g = _g->text().toInt(); - const uchar b = _b->text().toInt(); - const uchar a = _a->text().toInt(); - _rgba = qRgba(r,g,b,a); - redrawColorButton(); -} - -void QRGBAColorPicker::setColor(const type::RGBAColor& color) -{ - typedef unsigned char uchar; - constexpr uchar max = std::numeric_limits::max(); - const uchar r = uchar(max * color[0]); - const uchar g = uchar(max * color[1]); - const uchar b = uchar(max * color[2]); - const uchar a = uchar(max * color[3]); - QString str; - str.setNum(r); - _r->setText(str); - str.setNum(g); - _g->setText(str); - str.setNum(b); - _b->setText(str); - str.setNum(a); - _a->setText(str); - _rgba = qRgba(r, g, b, a); - - redrawColorButton(); -} - -void QRGBAColorPicker::setColor(const Vec4f& color) -{ - setColor(type::RGBAColor{color[0], color[1] , color[2] ,color[3]}); -} - -void QRGBAColorPicker::redrawColorButton() -{ - const int w=_colorButton->width(); - const int h=_colorButton->height(); - - QPixmap *pix=new QPixmap(25,20); - pix->fill(QColor(qRed(_rgba), - qGreen(_rgba), - qBlue(_rgba))); - _colorButton->setIcon(QIcon(*pix)); - - _colorButton->resize(w,h); -} - -void QRGBAColorPicker::raiseQColorDialog() -{ - typedef unsigned char uchar; - const uchar max = std::numeric_limits::max(); - int r,g,b,a; - - Vec4f color; -#if (QT_VERSION < QT_VERSION_CHECK(5, 12, 0)) - bool ok; - QColor qcolor = QColorDialog::getRgba(_rgba,&ok,this); - if( ok ) -#else - QColor qcolor = QColorDialog::getColor(_rgba, this); - if( qcolor.isValid() ) -#endif - { - const QRgb rgba=qcolor.rgb(); - r=qRed(rgba); - g=qGreen(rgba); - b=qBlue(rgba); - a=qAlpha(rgba); - color[0] = (float)r / max; - color[1] = (float)g / max; - color[2] = (float)b / max; - color[3] = (float)a / max; - setColor(color); - emit hasChanged(); - } -} - -} // namespace sofa::gui::qt::qrgbacolorpicker_h diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/QRGBAColorPicker.h b/Sofa/GUI/Qt/src/sofa/gui/qt/QRGBAColorPicker.h deleted file mode 100644 index 684bae13677..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/QRGBAColorPicker.h +++ /dev/null @@ -1,85 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include "DataWidget.h" -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/// Private namespace -namespace sofa::gui::qt::qrgbacolorpicker_h -{ - -using sofa::type::Vec4f ; - -/** - @class QRGBAColorPicker - Implement a widget to select a color either using a color wheel or by three values. -*/ -class QRGBAColorPicker : public QWidget -{ - Q_OBJECT - -signals: - void hasChanged(); - -public: - QRGBAColorPicker(QWidget* parent); - void setColor(const type::RGBAColor& color); - - void setColor( const Vec4f& color ); - type::RGBAColor getColor() const; - -protected: - QRgb _rgba; - QLineEdit* _r; - QLineEdit* _g; - QLineEdit* _b; - QLineEdit* _a; - QPushButton* _colorButton; - -protected slots: - void updateRGBAColor(); - void redrawColorButton(); - void raiseQColorDialog(); -}; - -} // namespace sofa::gui::qt::qrgbacolorpicker_h - -namespace sofa::gui::qt -{ - using sofa::gui::qt::qrgbacolorpicker_h::QRGBAColorPicker; -} // namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/QSofaListView.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/QSofaListView.cpp deleted file mode 100644 index d6cd100fa38..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/QSofaListView.cpp +++ /dev/null @@ -1,744 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#include "QSofaListView.h" -#include "QDisplayPropertyWidget.h" -#include "GraphListenerQListView.h" -#include "ModifyObject.h" -#include "GenGraphForm.h" -#include "RealGUI.h" -#include -#include -#include -#include -#include -#include -#include -#include // version macro -#include -#include - -#include -#include -#include -#include -#include - -using namespace sofa::simulation; -using namespace sofa::core::objectmodel; -using namespace sofa::gui::common; - -namespace sofa::gui::qt -{ - -QSofaListView::QSofaListView(const SofaListViewAttribute& attribute, - QWidget* parent, - const char* name, - Qt::WindowFlags f): - SofaSceneGraphWidget(parent), - graphListener_(nullptr), - AddObjectDialog_(nullptr), - attribute_(attribute), - propertyWidget(nullptr) -{ - this->setObjectName(name); - this->setWindowFlags(f); - //List of objects - //Read the object.txt that contains the information about the objects which can be added to the scenes within a given BoundingBox and scale range - std::string object ( "config/object.txt" ); - - if( sofa::helper::system::DataRepository.findFile ( object ) ) - { - list_object.clear(); - std::ifstream end(object.c_str()); - std::string s; - while( end >> s ) - { - list_object.push_back(s); - } - end.close(); - } - - this->setColumnCount(2); -#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) - header()->setResizeMode(0, QHeaderView::Interactive); - header()->setResizeMode(1, QHeaderView::Stretch); -#else - header()->setSectionResizeMode(0, QHeaderView::Interactive); - header()->setSectionResizeMode(1, QHeaderView::Stretch); -#endif // SOFA_QT5 - QStringList headerLabels; - headerLabels << "Name" << "Class"; - this->setHeaderLabels(headerLabels); - - setRootIsDecorated(true); - setIndentation(8); - - graphListener_ = new GraphListenerQListView(this); - - this->setContextMenuPolicy(Qt::CustomContextMenu); - connect(this, &QSofaListView::customContextMenuRequested ,this, &QSofaListView::RunSofaRightClicked); - connect(this, &QSofaListView::itemDoubleClicked, this, &QSofaListView::RunSofaDoubleClicked); - connect(this, &QSofaListView::itemClicked, this, [&](QTreeWidgetItem *item, int){ updateMatchingObjectmodel(item); }); -} - -QSofaListView::~QSofaListView() -{ - delete graphListener_; -} - - -void QSofaListView::Clear(Node* /*rootNode*/) -{ - /* - if(graphListener_ != nullptr) - { - delete graphListener_; - } - - CloseAllDialogs(); - clear(); - graphListener_ = new GraphListenerQListView(this); - - this->setSortingEnabled(false); - - rootNode->addListener(graphListener_); - update(); - - std::map::iterator graph_iterator; - for (graph_iterator = graphListener_->items.begin(); - graph_iterator != graphListener_->items.end(); - ++graph_iterator) - { - Node* node = dynamic_cast< Node* >(graph_iterator->first); - if (node!=nullptr && !node->isActive()) - { - object_.ptr.Node = node; - object_.type = typeNode; - emit RequestActivation(object_.ptr.Node, node->isActive()); - } - } - */ -} - -void QSofaListView::CloseAllDialogs() -{ - emit Close(); - assert( map_modifyObjectWindow.empty() ); - assert( map_modifyDialogOpened.empty() ); - -} - -void QSofaListView::modifyUnlock(void* Id) -{ - map_modifyDialogOpened.erase( Id ); - map_modifyObjectWindow.erase( Id ); -} - -/// Traverse the item tree and retrieve the item that are expanded. The path of the node -/// that are expanded are stored in the pathes std::vector::std::string>. -void QSofaListView::getExpandedNodes(QTreeWidgetItem* item, std::vector& pathes) -{ - if(!item) - return; - - /// We have reached a leaf of the hierarchy or it is closed...so we save the path - if( !item->isExpanded() && graphListener_->findObject(item)->toBaseNode() != nullptr ) - return; - - const BaseNode* parentNode = graphListener_->findObject(item)->toBaseNode() ; - if(parentNode == nullptr) - return; - - const std::string path = parentNode->getPathName(); - pathes.push_back(path); - - for(int i=0 ; ichildCount() ; i++) - { - QTreeWidgetItem* child = item->child(i); - const BaseNode* childNode = graphListener_->findObject(child)->toBaseNode() ; - - if(childNode==nullptr) - continue; - - if( childNode->getParents()[0] == parentNode ) - getExpandedNodes(child, pathes) ; - } - - return ; -} - -void QSofaListView::getExpandedNodes(std::vector& pathes) -{ - LockContextManager lock(this, true); - QTreeWidgetItem* rootitem = this->topLevelItem(0) ; - getExpandedNodes(rootitem,pathes) ; -} - -void QSofaListView::collapseNode() -{ - collapseNode(currentItem()); -} - -void QSofaListView::collapseNode(QTreeWidgetItem* item) -{ - if (!item) return; - - LockContextManager lock(this, true); - for(int i=0 ; ichildCount() ; i++) - { - QTreeWidgetItem* child = item->child(i); - child->setExpanded(false); - } - item->setExpanded ( true ); -} - -void QSofaListView::expandPath(const std::string& path) -{ - if(path.empty()) - return; - - if(path.data()[0] != '/') - return; - - Node* match = down_cast( graphListener_->findObject(this->topLevelItem(0))->toBaseNode() ); - - QStringList tokens = QString::fromStdString(path).split('/') ; - - for(int i=1;igetChild(tokens[i].toStdString()); - - if(match == nullptr) - return; - - if(graphListener_->items.find(match) != graphListener_->items.end()) - { - QTreeWidgetItem* item = graphListener_->items[match] ; - item->setExpanded ( true ); - } - } -} - -void QSofaListView::expandPathFrom(const std::vector& pathes) -{ - LockContextManager lock(this, true); - for(auto& path : pathes) - { - expandPath(path) ; - } -} - - -void QSofaListView::expandNode() -{ - expandNode(currentItem()); -} - -void QSofaListView::expandNode(QTreeWidgetItem* item) -{ - if (!item) - return; - - LockContextManager lock(this, true); - item->setExpanded ( true ); - - for(int i=0 ; ichildCount() ; i++) - { - QTreeWidgetItem* child = item->child(i); - child->setExpanded(true); - expandNode(child); - } - -} - -void QSofaListView::setRoot(Node* root) -{ - if(!root) - return; - - CloseAllDialogs(); - clear(); - - if(graphListener_) - delete graphListener_; - graphListener_ = new GraphListenerQListView(this); - - setSortingEnabled(false); - - const bool lockStatus = m_isLocked; - m_isLocked=false; - root->addListener(graphListener_); - graphListener_->onBeginAddChild(nullptr, root); - m_isLocked=lockStatus; - m_isDirty=false; - - emit dirtynessChanged(m_isDirty); -} - -void QSofaListView::update() -{ - if(!m_isDirty) - return; - - m_isDirty=false; - - if(!graphListener_ || !this->topLevelItem(0)) - { - emit dirtynessChanged(m_isDirty); - return; - } - emit dirtynessChanged(m_isDirty); -} - -void QSofaListView::updateMatchingObjectmodel(QTreeWidgetItem* item, int) -{ - updateMatchingObjectmodel(item); -} - -void QSofaListView::updateMatchingObjectmodel(QTreeWidgetItem* item) -{ - BaseData* data = nullptr; - Base* base = nullptr; - BaseObject* object = nullptr; - BaseNode* basenode = nullptr; - if(item == nullptr) - { - object_.ptr.Node = nullptr; - } - else - { - base = graphListener_->findObject(item); - if(base == nullptr) - { - data = graphListener_->findData(item); - assert(data); - object_.ptr.Data = data; - object_.type = typeData; - return; - } - basenode = base->toBaseNode(); - if( basenode == nullptr) - { - object = dynamic_cast(base); - object_.ptr.Object = object; - object_.type = typeObject; - } - else - { - object_.ptr.Node = down_cast(basenode); - object_.type = typeNode; - } - } - - addInPropertyWidget(item, true); -} - -void QSofaListView::addInPropertyWidget(QTreeWidgetItem *item, bool clear) -{ - if(!item) - return; - - Base* object = graphListener_->findObject(item); - if(object == nullptr) - return; - - if(propertyWidget) - { - propertyWidget->addComponent(object->getName().c_str(), object, item, clear); - - propertyWidget->show(); - } -} - -void QSofaListView::contextMenuEvent(QContextMenuEvent *event) -{ - event->accept(); -} - -void QSofaListView::focusObject() -{ - if( object_.isObject()) - emit( focusChanged(object_.ptr.Object)); - -} - -void QSofaListView::focusNode() -{ - if( object_.isNode()) - emit( focusChanged(object_.ptr.Node)); -} - -/*****************************************************************************************************************/ -void QSofaListView::RunSofaRightClicked( const QPoint& point) -{ - QTreeWidgetItem *item = this->itemAt( point ); - - if( item == nullptr) return; - - updateMatchingObjectmodel(item); - - QAction* act; - QMenu *contextMenu = new QMenu ( this ); - contextMenu->setObjectName( "ContextMenu"); - if( object_.isNode() ) - { - act = contextMenu->addAction("Focus", this,SLOT(focusNode())); - const bool enable = object_.ptr.Node->f_bbox.getValue().isValid() && !object_.ptr.Node->f_bbox.getValue().isFlat(); - act->setEnabled(enable); - } - if( object_.isObject() ) - { - act = contextMenu->addAction("Focus", this,SLOT(focusObject())); - const bool enable = object_.ptr.Object->f_bbox.getValue().isValid() && !object_.ptr.Object->f_bbox.getValue().isFlat() ; - act->setEnabled(enable); - } - - contextMenu->addSeparator(); - - //Creation of the context Menu - if ( object_.type == typeNode) - { - act = contextMenu->addAction("Collapse", this,SLOT(collapseNode())); - act = contextMenu->addAction("Expand", this,SLOT(expandNode())); - contextMenu->addSeparator(); - /*****************************************************************************************************************/ - if (object_.ptr.Node->isActive()) - { - act = contextMenu->addAction("Deactivate", this,SLOT(DeactivateNode())); - } - else - { - act = contextMenu->addAction("Activate", this,SLOT(ActivateNode())); - } - if (object_.ptr.Node->isSleeping()) - { - act = contextMenu->addAction("Wake up", this,SLOT(WakeUpNode())); - } - else - { - act = contextMenu->addAction("Put to sleep", this,SLOT(PutNodeToSleep())); - } - contextMenu->addSeparator(); - /*****************************************************************************************************************/ - act = contextMenu->addAction("Save Node", this,SLOT(SaveNode())); - act = contextMenu->addAction("Export OBJ", this,SLOT(exportOBJ())); - - if ( attribute_ == SIMULATION) - { - act = contextMenu->addAction("Remove Node", this,SLOT(RemoveNode())); - //If one of the elements or child of the current node is beeing modified, you cannot allow the user to erase the node - if ( !isNodeErasable ( object_.ptr.Node ) ) - { - act->setEnabled(false); - } - } - } - act = contextMenu->addAction("Modify", this,SLOT(Modify())); - - if( object_.isBase() ) - { - contextMenu->addSeparator(); - act = contextMenu->addAction("Go to Scene...", this, SLOT(openInstanciation())); - act->setEnabled(object_.asBase()->getInstanciationSourceFileName() != ""); - act = contextMenu->addAction("Go to Implementation...", this, SLOT(openImplementation())); - act->setEnabled(object_.asBase()->getDefinitionSourceFileName() != ""); - } - - contextMenu->addSeparator(); - act = contextMenu->addAction("Copy file path", this,SLOT(copyFilePathToClipBoard())); - act = contextMenu->addAction("Open file in editor", this,SLOT(openInEditor())); - - contextMenu->exec ( this->mapToGlobal(point) /*, index */); -} - -void QSofaListView::RunSofaDoubleClicked(QTreeWidgetItem* item, int /*index*/) -{ - if(item == nullptr) - { - return; - } - - item->setExpanded( !item->isExpanded()); - Modify(); -} - -/*****************************************************************************************************************/ -void QSofaListView::nodeNameModification(simulation::Node* node) -{ - QTreeWidgetItem *item=graphListener_->items[node]; - - const QString nameToUse(node->getName().c_str()); - item->setText(0,nameToUse); - - typedef std::multimap::iterator ItemIterator; - const std::pair range=graphListener_->nodeWithMultipleParents.equal_range(item); - - for (ItemIterator it=range.first; it!=range.second; ++it) it->second->setText(0,nameToUse); -} - - -void QSofaListView::DeactivateNode() -{ - emit RequestActivation(object_.ptr.Node,false); - currentItem()->setExpanded(false); - -} - -void QSofaListView::ActivateNode() -{ - emit RequestActivation(object_.ptr.Node,true); -} - -void QSofaListView::PutNodeToSleep() -{ - emit RequestSleeping(object_.ptr.Node, true); -} - -void QSofaListView::WakeUpNode() -{ - emit RequestSleeping(object_.ptr.Node, false); -} - -void QSofaListView::SaveNode() -{ - if( object_.ptr.Node != nullptr) - { - LockContextManager lock(this, true); - Node * node = object_.ptr.Node; - emit RequestSaving(node); - } -} -void QSofaListView::exportOBJ() -{ - if( object_.ptr.Node != nullptr) - { - LockContextManager lock(this, true); - Node * node = object_.ptr.Node; - emit RequestExportOBJ(node,true); - } -} - -void QSofaListView::RemoveNode() -{ - if( object_.type == typeNode) - { - LockContextManager lock(this, true); - const Node::SPtr node = object_.ptr.Node; - if ( node == node->getRoot() ) - { - if ( QMessageBox::warning ( this, "Removing root", "root node cannot be removed" ) ) - return; - } - else - { - node->detachFromGraph(); - node->execute(sofa::core::execparams::defaultInstance()); - emit NodeRemoved(); - } - } -} - -void QSofaListView::Modify() -{ - void *current_Id_modifyDialog = nullptr; - LockContextManager lock(this, true); - if ( currentItem() != nullptr ) - { - ModifyObjectFlags dialogFlags = ModifyObjectFlags(); - dialogFlags.setFlagsForSofa(); - ModifyObject* dialogModifyObject = nullptr; - - if (object_.type == typeData) //user clicked on a data - { - current_Id_modifyDialog = object_.ptr.Data; - } - if (object_.type == typeNode) - { - current_Id_modifyDialog = object_.ptr.Node; - } - if(object_.type == typeObject) - { - current_Id_modifyDialog = object_.ptr.Object; - } - assert(current_Id_modifyDialog != nullptr); - - //Opening of a dialog window automatically created - - const std::map< void*, QDialog* >::iterator testWindow = map_modifyObjectWindow.find( current_Id_modifyDialog); - if ( testWindow != map_modifyObjectWindow.end()) - { - //Object already being modified: no need to open a new window - (*testWindow).second->raise(); - return; - } - - dialogModifyObject = new ModifyObject(current_Id_modifyDialog,currentItem(),this,dialogFlags,currentItem()->text(0).toStdString().c_str()); - if(object_.type == typeData) - dialogModifyObject->createDialog(object_.ptr.Data); - if(object_.type == typeNode) - dialogModifyObject->createDialog(dynamic_cast(object_.ptr.Node)); - if(object_.type == typeObject) - dialogModifyObject->createDialog(dynamic_cast(object_.ptr.Object)); - - map_modifyDialogOpened.insert( std::make_pair ( current_Id_modifyDialog, currentItem()) ); - map_modifyObjectWindow.insert( std::make_pair(current_Id_modifyDialog, dialogModifyObject)); - connect ( dialogModifyObject, &ModifyObject::objectUpdated, this, &QSofaListView::Updated ); - connect ( this, &QSofaListView::Close, dialogModifyObject, &ModifyObject::closeNow ); - connect ( dialogModifyObject, &ModifyObject::dialogClosed, this, &QSofaListView::modifyUnlock ); - connect ( dialogModifyObject, &ModifyObject::nodeNameModification, this, &QSofaListView::nodeNameModification ); - connect ( dialogModifyObject, &ModifyObject::dataModified, this, &QSofaListView::dataModified ); - dialogModifyObject->show(); - dialogModifyObject->raise(); - } -} - -void QSofaListView::UpdateOpenedDialogs() -{ - std::map::const_iterator iter; - for(iter = map_modifyObjectWindow.begin(); iter != map_modifyObjectWindow.end() ; ++iter) - { - ModifyObject* modify = reinterpret_cast(iter->second); - modify->updateTables(); - } -} - -void QSofaListView::ExpandRootNodeOnly() -{ - this->expandToDepth(0); -} - -/// @brief Open a file at given path and line number using an external editor. -/// -/// The external editor is defined in a QSettings with the following entries: -/// [General] -/// ExternalEditor=qtcreator -/// ExternalEditorParams=-client ${filename}:${fileno} -/// where ${filename} is expanded with the full path to the file -/// where ${fileno} is expanded with the line number to open at. -void openInExternalEditor(const std::string filename, const int fileloc) -{ - const QFileInfo f(filename.c_str()); - - const std::string settingsFile = BaseGUI::getConfigDirectoryPath() + "/QSettings.ini"; - QSettings settings(settingsFile.c_str(), QSettings::IniFormat); - - /// In case the setting file does not contains the needed entries, let's put default ones - /// based on qtcreator. - if(!settings.contains("ExternalEditor")) - settings.setValue("ExternalEditor", "qtcreator"); - if(!settings.contains("ExternalEditorParams")) - settings.setValue("ExternalEditorParams", "-client ${filename}:${fileno}"); - - const QString editor = settings.value("ExternalEditor").toString(); - QString params = settings.value("ExternalEditorParams").toString(); - - params.replace("${filename}", f.absoluteFilePath()); - params.replace("${fileno}", QString::number(fileloc)); - const QStringList paramsAsList = params.split(QRegularExpression("(\\ )")); - if ( QProcess::execute(editor, paramsAsList) != 0 ) - { - msg_warning("QSofaListView") << "Unable to execute \"" << editor.toStdString() << " " - << params.toStdString() << "\"" << msgendl - << " The file will NOT be opened at the right line." << msgendl - << " Set your preferred editor in: " << settingsFile << msgendl - << " Falling back to your system default editor."; - - QDesktopServices::openUrl(QUrl::fromLocalFile( f.absoluteFilePath() )); - } -} - -void QSofaListView::openInstanciation() -{ - if(object_.isBase()) - { - openInExternalEditor(object_.asBase()->getInstanciationSourceFileName(), - object_.asBase()->getInstanciationSourceFilePos()); - } -} - -void QSofaListView::openImplementation() -{ - if(object_.isBase()) - { - openInExternalEditor(object_.asBase()->getDefinitionSourceFileName(), - object_.asBase()->getDefinitionSourceFilePos()); - } -} - - -void QSofaListView::openInEditor() -{ - const QFileInfo finfo(QApplication::activeWindow()->windowFilePath()); - QDesktopServices::openUrl(QUrl::fromLocalFile(finfo.absoluteFilePath())); -} - -void QSofaListView::copyFilePathToClipBoard() -{ - const QFileInfo finfo(QApplication::activeWindow()->windowFilePath()); - QApplication::clipboard()->setText(finfo.absoluteFilePath()) ; -} - -/*****************************************************************************************************************/ -// Test if a node can be erased in the graph : the condition is that none of its children has a menu modify opened -bool QSofaListView::isNodeErasable ( BaseNode* node) -{ - const QTreeWidgetItem* item = graphListener_->items[node]; - if(item == nullptr) - { - return false; - } - // check if there is already a dialog opened for that item in the graph - std::map< void*, QTreeWidgetItem*>::iterator it; - for (it = map_modifyDialogOpened.begin(); it != map_modifyDialogOpened.end(); ++it) - { - if (it->second == item) return false; - } - - //check the item childs - for(int i=0 ; ichildCount() ; i++) - { - const QTreeWidgetItem *child = item->child(i); - for( it = map_modifyDialogOpened.begin(); it != map_modifyDialogOpened.end(); ++it) - { - if( it->second == child) return false; - } - } - - return true; - -} - -void QSofaListView::Export() -{ - Node* root = down_cast( graphListener_->findObject(this->topLevelItem(0))->toBaseNode() ); - GenGraphForm* form = new sofa::gui::qt::GenGraphForm(this); - form->setScene ( root ); - std::string gname(((RealGUI*) (QApplication::topLevelWidgets()[0]))->windowFilePath().toStdString()); - const std::size_t gpath = gname.find_last_of("/\\"); - const std::size_t gext = gname.rfind('.'); - if (gext != std::string::npos && (gpath == std::string::npos || gext > gpath)) - gname = gname.substr(0,gext); - form->filename->setText(gname.c_str()); - form->show(); -} - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/QSofaListView.h b/Sofa/GUI/Qt/src/sofa/gui/qt/QSofaListView.h deleted file mode 100644 index bb1b5f1e66d..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/QSofaListView.h +++ /dev/null @@ -1,174 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once -#include - -#include -#include -#include -#include -#include - - -#include -#include -#include -#include -#include - -#include - -namespace sofa::gui::qt -{ - -class SOFA_GUI_QT_API QSofaListView : public SofaSceneGraphWidget -{ - Q_OBJECT -public: - class LockContextManager - { - public: - QSofaListView* self{nullptr}; - bool state{true}; - - LockContextManager(QSofaListView* view, bool isLocked) - { - self = view; - state = view->isLocked(); - if(isLocked) - view->lock(); - else - view->unLock(); - } - - ~LockContextManager() - { - if(state) - self->lock(); - else - self->unLock(); - } - }; - - QSofaListView(const SofaListViewAttribute& attribute, - QWidget* parent = nullptr, - const char* name = nullptr, - Qt::WindowFlags f = Qt::WindowType::Widget ); - ~QSofaListView() override; - - GraphListenerQListView* getListener() const { return graphListener_; } - - void setPropertyWidget(QDisplayPropertyWidget* propertyWid) {propertyWidget = propertyWid;} - void addInPropertyWidget(QTreeWidgetItem *item, bool clear); - - void Clear(sofa::simulation::Node* rootNode); - - /// Updates the view so it is synchronized with the simulation graph. - /// The view can be visually de-synchronized with the simulation graph. This happens - /// when the view is "frozen" for performance reason. In that case, use isDirty to - /// get current view state or the dirtynessChanged() signal. - /// To resynchronize the view call the update method. - void update(); - void setRoot(sofa::simulation::Node*); - - SofaListViewAttribute getAttribute() const { return attribute_; } - - void contextMenuEvent(QContextMenuEvent *event) override; - - void expandPathFrom(const std::vector& pathes); - void getExpandedNodes(std::vector&); - - void loadObject ( std::string path, double dx, double dy, double dz, double rx, double ry, double rz,double scale ) = delete; - -public Q_SLOTS: - void Export(); - void CloseAllDialogs(); - void UpdateOpenedDialogs(); - void ExpandRootNodeOnly(); - -Q_SIGNALS: - void Close(); - void Lock(bool); - void RequestSaving(sofa::simulation::Node*); - void RequestExportOBJ(sofa::simulation::Node* node, bool exportMTL); - void RequestActivation(sofa::simulation::Node*,bool); - void RequestSleeping(sofa::simulation::Node*, bool); - void RootNodeChanged(sofa::simulation::Node* newroot, const char* newpath); - void NodeRemoved(); - void Updated(); - void NodeAdded(); - void focusChanged(sofa::core::objectmodel::BaseObject*); - void focusChanged(sofa::core::objectmodel::BaseNode*); - void dataModified( QString ); - - -protected Q_SLOTS: - void SaveNode(); - void exportOBJ(); - void collapseNode(); - void expandNode(); - void modifyUnlock(void* Id); - void RemoveNode(); - void Modify(); - void openInEditor(); - void openInstanciation(); - void openImplementation(); - void copyFilePathToClipBoard(); - void DeactivateNode(); - void ActivateNode(); - void PutNodeToSleep(); - void WakeUpNode(); - - void updateMatchingObjectmodel(QTreeWidgetItem* item, int); - void updateMatchingObjectmodel(QTreeWidgetItem* item); - - void RunSofaRightClicked(const QPoint& point); - void RunSofaDoubleClicked( QTreeWidgetItem* item, int index); - - void nodeNameModification( simulation::Node*); - void focusObject(); - void focusNode(); - -protected: - void expandPath(const std::string& path) ; - void getExpandedNodes(QTreeWidgetItem* item, std::vector&) ; - void collapseNode(QTreeWidgetItem* item); - void expandNode(QTreeWidgetItem* item); - - void transformObject ( sofa::simulation::Node *node, double dx, double dy, double dz, double rx, double ry, double rz, double scale ) = delete; - - bool isNodeErasable( core::objectmodel::BaseNode* node); - - std::list collectNodesToChange(core::objectmodel::BaseNode* node); - std::map< void*, QTreeWidgetItem* > map_modifyDialogOpened; - std::map< void*, QDialog* > map_modifyObjectWindow; - GraphListenerQListView* graphListener_; - std::vector< std::string > list_object; - AddObject* AddObjectDialog_; - ObjectModel object_; - SofaListViewAttribute attribute_; - QDisplayPropertyWidget* propertyWidget; - - -}; - -} //namespace sofa::gui::qt diff --git a/Sofa/GUI/Qt/src/sofa/gui/qt/QSofaStatWidget.cpp b/Sofa/GUI/Qt/src/sofa/gui/qt/QSofaStatWidget.cpp deleted file mode 100644 index e88631b6768..00000000000 --- a/Sofa/GUI/Qt/src/sofa/gui/qt/QSofaStatWidget.cpp +++ /dev/null @@ -1,147 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU General Public License as published by the Free * -* Software Foundation; either version 2 of the License, or (at your option) * -* any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * -* more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#include "QSofaStatWidget.h" -#include "GraphListenerQListView.h" -#include -#include -#include -#include - - -#include -#include -#include - -using namespace sofa::simulation; -using namespace sofa::core::objectmodel; - -namespace sofa::gui::qt -{ - -QSofaStatWidget::QSofaStatWidget(QWidget* parent) - : QWidget(parent) -{ - QVBoxLayout* layout = new QVBoxLayout(this); - statsLabel = new QLabel(this); - statsLabel->setText(QString("Collision Elements present :")); - statsLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); - statsLabel->setWordWrap(false); - - layout->addWidget(statsLabel); - - statsCounter = new QTreeWidget(this); - statsCounter->setHeaderLabels(QStringList() << "Name" << "Type" << "Size" << "Groups"); - - layout->addWidget(statsCounter); - -} - -void QSofaStatWidget::CreateStats(Node* root) -{ - sofa::type::vector< sofa::core::CollisionModel* > list_collisionModels; - root->get< sofa::core::CollisionModel >( &list_collisionModels, BaseContext::SearchDown); - - if (items_stats.size() != 0) - { - delete items_stats[0].second; - items_stats.clear(); - } - - for(int i=0 ; itopLevelItemCount() ; i++) - delete statsCounter->takeTopLevelItem(i); - - - addCollisionModelsStat(list_collisionModels); - - addSummary(); - - -} - -void QSofaStatWidget::addCollisionModelsStat(const sofa::type::vector< sofa::core::CollisionModel* >& v) -{ - std::map< BaseContext*, QTreeWidgetItem* > listStats; - for (unsigned int i=0; iisActive()) continue; - std::map< BaseContext*, QTreeWidgetItem* >::iterator it = listStats.find(v[i]->getContext()); - QTreeWidgetItem *item; - if (it != listStats.end()) - { - item = new QTreeWidgetItem((*it).second); - } - else - { - QTreeWidgetItem *node = new QTreeWidgetItem(statsCounter); - node->setText(0,QString(v[i]->getContext()->getName().c_str())); - const QPixmap* pix = getPixmap(v[i]->getContext(), false,false,false); - if (pix) node->setIcon(0, QIcon(*pix)); - listStats.insert(std::make_pair(v[i]->getContext(), node)); - item = new QTreeWidgetItem(node); - node->setExpanded(true); - } - assert(item); - item->setText(0,v[i]->getName().c_str()); - item->setText(1,QString(v[i]->getClassName().c_str())); - item->setText(0,v[i]->getName().c_str()); - item->setText(2,QString::number(v[i]->getSize())); - { - const std::set& groups = v[i]->getGroups(); - QString groupString; - std::set::const_iterator it = groups.begin(), itend = groups.end(); - for( ; it != itend ; ++it ) groupString += QString::number(*it) + ", "; - item->setText(3,groupString); - } - items_stats.push_back(std::make_pair(v[i], item)); - } -} - -void QSofaStatWidget::addSummary() -{ - std::set< std::string > nameElement; - std::map< std::string, int > mapElement; - for (unsigned int i=0; i < items_stats.size(); i++) - nameElement.insert(items_stats[i].first->getClassName()); - - - for (unsigned int i=0; i < items_stats.size(); i++) - mapElement[items_stats[i].first->getClassName()] += (items_stats[i].second->text(2).toInt()); - - - std::string textStats("